ce98c7b82c8f8588b4c8950da068cf9e432f8df6
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0d492f7c1d |
Text measuring for hosts, colour emoji, caller-supplied faces, aligned rich text and a wheel hook in detents
A host that lays text out with its own measurement (rustdroid sizes Android views from it before ltk draws them) needs ltk to measure exactly as it renders. Several gaps made the two disagree; this closes them and adds what such a host needs from the pointer and the paragraph widgets. Shaping. `shape_line` shaped the first bidi paragraph only, so any text after a line break measured zero width; it now shapes every paragraph. Shaped glyphs keep the cluster rustybuzz reports (`ShapedGlyph::cluster`, and `PositionedGlyph::cluster` as a byte offset into the line), which `caret_offsets` uses to give one x per char boundary, splitting a cluster's advance evenly across its chars. `caret_offsets`, `caret_offsets_with_font` (the same with a given face leading the resolver) and `line_metrics` are exported. The per-codepoint resolver shared by `measure_text` and the new functions is `default_resolver`: primary font, then the system fallback chain, then the primary again for `.notdef`, the same choice the renderers make. Caller-supplied faces. `draw_text_with_font` and `measure_text_with_font` built a `FontHandle` with empty bytes for a face that was not the canvas default, so HarfBuzz could not shape it and the text silently fell back to the system chain. `FontFace` now exposes the file it was parsed from (`data`, `face_index`) and `FontHandle::of` builds a complete handle from a face; both renderers use it. The GLES renderer also registers the preferred face in its glyph font list so its glyphs are rasterised. Colour emoji. `FontFace::color_glyph` decodes, once per glyph, the PNG strike a CBDT or sbix font draws a glyph with, as a `ColorGlyph` (straight RGBA plus its box in ems). Both renderers draw such glyphs as images at the pen position, scaled by the text size and faded by the text colour's alpha, instead of rasterising an empty outline. NotoColorEmoji joins the fallback chain after DejaVu Sans. `ColorGlyph` is exported. Rich text alignment. `RichText` gains `align( TextAlign )`, applied per wrapped line; link hit rects follow the aligned line, so a centred paragraph with links stays tappable where it is drawn. Wrap pad. A wrapping `Text` wraps its lines to the rect less 4 px and insets a left-aligned line by it. The constant is now public as `ltk::TEXT_WRAP_PAD`, documented as the extra width a host must add when it sizes a label's rect from its own measurement; without it a label sized to its text wrapped its last word. Wheel. `App::on_pointer_wheel( x, y, notches_x, notches_y ) -> bool` is called before `on_pointer_axis` for a scroll outside any ltk viewport that carries discrete detents (`value120`, or `discrete` on older compositors), with the number of detents per axis, fractional on high-resolution wheels. Returning `true` consumes the event; the default `false` keeps the previous behaviour, where `on_pointer_axis` receives the raw axis value scaled by 10 for wheels. A host that maps detents to its own unit (Android's AXIS_VSCROLL is one unit per detent) no longer has to guess the compositor's axis scale. debian/changelog gains the 0.3.0-4 entry describing the shaping, caret and caller-supplied face changes. |
||
|
|
a1a4d51677 |
An on-screen keyboard of ltk's own, driven by layout files; TextEdit::purpose for the row a terminal needs; the wheel scrolls a multiline field; a notebook page fills its height
Text entry on a phone comes from `zwp_text_input_v3`: a focused field enables the protocol and the compositor's input method, squeekboard today, draws a keyboard and commits text through it. Eydos wants to drop that process, so the keyboard has to come from somewhere else, and the natural place is the toolkit that already draws everything else and knows which field has the focus and what it is for. This commit puts one in ltk, as data files plus a panel any host can drive: ltk's runtime shows it under a focused text field when no input method is there, and forge will draw the same panel in its chrome and deliver the keys to the focused client, which is the second half and is not in this commit. Along the way the demo that exercises it exposed two gaps in the widgets, fixed here too.
Layouts are data. `keyboards/<language>.json`, one file per layout, searched in `LTK_KEYBOARDS_DIR` when set, then `$XDG_DATA_HOME/ltk/keyboards` or `~/.local/share/ltk/keyboards`, then `/usr/share/ltk/keyboards`; `osk::layout::search_paths()`, `available()` and `load( name )` are the API, and the English layout is compiled in as the last resort. A file has `name`, a `label` for the layout-switch key, `layers` and `variants`. A layer is a list of rows; a row a list of keys; a key is a bare string for the text it types, or an object with an `action` (`shift`, `backspace`, `enter`, `space`, `layer`, `layout`, `hide`), the target `layer`, a `label`, and a `width` in key units, `1` by default, which share the row's width so a row of nine keys comes out centred under one of ten. `base` is mandatory; a missing `shift` layer is derived from it by uppercasing. `variants` maps a key's text to what a long press offers, and the shifted form of a key uses its base's, uppercased. A key may also be a named key, `{ "key": "Escape" }`, `Tab`, the arrows, `Home`, `End`, `Page_Up`, `Page_Down`, `Delete`, `Insert`, `F1` to `F12`, or a sticky modifier, `{ "modifier": "ctrl" }` or `alt`. `en.json` and `es.json` ship, with symbol layers and the accented variants each language needs. The files go into the registry copy through `make install` and into `ltk-theme-default` under `/usr/share/ltk/keyboards`.
The terminal row. A terminal needs Escape, Tab, Ctrl, Alt, the arrows and the shell's symbols, and no other application does, so those are not a layout but a row laid above any language's layout: `keyboards/bar-terminal.json`, made of the named keys and modifiers above plus `-`, `/`, `|`, `\` and `~` with their variants. Files named `bar-*.json` are rows, not layouts, and `available()` leaves them out, so the layout-switch key does not cycle onto one; the first version listed it and a tap on that key made the keyboard vanish behind a duplicated bar. Which applications get the row is decided by what the field says it is for: `TextEdit::purpose( InputPurpose::Terminal )`, snapshotted into the handlers like `secure`, which the built-in panel reads through `set_purpose` and which also goes out as the `terminal` content purpose of `zwp_text_input_v3`, the same one VTE-based terminals declare, so a compositor hosting the panel can pick the row from the protocol; for a client that declares nothing it can decide by `app_id`.
`osk::Panel` is a state machine a host drives. It holds the loaded layouts, the current layer, shift as off, one-shot or locked, the sticky modifiers, the purpose and its extra row, the geometry of all of it inside the rectangle the host gave it with `set_rect( rect, scale )`, and the press in flight. The host feeds it `press_down`, `press_move`, `press_up` and, from its own timer, `tick`, each returning an `Event`: `Text`, `Backspace`, `Enter`, `Hide`, `Key( keysym, modifiers )` for what text cannot carry, an arrow, Escape, Tab, or a character while Ctrl or Alt is held, or `Changed` when only the panel's look changed. A key types on release; a press held 450 ms opens its variants, which the finger slides onto and the release picks; backspace fires on press and repeats every 60 ms after the same delay; shift is one-shot and locks on a double tap within 350 ms; a modifier stays down for the next key and a character typed under it goes out as a `Key`, since text cannot carry Ctrl. `next_deadline()` says when `tick` next has work, `height( scale )` what the panel needs, `press_down` answers `None` for a press outside it so the host treats that as its own. `draw( canvas )` paints it with the active palette; the action keys show theme icons rather than glyphs, `keyboard/shift`, `keyboard/backspace`, `keyboard/enter`, `general/down-simple` for hide, `accessibility/languages` for the layout key, the `general` arrows for the terminal row, rasterised once per size and tint, because the first version drew `⇧` and `🌐` and the fallback chain has no glyph for the latter, which showed as a question mark; a layout key with no label of its own shows the active layout's. Nothing in `Panel` knows about text fields or Wayland, which is what lets a compositor host it. Fifteen tests cover the layout parser, the derived shift layer, variants following case, the bar filtering, and the panel's taps, one-shot and locked shift, variants, repeat, layer and layout switches and the terminal row.
The runtime shows it. `event_loop/osk.rs` puts a `Panel` on a surface when a text field takes the focus and `builtin_osk` holds: the compositor advertised no `zwp_text_input_manager_v3`, or `LTK_BUILTIN_OSK=1` is set, which is how to try it on a desktop that has an input method, or the app returns `true` from the new `App::builtin_keyboard()`. The panel is placed at the bottom of the surface by both renderers, and the widget tree is laid out in what is left above it, so the focused field is never covered. Presses, motion and releases from the pointer and from touch are offered to the panel before the gesture machine, a press on the already focused field brings a hidden panel back, and the run loop's timeout now takes the minimum of the long-press, tooltip and panel wakeups so variants and repeat fire on time. `Text` goes through the same insertion as a physical key, `Key` through the same dispatch, with Ctrl applied for its duration, `Enter` submits or inserts a newline in a multiline field, `Hide` keeps the focus and takes the panel away; losing the focus removes it. The layout is picked from `$LANG`. `ltk::InputPurpose` is exported, and `docs/architecture.md` gains the section that says all of this, with the compositor's part sketched.
The demo. `examples/inputs` becomes a notebook with the login page it had and a Shell page: one multiline field that holds a transcript, prompt included, declared a terminal, so the row appears; Enter runs the line under the last prompt (`echo`, `ls`, `pwd`, `clear`), the arrows move through the transcript, and a Ctrl combination interrupts the line the way a terminal shows it. Escape quits only from the login page, because a key the app answers never reaches the field, which is what first made the arrows dead.
Two widget gaps the demo found. `Notebook` gave its page the page's preferred height, so a page that fills its space, a scrolled list or a flex, got none and showed nothing: the active page is now wrapped in `flex` and gets the height under the strip. And the multiline field had no way to scroll: it chose its first visible line from the cursor alone on every draw, so the transcript ran under the keyboard and the wheel did nothing. `text_edit/scroll.rs` gives it a `TextScroll` the runtime keeps per field beside the cursor and the selection, in the frame state, the draw context and the `core` runtime alike: the first line asked for by hand and the cursor at that moment. The view holds there while the cursor stays put and, once it moves, follows it by the least it has to, as an editor does; without a request the result is exactly the old rule. `Window::resolve` computes the visible lines once for drawing, hit testing and the wheel, so they cannot disagree, and replaces the copy each of the first two had. In `on_pointer_axis` the vertical motion is offered to the multiline field under the pointer first, three lines per notch from `value120` or `discrete`, touchpad pixels accumulated until they add up to a line, and only when the field cannot move that way does it go to a `scroll` around it. `TextScroll` is public but opaque, since `Element::draw` is public and needs it; `Default` is the cursor-following behaviour. Six tests cover the hold, the follow, the clamping, the notch and the accumulation. A read-only field is not interactive and does not take the wheel; touch drags inside a field do not scroll it yet.
Not done. Forge does not host the panel yet, so on the phone squeekboard is still what appears. The panel's look is a first pass, sized in logical units with the theme's palette, to be restyled from mockups. Only `en` and `es` ship.
|
||
|
|
58b8dd7b90 |
Focus requests reach widgets clipped by a scroll viewport, and stop retrying after a few frames
A scroll viewport keeps in `widget_rects` only the widgets at least partly inside its visible area (`draw/layout.rs`, the translation of the sub-canvas rects to surface space). A focus request whose target sits below the fold therefore never finds it: `App::take_focus_request` was honoured only for widgets already laid out on screen. The Wi-Fi page of settings is one such case by construction: tapping a secured network inserts the password form under the row and asks for focus on the field, so for a row in the lower part of the list the field is born outside the viewport and the request misses. Worse, the miss branch parked the id in `focus_retry`, marked the view dirty and requested a redraw on every iteration, so the application rebuilt and repainted at the compositor's frame rate for as long as the widget stayed hidden. The layout pass now records the widgets it drops for carrying a `WidgetId`: `DrawCtx::scroll_clipped` holds `(id, scroll flat_idx, rect)` with the rect in the scroll's content coordinates, the same frame the navigable-item list uses, and `draw_frame` hands it to `FrameState`. When a focus request misses, `AppData::reveal_clipped_widget` looks the id up in that list on the main surface and the overlays, finds the viewport in `scroll_rects`, and moves its offset on the axes the scroll allows so the rect lands inside; the next frame lays the widget out and the retry focuses it. Nested scrolls resolve one viewport per frame. The rule that computes the new offset is `scroll::offset_into_view`, extracted from the keyboard navigation in `input/keyboard/nav.rs`, which now calls it instead of carrying its own copy; unit tests cover the three cases. `focus_retry` carries a budget of drawn frames (`FOCUS_RETRY_FRAMES`, four), decremented only on iterations that actually drew, so a request made while a frame callback is pending does not lose its chance. A fresh request from the application replaces a pending retry with a full budget. When the budget runs out the request is dropped, which ends the redraw loop for a widget that never appears. Documented in the `take_focus_request` doc comment, the cookbook and the changelog; Debian revision 0.3.0-3. |
||
|
|
492553829e |
widgets, input: PatternGrid for pattern unlock, framed icon buttons, and text keys that bubble past a focused non-text widget; Debian 0.3.0-2
PatternGrid is a 3×3 drag widget for the unlock pattern. It is stateless: the app owns the visited nodes and passes them back in for drawing; a press or a drag that reaches a node within the snap radius fires on_node with the index, 0 to 8 row-major, and release fires on_release, so the crossing and minimum-length rules stay in the app. Geometry lives in widget/pattern_grid/theme.rs (size, dot and node radii, stroke width, snap radius, ring alpha), and the snap radius is resolved through the widget-scaling mode at layout time like the slider thumb (set_pattern_snap_px). The gesture machine reuses the slider's drag ownership: WidgetHandlers::is_drag_target covers both, so hover painting, pointer capture and release behave the same, and hover_affects_paint no longer special-cases sliders. The a11y tree maps it to GenericContainer, since a drawn gesture has no assistive equivalent and the app must offer another method. Exported as pattern_grid / PatternGrid, documented in widgets.md, theming.md and architecture.md, with tests for node centres, a wide rect, snapping, the builders, the handler snapshot and map_msg, and a Security tab in examples/widgets.rs that shows the drawn path. Icon buttons given a width or height are framed keys: preferred_size honours the explicit size instead of the icon size, and drawing paints the variant's background, border and focus ring, shared with text buttons through paint_frame, then centres the icon. An icon button without a size keeps the circular hover overlay it had. This is what lets the PIN keypad's backspace and OK keys sit in the same circles as the digits. Keyboard routing now matches what App::on_key documents. handle_key_default, Backspace, Delete and Space checked focused_idx.is_some() and sent the key into text insertion whenever any widget had focus, which is a no-op on a button, so a focused button or pressable swallowed typed characters. focused_text_input replaces the five copies of that check; characters, Backspace and Delete stay in a focused text input and reach the app otherwise, Return and Space still go to the focused widget's submit or press message when it has one, and the arrow, Home and End handlers use the same helper. The lockscreen relies on this to take PIN digits with its dots row focused. debian/changelog: 0.3.0-2. |
||
|
|
ccf07de593 |
Session management: xdg-session-management-v1 client, mandatory App::app_id / save_state / restore_state, runtime-managed state persistence and clean exit on signals (0.3.0)
Applications built on ltk had no way to come back where the user left them: the toolkit hardcoded `app_id = "ltk"` on every toplevel, never wrote anything to disk, and died on SIGTERM without a chance to save. This release gives the runtime the whole plumbing and asks each application only for the bytes worth keeping, in the spirit of Android's saved-instance state. The `App` trait gains three mandatory methods, deliberately without default bodies so every application states its position: `app_id()` (reverse-DNS, used for `xdg_toplevel.set_app_id`, the AccessKit application name and the state directory — the `app_id` element of the deprecated `window_config` tuple is now ignored and a one-time warning reports a mismatch), `save_state() -> Option<Vec<u8>>` and `restore_state(Vec<u8>)`. The bytes are opaque; the trait carries no serde bound. Their rustdoc is the contract: when the runtime saves, where the files live, when the bytes come back and when they do not, what must never go in them, and a worked serde_json example. The runtime persists under `$XDG_STATE_HOME/<app_id>/` (falling back to `~/.local/state`): `session.json` holds the compositor session id, a clean-exit marker and the writer's pid; `state.bin` holds the application bytes. Writes are atomic (temp file + rename, mode 0600, directory 0700) and best-effort. State is saved every 30 s when the bytes changed, once after the event loop exits (which covers `on_close_requested`, `requested_exit` and lost connections), and on SIGTERM/SIGINT — a calloop signal source, installed before any thread exists, now turns those into a clean exit of the loop instead of process death. `restore_state` runs synchronously in `try_run` before the window is created and before the first `view()`, and only when the process is relaunched as part of a session restore (`LTK_SESSION_RESTORE=1`, removed from the environment before the app can spawn children) or when the previous run left `clean_exit: false`; a plain launch starts fresh. A second concurrent instance detects the live pid and runs with persistence disabled rather than clobbering the first. The compositor side of geometry restore goes through `xdg-session-management-v1`. Neither wayland-protocols nor sctk ship generated code for it yet, so the XML is vendored under `protocols/` and `wayland-scanner` generates the client module in-tree (`src/protocol/`), resolving the crate names through sctk's reexports so the bindings stay on the crate instances sctk links. Before the first commit of a `ShellMode::Window` toplevel the runtime binds `xdg_session_manager_v1`, calls `get_session(reason, stored_id)` and `restore_toplevel(toplevel, "main")`; the three window-creation paths in `run.rs` are folded into one `make_window` helper so the attach always sits immediately before `commit()`. `created` persists the id, `replaced` destroys the objects and stops persisting. Compositors without the global lose only the geometry half. Layer-shell and session-lock surfaces skip the whole machinery. Every `App` implementor in the tree is updated: the twelve examples (`showcase`, `scroll` and `mini_shell` persist real state; the rest return `None`), both integration tests (`event_loop_flow` gains `save_restore_round_trip`), the in-source and markdown doctests, README, onboarding, cookbook (new recipe "Surviving relaunch: session state") and architecture docs, and the changelog. `src/session_state.rs` carries unit tests over a temporary state directory. `Makefile install` now copies `protocols/` into the cargo registry — without it downstream builds would fail inside the proc-macro — and `debian/copyright` covers the vendored XML. The trait change is breaking, hence 0.3.0. Also fixes the pre-existing `viewport_tests` module in `render/mod.rs`, which used `Length` without importing it and broke `cargo test`. |
||
|
|
d4d7ee742e |
Bump to 0.2.0: SW/GLES paint parity, shared font resolution, FrameState refactor, docs and packaging fixes
Rendering parity (software ↔ GLES). The software backend now rounds glyph pen positions and image destinations to the nearest integer pixel, matching what the GLES backend already did; previously it truncated, so text and 1:1 images could land up to half a pixel off between the two backends and the bilinear sample read ~1 px softer than the source. Gradients and shadows are deliberately left unimplemented on the software backend, and the GLES multi-rect `glScissor` clip is left coarse on purpose: making it exact would need stencil bits the EGL config does not carry, or routing the partial-redraw path through the offscreen clip layer, which would break its `fill` / `clear_rects_transparent` scissor semantics. Adds software-backend pixel tests covering the snapping. Font resolution unification. The system-font candidate chain, `find_font_opt` and `load_default_font_bytes` lived in two copies (`render/helpers` and `gles_render/helpers`) that had already diverged — one resolved through `find_font_opt`, the other inlined the candidate loop — and now live once in `system_fonts`. The two per-backend `OnceLock` default-font caches and `primary_handle` collapse into a single `system_fonts::default_handle`. Module docs and the `font_registry` caller are updated accordingly. Shared image validation and rect inflation. `draw_image_data`'s dimension check and its one-line warning were byte-duplicated across both backends and are now `render::helpers::validate_rgba_dims`. The six manual symmetric `Rect`-inflate literals in the GLES primitives reuse the existing `Rect::expand`. FrameState and DrawCtx de-duplication. The eleven `SurfaceState` fields the draw pass owns and threads through `DrawCtx` — `widget_rects`, the cursor / selection maps, the scroll state, `accessible_extras`, `prev_focused` / `prev_hovered` / `prev_pressed` — move into a `FrameState` sub-struct. The per-frame `build_draw_ctx` / `commit_draw_ctx` helpers can then borrow `&mut ss.frame`, disjoint from `ss.canvas` and `ss.pool`, so the four frame paths (software / GLES × full / partial) replace their duplicated `DrawCtx` construction and write-back with a single helper call each. A whole-`SurfaceState` borrow could not express this (partial borrows do not cross function boundaries), which is why the helpers take the sub-struct. `content_dirty` stays on `SurfaceState` — it is an invalidation flag, not frame state — and is reset at the call site. rich_text tests. Adds the previously-missing `tests.rs` for the `RichText` widget: one hit rect per visual line a link spans (the widget's core invariant), the single-line and no-link cases, preferred-size growth with hard line breaks, and `map_msg` range preservation — all headless against a software `Canvas`, with line counts forced by `\n` so they do not depend on any system font's measured width. Documentation. Fills the rustdoc gaps on the embedder-facing surface: `core::UiSurface` accessors, the `egl_context` public API, the `GlesCanvas` methods, `theme::typography` and `theme::error`, and the `RichText` / `Text` builders; adds a crate-level "Rendering backends" overview. `CHANGELOG.md` is added (0.2.0 / 0.1.0), and `docs/widgets.md` / `docs/cookbook.md` gain `rich_text`, `external`, and the CPU-draw / path-clip / externally-laid-out-tree recipes. `debian/changelog` gets the 0.2.0-1 entry. Private intra-doc links to `system_fonts` are demoted to code spans so `cargo doc` is warning-free. Packaging. The `libltk-dev` registry crate shipped a `Cargo.toml` declaring the `lookup` bench while the `Makefile` install copied only `src/`, so Cargo refused to parse the manifest over a missing `benches/lookup.rs`; the install now ships `benches/` as well (the file alone satisfies the parse — criterion is a dev-dependency and is not resolved when the crate is consumed as a library). `Cargo.toml` is bumped to 0.2.0 to match the package version and the `ltk-0.2.0` registry directory. |
||
|
|
b8a32cfb96 | Added pkg-config debian dependency | ||
|
|
9ca3b60f3a |
ltk: responsive padding/spacing and scrolling, expanded theme palette, and bundled Adwaita cursors
A mixed pass over the default theme and the layout/input core, plus the toolkit's own cursor set. Grouped by area below. == Responsive sizing == Add `Length::dp( px )` — a "design pixel". It interprets `px` against a configurable reference vmin (default 412 px, the eydos mobile reference width) and returns `Vmin( px / reference * 100 ).clamp( px * 0.7, px * 1.5 )`, so a value authored against a mock-up scales with the surface without collapsing on tiny screens or ballooning on a 4K desktop. The reference is process-global, set via `set_design_reference()` and read via `design_reference()` (stored as f32 bits in an AtomicU32); both are re-exported from `lib.rs`. Make container and grid insets relative. `Container`'s four padding fields become `Length` instead of `f32`; every setter (`padding`, `padding_h`, `padding_v`, `padding_top`/`right`/`bottom`/`left`) now takes `impl Into<Length>`, so existing `f32` call sites keep compiling via the `From<f32>` shim. The values are resolved against the viewport in `Container::preferred_size` and in the container draw path (`draw/layout.rs`). `WrapGrid`'s `spacing_x`, `spacing_y` and `padding` get the same treatment, with a `resolved( canvas )` helper funnelling the per-frame resolution and `grid()` seeding `Length::px` defaults. Container tests now compare against `Length::px( … )`. == Scrolling == `Scroll::preferred_size` is now axis-aware. A horizontal-only scroll reports its child's natural height rather than claiming all remaining vertical space, so it no longer steals Y from its siblings when it sits inside a `Column`; vertical and both-axis scrolls keep the spacer-like `( max_width, 0.0 )`. `Column`'s space-distribution correspondingly treats a `Scroll` as a vertical space-claimer only when its axis allows Y. Disambiguate nested scroll viewports by direction. On press the gesture state now collects every scroll viewport under the point (`scroll_candidates`, innermost first) instead of committing to one; on the first 8 px of motion it locks onto the candidate whose axis matches the dominant direction (`scroll_locked`), so a horizontal scroller nested inside a vertical list no longer grabs the wrong axis. The pointer scroll hit test is aligned to the same innermost-first ordering. == Theme palette == `themes/default/theme.json` gains named colours (green / green-deep, yellow, orange / orange-deep, pink / pink-soft, sky-deep, error / error-soft, neutral-tertiary) and new semantic slots in both light and dark modes: `danger`, `text-tertiary`, `accept`, `chip` / `chip-active` / `chip-active-fg`, and `avatar-1` … `avatar-9`. == Cursors == Bundle GNOME's Adwaita cursor theme — the cursors GNOME Shell uses — into `themes/default/cursors/` so a Wayland compositor can draw consistent, complete pointers for ltk applications without the toolkit rasterising cursors itself and without depending on adwaita-icon-theme being installed on the target. The cursors are copied verbatim in XCursor binary format: 35 image files, one per CSS/freedesktop cursor name (default, text, pointer, *-resize, …), plus 27 customary X11 alias symlinks (arrow → default, hand2 → pointer, …); a sibling `cursor.theme` makes the tree a valid XCursor theme. The existing `ltk-theme-default.install` copies `themes/default` recursively, so the directory ships with no packaging change. Applications keep declaring a `CursorShape` per widget over `wp_cursor_shape_v1`; the compositor resolves it against the active theme's `cursors/` directory by name, and the set covers all 34 `CursorShape` variants. Document the set in `themes/default/cursors/README.md` (what it is, the XCursor layout, the full shape list, how the compositor consumes it, guidance for forks) and `themes/default/cursors/LICENSE.md` (attribution and licence options, modelled on the icons catalogue LICENSE). `lib.rs` lists the cursors in its third-party-assets section. Close out licensing in `debian/copyright`: a `Files: themes/default/cursors/*` paragraph records the upstream dual offer (CC-BY-SA-3.0 or LGPL-3, and CC-BY-SA-4.0 for the newer assets) attributed to the GNOME Project, with standalone CC-BY-SA-3.0, CC-BY-SA-4.0 and LGPL-3 paragraphs (summary-plus-canonical-URL for the CC licences, matching the existing CC-BY-4.0 entry; LGPL-3 referencing /usr/share/common-licenses/LGPL-3). The files are unmodified from upstream, so there is nothing to declare under the ShareAlike "indicate if changes were made" clause. Add `tests/cursor_assets.rs`: every `CursorShape` name resolves to a valid XCursor file (Xcur magic, following symlinks), `cursor.theme` is present, no entry is a dangling symlink, and the expected-name list stays in sync with the enum's 34 variants. |
||
|
|
bbab5e238d | First commit. Version 0.1.0 |