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.
This commit is contained in:
@@ -6,6 +6,8 @@ All notable changes to `ltk` are documented here. The format is based on [Keep a
|
||||
|
||||
### Added
|
||||
|
||||
- **The user's files, alike natively and in a browser: `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. Natively they run `std::fs` on a worker thread of ltk's, and each copy on a thread of its own; in a browser they are forge/web's `fs_*` requests over the app's connection, contents travelling as a `Uint8Array` beside the JSON as the second argument of the connection's `request` and of the client's event handler, and what is asked before the app connects is sent once it has, or fails with `Unsupported` on a page without a desktop. Contents travel in chunks: a large file is read with `read_range` and written through a `Writer`, chunk by chunk, `files::CHUNK` (1 MiB) at a time, and its contents appear only once the writer is closed, a new file showing empty until then in a browser; natively it is written beside the file and renamed over it, and a writer dropped unclosed leaves the file as it was. A copy runs where the files are, reports its progress every 100 ms, never replaces an existing file, and, cancelled through its `Operation` or failed, leaves nothing behind. Errors are the same `io::ErrorKind`s on every platform. `Entry` and `Metadata` give a file's kind, size and times, the access time natively only. `mount` asks the browser for a folder of the user's computer, from the user's press, and fails with `Unsupported` natively. `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`. `files::mime_type( name )` gives a file's type by shared-mime-info's `globs2`, the resource directory's `mime/globs2` first, which `scripts/android-assets.sh`, and so `web-assets.sh`, now copies into the tree with shared-mime-info's `subclasses` (`MIME_DIR` overrides where from), then the system's. `rename` never replaces what is at `to`, natively as in a browser. Tests cover writing, aborting, listing, ranges, removing, copying a tree with its progress, a cancelled copy, a copy that would replace, and the globs.
|
||||
- **`ltk::web::shell_request( json )`** — 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.
|
||||
- **`Canvas::draw_image_keyed( key, rgba, w, h, dest, opacity )`** — `draw_image_data` with `key` naming the pixels in place of a fingerprint of them, so a caller that draws the same images on every frame, such as a terminal's glyphs, does not hash every one of them each time. Every draw with the same `key` must pass the same pixels, which are read only when the image is not cached; a key never names an image drawn by content.
|
||||
- **Fullscreen in a browser's desktop** — on a page with forge/web, `App::fullscreen` and `App::start_fullscreen` ask forge/web for the main window to fill the desktop with a `set_fullscreen` request, as Wayland's `xdg_toplevel.set_fullscreen`, where they did nothing; alone on a page the app's canvas still takes the browser's fullscreen.
|
||||
- **A browser app hears of the desktop's windows** — on a page with forge/web, the web backend asks for the list of toplevels when it connects and turns its `toplevel_opened` and `toplevel_closed` events into `App::on_toplevel_event`, as the Wayland backend does with `ext-foreign-toplevel-list-v1`, so a shell's dock knows which applications have windows open and names each by its `identifier`.
|
||||
|
||||
Reference in New Issue
Block a user