ltk ran on Wayland and Android only. Running crustace and its apps in a browser needs, first, ltk apps running there; a window manager for the page is the next step. The runtime was already split from Wayland by the platform layer, so what kept it out of a browser was what the core and the runtime took from the operating system: calloop's epoll loop, EGL, a monotonic clock, a file system, threads and environment variables. The core builds for wasm32-unknown-unknown. `ltk::ChannelSender` is ltk's own type instead of a re-export of calloop's `Sender`, with the same `send` and `Clone`, so apps that only send and clone it build unchanged. The runtime arms its timers and channels through `event_loop/sources.rs` instead of a calloop `LoopHandle`: calloop's natively, and in a browser a queue the backend runs, which takes the due timers out, runs them on `AppData` and hands them back, and does not arm again one removed meanwhile. calloop, khronos-egl and raw-window-handle are dependencies off wasm32 only, where `egl_context.rs`, `UiSurface::try_new_gles` and `from_current_gles_loader` and the native GL names of `BorrowedGlesTexture` are left out; `sub_canvas` clones the uniform locations, which are `Copy` natively and only `Clone` on wasm. The monotonic clock is `crate::time::Instant`, `web_time`'s on wasm32, where `chrono` takes its `wasmbind` feature; the one subtraction that could go below the clock's origin, a surface's first `last_draw`, saturates. Themes, fonts, keyboard layouts and images are read through `vfs.rs`, the file system natively and on wasm32 files held in memory, so a wallpaper or an `Image::from_path` is now decoded from the file's bytes and its format told from its contents rather than its extension. The text-scale watcher, which runs gsettings on a thread, is left out on wasm32, and the built-in keyboard takes its language from `platform::language()`, `LANG` or the page's. criterion is a dev-dependency off wasm32 only, since rayon does not build there. The browser backend is the `web` feature, `platform/web/`, with wasm-bindgen, wasm-bindgen-futures, js-sys and web-sys. `build.rs` refuses it with another backend or for another target. A page's main thread must not block, so `ltk::run` sets the app up and returns, and the browser drives the loop: `AppData` lives in a closure a thread-local loop holds, which a `setTimeout` runs for messages, timers and page events and a `requestAnimationFrame` runs to draw. Event listeners and `ChannelSender::send` only queue and schedule, never touching `AppData`, so nothing they set off runs inside an iteration. The app draws on a canvas laid over the whole page, or the one `ltk::web::set_canvas` gives, through WebGL2 with the GLES canvas unchanged, its frame kept in its FBO, or without WebGL2 through `putImageData`. A lost context drops the GPU canvases and draws nothing until it is restored on a new `glow::Context`, so the glyph atlas and the caches of the old one are not reused. The main surface's logical size is the canvas's CSS size; the window's `resize` refits it, and on ltk's own canvas a hidden element padded by `env( safe-area-inset-* )` gives the insets. Input comes from Pointer Events, a mouse as the pointer and fingers and pens as touches, the wheel, in detents when it scrolls lines, and keys, whose keysym comes from `KeyboardEvent.key` and code from `.code` as the Linux input code Wayland reports; keys repeat on the browser's clock, AltGr types its character, and on macOS Command is Ctrl. ltk takes every key the browser lets a page have, and while fullscreen those `navigator.keyboard.lock()` adds in Chromium. Copies go to `navigator.clipboard.writeText`; a page reads the clipboard only from a `paste` event, so Ctrl+V is let through to the browser and held until its `paste` has put the text in `AppData::clipboard`, and a paste from the browser's menu comes with a Ctrl+V of its own. The cursor's shape is the canvas's CSS `cursor`. The built-in keyboard serves text where the page is used by touch. Panics go to the browser's console. When the app exits, the listeners are removed, the canvas receives an `ltk-exit` event and the app is dropped. The new `RunError::NoDocument` is returned where there is no page, as in a worker. Resources are the tree an Android package carries. `scripts/web-assets.sh` builds it through `android-assets.sh`, and `ltk::web::load_resources( base ).await` fetches its `MANIFEST` and every file it lists, with the manifest's hash in their URLs so a changed tree is not served from the browser's cache, into memory under `/ltk`, which becomes the resource directory. The per-script fallback fonts are read through `resources::read_font`, so a path that does not exist is found by file name in the resource directory's `fonts/`, where a page bundles them. A surface's scale may be fractional. `SurfaceState::scale_factor` is an `f32`, which `Platform::prepare_canvas`, `Presentation::scale` and the paint path take as it is; a surface's physical size is its logical size times the scale, rounded once, and `buffer_scale()` gives the whole number Wayland's buffer scale and regions need. Wayland and Android still set whole scales, so nothing changes there; the browser backend sets the page's `devicePixelRatio`, so the canvas holds the display's own pixels at any zoom. `App::on_scale_changed` keeps its `u32`, the scale rounded, and `App::on_resize` gives the exact physical size. The fallback-theme banner placed its text as if `draw_text` took the top of the line rather than its baseline, so most of the text fell above the banner, out of the surface; it is now centred as the title bar's is. Building. `make web-check` typechecks the backend. `make web-deps` installs Debian's std for wasm32 and wasm-bindgen-cli, whose version must be the one of the wasm-bindgen crate in `Cargo.lock`: the crate is pinned to 0.2.117, the last whose CLI builds with Debian's rustc 1.85, and since the lock the CLI was published with needs a newer rustc and `cargo install` ignores rust-version when it resolves, the CLI is built from its sources with a lock resolved for the rust-version it declares, into `target/tools`, where the Makefile finds it. `make web-assets` builds the resources with the in-tree default theme and the fallback fonts `WEB_FONTS` names. `examples/web_hello.rs`, a counter and a text field, and `web/index.html` are the smoke test: `make web-example` builds them into `target/web/` and `make web-serve` serves them. `make install` now copies `web/` into the registry. `README.md`, `docs/architecture.md` and `CHANGELOG.md` describe the backend, and `docs/cookbook.md` points to `ChannelSender`'s new home. Not done. In a browser there are no overlays, secondary surfaces or subsurfaces, no dead keys or input methods, no accessibility tree and no primary selection, and a canvas that changes size while the window does not is not refitted. Pasting relies on the browser sending `paste` to a focused canvas, which Chrome and Firefox do. The page fetches its whole resource tree, about 7 MB, before the app starts. There are no tests for the browser backend, `Sources`' queue or `vfs.rs`'s in-memory files, and fractional scales may leave one-pixel artefacts where edges fall between pixels.
442 lines
16 KiB
Markdown
442 lines
16 KiB
Markdown
# ltk
|
|
|
|
`ltk` is a public Rust UI toolkit for Wayland applications.
|
|
|
|
It is developed by Liberux as part of the Eydos stack, where it powers
|
|
shell and application surfaces, but it is published as a reusable library for
|
|
third-party developers building their own Wayland software.
|
|
|
|
Being written in Rust is also part of the project's value proposition:
|
|
|
|
- memory safety without a garbage collector
|
|
- predictable resource lifetimes through ownership and borrowing
|
|
- good control over allocations and data movement in rendering-heavy code
|
|
- a strong fit for low-level UI, graphics, and system-integration work
|
|
|
|
For a Wayland toolkit, that combination is useful in practice: it reduces an
|
|
entire class of memory-management bugs common in lower-level UI stacks while
|
|
still allowing tight control over performance-sensitive paths.
|
|
|
|
## What It Is
|
|
|
|
`ltk` is a lightweight, declarative toolkit with an Elm-shaped model:
|
|
|
|
- implement `App`
|
|
- return an `Element<Msg>` tree from `view()`
|
|
- update your state in `update()`
|
|
- run the event loop with `ltk::run(app)`
|
|
|
|
The runtime handles layout, drawing, input dispatch, focus, overlays, and
|
|
backend selection between GLES and software rendering.
|
|
|
|
## What It Is Not
|
|
|
|
`ltk` is not:
|
|
|
|
- a browser UI toolkit
|
|
- a cross-platform desktop toolkit
|
|
- a general-purpose web-style framework
|
|
|
|
Today it is specifically a Wayland toolkit. If you are building native Wayland
|
|
applications, panels, launchers, lock screens, or other shell-adjacent
|
|
surfaces, it is in scope. If you need Windows, macOS, or browser targets, it
|
|
is not.
|
|
|
|
## Project Status
|
|
|
|
`ltk` is a public library intended for third-party use, but it is still shaped
|
|
by real production needs inside the Liberux / Eydos ecosystem.
|
|
|
|
That means:
|
|
|
|
- the API is usable for external applications today
|
|
- the project is optimized first for native Wayland workloads
|
|
- some advanced APIs are still more shell-oriented than app-oriented
|
|
- public documentation and examples are present, but the project is not trying
|
|
to present itself as a cross-platform beginner toolkit
|
|
|
|
If you are evaluating `ltk` for a third-party application, the right mental
|
|
model is "public Wayland toolkit with production consumers" rather than
|
|
"experimental demo crate".
|
|
|
|
## Why Third Parties Might Use It
|
|
|
|
`ltk` is designed around a few practical goals:
|
|
|
|
- low idle wakeups and event-driven redraws
|
|
- partial redraws and damage tracking
|
|
- a simple declarative tree instead of retained widgets
|
|
- direct support for normal windows, layer-shell, and ext-session-lock surfaces
|
|
- a runtime-free core (`ltk::core::UiSurface`) for embedding layout and drawing
|
|
without `ltk::run()`
|
|
|
|
This makes it especially relevant for:
|
|
|
|
- Wayland applications
|
|
- Linux shells that adapt to any screen, from phones to desktops
|
|
- launchers and dashboards
|
|
- greeters and lock screens
|
|
- compositor-side or embedded UI surfaces
|
|
|
|
## Quick Start
|
|
|
|
Add `ltk` to your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
ltk = { path = "../ltk" }
|
|
```
|
|
|
|
If you consume `ltk` from outside this workspace, replace the `path` dependency with a versioned one.
|
|
|
|
Minimal app:
|
|
|
|
```rust
|
|
use ltk::{ App, Element, button, column, spacer, text };
|
|
|
|
#[derive(Clone)]
|
|
enum Msg
|
|
{
|
|
Increment,
|
|
}
|
|
|
|
struct CounterApp
|
|
{
|
|
value: u32,
|
|
}
|
|
|
|
impl App for CounterApp
|
|
{
|
|
type Message = Msg;
|
|
|
|
fn app_id( &self ) -> &str { "net.example.Counter" }
|
|
fn save_state( &self ) -> Option<Vec<u8>> { None }
|
|
fn restore_state( &mut self, _state: Vec<u8> ) {}
|
|
|
|
fn view( &self ) -> Element<Msg>
|
|
{
|
|
column::<Msg>()
|
|
.padding( 32.0 )
|
|
.spacing( 16.0 )
|
|
.center_y( true )
|
|
.push( text( "Hello from ltk" ).size( 28.0 ) )
|
|
.push( text( format!( "Count: {}", self.value ) ).size( 18.0 ) )
|
|
.push( spacer() )
|
|
.push( button( "Increment" ).on_press( Msg::Increment ) )
|
|
.into()
|
|
}
|
|
|
|
fn update( &mut self, msg: Msg )
|
|
{
|
|
match msg
|
|
{
|
|
Msg::Increment => self.value += 1,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main()
|
|
{
|
|
ltk::run( CounterApp { value: 0 } );
|
|
}
|
|
```
|
|
|
|
`app_id` names the window and the `$XDG_STATE_HOME/<app_id>/` session
|
|
directory; `save_state` / `restore_state` are the opaque bytes ltk persists
|
|
for session restore and crash recovery — return `None` when you have nothing
|
|
to keep.
|
|
|
|
## Requirements
|
|
|
|
`ltk` currently assumes:
|
|
|
|
- **Rust 1.85** or newer (the toolchain shipped with Debian stable; declared
|
|
as `rust-version` in `Cargo.toml`).
|
|
- A running **Wayland** session — there is no X11 backend.
|
|
- System headers for `libwayland`, `libegl` and `libxkbcommon` at compile
|
|
time, and `rust-clippy` for `make clippy`. On Debian, `make deps`
|
|
installs them:
|
|
```bash
|
|
sudo apt-get install cargo rustc rust-clippy pkg-config libwayland-dev libegl-dev libxkbcommon-dev
|
|
```
|
|
`make android-deps` installs what the `android-*` targets need: `rust-src`,
|
|
`google-android-ndk-r27c-installer` (non-free), `aapt`, `zipalign`,
|
|
`apksigner`, `android-framework-res`, `librsvg2-bin`, `default-jdk-headless`,
|
|
`art-standalone` (for `dx`), `google-android-platform-34-installer` (non-free) and
|
|
`android-sdk-platform-tools`.
|
|
`make web-deps` installs what the `web-*` targets need: `libstd-rust-dev-wasm32` and `curl` from Debian, and `wasm-bindgen-cli` at the version of the `wasm-bindgen` crate in `Cargo.lock`, built from its sources into `target/tools`, where the Makefile finds it.
|
|
- A usable system font (`fonts-sora`, `fonts-liberation`, `fonts-dejavu`,
|
|
…). If none is installed `ltk` falls back to an embedded Sora Regular
|
|
build with a stderr warning.
|
|
- A theme named `default`, installed system-wide (the
|
|
`ltk-theme-default` Debian package drops it under
|
|
`/usr/share/ltk/themes/default/`) or exposed through
|
|
`LTK_THEMES_DIR` for development.
|
|
|
|
Rendering backend selection is automatic:
|
|
|
|
- **GLES** when EGL is available (every modern Wayland compositor).
|
|
- **Software** fallback otherwise.
|
|
- Set `LTK_FORCE_SOFTWARE=1` to force the software path even when EGL is
|
|
available — useful for headless test runs and for diagnosing
|
|
driver-specific bugs.
|
|
|
|
For development inside this repository:
|
|
|
|
```bash
|
|
export LTK_THEMES_DIR="$PWD/themes"
|
|
cargo run --example showcase
|
|
```
|
|
|
|
## Examples
|
|
|
|
Useful entry points in this repository:
|
|
|
|
- `cargo run --example showcase`
|
|
- `cargo run --example responsive`
|
|
- `cargo run --example widgets`
|
|
- `cargo run --example inputs`
|
|
- `cargo run --example scroll`
|
|
- `cargo run --example combo`
|
|
- `cargo run --example dialog`
|
|
- `cargo run --example sliders`
|
|
- `cargo run --example pickers`
|
|
- `cargo run --example carousel`
|
|
- `cargo run --example clip_path`
|
|
- `cargo run --example mini_shell`
|
|
- `make web-serve`, then <http://localhost:8000/>: `web_hello` in a browser, on the in-tree default theme
|
|
|
|
In general:
|
|
|
|
- start with `showcase` for a regular app window
|
|
- use `responsive` to see the fluid vs physical modes on stock widgets
|
|
- use `widgets` to see the core controls
|
|
- use `mini_shell` if you need overlays, theme switching, or shell-style
|
|
composition
|
|
|
|
## Public API Overview
|
|
|
|
Most applications should start with this subset:
|
|
|
|
- `App`
|
|
- `Element<Msg>`
|
|
- widgets such as `button`, `text`, `text_edit`, `img_widget`
|
|
- layouts such as `column`, `row`, `stack`, `grid`, `spacer`
|
|
- `Color`
|
|
- `run`
|
|
|
|
More advanced APIs are available when needed:
|
|
|
|
- `overlays()`
|
|
- `shell_mode()` and layer-shell controls
|
|
- `set_channel_sender()` and `poll_external()`
|
|
- gesture hooks such as `on_swipe_*`
|
|
- `core::UiSurface`
|
|
- runtime theme APIs
|
|
- `osk::Panel` and its layout files, for a host that draws the on-screen keyboard itself
|
|
|
|
## Responsive Design
|
|
|
|
`ltk` offers **two** first-class ways to make an interface adapt to the
|
|
display, chosen per value or per process:
|
|
|
|
- **Fluid** — sizes are a fraction of the surface, tracking the short side
|
|
(width in portrait, height in landscape). Best for full-screen system
|
|
surfaces. Written with `Length::vmin` / `orient` / `fluid` and bounded with
|
|
`.clamp`.
|
|
- **Physical** — sizes stay a constant real-world size across displays (the
|
|
mainstream HiDPI `dp` model). Best for conventional windowed apps. Written
|
|
with `Length::dp` plus `set_density`.
|
|
|
|
Stock widgets follow the process-wide mode (`set_widget_scaling`, fluid by
|
|
default); an explicit `Length` on a widget always overrides it. The full
|
|
mechanics live in [`docs/architecture.md`](docs/architecture.md#responsive-sizing),
|
|
a walkthrough in [`docs/onboarding.md`](docs/onboarding.md), and the per-item
|
|
reference in the `Length` / `WidgetScaling` rustdoc.
|
|
|
|
## Windows and Shell Surfaces
|
|
|
|
By default, `ltk` creates a regular `xdg-shell` window.
|
|
|
|
That is the right starting point for:
|
|
|
|
- normal applications
|
|
- internal tools
|
|
- prototypes
|
|
|
|
Switch to layer-shell only when you are building shell surfaces such as:
|
|
|
|
- top bars
|
|
- docks
|
|
- homescreens
|
|
- notifications
|
|
- greeters
|
|
- lock screens
|
|
|
|
For a screen locker, use `ShellMode::SessionLock` instead of layer-shell: it
|
|
presents an `ext-session-lock-v1` surface that the compositor keeps on top of
|
|
everything until the app returns `true` from `requested_exit()`, which makes
|
|
the runtime call `unlock` and lift the lock.
|
|
|
|
## Performance Notes
|
|
|
|
`ltk` is designed to sleep when idle and redraw only on real work.
|
|
|
|
The main rules for downstream applications are:
|
|
|
|
- keep `view()` pure and cheap
|
|
- do not perform I/O inside `view()`
|
|
- use `poll_interval()` sparingly
|
|
- return `true` from `is_animating()` only while something is actually moving — note that on the software backend animation redraws are capped at ~30 Hz by default (override with `App::cap_software_animation`)
|
|
- cache decoded images and expensive derived state in your app
|
|
|
|
Set `LTK_PERF_WARN=1` during development for opt-in diagnostics: it warns about stuck animations, sustained software-render animation, and low `poll_interval` values.
|
|
|
|
The library already provides:
|
|
|
|
- event-driven redraw scheduling
|
|
- per-surface invalidation
|
|
- partial redraws for interaction-only changes
|
|
- GPU and software backends behind the same widget API
|
|
|
|
## Backend Differences
|
|
|
|
The public API is the same across backends, but visual parity is not perfect
|
|
yet. The widget tree, layout, hit-testing, text, images, fills, strokes and
|
|
single-rect / path clipping all paint identically on both paths. The gaps are in
|
|
gradients, the shadow / backdrop pipeline, and multi-rect clipping: the GLES
|
|
backend installs the bounding-box union of the rects as its `glScissor` — a
|
|
coarse clip that does not cull pixels between disjoint rects — while the
|
|
software backend clips each rect exactly.
|
|
|
|
Effects that currently render only on the **GLES** backend, and degrade on the
|
|
**Software** backend:
|
|
|
|
- **Gradients** (linear and radial, via `Canvas::fill_paint_rect`) — rendered with
|
|
dedicated shaders on GLES; on software they collapse to a flat fill from the
|
|
first stop (tiny-skia can render gradients natively, but that is not wired up
|
|
yet).
|
|
- **Outer drop shadows** (`Canvas::fill_shadow_outer`) — themed surfaces that
|
|
declare a `Shadow` slot show the soft halo on GLES and a flat fill on
|
|
software.
|
|
- **Inner / inset shadows** (`Canvas::fill_shadow_inset`) — `InsetShadow`
|
|
slots paint nothing on software.
|
|
- **Inset shadow blend modes** — `PlusLighter`, `Multiply`, `Screen` and
|
|
`Overlay` are GLES-only; the GLES `Overlay` path snapshots the framebuffer
|
|
and computes the CSS Overlay formula in-shader, which has no software
|
|
equivalent today.
|
|
|
|
Calls to these APIs are safe on both backends — they simply produce a flatter
|
|
appearance under software. No widget panics, returns an error, or skips
|
|
unrelated drawing.
|
|
|
|
If your application leans heavily on shadows or inset effects, validate both
|
|
rendering paths before shipping. Force the software path with:
|
|
|
|
```bash
|
|
LTK_FORCE_SOFTWARE=1 cargo run --example showcase
|
|
```
|
|
|
|
Closing this gap (porting the shadow / inset-shadow pipeline to tiny-skia) is
|
|
on the roadmap.
|
|
|
|
## Documentation
|
|
|
|
| File | When to read it |
|
|
| --- | --- |
|
|
| [`docs/onboarding.md`](docs/onboarding.md) | First hour with the library — environment, first app, what to ignore at first. |
|
|
| [`docs/architecture.md`](docs/architecture.md) | Runtime model, overlays, animation, theming, performance and where the cost of a frame lives. |
|
|
| [`docs/widgets.md`](docs/widgets.md) | Per-widget catalogue: what each one is, when to use it, minimal example, see-also. |
|
|
| [`docs/theming.md`](docs/theming.md) | JSON theme schema, slot conventions, runtime APIs. |
|
|
| [`docs/backends.md`](docs/backends.md) | Software/GLES capability matrix — what renders identically, what degrades, what is GPU-only. |
|
|
| [`docs/navigation.md`](docs/navigation.md) | Navigation state and the back button: what back closes and in which order, why the answer is derived from the model, how it lines up with session state, `NavStack`. |
|
|
| [`docs/cookbook.md`](docs/cookbook.md) | Concrete recipes — slide-in panels, password fields, runtime theme toggle, channel-driven state, embedding without `ltk::run`. |
|
|
| `cargo doc --open` | Per-item rustdoc for the public API. |
|
|
| [`CHANGELOG.md`](CHANGELOG.md) | What changed in each release, and what is pending unreleased. |
|
|
| [`code_style_guide.md`](code_style_guide.md) | The Modified Allman style rules the codebase follows. |
|
|
| [`SECURITY.md`](SECURITY.md) | How to report a vulnerability and what is in / out of scope. |
|
|
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Build, test, code style, patch shape. |
|
|
|
|
Recommended reading order for a new contributor:
|
|
|
|
1. run `examples/showcase.rs`
|
|
2. read `docs/onboarding.md`
|
|
3. browse `docs/widgets.md` for the catalogue
|
|
4. dip into `docs/cookbook.md` when you hit a specific shape
|
|
5. open `docs/architecture.md` once you need overlays, animations, or
|
|
runtime theming.
|
|
|
|
## Relationship to Liberux and Eydos
|
|
|
|
Liberux is the promoter and primary maintainer of `ltk`.
|
|
|
|
The project exists because Eydos needs a native Wayland toolkit for its
|
|
own shell and application stack, but `ltk` is intentionally published as a
|
|
public library rather than kept as a private internal component. Third-party
|
|
developers are part of the intended audience.
|
|
|
|
That origin matters because it explains the current priorities:
|
|
|
|
- strong Wayland focus
|
|
- support for layer-shell and shell-style overlays
|
|
- attention to power usage on battery-powered devices
|
|
- theming and runtime surfaces that fit an operating system environment
|
|
|
|
## License
|
|
|
|
This project is licensed under `LGPL-2.1-only`.
|
|
|
|
That means third parties can use `ltk` in their own applications, including
|
|
proprietary ones, subject to the obligations of the GNU Lesser General Public
|
|
License v2.1. If you are planning a commercial or closed-source product, read
|
|
the license text carefully and make sure your distribution model complies with
|
|
it.
|
|
|
|
See [LICENSE](./LICENSE).
|
|
|
|
## Third-party assets
|
|
|
|
`ltk`'s default theme bundles two third-party asset sets that travel under
|
|
their own licences. Anyone redistributing the toolkit (or a binary that
|
|
embeds the default theme) must propagate the attributions below.
|
|
|
|
- **Symbolic icons** under `themes/default/icons/catalogue/` come from
|
|
Streamline's *Core Line Free* set, distributed under
|
|
[Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0/)
|
|
(CC BY 4.0). © Streamline. Some files have been modified for the
|
|
symbolic-tinting pipeline; details in
|
|
[`themes/default/icons/catalogue/LICENSE.md`](./themes/default/icons/catalogue/LICENSE.md).
|
|
Upstream: <https://www.streamlinehq.com/icons/core-line-free>.
|
|
- **Sora Regular** (`src/theme/fallback/Sora-Regular.otf`) is the
|
|
embedded font fallback, distributed under
|
|
[SIL Open Font Licence 1.1](https://scripts.sil.org/OFL).
|
|
© 2019-2020 The Sora Project Authors, Jonathan Barnbrook, Julián Moncada.
|
|
Upstream: <https://github.com/sora-xor/sora-font>.
|
|
|
|
The remaining artwork in the default theme — wallpapers, lockscreens,
|
|
launcher logo, brand-mark variants and per-application icons — is
|
|
original to Liberux Labs and travels under the toolkit's own
|
|
`LGPL-2.1-only` licence.
|
|
|
|
The full Debian-style declaration of every asset and its licence lives
|
|
in [`debian/copyright`](./debian/copyright); that is the file the `.deb`
|
|
ships under `/usr/share/doc/libltk*/copyright`.
|
|
|
|
## Contributing
|
|
|
|
Patches and bug reports are welcome. Read
|
|
[`CONTRIBUTING.md`](CONTRIBUTING.md) for the practical mechanics: build
|
|
prerequisites, how to run tests, the project's Modified Allman code
|
|
style, and what shape a pull request should take.
|
|
|
|
For security-sensitive issues see [`SECURITY.md`](SECURITY.md) — please
|
|
do not file those through the public issue tracker.
|
|
|
|
If you are evaluating `ltk` for a third-party product and are unsure
|
|
whether your use case is in scope, open a discussion before writing
|
|
code. That is especially useful when you are:
|
|
|
|
- missing an app-facing example,
|
|
- blocked by a shell-oriented assumption in the API,
|
|
- trying to understand whether a given platform target is realistic.
|