Files
ltk/scripts/android-assets.sh
admin cf187a3dcc
CI / build + test (push) Canceled after 0s
CI / cargo audit (push) Canceled after 0s
The user's files through ltk::files, alike natively and in a browser, and a shell's requests answered in a browser through ltk::web::shell_request
An app read the user's files with `std::fs`, which a browser does not have: there a page reaches a folder of the user's computer only through forge/web, which holds it through the File System Access API and answers asynchronously (forge/web/DESIGN.md, section 11). An app that builds for both, such as Crate, had no way to list, read or write a file in a browser, and a page's memory cannot hold a large file whole.
ltk::files. Asynchronous operations on the user's files whose results reach the app as messages through its `ChannelSender`: `list`, `stat`, `read`, `read_range`, `write`, `open_write`, `create_dir`, `rename`, `remove`, `remove_all`, `copy`, `copy_all`, `mount` and `unmount`, each taking the sender and a `done` that turns the `io::Result` into a message. Results come in no set order, so an app that has moved on checks that one is still the one it waits for. Errors are the same `io::ErrorKind`s on every platform, `Interrupted` for a cancelled copy. `Entry` and `Metadata` give a file's kind, size and times, the access time natively only. `ltk::vfs` stays what reads an app's own resources.
Natively. `std::fs` on one worker thread of ltk's, in the order the app asked (`files/native.rs`), and each copy on a thread of its own, so a long one holds nothing else up (`files/copy.rs`). A file written whole or through a `Writer` goes beside its target as a temporary file, synced and renamed over it on commit and removed if dropped before, so a file being written is never seen half done. `rename` never replaces what is at `to`, as in a browser.
In a browser. Each operation is one of forge/web's `fs_*` requests over the app's connection (`platform/web/files.rs`), carrying a number its `fs_reply` answers with; contents travel as a `Uint8Array` beside the JSON, the second argument of the connection's `request` and of the client's event handler, so `on_event` now takes it. What is asked before the app has connected is sent once it has; on a page without a desktop, or once the connection has closed, it fails with `Unsupported`. forge/web's error codes become their `io::ErrorKind`s, and `fs_progress` reaches a copy's `progress`. Without the web backend, on wasm32 every operation fails with `Unsupported` (`files/unsupported.rs`).
Chunks. Contents never travel whole unless they are small: a large file is read with `read_range` and written through a `Writer`, chunk by chunk, `files::CHUNK` (1 MiB) at a time, the next sent once the last is answered. A `Writer` dropped without `close` is aborted and the file left as it was; in a browser a new file shows empty until closed. A copy runs where the files are, its contents never passing through the app: it reports its progress every 100 ms, never replaces an existing file, and, cancelled through its `Operation` or failed, leaves nothing behind.
Mounts and the desktop's notices. `mount` asks the browser for a folder of the user's computer, from the user's press, and `unmount` lets it go; natively both fail with `Unsupported`. `App::on_files_changed`, `App::on_mounts_changed` and `App::on_open_files` hear of forge/web's `fs_changed`, `fs_mounts` and `open_files`, which the web backend queues as page events; natively they are not called yet.
Types. `files::mime_type( name )` gives a file's type by shared-mime-info's `globs2`, with its weights, its case-sensitive globs and fnmatch's `*`, `?` and bracket classes, from the resource directory's `mime/` first, then the user's and the system's. `scripts/android-assets.sh`, and so `web-assets.sh`, now copies shared-mime-info's `globs2` and `subclasses` into the tree's `mime/` (`MIME_DIR` overrides where from), so a page has them.
ltk::web::shell_request. A shell message that waits for forge/web's reply, such as `get_window_thumbnail`: sent as `shell_send` sends one and answered at once with forge/web's reply as JSON, or `None` before the app has connected, since a request is not kept for later, and when forge/web replies nothing. A shell's requests that wait for an answer natively got none in a browser. `shell.rs` parses a message once for both, and `send` returns forge/web's reply.
Tests cover writing and committing, aborting, listing, ranges, removing, copying a tree with its progress, a cancelled copy, a copy that would replace, and the globs. `CHANGELOG.md` describes the changes.
2026-10-22 00:15:39 +02:00

81 lines
2.3 KiB
Bash
Executable File

#!/bin/sh
# Build the `ltk/` tree an Android package carries under `assets/`: the
# themes, the font files their theme.json names by absolute path, the
# on-screen keyboard's layouts, and the MANIFEST that src/platform/android/assets.rs unpacks from, whose first
# line is a hash of the contents so a changed tree is unpacked again.
# web-assets.sh builds the same tree for a page through this script.
#
# Usage: scripts/android-assets.sh OUT_DIR [THEMES_DIR [THEME_ID...]]
# THEMES_DIR defaults to /usr/share/ltk/themes, THEME_ID to `default`.
# EXTRA_FONTS, space-separated paths, adds fonts the app itself reads from
# `ltk::resource_dir()/fonts`, such as a terminal's monospace face.
# KEYBOARDS_DIR defaults to the `keyboards/` next to this script's directory.
# MIME_DIR, shared-mime-info's directory, whose globs2, which
# `ltk::files::mime_type` reads from `mime/`, and subclasses are copied there,
# defaults to /usr/share/mime.
set -eu
keyboards=${KEYBOARDS_DIR:-$( cd "$( dirname "$0" )/.." && pwd )/keyboards}
if [ $# -lt 1 ]
then
echo "usage: $0 OUT_DIR [THEMES_DIR [THEME_ID...]]" >&2
exit 2
fi
out=$1
shift
themes=/usr/share/ltk/themes
if [ $# -gt 0 ]
then
themes=$1
shift
fi
[ $# -gt 0 ] || set -- default
root=$out/ltk
rm -rf "$root"
mkdir -p "$root/themes" "$root/fonts" "$root/keyboards"
cp "$keyboards"/*.json "$root/keyboards/"
for id in "$@"
do
cp -r "$themes/$id" "$root/themes/$id"
# ltk draws no cursor of its own on Android.
rm -rf "$root/themes/$id/cursors" "$root/themes/$id/cursor.theme"
grep -o '"path"[[:space:]]*:[[:space:]]*"/[^"]*"' "$themes/$id/theme.json" \
| sed 's/.*"\(\/[^"]*\)"$/\1/' \
| while read -r font
do
if [ -f "$font" ]
then
cp "$font" "$root/fonts/"
else
echo "android-assets: $font not found, skipped" >&2
fi
done
done
for font in ${EXTRA_FONTS:-}
do
cp "$font" "$root/fonts/"
done
mime=${MIME_DIR:-/usr/share/mime}
for table in globs2 subclasses
do
if [ -f "$mime/$table" ]
then
mkdir -p "$root/mime"
cp "$mime/$table" "$root/mime/$table"
else
echo "android-assets: $mime/$table not found, skipped" >&2
fi
done
cd "$root"
hash=$( find . -type f ! -name MANIFEST -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | cut -d' ' -f1 )
{
printf '# %s\n' "$hash"
find . -type f ! -name MANIFEST | sed 's|^\./||' | LC_ALL=C sort
} > MANIFEST