Files
admin 45b8ddd483
CI / build + test (push) Canceled after 0s
CI / cargo audit (push) Canceled after 0s
A print dialog with a preview, CUPS and the print portal, a Windows backend, and ltk::dirs for the user's folders
Printing. An app that wanted to print had nothing in ltk to ask the user where and how, nor to reach a printer. `ltk::print` (`src/print/`) is a dialog built as `FileChooser` is: the app holds a `PrintDialog`, puts its `view()` in its own and hands its `PrintDialogMsg`s to `update()`, which returns a `PrintEvent`; `wrap` makes the app's message of the dialog's, so the printer list reaches it through the app's `ChannelSender`. It offers the destination, the printers CUPS knows with their state and "Save as PDF", the number of copies, all pages or a range such as `1-3, 5, 8-`, the paper (A4, Letter, Legal, A3, A5), the orientation, the margins, the scale, two-sided printing, colour and background graphics, the copies, sides and colour only for a printer. `PrintOptions` holds what was chosen and survives between dialogs, `parse_pages` and `format_pages` read and write the ranges. On a desktop the dialog is a card with ltk's `dialog()`, the preview on the left and the settings on the right; `compact()` makes it a whole page for a phone. `.printer( name )` and `.save_as_pdf()` choose where it starts.
The preview. With `.preview( true )` the dialog asks the app for one with `PrintEvent::Preview( options )` whenever a setting that moves the pages changes, and shows what the app hands back to `PrintDialogMsg::previewed`, a `Preview` of some pages as RGBA with the document's total, one page at a time with buttons to move between them, greyed when colour is off. While one is being rendered a new request waits and is sent only if the layout changed meanwhile, so turning the scale quickly does not stack renders; until the first arrives the sheet's box shows "Preparing the preview…", kept to the sheet's width so it does not push the settings aside.
CUPS. `print/cups.rs` loads `libcups.so.2` with `dlopen` at first use, so ltk neither links against it nor needs its headers, and a system without CUPS only loses its printers. It uses the destinations API, which also lists the network's driverless printers that have no queue: `printers()` with the default first, `print( printer, title, options, pdf )` sending the PDF as one job with `copies`, `media` as a PWG name, `sides` and `print-color-mode`, `jobs()`, `cancel()` and `set_default()`, which writes the user's `lpoptions`. `ltk::print::send`, `list_printers`, `jobs`, `cancel_job` and `set_default` run them on a thread and answer through the app's sender; `ltk::print::blocking` exposes them as they are, for code that has its own thread, such as a portal backend. `print/none.rs` stands in on Windows, Android and the browser, where only saving as PDF is offered.
The portal. An app in a Flatpak cannot reach the CUPS socket. When `/.flatpak-info` exists, the dialog shows nothing of its own: it calls `org.freedesktop.portal.Print.PreparePrint` with the options as GTK's print settings and page setup, and the portal's own dialog answers; `Destination::Portal` then carries its token, and `send` passes the PDF to `Print` through a memfd. The Response signal is subscribed to on the request's path before the call, so a quick answer is not missed. `print/gtk.rs` translates `PrintOptions` to and from GTK's keys (`n-copies`, `page-ranges` counted from zero, `duplex`, `use-color`, `scale` in percent, the paper by its PWG name and size in millimetres), which a portal backend uses for the other direction. zbus 4, the one accesskit_unix already brings, joins the `wayland` feature for it.
The dialog's strings are in ltk's seven locales. `file_chooser::view::icon` is shared with the dialog.
Windows. The `windows` feature runs `ltk::run` in a Win32 window on Windows 10 1703 or later, with Windows's own frame, drawn through ANGLE's EGL and OpenGL ES when `libEGL.dll` and `libGLESv2.dll` sit beside the executable and in software through GDI otherwise (`src/platform/windows/`). It sizes the window by `App::window_size_hint` at the display's DPI and follows per-monitor DPI changes at fractional scales and the accessibility text size; it takes the mouse, the wheel, touch through `WM_POINTER`, and keys with Windows's repeat, dead keys and AltGr; it has the system clipboard, the cursor's shape, full screen, and moves and resizes begun from the app's own chrome, and keeps drawing while the window is moved or resized. Themes, fonts, keyboard layouts and MIME globs come from `ltk\` beside the executable, which `ltk::resource_dir` returns there by default; the font fallback chain and the monospace face are Windows's own, the system face stays the embedded Sora, and the file chooser offers each drive's root. A Win32 message loop cannot wait on calloop, so the runtime's timers and channels go through the queue the web backend already used: `build.rs` sets `cfg( queued_sources )` for both, and `AppData::dispatch_sources` moves from the web backend to `event_loop/sources.rs`. `Canvas::present_to` presents a GPU frame stretched over a window that a fractional scale left a pixel off it, and `EglContext::new_windows` and `create_native_surface` give ANGLE's display and an `HWND`'s surface. `App::save_state` is saved into `%LOCALAPPDATA%\<app_id>\` every 30 seconds, on exit and when the user's session ends, and restored as on Wayland: the part of the Wayland session runtime that does not speak the protocol, deciding why the app starts and handing it its bytes, is now `session_state::bootstrap`, which both backends call, with the file modes and the check for a running instance done each platform's way. `RunError::Window` reports a window Windows refused. `make windows-deps` and `make windows-check` typecheck the backend from Debian, compiling std from `rust-src`. `docs/windows.md` describes building, what ships beside the executable and what the backend does.
ltk::dirs. `home`, `data_home`, `config_home`, `cache_home` and `state_home`: XDG's on Linux, with their usual fallbacks under the home folder, and on Windows `USERPROFILE`, `APPDATA` for data and settings and `LOCALAPPDATA` for the cache and state, an `XDG_*` variable that is set winning everywhere. An app finds its files there instead of reading `HOME` and the XDG variables itself, which on Windows are unset; ltk's own lookups of themes, keyboard layouts, MIME globs, the file chooser's folders and the session state now go through it, and `state_dir` takes the base it is given, its tests with it.
Docs. The links to `WidgetId` in the crate's, `types`' and `App`'s documentation name the struct, since the `const fn` of the same name made them ambiguous to rustdoc. `README.md` names the other backends and `CHANGELOG.md` records the Windows backend and `ltk::dirs`.
Not done. The print dialog has no CHANGELOG entry nor a page in `docs/widgets.md`. Under the portal there is no preview, since the portal's dialog gets the document only at the end. The desktop card's settings column does not scroll, so with a list open in a low window it may not fit. Tests cover the page ranges and the translation to GTK's keys, not the dialog, CUPS or the portal. On Windows there are no overlays, secondary surfaces or subsurfaces, no preedit for the input method and no accessibility.
2026-11-01 22:49:18 +01:00

179 lines
8.3 KiB
Makefile

REGISTRY ?= /usr/share/cargo/registry/ltk
DOCDIR ?= /usr/share/doc/libltk-doc/html
# The Android build uses Debian's own packages: rustc and rust-src of the
# same version, whose std `-Zbuild-std` compiles for the target, and the NDK
# that google-android-ndk-r27c-installer (non-free) unpacks.
NDK ?= /usr/lib/android-sdk/ndk/27.2.12479018
ANDROID_API ?= 26
ANDROID_TARGET ?= aarch64-linux-android
NDK_BIN = $(NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin
NDK_CC = $(NDK_BIN)/$(ANDROID_TARGET)$(ANDROID_API)-clang
ANDROID_ENV = RUSTC_BOOTSTRAP=1 \
CC_$(subst -,_,$(ANDROID_TARGET))=$(NDK_CC) \
CXX_$(subst -,_,$(ANDROID_TARGET))=$(NDK_CC)++ \
AR_$(subst -,_,$(ANDROID_TARGET))=$(NDK_BIN)/llvm-ar \
CARGO_TARGET_$(shell echo $(ANDROID_TARGET) | tr a-z- A-Z_)_LINKER=$(NDK_CC)
ANDROID_CARGO = -Zbuild-std=std,panic_abort --target $(ANDROID_TARGET) --no-default-features --features android
# Where `android-assets` writes the `ltk/` tree an APK carries under `assets/`.
ANDROID_ASSETS ?= target/android/assets
# The example APK, packaged with aapt, zipalign, apksigner and
# android-framework-res, and installed with adb (android-sdk-platform-tools).
ANDROID_ABI ?= arm64-v8a
ANDROID_APK ?= target/android/ltk-hello.apk
# The browser build uses Debian's std for wasm32 (libstd-rust-dev-wasm32) and
# wasm-bindgen-cli, which Debian does not package and whose version must be
# the one of the wasm-bindgen crate ltk pins.
WEB_TARGET = wasm32-unknown-unknown
WEB_CARGO = --target $(WEB_TARGET) --no-default-features --features web
WEB_OUT ?= target/web
# web_hello runs alone on a page; web_shell needs forge/web, whose `make hello` serves both.
WEB_EXAMPLES = web_hello web_shell
# Fallback fonts a page bundles beyond the theme's, for text Sora does not
# cover: Cyrillic, Greek and extended Latin, symbols, arrows and box drawing.
WEB_FONTS ?= $(wildcard /usr/share/fonts/truetype/noto/NotoSans-Regular.ttf /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf /usr/share/fonts/truetype/noto/NotoSansSymbols2-Regular.ttf)
# Where web-deps installs wasm-bindgen-cli, so nothing needs to be on PATH;
# one already on PATH is used when there is none here.
WEB_TOOLS = target/tools
WASM_BINDGEN ?= $(or $(wildcard $(WEB_TOOLS)/bin/wasm-bindgen),$(shell command -v wasm-bindgen 2>/dev/null))
# The Windows build, like the Android one, compiles std from Debian's rust-src
# with `-Zbuild-std`, since Debian packages no std for the target, and links
# with MinGW (gcc-mingw-w64-x86-64).
WINDOWS_TARGET ?= x86_64-pc-windows-gnu
WINDOWS_ENV = RUSTC_BOOTSTRAP=1 \
CC_$(subst -,_,$(WINDOWS_TARGET))=x86_64-w64-mingw32-gcc \
AR_$(subst -,_,$(WINDOWS_TARGET))=x86_64-w64-mingw32-ar \
CARGO_TARGET_$(shell echo $(WINDOWS_TARGET) | tr a-z- A-Z_)_LINKER=x86_64-w64-mingw32-gcc
WINDOWS_CARGO = -Zbuild-std=std,panic_abort --target $(WINDOWS_TARGET) --no-default-features --features windows
# Targets are recipes, not files. Listing them as PHONY tells make to
# ignore filesystem entries with the same name — without this `examples`
# silently no-ops because the `examples/` directory exists, and `doc`
# would do the same once `target/doc` is around.
.PHONY: all deps android-deps test doctest-md stylecheck clippy audit doc install examples clean distclean android-check android-assets android-apk android-install windows-deps windows-check web-deps web-check web-assets web-example web-serve
all:
cargo build --release
# What building, testing and linting need, from Debian.
deps:
sudo apt-get install -y cargo rustc rust-clippy pkg-config libwayland-dev libegl-dev libxkbcommon-dev
# What the android-* targets need, from Debian: std's sources for -Zbuild-std,
# the NDK installer (non-free), the APK tools, rsvg-convert for SVG icons, javac, dx and
# android.jar (non-free) for the keep-alive service, and adb.
android-deps:
sudo apt-get install -y rust-src google-android-ndk-r27c-installer aapt zipalign apksigner android-framework-res librsvg2-bin default-jdk-headless art-standalone google-android-platform-34-installer android-sdk-platform-tools
test:
cargo test --features test-support
# Typecheck the `no_run` snippets in docs/*.md by feeding each markdown
# file to `rustdoc --test`. Catches API drift in cookbook + widget
# reference: drops a renamed builder, removed function, etc. surface
# here just like `cargo test --doc` does for in-source doctests.
doctest-md:
./scripts/doctest-md.sh
# Mechanical style checks (tabs, attribute-bracket spacing) — the
# grep-verifiable subset of code_style_guide.md.
stylecheck:
./scripts/style-check.sh
# Semantic lints, mirroring the CI invocation. Warnings are errors so a
# local run predicts the CI verdict.
clippy:
cargo clippy --workspace --all-targets --features test-support -- -D warnings
# Typecheck the Android backend. Needs rust-src and the NDK (see NDK above).
android-check:
@test -x $(NDK_CC) || { echo "No NDK compiler at $(NDK_CC); install google-android-ndk-r27c-installer or set NDK="; exit 1; }
$(ANDROID_ENV) cargo check $(ANDROID_CARGO)
# The in-tree default theme and the fonts it names, as an APK carries them.
android-assets:
./scripts/android-assets.sh $(ANDROID_ASSETS) themes default
# examples/android_hello.rs as an APK, with the in-tree default theme.
android-apk: android-assets
@test -x $(NDK_CC) || { echo "No NDK compiler at $(NDK_CC); install google-android-ndk-r27c-installer or set NDK="; exit 1; }
$(ANDROID_ENV) cargo build --release --example android_hello $(ANDROID_CARGO)
ANDROID_ABI=$(ANDROID_ABI) MIN_SDK=$(ANDROID_API) ./scripts/android-apk.sh $(ANDROID_APK) \
target/$(ANDROID_TARGET)/release/examples/libandroid_hello.so net.liberux.ltk.Hello "ltk hello" $(ANDROID_ASSETS)
android-install: android-apk
adb install -r $(ANDROID_APK)
# What the windows-* targets need, from Debian: std's sources for -Zbuild-std and MinGW.
windows-deps:
sudo apt-get install -y rust-src gcc-mingw-w64-x86-64
# Typecheck the Windows backend.
windows-check:
$(WINDOWS_ENV) cargo check $(WINDOWS_CARGO)
# What the web-* targets need: Debian's std for wasm32, and wasm-bindgen-cli at the version ltk pins, which scripts/wasm-bindgen-cli.sh builds.
web-deps:
sudo apt-get install -y libstd-rust-dev-wasm32 curl
./scripts/wasm-bindgen-cli.sh $(WEB_TOOLS)
# Typecheck the browser backend.
web-check:
cargo check $(WEB_CARGO)
# The in-tree default theme, the fonts it names and $(WEB_FONTS), as a page serves them.
web-assets:
EXTRA_FONTS="$(WEB_FONTS)" ./scripts/web-assets.sh $(WEB_OUT) themes default
# examples/web_hello.rs as a page under $(WEB_OUT), with its resources.
web-example: web-assets
@test -n "$(WASM_BINDGEN)" || { echo "No wasm-bindgen in $(WEB_TOOLS)/bin or on PATH; run make web-deps"; exit 1; }
cargo build --release $(addprefix --example ,$(WEB_EXAMPLES)) $(WEB_CARGO)
for example in $(WEB_EXAMPLES); do \
$(WASM_BINDGEN) --target web --no-typescript --out-dir $(WEB_OUT)/pkg target/$(WEB_TARGET)/release/examples/$$example.wasm || exit 1; \
done
cp web/index.html $(WEB_OUT)/
web-serve: web-example
cd $(WEB_OUT) && python3 -m http.server 8000
audit:
@command -v cargo-audit >/dev/null 2>&1 || cargo install cargo-audit --locked
cargo audit
doc:
cargo doc --no-deps
install: doc
install -d $(REGISTRY)
cp -r src benches examples locales protocols keyboards scripts java web build.rs Cargo.toml liberux.toml $(REGISTRY)/
cp debian/cargo-checksum.json $(REGISTRY)/.cargo-checksum.json
install -d $(DOCDIR)
cp -r target/doc/* $(DOCDIR)/
examples:
LTK_THEMES_DIR=themes cargo run --release --example showcase
LTK_THEMES_DIR=themes cargo run --release --example responsive
LTK_THEMES_DIR=themes cargo run --release --example inputs
LTK_THEMES_DIR=themes cargo run --release --example scroll
LTK_THEMES_DIR=themes cargo run --release --example sliders
LTK_THEMES_DIR=themes cargo run --release --example combo
LTK_THEMES_DIR=themes cargo run --release --example mini_shell
LTK_THEMES_DIR=themes cargo run --release --example widgets
LTK_THEMES_DIR=themes cargo run --release --example pickers
LTK_THEMES_DIR=themes cargo run --release --example dialog
LTK_THEMES_DIR=themes cargo run --release --example carousel
LTK_THEMES_DIR=themes cargo run --release --example clip_path
LTK_THEMES_DIR=themes cargo run --release --example tab_strip
LTK_THEMES_DIR=themes cargo run --release --example file_chooser
clean:
dh_clean
cargo clean
rm -rf target
distclean: clean
rm -f Cargo.lock