Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f055935d4 | ||
|
|
63179b9c47 | ||
|
|
d1df30d6d0 | ||
|
|
9953865128 |
@@ -1,5 +1,20 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.3 (2026-10-05)
|
||||||
|
|
||||||
|
- Backlight: a config with a `!backlight` widget on a machine without `/sys/class/backlight` entries (or with a `device` name that does not exist) panicked at start ("did not find backlight device"), fatal with `panic = "abort"`. The widget now warns once on stderr, renders empty and ignores clicks, so one config can be shared between a laptop and a desktop.
|
||||||
|
|
||||||
|
## 0.4.2 (2026-10-03)
|
||||||
|
|
||||||
|
- Fonts: a `font_paths` entry pointing at a file that does not exist on this machine made the font-loader thread panic and abort wldash ("Font file not found"). Pinned paths that are missing now fall back to fontconfig by name, with one line on stderr; a font that cannot be resolved at all exits with a clear message instead of a backtrace.
|
||||||
|
- `--write-default-config` no longer writes absolute font paths. Configs shared between machines with different font package layouts (`fonts/noto` vs `fonts/google-noto`) were breaking on the second host; names are resolved per machine instead. Existing configs keep working, and `font_paths` can still pin a file on purpose.
|
||||||
|
|
||||||
|
## 0.4.1 (2026-10-03)
|
||||||
|
|
||||||
|
- No more desktop-file cache: wldash scanned `~/.cache/wldash/desktop.json` (on NFS for shared homes, and stale after every package install) and needed a manual `--desktop-refresh`. Desktop files are now scanned on a background thread at every start, a few milliseconds over local `/usr/share`. `--desktop-refresh` is a no-op kept for key bindings; `simd-json` is gone.
|
||||||
|
- Calculator history and the Escape-saved prompt moved from `$XDG_CACHE_HOME/wldash` to `$XDG_STATE_HOME/wldash` (`~/.local/state`), where small roaming state belongs; old files are still read.
|
||||||
|
- Launcher: `Type=Link` entries open their URL with `xdg-open`; `Terminal=true` entries run inside `$TERMINAL -e` when that variable is set.
|
||||||
|
|
||||||
## 0.4.0 (2026-10-03)
|
## 0.4.0 (2026-10-03)
|
||||||
|
|
||||||
First release of the project.uhhm.no fork, packaged for the `[uhhm]` Arch registry. Numbering: upstream's old code base was tagged up to v0.3.0 and the rewrite called itself 0.2.0; this fork continues above both so pacman sees a monotonic version.
|
First release of the project.uhhm.no fork, packaged for the `[uhhm]` Arch registry. Numbering: upstream's old code base was tagged up to v0.3.0 and the rewrite called itself 0.2.0; this fork continues above both so pacman sees a monotonic version.
|
||||||
|
|||||||
Generated
+2
-217
@@ -8,18 +8,6 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -188,15 +176,6 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "float-cmp"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fontconfig"
|
name = "fontconfig"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -225,38 +204,6 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"js-sys",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "halfbrown"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5681137554ddff44396e5f149892c769d45301dd9aa19c51602a89ee214cb0ec"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown 0.13.2",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.3"
|
version = "0.14.3"
|
||||||
@@ -299,7 +246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.3",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -334,70 +281,6 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-core"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46"
|
|
||||||
dependencies = [
|
|
||||||
"lexical-parse-float",
|
|
||||||
"lexical-parse-integer",
|
|
||||||
"lexical-util",
|
|
||||||
"lexical-write-float",
|
|
||||||
"lexical-write-integer",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-parse-float"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f"
|
|
||||||
dependencies = [
|
|
||||||
"lexical-parse-integer",
|
|
||||||
"lexical-util",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-parse-integer"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9"
|
|
||||||
dependencies = [
|
|
||||||
"lexical-util",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-util"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc"
|
|
||||||
dependencies = [
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-write-float"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862"
|
|
||||||
dependencies = [
|
|
||||||
"lexical-util",
|
|
||||||
"lexical-write-integer",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lexical-write-integer"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446"
|
|
||||||
dependencies = [
|
|
||||||
"lexical-util",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.152"
|
version = "0.2.152"
|
||||||
@@ -693,26 +576,6 @@ dependencies = [
|
|||||||
"pest_derive",
|
"pest_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ref-cast"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
|
|
||||||
dependencies = [
|
|
||||||
"ref-cast-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ref-cast-impl"
|
|
||||||
version = "1.0.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusttype"
|
name = "rusttype"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -764,17 +627,6 @@ dependencies = [
|
|||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.113"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_yaml"
|
name = "serde_yaml"
|
||||||
version = "0.9.31"
|
version = "0.9.31"
|
||||||
@@ -799,28 +651,6 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd-json"
|
|
||||||
version = "0.13.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2faf8f101b9bc484337a6a6b0409cf76c139f2fb70a9e3aee6b6774be7bfbf76"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"halfbrown",
|
|
||||||
"lexical-core",
|
|
||||||
"ref-cast",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"simdutf8",
|
|
||||||
"value-trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simdutf8"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -836,12 +666,6 @@ version = "1.13.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_assertions"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@@ -930,18 +754,6 @@ version = "0.2.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "value-trait"
|
|
||||||
version = "0.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dad8db98c1e677797df21ba03fca7d3bf9bec3ca38db930954e4fe6e1ea27eb4"
|
|
||||||
dependencies = [
|
|
||||||
"float-cmp",
|
|
||||||
"halfbrown",
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -964,12 +776,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.90"
|
version = "0.2.90"
|
||||||
@@ -1265,7 +1071,7 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wldash"
|
name = "wldash"
|
||||||
version = "0.4.0"
|
version = "0.4.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"calloop",
|
"calloop",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1279,7 +1085,6 @@ dependencies = [
|
|||||||
"rusttype",
|
"rusttype",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"simd-json",
|
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
@@ -1316,23 +1121,3 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.7.32"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.7.32"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wldash"
|
name = "wldash"
|
||||||
version = "0.4.0"
|
version = "0.4.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -38,5 +38,4 @@ libpulse-binding = "2.28"
|
|||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
simd-json = "0.13"
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# project.uhhm.no (namespace bl). pkgver is the version; the tag has
|
# project.uhhm.no (namespace bl). pkgver is the version; the tag has
|
||||||
# to agree with it and with Cargo.toml.
|
# to agree with it and with Cargo.toml.
|
||||||
pkgname=wldash
|
pkgname=wldash
|
||||||
pkgver=0.4.0
|
pkgver=0.4.3
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Dashboard/launcher/control-panel for wlroots-style Wayland compositors (layer-shell)"
|
pkgdesc="Dashboard/launcher/control-panel for wlroots-style Wayland compositors (layer-shell)"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ per-host layouts apart on NFS-shared homes.
|
|||||||
|
|
||||||
To generate a new default configuration, run `wldash --write-default-config`. To configure, edit `$XDG_CONFIG_HOME/wldash/config.yml` (or if `XDG_CONFIG_HOME` is not set, `~/.config/wldash/config.yml`). If `/etc/wldash/config.yml` exists it takes precedence over the per-user file; `--config FILE` overrides both.
|
To generate a new default configuration, run `wldash --write-default-config`. To configure, edit `$XDG_CONFIG_HOME/wldash/config.yml` (or if `XDG_CONFIG_HOME` is not set, `~/.config/wldash/config.yml`). If `/etc/wldash/config.yml` exists it takes precedence over the per-user file; `--config FILE` overrides both.
|
||||||
|
|
||||||
To regenerate the desktop file cache, run `wldash --desktop-refresh`.
|
Fonts are named (`sans`, `monospace`, or any fontconfig name) and resolved through fontconfig on the machine wldash runs on. `font_paths` in the config can pin a name to a file; if that file does not exist on the current machine, wldash says so on stderr and falls back to fontconfig rather than failing, so one config can roam between hosts with different font package layouts.
|
||||||
|
|
||||||
|
Desktop files are scanned every time wldash starts (on a background thread), so there is no cache to refresh; `--desktop-refresh` is kept as a no-op for old key bindings. Calculator history and the prompt saved on Escape live in `$XDG_STATE_HOME/wldash` (`~/.local/state/wldash`); older installs' files under `~/.cache/wldash` are still read.
|
||||||
|
|
||||||
For more info, look in `src/config.rs`.
|
For more info, look in `src/config.rs`.
|
||||||
|
|
||||||
|
|||||||
+11
-17
@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::Events,
|
event::Events,
|
||||||
fonts::{FontMap, find_font},
|
fonts::FontMap,
|
||||||
widgets::{
|
widgets::{
|
||||||
Backlight, Battery, Calendar, Clock, Date, HorizontalLayout, IndexedLayout, Interface,
|
Backlight, Battery, Calendar, Clock, Date, HorizontalLayout, IndexedLayout, Interface,
|
||||||
InvertedHorizontalLayout, InvertedVerticalLayout, Layout, Line, Margin, PulseAudio,
|
InvertedHorizontalLayout, InvertedVerticalLayout, Layout, Line, Margin, PulseAudio,
|
||||||
@@ -89,8 +89,6 @@ impl Config {
|
|||||||
pub fn generate_v1() -> Config {
|
pub fn generate_v1() -> Config {
|
||||||
let has_battery = Battery::detect();
|
let has_battery = Battery::detect();
|
||||||
let has_backlight = Backlight::detect();
|
let has_backlight = Backlight::detect();
|
||||||
let sans = find_font("sans");
|
|
||||||
let monospace = find_font("monospace");
|
|
||||||
|
|
||||||
let mut bars = Vec::new();
|
let mut bars = Vec::new();
|
||||||
if has_battery {
|
if has_battery {
|
||||||
@@ -154,13 +152,12 @@ impl Config {
|
|||||||
])),
|
])),
|
||||||
};
|
};
|
||||||
|
|
||||||
let map = HashMap::from([
|
|
||||||
("sans".to_string(), sans),
|
|
||||||
("monospace".to_string(), monospace),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
font_paths: Some(map),
|
// Left unset on purpose: "sans"/"monospace" are resolved through
|
||||||
|
// fontconfig on each machine at start. Pinning absolute paths
|
||||||
|
// here broke configs shared between hosts with different font
|
||||||
|
// package layouts. Set font_paths yourself to override.
|
||||||
|
font_paths: None,
|
||||||
widget,
|
widget,
|
||||||
mode: OperationMode::LayerSurface((1024, 768)),
|
mode: OperationMode::LayerSurface((1024, 768)),
|
||||||
background: Some(0xFAFAFAFA),
|
background: Some(0xFAFAFAFA),
|
||||||
@@ -171,8 +168,6 @@ impl Config {
|
|||||||
pub fn generate_v2(clay: bool) -> Config {
|
pub fn generate_v2(clay: bool) -> Config {
|
||||||
let has_battery = Battery::detect();
|
let has_battery = Battery::detect();
|
||||||
let has_backlight = Backlight::detect();
|
let has_backlight = Backlight::detect();
|
||||||
let sans = find_font("sans");
|
|
||||||
let monospace = find_font("monospace");
|
|
||||||
|
|
||||||
let mut bars = Vec::new();
|
let mut bars = Vec::new();
|
||||||
if has_battery {
|
if has_battery {
|
||||||
@@ -244,13 +239,12 @@ impl Config {
|
|||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let map = HashMap::from([
|
|
||||||
("sans".to_string(), sans),
|
|
||||||
("monospace".to_string(), monospace),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
font_paths: Some(map),
|
// Left unset on purpose: "sans"/"monospace" are resolved through
|
||||||
|
// fontconfig on each machine at start. Pinning absolute paths
|
||||||
|
// here broke configs shared between hosts with different font
|
||||||
|
// package layouts. Set font_paths yourself to override.
|
||||||
|
font_paths: None,
|
||||||
widget,
|
widget,
|
||||||
mode: if clay {
|
mode: if clay {
|
||||||
OperationMode::XdgToplevel
|
OperationMode::XdgToplevel
|
||||||
|
|||||||
+84
-21
@@ -9,21 +9,48 @@ use std::{
|
|||||||
/// FontRef is used to store Fonts on widgets.
|
/// FontRef is used to store Fonts on widgets.
|
||||||
pub type FontRef<'a> = &'a rusttype::Font<'a>;
|
pub type FontRef<'a> = &'a rusttype::Font<'a>;
|
||||||
|
|
||||||
|
/// Ask fontconfig for the file backing a font name ("sans", "monospace",
|
||||||
|
/// "Noto Sans", ...), the way fc-match does.
|
||||||
#[cfg(feature = "fontconfig")]
|
#[cfg(feature = "fontconfig")]
|
||||||
pub(crate) fn find_font(name: &str) -> String {
|
pub(crate) fn find_font(name: &str) -> Result<String, String> {
|
||||||
use fontconfig::Fontconfig as FontConfig;
|
use fontconfig::Fontconfig as FontConfig;
|
||||||
let fc = FontConfig::new().unwrap();
|
let fc = FontConfig::new().ok_or("could not initialise fontconfig")?;
|
||||||
fc.find(name, None)
|
let found = fc
|
||||||
.unwrap()
|
.find(name, None)
|
||||||
|
.ok_or_else(|| format!("fontconfig has no match for {:?}", name))?;
|
||||||
|
found
|
||||||
.path
|
.path
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.map(|s| s.to_string())
|
||||||
.to_string()
|
.ok_or_else(|| format!("font path for {:?} is not UTF-8", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "fontconfig"))]
|
#[cfg(not(feature = "fontconfig"))]
|
||||||
pub(crate) fn find_font(_name: &str) -> String {
|
pub(crate) fn find_font(name: &str) -> Result<String, String> {
|
||||||
panic!("fontconfig not enabled so font search not available");
|
Err(format!(
|
||||||
|
"no path configured for font {:?} and wldash was built without fontconfig",
|
||||||
|
name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the file to load for `name`: a path pinned in the config if it
|
||||||
|
/// exists on this machine, otherwise whatever fontconfig picks for the name.
|
||||||
|
///
|
||||||
|
/// Configs roam between machines on a shared home, and font packages do
|
||||||
|
/// not keep the same file layout everywhere (Noto under fonts/noto on one
|
||||||
|
/// distribution, fonts/google-noto on another), so a pinned path that is
|
||||||
|
/// missing is a reason to fall back, not to abort.
|
||||||
|
pub(crate) fn resolve_font_path(name: &str, pinned: Option<&str>) -> Result<String, String> {
|
||||||
|
if let Some(p) = pinned {
|
||||||
|
if Path::new(p).is_file() {
|
||||||
|
return Ok(p.to_string());
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"wldash: configured font file for {:?} not found: {}; asking fontconfig instead",
|
||||||
|
name, p
|
||||||
|
);
|
||||||
|
}
|
||||||
|
find_font(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FontLoader is a marker struct that is used to load files
|
/// FontLoader is a marker struct that is used to load files
|
||||||
@@ -31,17 +58,20 @@ pub(crate) struct FontLoader;
|
|||||||
|
|
||||||
impl FontLoader {
|
impl FontLoader {
|
||||||
/// Given a path, loads it as a Font, which can be rendered to the screen.
|
/// Given a path, loads it as a Font, which can be rendered to the screen.
|
||||||
pub(crate) fn from_path<'a, P>(path: P) -> Option<Font<'a>>
|
pub(crate) fn from_path<'a, P>(path: P) -> Result<Font<'a>, String>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let mut file = File::open(path).expect("Font file not found");
|
let path = path.as_ref();
|
||||||
|
let mut file =
|
||||||
|
File::open(path).map_err(|e| format!("cannot open {}: {}", path.display(), e))?;
|
||||||
let mut data = match file.metadata() {
|
let mut data = match file.metadata() {
|
||||||
Ok(metadata) => Vec::with_capacity(metadata.len() as usize),
|
Ok(metadata) => Vec::with_capacity(metadata.len() as usize),
|
||||||
Err(_) => vec![],
|
Err(_) => vec![],
|
||||||
};
|
};
|
||||||
file.read_to_end(&mut data).unwrap();
|
file.read_to_end(&mut data)
|
||||||
Font::try_from_vec(data)
|
.map_err(|e| format!("cannot read {}: {}", path.display(), e))?;
|
||||||
|
Font::try_from_vec(data).ok_or_else(|| format!("{} is not a usable font", path.display()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,19 +132,28 @@ impl FontMap {
|
|||||||
self.font_paths.insert(font_name, font_path);
|
self.font_paths.insert(font_name, font_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load every queued font. Runs on its own thread; a font that cannot
|
||||||
|
/// be found or read is a configuration problem the user has to see, so
|
||||||
|
/// it is reported in one line and the process exits instead of
|
||||||
|
/// aborting with a panic backtrace.
|
||||||
pub fn load_fonts(&mut self) {
|
pub fn load_fonts(&mut self) {
|
||||||
for (font_name, v) in self.required_fonts.iter() {
|
for (font_name, v) in self.required_fonts.iter() {
|
||||||
let path = match self.font_paths.get(font_name) {
|
let pinned = self.font_paths.get(font_name).map(|s| s.as_str());
|
||||||
Some(res) => res,
|
let path = match resolve_font_path(font_name, pinned) {
|
||||||
_ => {
|
Ok(p) => p,
|
||||||
let s = find_font(font_name);
|
Err(e) => {
|
||||||
self.font_paths.insert(font_name, s);
|
eprintln!("wldash: cannot resolve font {:?}: {}", font_name, e);
|
||||||
self.font_paths.get(font_name).unwrap()
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fontref = Box::leak(Box::new(
|
let font = match FontLoader::from_path(&path) {
|
||||||
FontLoader::from_path(path).expect("unable to load font"),
|
Ok(f) => f,
|
||||||
));
|
Err(e) => {
|
||||||
|
eprintln!("wldash: cannot load font {:?}: {}", font_name, e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let fontref = Box::leak(Box::new(font));
|
||||||
for (size, preload) in &v.1 {
|
for (size, preload) in &v.1 {
|
||||||
if let Some(font) = self.fonts.get_mut(&(font_name, ComparableF32(*size))) {
|
if let Some(font) = self.fonts.get_mut(&(font_name, ComparableF32(*size))) {
|
||||||
font.add_str_to_cache(preload);
|
font.add_str_to_cache(preload);
|
||||||
@@ -160,3 +199,27 @@ impl MaybeFontMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::resolve_font_path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pinned_path_wins_when_present() {
|
||||||
|
let dir = std::env::temp_dir().join(format!("wldash-font-test-{}", std::process::id()));
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
|
let f = dir.join("x.ttf");
|
||||||
|
std::fs::write(&f, b"not really a font").unwrap();
|
||||||
|
let p = f.to_str().unwrap();
|
||||||
|
assert_eq!(resolve_font_path("sans", Some(p)).unwrap(), p);
|
||||||
|
std::fs::remove_dir_all(&dir).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_pinned_path_falls_back_to_lookup() {
|
||||||
|
// Whatever fontconfig answers (or an error without fontconfig),
|
||||||
|
// the missing pinned path itself must never be returned.
|
||||||
|
let r = resolve_font_path("sans", Some("/nonexistent/wldash/font.ttf"));
|
||||||
|
assert_ne!(r.as_deref().ok(), Some("/nonexistent/wldash/font.ttf"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-11
@@ -23,10 +23,7 @@ use event::{Event, Events};
|
|||||||
use fonts::{FontMap, MaybeFontMap};
|
use fonts::{FontMap, MaybeFontMap};
|
||||||
use keyboard::{KeyRepeatSource, RepeatMessage};
|
use keyboard::{KeyRepeatSource, RepeatMessage};
|
||||||
use state::State;
|
use state::State;
|
||||||
use utils::{
|
use utils::xdg;
|
||||||
desktop::{load_desktop_files, write_desktop_cache},
|
|
||||||
xdg,
|
|
||||||
};
|
|
||||||
use widgets::{Geometry, Widget};
|
use widgets::{Geometry, Widget};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@@ -50,7 +47,8 @@ OPTIONS:
|
|||||||
else ~/.config/wldash/config.yml)
|
else ~/.config/wldash/config.yml)
|
||||||
--write-default-config [v1|v2|toplevel] generate and write a new config file
|
--write-default-config [v1|v2|toplevel] generate and write a new config file
|
||||||
(v2 by default)
|
(v2 by default)
|
||||||
--desktop-refresh refresh desktop file cache
|
--desktop-refresh no-op kept for old key bindings
|
||||||
|
(desktop files are scanned at start)
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -68,9 +66,7 @@ fn ensure_dir_exists(path: &str) {
|
|||||||
|
|
||||||
fn create_required_directories() {
|
fn create_required_directories() {
|
||||||
let dir_config_wldash = format!("{}/wldash", xdg::config_folder());
|
let dir_config_wldash = format!("{}/wldash", xdg::config_folder());
|
||||||
let dir_cache_wldash = format!("{}/wldash", xdg::cache_folder());
|
|
||||||
ensure_dir_exists(&dir_config_wldash);
|
ensure_dir_exists(&dir_config_wldash);
|
||||||
ensure_dir_exists(&dir_cache_wldash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default config path. On a shared (e.g. NFS-roamed) home, a host-specific
|
/// Default config path. On a shared (e.g. NFS-roamed) home, a host-specific
|
||||||
@@ -119,10 +115,7 @@ fn main() {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
Some(ref s) if s == "--desktop-refresh" => {
|
Some(ref s) if s == "--desktop-refresh" => {
|
||||||
let v = load_desktop_files();
|
eprintln!("wldash: desktop files are scanned at every start; nothing to refresh");
|
||||||
if let Err(e) = write_desktop_cache(&v) {
|
|
||||||
die(format!("could not write desktop cache: {}", e));
|
|
||||||
}
|
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
Some(ref s) if s == "--help" => {
|
Some(ref s) if s == "--help" => {
|
||||||
|
|||||||
+9
-19
@@ -2,20 +2,17 @@ use std::{
|
|||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
error::Error,
|
error::Error,
|
||||||
fs::{File, read_to_string},
|
fs::read_to_string,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
io::{Error as io_error, ErrorKind},
|
io::{Error as io_error, ErrorKind},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{self, Deserialize, Serialize};
|
|
||||||
use simd_json;
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::utils::{inish, xdg};
|
use crate::utils::{inish, xdg};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub struct Desktop {
|
pub struct Desktop {
|
||||||
pub entry_type: String,
|
pub entry_type: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -83,20 +80,13 @@ impl Hash for Desktop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_desktop_cache() -> Result<Vec<Desktop>, Box<dyn Error>> {
|
/// Scan every XDG applications directory for launchable entries.
|
||||||
match File::open(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
|
///
|
||||||
Ok(f) => simd_json::from_reader(f).map_err(|e| e.into()),
|
/// This used to be cached as JSON under XDG_CACHE_HOME and refreshed by
|
||||||
Err(e) => Err(e.into()),
|
/// hand with `--desktop-refresh`. The scan is a few milliseconds over
|
||||||
}
|
/// local /usr/share and runs on a background thread, while the cache sat
|
||||||
}
|
/// on the (possibly NFS-mounted) home directory and went stale whenever a
|
||||||
|
/// package was installed. So: no cache.
|
||||||
pub fn write_desktop_cache(v: &Vec<Desktop>) -> Result<(), Box<dyn Error>> {
|
|
||||||
match File::create(format!("{}/wldash/desktop.json", xdg::cache_folder())) {
|
|
||||||
Ok(f) => simd_json::to_writer(f, v).map_err(|e| e.into()),
|
|
||||||
Err(e) => Err(e.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_desktop_files() -> Vec<Desktop> {
|
pub fn load_desktop_files() -> Vec<Desktop> {
|
||||||
let dirs = xdg::data_folders();
|
let dirs = xdg::data_folders();
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ pub fn cache_folder() -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// XDG_STATE_HOME: small persistent state (calculator history, saved
|
||||||
|
/// prompt). Unlike a cache this is worth roaming with the home directory.
|
||||||
|
pub fn state_folder() -> String {
|
||||||
|
match env::var_os("XDG_STATE_HOME") {
|
||||||
|
Some(s) => s.into_string().unwrap(),
|
||||||
|
None => {
|
||||||
|
let home = env::var_os("HOME").unwrap().into_string().unwrap();
|
||||||
|
format!("{}/.local/state", home)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn config_folder() -> String {
|
pub fn config_folder() -> String {
|
||||||
match env::var_os("XDG_CONFIG_HOME") {
|
match env::var_os("XDG_CONFIG_HOME") {
|
||||||
Some(s) => s.into_string().unwrap(),
|
Some(s) => s.into_string().unwrap(),
|
||||||
|
|||||||
+43
-11
@@ -26,7 +26,11 @@ fn write_file_as_u64(path: &Path, value: u64) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Backlight {
|
pub struct Backlight {
|
||||||
device_path: PathBuf,
|
/// `None` when no backlight exists on this machine: the widget then
|
||||||
|
/// renders empty and ignores clicks instead of taking wldash down.
|
||||||
|
/// Configs are shared between hosts, so a laptop config running on a
|
||||||
|
/// desktop must not panic here.
|
||||||
|
device_path: Option<PathBuf>,
|
||||||
cur: u64,
|
cur: u64,
|
||||||
max: u64,
|
max: u64,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
@@ -46,10 +50,13 @@ fn find_device() -> Option<PathBuf> {
|
|||||||
|
|
||||||
impl Backlight {
|
impl Backlight {
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
if let Ok(cur) = read_file_as_u64(self.device_path.join("brightness").as_path()) {
|
let Some(dev) = &self.device_path else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Ok(cur) = read_file_as_u64(dev.join("brightness").as_path()) {
|
||||||
self.cur = cur;
|
self.cur = cur;
|
||||||
}
|
}
|
||||||
if let Ok(max) = read_file_as_u64(self.device_path.join("max_brightness").as_path()) {
|
if let Ok(max) = read_file_as_u64(dev.join("max_brightness").as_path()) {
|
||||||
self.max = max;
|
self.max = max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,11 +65,30 @@ impl Backlight {
|
|||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new(path: Option<&str>, fm: &mut FontMap, font: &'static str, size: f32) -> BarWidget {
|
pub fn new(path: Option<&str>, fm: &mut FontMap, font: &'static str, size: f32) -> BarWidget {
|
||||||
let device_path = match path {
|
let device_path = match path {
|
||||||
Some(path) => Some(Path::new("/sys/class/backlight").join(path)),
|
Some(path) => {
|
||||||
None => find_device(),
|
let p = Path::new("/sys/class/backlight").join(path);
|
||||||
|
if p.is_dir() {
|
||||||
|
Some(p)
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"wldash: backlight device {:?} not found, widget disabled",
|
||||||
|
p
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let p = find_device();
|
||||||
|
if p.is_none() {
|
||||||
|
eprintln!(
|
||||||
|
"wldash: no backlight device under /sys/class/backlight, widget disabled"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
p
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let mut dev = Backlight {
|
let mut dev = Backlight {
|
||||||
device_path: device_path.expect("did not find backlight device"),
|
device_path,
|
||||||
cur: 0,
|
cur: 0,
|
||||||
max: 0,
|
max: 0,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
@@ -77,9 +103,13 @@ impl Backlight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, brightness: f32) {
|
pub fn set(&mut self, brightness: f32) {
|
||||||
|
let Some(dev) = &self.device_path else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let val = (self.max as f32 * brightness.clamp(0.0, 1.0)) as u64;
|
let val = (self.max as f32 * brightness.clamp(0.0, 1.0)) as u64;
|
||||||
let _ = write_file_as_u64(self.device_path.join("brightness").as_path(), val);
|
let brightness_path = dev.join("brightness");
|
||||||
if let Ok(cur) = read_file_as_u64(self.device_path.join("brightness").as_path()) {
|
let _ = write_file_as_u64(&brightness_path, val);
|
||||||
|
if let Ok(cur) = read_file_as_u64(&brightness_path) {
|
||||||
self.cur = cur;
|
self.cur = cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,12 +123,14 @@ impl BarWidgetImpl for Backlight {
|
|||||||
self.dirty
|
self.dirty
|
||||||
}
|
}
|
||||||
fn value(&mut self) -> f32 {
|
fn value(&mut self) -> f32 {
|
||||||
|
self.dirty = false;
|
||||||
|
if self.device_path.is_none() {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
if self.max == 0 || self.cur > self.max {
|
if self.max == 0 || self.cur > self.max {
|
||||||
// what.
|
// Sysfs handed us nonsense; show full rather than a bogus ratio.
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dirty = false;
|
|
||||||
self.cur as f32 / self.max as f32
|
self.cur as f32 / self.max as f32
|
||||||
}
|
}
|
||||||
fn color(&self) -> Color {
|
fn color(&self) -> Color {
|
||||||
|
|||||||
+49
-24
@@ -16,7 +16,7 @@ use crate::{
|
|||||||
fonts::FontMap,
|
fonts::FontMap,
|
||||||
keyboard::{KeyEvent, keysyms},
|
keyboard::{KeyEvent, keysyms},
|
||||||
utils::{
|
utils::{
|
||||||
desktop::{Desktop, load_desktop_cache, load_desktop_files, write_desktop_cache},
|
desktop::{Desktop, load_desktop_files},
|
||||||
xdg,
|
xdg,
|
||||||
},
|
},
|
||||||
widgets::{Geometry, Widget},
|
widgets::{Geometry, Widget},
|
||||||
@@ -225,6 +225,29 @@ fn strip_field_codes(exec: &str) -> String {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Path of a small persistent state file (calc history, saved prompt)
|
||||||
|
/// under $XDG_STATE_HOME/wldash.
|
||||||
|
fn state_path(name: &str) -> String {
|
||||||
|
format!("{}/wldash/{}", xdg::state_folder(), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a state file, falling back to where older versions kept it
|
||||||
|
/// ($XDG_CACHE_HOME/wldash) so nothing is lost on upgrade.
|
||||||
|
fn read_state(name: &str) -> Option<String> {
|
||||||
|
read_to_string(state_path(name))
|
||||||
|
.or_else(|_| read_to_string(format!("{}/wldash/{}", xdg::cache_folder(), name)))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create (truncate) a state file, making the directory on demand.
|
||||||
|
fn create_state(name: &str) -> Option<File> {
|
||||||
|
let path = state_path(name);
|
||||||
|
if let Some(dir) = std::path::Path::new(&path).parent() {
|
||||||
|
let _ = std::fs::create_dir_all(dir);
|
||||||
|
}
|
||||||
|
File::create(path).ok()
|
||||||
|
}
|
||||||
|
|
||||||
trait InterfaceWidget {
|
trait InterfaceWidget {
|
||||||
/// Number of selectable rows (used to clamp the selection).
|
/// Number of selectable rows (used to clamp the selection).
|
||||||
fn rows(&self) -> usize;
|
fn rows(&self) -> usize;
|
||||||
@@ -253,16 +276,8 @@ impl Launcher {
|
|||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("desktopini".to_string())
|
.name("desktopini".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let mut options = options.lock().unwrap();
|
let loaded = load_desktop_files();
|
||||||
*options = match load_desktop_cache() {
|
*options.lock().unwrap() = loaded;
|
||||||
Ok(v) => v,
|
|
||||||
Err(_) => {
|
|
||||||
let v = load_desktop_files();
|
|
||||||
write_desktop_cache(&v).unwrap();
|
|
||||||
v
|
|
||||||
}
|
|
||||||
};
|
|
||||||
drop(options);
|
|
||||||
let mut events = events.lock().unwrap();
|
let mut events = events.lock().unwrap();
|
||||||
events.add_event(Event::LauncherUpdate);
|
events.add_event(Event::LauncherUpdate);
|
||||||
})
|
})
|
||||||
@@ -306,9 +321,19 @@ impl InterfaceWidget for Launcher {
|
|||||||
fn trigger(&mut self, intf: &mut InnerInterface) {
|
fn trigger(&mut self, intf: &mut InnerInterface) {
|
||||||
if self.matches.len() > intf.selection {
|
if self.matches.len() > intf.selection {
|
||||||
let d = &self.matches[intf.selection];
|
let d = &self.matches[intf.selection];
|
||||||
if let Some(exec) = &d.exec {
|
let cmd = match (&d.exec, &d.url) {
|
||||||
self.exec(&strip_field_codes(exec));
|
(Some(exec), _) => strip_field_codes(exec),
|
||||||
}
|
// Type=Link entries have no Exec; open the URL instead.
|
||||||
|
(None, Some(url)) => format!("xdg-open '{}'", url.replace('\'', "'\\''")),
|
||||||
|
(None, None) => return,
|
||||||
|
};
|
||||||
|
// Terminal=true: run inside the user's terminal when one is
|
||||||
|
// configured, otherwise as before.
|
||||||
|
let cmd = match (d.term, std::env::var("TERMINAL")) {
|
||||||
|
(true, Ok(term)) if !term.is_empty() => format!("{} -e {}", term, cmd),
|
||||||
|
_ => cmd,
|
||||||
|
};
|
||||||
|
self.exec(&cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,9 +527,9 @@ impl Calc {
|
|||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("calc".to_string())
|
.name("calc".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let s = match read_to_string(format!("{}/wldash/calc", xdg::cache_folder())) {
|
let s = match read_state("calc") {
|
||||||
Ok(s) => s,
|
Some(s) => s,
|
||||||
_ => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut old = old_local.lock().unwrap();
|
let mut old = old_local.lock().unwrap();
|
||||||
@@ -524,9 +549,9 @@ impl Calc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sync(&self) {
|
fn sync(&self) {
|
||||||
let mut f = match File::create(format!("{}/wldash/calc", xdg::cache_folder())) {
|
let mut f = match create_state("calc") {
|
||||||
Ok(f) => f,
|
Some(f) => f,
|
||||||
_ => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let old = self.old.lock().unwrap();
|
let old = self.old.lock().unwrap();
|
||||||
@@ -713,9 +738,9 @@ impl Interface {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(mut f) = File::create(format!("{}/wldash/prompt", xdg::cache_folder())) {
|
if let Some(mut f) = create_state("prompt") {
|
||||||
write!(f, "{}", self.inner.prompt.input).unwrap();
|
let _ = write!(f, "{}", self.inner.prompt.input);
|
||||||
f.sync_data().unwrap();
|
let _ = f.sync_data();
|
||||||
}
|
}
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
@@ -744,7 +769,7 @@ impl Interface {
|
|||||||
widget.update(&self.inner);
|
widget.update(&self.inner);
|
||||||
}
|
}
|
||||||
keysyms::KEY_r if event.modifiers.ctrl => {
|
keysyms::KEY_r if event.modifiers.ctrl => {
|
||||||
if let Ok(s) = read_to_string(format!("{}/wldash/prompt", xdg::cache_folder())) {
|
if let Some(s) = read_state("prompt") {
|
||||||
self.inner.prompt.set(&s);
|
self.inner.prompt.set(&s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user