Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1e1743b5e | ||
|
|
37458642df | ||
|
|
862c46c7ec | ||
|
|
afedf0214d | ||
|
|
64f54040ef | ||
|
|
d20972cb32 | ||
|
|
1320639d4e | ||
|
|
907a565f3d | ||
|
|
ec69649a04 | ||
|
|
c4cdf4be8b | ||
|
|
00f973c3a4 | ||
|
|
5d153a02fc | ||
|
|
e274ba39cd | ||
|
|
8c75bc46ac | ||
|
|
df77020c52 | ||
|
|
0fb5fa8c2a | ||
|
|
cf87bb4e8d | ||
|
|
badfcc0c2d | ||
|
|
c9e66d4664 | ||
|
|
a3f90adaf1 | ||
|
|
47420d83bf | ||
|
|
4662f60ac5 | ||
|
|
94693c92e3 | ||
|
|
8c774100f1 | ||
|
|
b4db0226cd | ||
|
|
ff17ec1996 | ||
|
|
c48029664d | ||
|
|
58d55db660 | ||
|
|
73158c2fce | ||
|
|
1c032ae8e3 |
43
.github/scripts/sync-wiki.sh
vendored
Executable file
43
.github/scripts/sync-wiki.sh
vendored
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
TEMP_REPO_DIR="wiki_action_$GITHUB_REPOSITORY$GITHUB_SHA"
|
||||
TEMP_WIKI_DIR="temp_wiki_$GITHUB_SHA"
|
||||
|
||||
WIKI_DIR='docs'
|
||||
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "Token is not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#Clone repo
|
||||
echo "Cloning repo https://github.com/$GITHUB_REPOSITORY"
|
||||
git clone "https://$GITHUB_ACTOR:$GH_TOKEN@github.com/$GITHUB_REPOSITORY" "$TEMP_REPO_DIR"
|
||||
|
||||
#Clone wiki repo
|
||||
echo "Cloning wiki repo https://github.com/$GITHUB_REPOSITORY.wiki.git"
|
||||
cd "$TEMP_REPO_DIR"
|
||||
git clone "https://$GITHUB_ACTOR:$GH_TOKEN@github.com/$GITHUB_REPOSITORY.wiki.git" "$TEMP_WIKI_DIR"
|
||||
|
||||
#Get commit details
|
||||
author='Jake Stanger'
|
||||
email='mail@jstanger.dev'
|
||||
message='action: sync wiki'
|
||||
|
||||
echo "Copying edited wiki"
|
||||
cp -R "$TEMP_WIKI_DIR/.git" "$WIKI_DIR/"
|
||||
|
||||
echo "Checking if wiki has changes"
|
||||
cd "$WIKI_DIR"
|
||||
git config --local user.email "$email"
|
||||
git config --local user.name "$author"
|
||||
git add .
|
||||
|
||||
if git diff-index --quiet HEAD; then
|
||||
echo "Nothing changed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Pushing changes to wiki"
|
||||
git commit -m "$message" && git push "https://$GITHUB_ACTOR:$GH_TOKEN@github.com/$GITHUB_REPOSITORY.wiki.git"
|
||||
36
.github/workflows/build.yml
vendored
36
.github/workflows/build.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
@@ -24,8 +25,8 @@ jobs:
|
||||
- name: Install build deps
|
||||
run: sudo apt install libgtk-3-dev libgtk-layer-shell-dev
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/clippy-check@v1
|
||||
@@ -33,5 +34,32 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
||||
|
||||
|
||||
build-nix:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v17
|
||||
with:
|
||||
install_url: https://nixos.org/nix/install
|
||||
extra_nix_config: |
|
||||
auto-optimise-store = true
|
||||
experimental-features = nix-command flakes
|
||||
|
||||
- uses: cachix/cachix-action@v11
|
||||
with:
|
||||
name: jakestanger
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
|
||||
- run: nix build --print-build-logs
|
||||
27
.github/workflows/update-nix-flake-lock.yml
vendored
Normal file
27
.github/workflows/update-nix-flake-lock.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: update-nix-flake-lock
|
||||
on:
|
||||
workflow_dispatch: # allows manual triggering
|
||||
schedule:
|
||||
- cron: '0 0 1 * *' # first day of every month
|
||||
|
||||
jobs:
|
||||
update-nix-flake-lock:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v16
|
||||
with:
|
||||
extra_nix_config: |
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update flake.lock
|
||||
uses: DeterminateSystems/update-flake-lock@vX
|
||||
with:
|
||||
pr-title: "Update flake.lock" # Title of PR to be created
|
||||
pr-labels: | # Labels to be set on the PR
|
||||
dependencies
|
||||
automated
|
||||
17
.github/workflows/wiki.yml
vendored
Normal file
17
.github/workflows/wiki.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Sync Wiki
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Sync Wiki
|
||||
run: ./.github/scripts/sync-wiki.sh
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v0.7.0] - 2022-11-05
|
||||
### :sparkles: New Features
|
||||
- [`fad90fd`](https://github.com/JakeStanger/ironbar/commit/fad90fdad683a612497ac7822a66a90f43fce0a2) - **sys-info**: add loads more formatting tokens *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`dec402e`](https://github.com/JakeStanger/ironbar/commit/dec402edd9d6c5b8677ff337699ad99ebc69b776) - **sys-info**: config options for refresh intervals *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`91c57ed`](https://github.com/JakeStanger/ironbar/commit/91c57edc73f15397ea0de70c4a6a6532c35caf2a) - **sys-info**: pango markup support *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`ec1d596`](https://github.com/JakeStanger/ironbar/commit/ec1d59677b13c9654a98d78f909ba2d0fcfbb72d) - **logging**: `IRONBAR_LOG` and `IRONBAR_FILE_LOG` env vars *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`493df6b`](https://github.com/JakeStanger/ironbar/commit/493df6bb49fec8c465706d3f9b395728ba73a621) - **mpd**: add volume slider to popup *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`3750124`](https://github.com/JakeStanger/ironbar/commit/3750124d8cfb4783932a6b3359384f245fcd2394) - new custom module *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`b7792a4`](https://github.com/JakeStanger/ironbar/commit/b7792a415e09fc535750ea5af530f91aa791c4bc) - env var to set custom css location *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`ad77dc4`](https://github.com/JakeStanger/ironbar/commit/ad77dc4e4c2f80fcb4c9604c796be0f981e895ee) - improved logging & error handling *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :bug: Bug Fixes
|
||||
- [`9e6dbbd`](https://github.com/JakeStanger/ironbar/commit/9e6dbbd131a09f101b0d490265fe7d4ec564e38c) - **sys-info**: tokens not replaced if more than one in string *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`f17ae7a`](https://github.com/JakeStanger/ironbar/commit/f17ae7a415b931c64942de085e8889f37b3f9b11) - **script**: not parsing pango markup *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`b66bd78`](https://github.com/JakeStanger/ironbar/commit/b66bd788b23256a2127a1352693fdd3f929d9c4b) - logging for creating bar incorrect still *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`3c43c20`](https://github.com/JakeStanger/ironbar/commit/3c43c20c6ae53a9aa6b67770b0c489806784f4ac) - weird behaviour when config does not exist *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`70e1b52`](https://github.com/JakeStanger/ironbar/commit/70e1b526a9681b16545d7f05d77470d76bd8819e) - **logging**: file log not capturing panics *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`cbd0c49`](https://github.com/JakeStanger/ironbar/commit/cbd0c49e251b5c8e0289ca6200a393d89994992d) - css watcher not working *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`e693c1c`](https://github.com/JakeStanger/ironbar/commit/e693c1c166eef0b5edcdcd033bb12d572e4e5f04) - **mpd**: volume slider causing mpd server errors *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`3a83bd3`](https://github.com/JakeStanger/ironbar/commit/3a83bd31ab165869f7f274b054b2f16485261fd1) - able to insert duplicate keys into collection *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :recycle: Refactors
|
||||
- [`5ebc84c`](https://github.com/JakeStanger/ironbar/commit/5ebc84c7b98cc648a659ca37fdc0f041057f0ea4) - **logging**: consts for default log levels *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`bc625b9`](https://github.com/JakeStanger/ironbar/commit/bc625b929b8644ce92f275b5d98cdf74b93fe067) - clippy & fmt *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
### :memo: Documentation Changes
|
||||
- [`9d9c275`](https://github.com/JakeStanger/ironbar/commit/9d9c2753137331ae85ac8ab7d75a6de9a9c82042) - update CHANGELOG.md for v0.6.0 [skip ci] *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`27d0479`](https://github.com/JakeStanger/ironbar/commit/27d04795af1c25fe5f765c7480d5dd5d096a8ab7) - **readme**: add warning about crate being outdated *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`a06c4bc`](https://github.com/JakeStanger/ironbar/commit/a06c4bccca6cb51935605ac9239e63024fb7c663) - **examples**: add full system info config *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
- [`0a331f3`](https://github.com/JakeStanger/ironbar/commit/0a331f31381f0d967793c0d8b7a14e2a43bf666f) - **readme**: remove warning about outdated cargo package *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
|
||||
|
||||
## [v0.6.0] - 2022-10-15
|
||||
### :sparkles: New Features
|
||||
- [`b188bc7`](https://github.com/JakeStanger/ironbar/commit/b188bc714614406935d8bb88a719adab2dfce32f) - initial support for running outside sway *(commit by [@JakeStanger](https://github.com/JakeStanger))*
|
||||
@@ -77,4 +109,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[v0.5.0]: https://github.com/JakeStanger/ironbar/compare/v0.4.0...v0.5.0
|
||||
[v0.5.1]: https://github.com/JakeStanger/ironbar/compare/v0.5.0...v0.5.1
|
||||
[v0.5.2]: https://github.com/JakeStanger/ironbar/compare/v0.5.1...v0.5.2
|
||||
[v0.6.0]: https://github.com/JakeStanger/ironbar/compare/v0.5.2...v0.6.0
|
||||
[v0.6.0]: https://github.com/JakeStanger/ironbar/compare/v0.5.2...v0.6.0
|
||||
[v0.7.0]: https://github.com/JakeStanger/ironbar/compare/v0.6.0...v0.7.0
|
||||
293
Cargo.lock
generated
293
Cargo.lock
generated
@@ -30,9 +30,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -75,34 +75,34 @@ version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 1.2.4",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
||||
checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 2.0.0",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.10.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
|
||||
checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"autocfg",
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 2.0.0",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -111,7 +111,7 @@ dependencies = [
|
||||
"slab",
|
||||
"socket2",
|
||||
"waker-fn",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -242,9 +242,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
@@ -254,9 +254,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||
|
||||
[[package]]
|
||||
name = "cairo-rs"
|
||||
version = "0.16.1"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9ee4a4ca9239c9a839453dce04b7ddee2f859ec4cd7acd1f5703b68db549c"
|
||||
checksum = "247e1183fa769ac22121f92276dae52f89acaf297f24b1320019f439b6e3b46f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cairo-sys-rs",
|
||||
@@ -268,9 +268,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cairo-sys-rs"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5119ea655ec777b523f0b57279e70f8a4542f61b0e98a48f892b4ef043fd4c5d"
|
||||
checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
@@ -279,12 +279,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.10.1"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee"
|
||||
checksum = "5bcf530afb40e45e14440701e5e996d7fd139e84a912a4d83a8d6a0fb3e58663"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nix 0.24.2",
|
||||
"nix 0.25.0",
|
||||
"slotmap",
|
||||
"thiserror",
|
||||
"vec_map",
|
||||
@@ -292,9 +292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.74"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
@@ -313,15 +313,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time 0.1.44",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
@@ -372,6 +372,15 @@ dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
@@ -410,22 +419,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.11"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"memoffset 0.7.1",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.12"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -442,9 +451,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
|
||||
checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -454,9 +463,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
|
||||
checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -469,15 +478,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
|
||||
checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
|
||||
checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -563,9 +572,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
@@ -664,7 +673,7 @@ version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"memoffset 0.6.5",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
@@ -789,9 +798,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fb526c8c3a075eda15f961820edf3e15fe18576ac4fbabbb324e4cc6c421e6"
|
||||
checksum = "0ba3e42776d1466938add08211734738d5c76e863a25b7a8064c4433a74a1a26"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gdk-pixbuf-sys",
|
||||
@@ -802,9 +811,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7df12d15c10c3c5a84d9fb4ba0e27659f6a2bdee4f27f8b17126da15d5ddd3f2"
|
||||
checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016"
|
||||
dependencies = [
|
||||
"gio-sys",
|
||||
"glib-sys",
|
||||
@@ -859,9 +868,9 @@ checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.16.2"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c1debf8d0315d69be0153aa76249db3c858ef69b7778ad3cc669e6d370c485"
|
||||
checksum = "1d4a17d999e6e4e05d87c2bb05b7140d47769bc53211711a33e2f91536458714"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures-channel",
|
||||
@@ -879,9 +888,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gio-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6da1bba9d3f2ab13a6e9932c40f240dc99ebc9f0bdc35cfb130d1a3df36f374c"
|
||||
checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
@@ -892,9 +901,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glib"
|
||||
version = "0.16.2"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5abffa711471e015eb93d65d6ea20e7e9f6f7951fc0a1042280439319b2de06"
|
||||
checksum = "50feee2f1e73be50e6634c901bfced69a0937c5e4e4673067ade85e093fa9bd7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"futures-channel",
|
||||
@@ -914,9 +923,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glib-macros"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e195c1311fa6b04d7b896ea39385f6bd60ef5d25bf74a7c11c8c3f94f6c1a572"
|
||||
checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
@@ -929,9 +938,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glib-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b33357bb421a77bd849f6a0bfcaf3b4b256a2577802971bb5dd522d530f27021"
|
||||
checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"system-deps",
|
||||
@@ -939,9 +948,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gobject-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63ca11a57400f3d4fda594e002844be47900c9fb8b29e2155c6e37a1f24e51b3"
|
||||
checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"libc",
|
||||
@@ -950,9 +959,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gtk"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200d06a383a5ce5fa5611fa37f5ab34286289ede05d41c1f21aa8a2e1975c0ba"
|
||||
checksum = "ca4ab4a5a19d45748f405d2e00201e86d351e80cc13b9d43dfb9be35033a8bd6"
|
||||
dependencies = [
|
||||
"atk",
|
||||
"bitflags",
|
||||
@@ -1096,9 +1105,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
@@ -1135,7 +1144,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ironbar"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"async_once",
|
||||
"chrono",
|
||||
@@ -1220,9 +1229,9 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "libcorn"
|
||||
version = "0.4.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0cc0f2cad23e81a2f48e4b0142a9f278bc7369a98b04cd7cc190ede637085da"
|
||||
checksum = "5684465f57a903e77cd58cc0bd9fcdc75b2b1a361731966183bf1f1743da4553"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pest",
|
||||
@@ -1233,9 +1242,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
@@ -1286,9 +1295,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
|
||||
checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1302,6 +1311,15 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@@ -1364,7 +1382,7 @@ dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1376,7 +1394,20 @@ dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1445,23 +1476,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
@@ -1501,9 +1523,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7208c60f224cf6e44c551df5ee2ef38f9da0fd29d7c5a0402000b8ab0520e798"
|
||||
checksum = "f6a83cd4015382dbb0f4fcf3ab7b277d4885711a62b2f2c1e6582a120094edad"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"gio",
|
||||
@@ -1515,9 +1537,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pango-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "922441c228366ed98d3534b87bc7c987c50564094c3abbc3513717786419252d"
|
||||
checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f"
|
||||
dependencies = [
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
@@ -1556,9 +1578,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a"
|
||||
checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -1566,9 +1588,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2"
|
||||
checksum = "423c2ba011d6e27b02b482a3707c773d19aec65cc024637aec44e19652e66f63"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -1576,9 +1598,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db"
|
||||
checksum = "3e64e6c2c85031c02fdbd9e5c72845445ca0a724d419aa0bc068ac620c9935c1"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -1589,9 +1611,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d"
|
||||
checksum = "57959b91f0a133f89a68be874a5c88ed689c19cd729ecdb5d762ebf16c64d662"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
@@ -1618,23 +1640,23 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2"
|
||||
checksum = "9f7d73f1eaed1ca1fb37b54dcc9b38e3b17d6c7b8ecb7abfffcac8d0351f17d4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-ffi",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
@@ -1721,11 +1743,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.3"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
|
||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -1733,9 +1754,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.3"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
|
||||
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
@@ -1765,9 +1786,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1785,9 +1806,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@@ -1880,9 +1901,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1948,6 +1969,15 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@@ -2056,9 +2086,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swayipc-types"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a558cee16f6eeb791722bbf060607cc53f7f9a47fe80055eca81bceebd34928e"
|
||||
checksum = "44b43b4059d825ccc04adf9726f944d0e3aa20938f4cff3b5c6b53198afcd6b3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2078,9 +2108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.26.7"
|
||||
version = "0.26.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c375d5fd899e32847b8566e10598d6e9f1d9b55ec6de3cdf9e7da4bdc51371bc"
|
||||
checksum = "29ddf41e393a9133c81d5f0974195366bd57082deac6e0eb02ed39b8341c2bb6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -2158,9 +2188,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
@@ -2169,13 +2199,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.16"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
@@ -2189,18 +2217,18 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.21.2"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
|
||||
checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -2209,6 +2237,7 @@ dependencies = [
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
@@ -2264,7 +2293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"time 0.3.16",
|
||||
"time 0.3.17",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
@@ -2388,9 +2417,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
|
||||
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
@@ -2739,9 +2768,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a408fd8a352695690f53906dc7fd036be924ec51ea5e05666ff42685ed0af5"
|
||||
checksum = "d69bb79b44e1901ed8b217e485d0f01991aec574479b68cb03415f142bc7ae67"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
@@ -2750,9 +2779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "3.7.1"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b794fb7f59af4105697b0449ba31731ee5dbb3e773a17dbdf3d36206ea1b1644"
|
||||
checksum = "5c817f416f05fcbc833902f1e6064b72b1778573978cfeac54731451ccc9e207"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"enumflags2",
|
||||
@@ -2764,9 +2793,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "3.7.1"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd58d4b6c8e26d3dd2149c8c40c6613ef6451b9885ff1296d1ac86c388351a54"
|
||||
checksum = "fdd24fffd02794a76eb10109de463444064c88f5adb9e9d1a78488adc332bfef"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ironbar"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "Customisable GTK Layer Shell wlroots/sway bar"
|
||||
@@ -10,7 +10,7 @@ derive_builder = "0.11.2"
|
||||
gtk = "0.16.0"
|
||||
gtk-layer-shell = "0.5.0"
|
||||
glib = "0.16.2"
|
||||
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread", "time"] }
|
||||
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread", "time", "process"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
tracing-error = "0.2.0"
|
||||
@@ -21,7 +21,7 @@ serde = { version = "1.0.141", features = ["derive"] }
|
||||
serde_json = "1.0.82"
|
||||
serde_yaml = "0.9.4"
|
||||
toml = "0.5.9"
|
||||
libcorn = "0.4.0"
|
||||
libcorn = "0.6.0"
|
||||
lazy_static = "1.4.0"
|
||||
async_once = "0.2.6"
|
||||
indexmap = "1.9.1"
|
||||
@@ -37,4 +37,4 @@ swayipc-async = { version = "2.0.1" }
|
||||
sysinfo = "0.26.4"
|
||||
wayland-client = "0.29.5"
|
||||
wayland-protocols = { version = "0.29.5", features = ["unstable_protocols", "client"] }
|
||||
smithay-client-toolkit = { version = "0.16.0", default-features = false, features=["calloop"] }
|
||||
smithay-client-toolkit = { version = "0.16.0", default-features = false, features = ["calloop"] }
|
||||
45
README.md
45
README.md
@@ -27,6 +27,49 @@ yay -S ironbar-git
|
||||
|
||||
[aur package](https://aur.archlinux.org/packages/ironbar-git)
|
||||
|
||||
### Nix Flake
|
||||
|
||||
#### Example
|
||||
Here is an example nix flake that uses ironbar, this is just a
|
||||
proof of concept, please adapt it to your config
|
||||
|
||||
```nix
|
||||
{
|
||||
# Add the ironbar flake input
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
inputs.ironbar = {
|
||||
url = "github:JakeStanger/ironbar";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
inputs.hm = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = inputs: {
|
||||
homeManagerConfigurations."USER@HOSTNAME" = inputs.hm.lib.homeManagerConfiguration {
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
modules = [
|
||||
# And add the home-manager module
|
||||
inputs.ironbar.homeManagerModules.default
|
||||
{
|
||||
# And configure
|
||||
programs.ironbar = {
|
||||
enable = true;
|
||||
config = {};
|
||||
style = "";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Binary Caching
|
||||
There is also a cachix cache at `https://app.cachix.org/cache/jakestanger`
|
||||
incase you don't want to compile ironbar!
|
||||
|
||||
### Source
|
||||
|
||||
```sh
|
||||
@@ -86,4 +129,4 @@ Please check [here](https://github.com/JakeStanger/ironbar/blob/master/CONTRIBUT
|
||||
|
||||
- [Waybar](https://github.com/Alexays/Waybar) - A lot of the initial inspiration, and a pretty great bar.
|
||||
- [Rustbar](https://github.com/zeroeightysix/rustbar) - Served as a good demo for writing a basic GTK bar in Rust
|
||||
- [Smithay Client Toolkit](https://github.com/Smithay/client-toolkit) - Essential in being able to communicate to Wayland
|
||||
- [Smithay Client Toolkit](https://github.com/Smithay/client-toolkit) - Essential in being able to communicate to Wayland
|
||||
|
||||
288
docs/Configuration guide.md
Normal file
288
docs/Configuration guide.md
Normal file
@@ -0,0 +1,288 @@
|
||||
By default, you get a single bar at the bottom of all your screens.
|
||||
To change that, you'll unsurprisingly need a config file.
|
||||
|
||||
This page details putting together the skeleton for your config to get you to a stage where you can start configuring modules.
|
||||
It may look long and overwhelming, but that is just because the bar supports a lot of scenarios!
|
||||
|
||||
If you want to see some ready-to-go config files check the [examples folder](https://github.com/JakeStanger/ironbar/tree/master/examples)
|
||||
and the example pages in the sidebar.
|
||||
|
||||
## 1. Create config file
|
||||
|
||||
The config file lives inside the `ironbar` directory in your XDG_CONFIG_DIR, which is usually `~/.config/ironbar`.
|
||||
|
||||
Ironbar supports a range of configuration formats, so you can pick your favourite:
|
||||
|
||||
- `config.json`
|
||||
- `config.toml`
|
||||
- `config.yaml`
|
||||
- `config.corn` (Experimental, includes variable support for re-using blocks.
|
||||
See [here](https://github.com/jakestanger/corn) for info)
|
||||
|
||||
You can also override the default config path using the `IRONBAR_CONFIG` environment variable.
|
||||
|
||||
## 2. Pick your use-case
|
||||
|
||||
Ironbar gives you a few ways to configure the bar to suit your needs.
|
||||
This allows you to keep your config simple and relatively flat if your use-case is simple,
|
||||
and make it more complex if required.
|
||||
|
||||
### a) I want the same bar across all monitors
|
||||
|
||||
Place the bar config inside the top-level object. This is automatically applied to each of your monitors.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"position": "bottom",
|
||||
"height": 42,
|
||||
"start": [],
|
||||
"center": [],
|
||||
"end": []
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
position = "bottom"
|
||||
height = 42
|
||||
start = []
|
||||
center = []
|
||||
end = []
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
position: "bottom"
|
||||
height: 42
|
||||
start: [ ]
|
||||
center: [ ]
|
||||
end: [ ]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```
|
||||
{
|
||||
position = "bottom"
|
||||
height = 42
|
||||
start = []
|
||||
center = []
|
||||
end = []
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### b) I want my config to differ across one or more monitors
|
||||
|
||||
Create a map/object called `monitors` inside the top-level object.
|
||||
Each of the map's keys should be an output name,
|
||||
and each value should be an object containing the bar config.
|
||||
|
||||
To find your output names, run `wayland-info | grep wl_output -A1`.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"monitors": {
|
||||
"DP-1": {
|
||||
"start": []
|
||||
},
|
||||
"DP-2": {
|
||||
"position": "bottom",
|
||||
"height": 30,
|
||||
"start": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[monitors]
|
||||
|
||||
[monitors.DP-1]
|
||||
start = []
|
||||
|
||||
[monitors.DP-2]
|
||||
position = "bottom"
|
||||
height = 30
|
||||
start = []
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
monitors:
|
||||
DP-1:
|
||||
start: [ ]
|
||||
DP-2:
|
||||
position: "bottom"
|
||||
height: 30
|
||||
start: [ ]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```
|
||||
{
|
||||
monitors.DP-1.start = []
|
||||
monitors.DP-2 = {
|
||||
position = "bottom"
|
||||
height = 30
|
||||
start = []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### c) I want one or more monitors to have multiple bars
|
||||
|
||||
Create a map/object called `monitors` inside the top-level object.
|
||||
Each of the map's keys should be an output name.
|
||||
If you want the screen to have multiple bars, use an array of bar config objects.
|
||||
If you want the screen to have a single bar, use an object.
|
||||
|
||||
To find your output names, run `wayland-info | grep wl_output -A1`.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"monitors": {
|
||||
"DP-1": [
|
||||
{
|
||||
"start": []
|
||||
},
|
||||
{
|
||||
"position": "top",
|
||||
"start": []
|
||||
}
|
||||
],
|
||||
"DP-2": {
|
||||
"position": "bottom",
|
||||
"height": 30,
|
||||
"start": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[monitors]
|
||||
|
||||
[[monitors.DP-1]]
|
||||
start = []
|
||||
|
||||
[[monitors.DP-2]]
|
||||
position = "top"
|
||||
start = []
|
||||
|
||||
[monitors.DP-2]
|
||||
position = "bottom"
|
||||
height = 30
|
||||
start = []
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
monitors:
|
||||
DP-1:
|
||||
- start: [ ]
|
||||
- position: "top"
|
||||
start: [ ]
|
||||
DP-2:
|
||||
position: "bottom"
|
||||
height: 30
|
||||
start: [ ]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```
|
||||
{
|
||||
monitors.DP-1 = [
|
||||
{ start = [] }
|
||||
{ position = "top" start = [] }
|
||||
]
|
||||
monitors.DP-2 = {
|
||||
position = "bottom"
|
||||
height = 30
|
||||
start = []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 3. Write your bar config(s)
|
||||
|
||||
Once you have the basic config structure set up, it's time to actually configure your bar(s).
|
||||
|
||||
Check [here](config) for an example config file for a fully configured bar in each format.
|
||||
|
||||
### 3.1 Top-level options
|
||||
|
||||
The following table lists each of the top-level bar config options:
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|-------------------|----------------------------------------|----------|-----------------------------------------------------------------------------------------|
|
||||
| `position` | `top` or `bottom` or `left` or `right` | `bottom` | The bar's position on screen. |
|
||||
| `anchor_to_edges` | `boolean` | `false` | Whether to anchor the bar to the edges of the screen. Setting to false centres the bar. |
|
||||
| `height` | `integer` | `42` | The bar's height in pixels. |
|
||||
| `start` | `Module[]` | `[]` | Array of left or top modules. |
|
||||
| `center` | `Module[]` | `[]` | Array of center modules. |
|
||||
| `end` | `Module[]` | `[]` | Array of right or bottom modules. |
|
||||
|
||||
### 3.2 Module-level options
|
||||
|
||||
The following table lists each of the module-level options that are present on **all** modules.
|
||||
For details on available modules and each of their config options, check the sidebar.
|
||||
|
||||
For information on the `Script` type, and embedding scripts in strings, see [here](script).
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|------------|--------------------|---------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| `show_if` | `Script [polling]` | `null` | Polls the script to check its exit code. If exit code is zero, the module is shown. For other codes, it is hidden. |
|
||||
| `on_click` | `Script [polling]` | `null` | Runs the script when the module is clicked. |
|
||||
| `tooltip` | `string` | `null` | Shows this text on hover. Supports embedding scripts between `{{double braces}}`. |
|
||||
4
docs/Home.md
Normal file
4
docs/Home.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Welcome to the Ironbar wiki.
|
||||
|
||||
Detail about each module, and their configuration and styling options can be found on the sidebar.
|
||||
You can also find an example configuration and stylesheet there.
|
||||
102
docs/Scripts.md
Normal file
102
docs/Scripts.md
Normal file
@@ -0,0 +1,102 @@
|
||||
There are various places inside the configuration (other than the `script` module)
|
||||
that allow script input to dynamically set values.
|
||||
|
||||
Scripts are passed to `sh -c`.
|
||||
|
||||
Two types of scripts exist: polling and watching:
|
||||
|
||||
- Polling scripts will run and wait for exit.
|
||||
Normally they will repeat this at an interval, hence the name, although in some cases they may only run on a user
|
||||
event.
|
||||
If the script exited code 0, the `stdout` will be used. Otherwise, `stderr` will be printed to the log.
|
||||
- Watching scripts start a long-running process. Every time the process writes to `stdout`, the last line is captured
|
||||
and used.
|
||||
|
||||
One should prefer to use watch-mode where possible, as it removes the overhead of regularly spawning processes.
|
||||
That said, there are some cases which only support polling. These are indicated by `Script [polling]` as the option
|
||||
type.
|
||||
|
||||
## Writing script configs
|
||||
|
||||
There are two available config formats for scripts, shorthand as a string, or longhand as an object.
|
||||
Shorthand can be used in all cases, but there are some cases (such as embedding scripts inside strings) where longhand
|
||||
cannot be used.
|
||||
|
||||
In both formats, `mode` is one of `poll` or `watch` and `interval` is the number of milliseconds to wait between
|
||||
spawning the script.
|
||||
|
||||
Both `mode` and `interval` are optional and can be excluded to fall back to their defaults of `poll` and `5000`
|
||||
respectively.
|
||||
|
||||
### Shorthand (string)
|
||||
|
||||
Shorthand scripts should be written in the format:
|
||||
|
||||
```
|
||||
mode:interval:script
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
poll:5000:uptime -p | cut -d ' ' -f2-
|
||||
```
|
||||
|
||||
#### Embedding
|
||||
|
||||
Some string config options support "embedding scripts". This allows you to mix static/dynamic content.
|
||||
An example of this is the common `tooltip` option.
|
||||
|
||||
Scripts can be embedded in these cases using `{{double braces}}` and the shorthand syntax:
|
||||
|
||||
```json
|
||||
"Up: {{30000:uptime -p | cut -d ' ' -f2-}}"
|
||||
```
|
||||
|
||||
### Longhand (object)
|
||||
|
||||
An object consisting of the `cmd` key and optionally the `mode` and/or `interval` keys.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "poll",
|
||||
"interval": 5000,
|
||||
"cmd": "uptime -p | cut -d ' ' -f2-"
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
mode: poll
|
||||
interval: 5000
|
||||
cmd: "uptime -p | cut -d ' ' -f2-"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```toml
|
||||
mode = "poll"
|
||||
interval = 5000
|
||||
cmd = "uptime -p | cut -d ' ' -f2-"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
mode = "poll"
|
||||
interval = 5000
|
||||
cmd = "uptime -p | cut -d ' ' -f2-"
|
||||
}
|
||||
```
|
||||
</details>
|
||||
20
docs/Styling guide.md
Normal file
20
docs/Styling guide.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Ironbar ships with no styles by default, so will fall back to the default GTK styles.
|
||||
|
||||
To style the bar, create a file at `~/.config/ironbar/style.css`.
|
||||
|
||||
Style changes are hot-loaded so there is no need to reload the bar.
|
||||
|
||||
A reminder: since the bar is GTK-based, it uses GTK's implementation of CSS,
|
||||
which only includes a subset of the full web spec (plus a few non-standard properties).
|
||||
|
||||
The below table describes the selectors provided by the bar itself.
|
||||
Information on styling individual modules can be found on their pages in the sidebar.
|
||||
|
||||
| Selector | Description |
|
||||
|----------------|-------------------------------------------|
|
||||
| `.background` | Top-level window |
|
||||
| `#bar` | Bar root box |
|
||||
| `#bar #start` | Bar left or top modules container box |
|
||||
| `#bar #center` | Bar center modules container box |
|
||||
| `#bar #end` | Bar right or bottom modules container box |
|
||||
| `.container` | All of the above |
|
||||
26
docs/_Sidebar.md
Normal file
26
docs/_Sidebar.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Guides
|
||||
|
||||
- [Configuration guide](configuration-guide)
|
||||
- [Scripts](scripts)
|
||||
- [Styling guide](styling-guide)
|
||||
|
||||
# Examples
|
||||
|
||||
- [Config](config)
|
||||
- [Stylesheet](stylesheet)
|
||||
|
||||
## Custom
|
||||
|
||||
- [Power Menu](power-menu)
|
||||
|
||||
# Modules
|
||||
|
||||
- [Clock](clock)
|
||||
- [Custom](custom)
|
||||
- [Focused](focused)
|
||||
- [Launcher](launcher)
|
||||
- [MPD](mpd)
|
||||
- [Script](script)
|
||||
- [Sys_Info](sys-info)
|
||||
- [Tray](tray)
|
||||
- [Workspaces](workspaces)
|
||||
BIN
docs/_imgs/custom-power-menu.png
Normal file
BIN
docs/_imgs/custom-power-menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
202
docs/examples/Config.md
Normal file
202
docs/examples/Config.md
Normal file
@@ -0,0 +1,202 @@
|
||||
The below config shows a module of each type being used.
|
||||
|
||||
The Corn format makes heavy use of variables
|
||||
to show how module configs can be easily referenced to improve readability
|
||||
and reduce config length when using multiple bars.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"start": [
|
||||
{
|
||||
"all_monitors": false,
|
||||
"name_map": {
|
||||
"1": "ﭮ",
|
||||
"2": "",
|
||||
"3": "",
|
||||
"Code": "",
|
||||
"Games": ""
|
||||
},
|
||||
"type": "workspaces"
|
||||
},
|
||||
{
|
||||
"favorites": [
|
||||
"firefox",
|
||||
"discord",
|
||||
"Steam"
|
||||
],
|
||||
"icon_theme": "Paper",
|
||||
"show_icons": true,
|
||||
"show_names": false,
|
||||
"type": "launcher"
|
||||
}
|
||||
],
|
||||
"end": [
|
||||
{
|
||||
"music_dir": "/home/jake/Music",
|
||||
"type": "mpd"
|
||||
},
|
||||
{
|
||||
"host": "chloe:6600",
|
||||
"type": "mpd"
|
||||
},
|
||||
{
|
||||
"path": "/home/jake/bin/phone-battery",
|
||||
"type": "script"
|
||||
},
|
||||
{
|
||||
"format": [
|
||||
"{cpu-percent}% ",
|
||||
"{memory-percent}% "
|
||||
],
|
||||
"type": "sys-info"
|
||||
},
|
||||
{
|
||||
"type": "clock"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[start]]
|
||||
all_monitors = false
|
||||
type = 'workspaces'
|
||||
|
||||
[start.name_map]
|
||||
1 = 'ﭮ'
|
||||
2 = ''
|
||||
3 = ''
|
||||
Code = ''
|
||||
Games = ''
|
||||
|
||||
[[start]]
|
||||
icon_theme = 'Paper'
|
||||
show_icons = true
|
||||
show_names = false
|
||||
type = 'launcher'
|
||||
favorites = [
|
||||
'firefox',
|
||||
'discord',
|
||||
'Steam',
|
||||
]
|
||||
|
||||
[[end]]
|
||||
music_dir = '/home/jake/Music'
|
||||
type = 'mpd'
|
||||
|
||||
[[end]]
|
||||
host = 'chloe:6600'
|
||||
type = 'mpd'
|
||||
|
||||
[[end]]
|
||||
path = '/home/jake/bin/phone-battery'
|
||||
type = 'script'
|
||||
|
||||
[[end]]
|
||||
type = 'sys-info'
|
||||
format = [
|
||||
'{cpu-percent}% ',
|
||||
'{memory-percent}% ',
|
||||
]
|
||||
|
||||
[[end]]
|
||||
type = 'clock'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
---
|
||||
start:
|
||||
- all_monitors: false
|
||||
name_map:
|
||||
"1": ﭮ
|
||||
"2":
|
||||
"3":
|
||||
Code:
|
||||
Games:
|
||||
type: workspaces
|
||||
- favorites:
|
||||
- firefox
|
||||
- discord
|
||||
- Steam
|
||||
icon_theme: Paper
|
||||
show_icons: true
|
||||
show_names: false
|
||||
type: launcher
|
||||
end:
|
||||
- music_dir: /home/jake/Music
|
||||
type: mpd
|
||||
- host: "chloe:6600"
|
||||
type: mpd
|
||||
- path: /home/jake/bin/phone-battery
|
||||
type: script
|
||||
- format:
|
||||
- "{cpu-percent}% "
|
||||
- "{memory-percent}% "
|
||||
type: sys-info
|
||||
- type: clock
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
let {
|
||||
$workspaces = {
|
||||
type = "workspaces"
|
||||
all_monitors = false
|
||||
name_map = {
|
||||
1 = "ﭮ"
|
||||
2 = ""
|
||||
3 = ""
|
||||
Games = ""
|
||||
Code = ""
|
||||
}
|
||||
}
|
||||
|
||||
$launcher = {
|
||||
type = "launcher"
|
||||
favorites = ["firefox" "discord" "Steam"]
|
||||
show_names = false
|
||||
show_icons = true
|
||||
icon_theme = "Paper"
|
||||
}
|
||||
|
||||
$mpd_local = { type = "mpd" music_dir = "/home/jake/Music" }
|
||||
$mpd_server = { type = "mpd" host = "chloe:6600" }
|
||||
|
||||
$sys_info = {
|
||||
type = "sys-info"
|
||||
format = ["{cpu-percent}% " "{memory-percent}% "]
|
||||
}
|
||||
|
||||
$tray = { type = "tray" }
|
||||
$clock = { type = "clock" }
|
||||
|
||||
$phone_battery = {
|
||||
type = "script"
|
||||
path = "/home/jake/bin/phone-battery"
|
||||
}
|
||||
|
||||
$start = [ $workspaces $launcher ]
|
||||
$end = [ $mpd_local $mpd_server $phone_battery $sys_info $clock ]
|
||||
}
|
||||
in {
|
||||
start = $start
|
||||
end = $end
|
||||
}
|
||||
```
|
||||
</details>
|
||||
142
docs/examples/Stylesheet.md
Normal file
142
docs/examples/Stylesheet.md
Normal file
@@ -0,0 +1,142 @@
|
||||
The below example is a full stylesheet for all modules:
|
||||
|
||||
```css
|
||||
* {
|
||||
/* a nerd font is required to be installed for icons */
|
||||
font-family: Noto Sans Nerd Font, sans-serif;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#bar {
|
||||
border-top: 1px solid #424242;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
.container#end > * + * {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.popup {
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid #424242;
|
||||
}
|
||||
|
||||
#workspaces .item {
|
||||
color: white;
|
||||
background-color: #2d2d2d;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#workspaces .item.focused {
|
||||
box-shadow: inset 0 -3px;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
#workspaces *:not(.focused):hover {
|
||||
box-shadow: inset 0 -3px;
|
||||
}
|
||||
|
||||
#launcher .item {
|
||||
border-radius: 0;
|
||||
background-color: #2d2d2d;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#launcher .item:not(.focused):hover {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
#launcher .open {
|
||||
border-bottom: 2px solid #6699cc;
|
||||
}
|
||||
|
||||
#launcher .focused {
|
||||
color: white;
|
||||
background-color: black;
|
||||
border-bottom: 4px solid #6699cc;
|
||||
}
|
||||
|
||||
#launcher .urgent {
|
||||
color: white;
|
||||
background-color: #8f0a0a;
|
||||
}
|
||||
|
||||
#script {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#sysinfo {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#tray .item {
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
#mpd {
|
||||
background-color: #2d2d2d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#popup-mpd {
|
||||
color: white;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#popup-mpd #album-art {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
#popup-mpd #title .icon, #popup-mpd #title .label {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
#popup-mpd #controls * {
|
||||
border-radius: 0;
|
||||
background-color: #2d2d2d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#popup-mpd #controls *:disabled {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
#clock {
|
||||
color: white;
|
||||
background-color: #2d2d2d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#popup-clock {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#popup-clock #calendar-clock {
|
||||
color: white;
|
||||
font-size: 2.5em;
|
||||
padding-bottom: 0.1em;
|
||||
}
|
||||
|
||||
#popup-clock #calendar {
|
||||
background-color: #2d2d2d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#popup-clock #calendar .header {
|
||||
padding-top: 1em;
|
||||
border-top: 1px solid #424242;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#popup-clock #calendar:selected {
|
||||
background-color: #6699cc;
|
||||
}
|
||||
|
||||
#focused {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
230
docs/examples/custom/Power-Menu.md
Normal file
230
docs/examples/custom/Power-Menu.md
Normal file
@@ -0,0 +1,230 @@
|
||||
Creates a button on the bar, which opens a popup. The popup contains a header, shutdown button, restart button, and uptime.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "clock"
|
||||
},
|
||||
{
|
||||
"bar": [
|
||||
{
|
||||
"on_click": "popup:toggle",
|
||||
"label": "",
|
||||
"name": "power-btn",
|
||||
"type": "button"
|
||||
}
|
||||
],
|
||||
"class": "power-menu",
|
||||
"popup": [
|
||||
{
|
||||
"orientation": "vertical",
|
||||
"type": "box",
|
||||
"widgets": [
|
||||
{
|
||||
"label": "Power menu",
|
||||
"name": "header",
|
||||
"type": "label"
|
||||
},
|
||||
{
|
||||
"type": "box",
|
||||
"widgets": [
|
||||
{
|
||||
"class": "power-btn",
|
||||
"on_click": "!shutdown now",
|
||||
"label": "<span font-size='40pt'></span>",
|
||||
"type": "button"
|
||||
},
|
||||
{
|
||||
"class": "power-btn",
|
||||
"on_click": "!reboot",
|
||||
"label": "<span font-size='40pt'></span>",
|
||||
"type": "button"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Up: {{30000:uptime -p | cut -d ' ' -f2-}}",
|
||||
"name": "uptime",
|
||||
"type": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "custom"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = 'clock'
|
||||
|
||||
[[end]]
|
||||
class = 'power-menu'
|
||||
type = 'custom'
|
||||
|
||||
[[end.bar]]
|
||||
on_click = 'popup:toggle'
|
||||
label = ''
|
||||
name = 'power-btn'
|
||||
type = 'button'
|
||||
|
||||
[[end.popup]]
|
||||
orientation = 'vertical'
|
||||
type = 'box'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
label = 'Power menu'
|
||||
name = 'header'
|
||||
type = 'label'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
type = 'box'
|
||||
|
||||
[[end.popup.widgets.widgets]]
|
||||
class = 'power-btn'
|
||||
on_click = '!shutdown now'
|
||||
label = '''<span font-size='40pt'></span>'''
|
||||
type = 'button'
|
||||
|
||||
[[end.popup.widgets.widgets]]
|
||||
class = 'power-btn'
|
||||
on_click = '!reboot'
|
||||
label = '''<span font-size='40pt'></span>'''
|
||||
type = 'button'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
label = '''Up: {{30000:uptime -p | cut -d ' ' -f2-}}'''
|
||||
name = 'uptime'
|
||||
type = 'label'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: clock
|
||||
- bar:
|
||||
- on_click: popup:toggle
|
||||
label:
|
||||
name: power-btn
|
||||
type: button
|
||||
class: power-menu
|
||||
popup:
|
||||
- orientation: vertical
|
||||
type: box
|
||||
widgets:
|
||||
- label: Power menu
|
||||
name: header
|
||||
type: label
|
||||
- type: box
|
||||
widgets:
|
||||
- class: power-btn
|
||||
on_click: '!shutdown now'
|
||||
label: <span font-size='40pt'></span>
|
||||
type: button
|
||||
- class: power-btn
|
||||
on_click: '!reboot'
|
||||
label: <span font-size='40pt'></span>
|
||||
type: button
|
||||
- label: 'Up: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
||||
name: uptime
|
||||
type: label
|
||||
type: custom
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
let {
|
||||
$power_menu = {
|
||||
type = "custom"
|
||||
class = "power-menu"
|
||||
|
||||
bar = [ { type = "button" name="power-btn" label = "" on_click = "popup:toggle" } ]
|
||||
|
||||
popup = [ {
|
||||
type = "box"
|
||||
orientation = "vertical"
|
||||
widgets = [
|
||||
{ type = "label" name = "header" label = "Power menu" }
|
||||
{
|
||||
type = "box"
|
||||
widgets = [
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!shutdown now" }
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!reboot" }
|
||||
]
|
||||
}
|
||||
{ type = "label" name = "uptime" label = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" }
|
||||
]
|
||||
} ]
|
||||
}
|
||||
} in {
|
||||
end = [ $power_menu ]
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
```css
|
||||
.power-menu {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.power-menu #power-btn {
|
||||
color: white;
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
.power-menu #power-btn:hover {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
.popup-power-menu {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.popup-power-menu #header {
|
||||
color: white;
|
||||
font-size: 1.4em;
|
||||
border-bottom: 1px solid white;
|
||||
padding-bottom: 0.4em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
.popup-power-menu .power-btn {
|
||||
color: white;
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid white;
|
||||
padding: 0.6em 1em;
|
||||
}
|
||||
|
||||
.popup-power-menu .power-btn + .power-btn {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.popup-power-menu .power-btn:hover {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
```
|
||||
77
docs/modules/Clock.md
Normal file
77
docs/modules/Clock.md
Normal file
@@ -0,0 +1,77 @@
|
||||
Displays the current date and time.
|
||||
Clicking on the widget opens a popup with the time and a calendar.
|
||||
|
||||

|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `clock`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|----------|--------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Detail on available tokens can be found here: <https://docs.rs/chrono/latest/chrono/format/strftime/index.html> |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "clock",
|
||||
"format": "%d/%m/%Y %H:%M"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = "clock"
|
||||
format = "%d/%m/%Y %H:%M"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: "clock"
|
||||
format: "%d/%m/%Y %H:%M"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "clock"
|
||||
format = "%d/%m/%Y %H:%M"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|-------------------------------|------------------------------------------------------------------------------------|
|
||||
| `#clock` | Clock widget button |
|
||||
| `#popup-clock` | Clock popup box |
|
||||
| `#popup-clock #calendar-clock` | Clock inside the popup |
|
||||
| `#popup-clock #calendar` | Calendar widget inside the popup. GTK provides some OOTB styling options for this. |
|
||||
272
docs/modules/Custom.md
Normal file
272
docs/modules/Custom.md
Normal file
@@ -0,0 +1,272 @@
|
||||
Allows you to compose custom modules consisting of multiple widgets, including popups.
|
||||
Labels can display dynamic content from scripts, and buttons can interact with the bar or execute commands on click.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `custom`
|
||||
|
||||
This module can be quite fiddly to configure as you effectively have to build a tree of widgets by hand.
|
||||
It is well worth looking at the examples.
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|---------|------------|---------|--------------------------------------|
|
||||
| `class` | `string` | `null` | Container class name. |
|
||||
| `bar` | `Widget[]` | `null` | List of widgets to add to the bar. |
|
||||
| `popup` | `Widget[]` | `[]` | List of widgets to add to the popup. |
|
||||
|
||||
### `Widget`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|---------------|------------------------------|--------------|---------------------------------------------------------------------------|
|
||||
| `widget_type` | `box` or `label` or `button` | `null` | Type of GTK widget to create. |
|
||||
| `name` | `string` | `null` | Widget name. |
|
||||
| `class` | `string` | `null` | Widget class name. |
|
||||
| `label` | `string` | `null` | [`label` and `button`] Widget text label. Pango markup supported. |
|
||||
| `on_click` | `string` | `null` | [`button`] Command to execute. More on this [below](#commands). |
|
||||
| `orientation` | `horizontal` or `vertical` | `horizontal` | [`box`] Whether child widgets should be horizontally or vertically added. |
|
||||
| `widgets` | `Widget[]` | `[]` | [`box`] List of widgets to add to this box. |
|
||||
|
||||
### Labels
|
||||
|
||||
Labels can interpolate text from scripts to dynamically show content.
|
||||
This can be done by including scripts in `{{double braces}}` using the shorthand script syntax.
|
||||
|
||||
For example, the following label would output your system uptime, updated every 30 seconds.
|
||||
|
||||
```
|
||||
Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}
|
||||
```
|
||||
|
||||
Both polling and watching mode are supported. For more information on script syntax, see [here](script).
|
||||
|
||||
### Commands
|
||||
|
||||
Buttons can execute commands that interact with the bar,
|
||||
as well as any arbitrary shell command.
|
||||
|
||||
To execute shell commands, prefix them with an `!`.
|
||||
For example, if you want to run `~/.local/bin/my-script.sh` on click,
|
||||
you'd set `on_click` to `!~/.local/bin/my-script.sh`.
|
||||
|
||||
The following bar commands are supported:
|
||||
|
||||
- `popup:toggle`
|
||||
- `popup:open`
|
||||
- `popup:close`
|
||||
|
||||
XML is arguably better-suited and easier to read for this sort of markup,
|
||||
but currently is not supported.
|
||||
Nonetheless, it may be worth comparing the examples to the below equivalent
|
||||
to help get your head around what's going on:
|
||||
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<custom class="power-menu">
|
||||
<bar>
|
||||
<button name="power-btn" label="" on_click="popup:toggle"/>
|
||||
</bar>
|
||||
<popup>
|
||||
<box orientation="vertical">
|
||||
<label name="header" label="Power menu" />
|
||||
<box>
|
||||
<button class="power-btn" label="" on_click="!shutdown now" />
|
||||
<button class="power-btn" label="" on_click="!reboot" />
|
||||
</box>
|
||||
<label name="uptime" label="Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" />
|
||||
</box>
|
||||
</popup>
|
||||
</custom>
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "clock"
|
||||
},
|
||||
{
|
||||
"bar": [
|
||||
{
|
||||
"on_click": "popup:toggle",
|
||||
"label": "",
|
||||
"name": "power-btn",
|
||||
"type": "button"
|
||||
}
|
||||
],
|
||||
"class": "power-menu",
|
||||
"popup": [
|
||||
{
|
||||
"orientation": "vertical",
|
||||
"type": "box",
|
||||
"widgets": [
|
||||
{
|
||||
"label": "Power menu",
|
||||
"name": "header",
|
||||
"type": "label"
|
||||
},
|
||||
{
|
||||
"type": "box",
|
||||
"widgets": [
|
||||
{
|
||||
"class": "power-btn",
|
||||
"on_click": "!shutdown now",
|
||||
"label": "<span font-size='40pt'></span>",
|
||||
"type": "button"
|
||||
},
|
||||
{
|
||||
"class": "power-btn",
|
||||
"on_click": "!reboot",
|
||||
"label": "<span font-size='40pt'></span>",
|
||||
"type": "button"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}",
|
||||
"name": "uptime",
|
||||
"type": "label"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "custom"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = 'clock'
|
||||
|
||||
[[end]]
|
||||
class = 'power-menu'
|
||||
type = 'custom'
|
||||
|
||||
[[end.bar]]
|
||||
on_click = 'popup:toggle'
|
||||
label = ''
|
||||
name = 'power-btn'
|
||||
type = 'button'
|
||||
|
||||
[[end.popup]]
|
||||
orientation = 'vertical'
|
||||
type = 'box'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
label = 'Power menu'
|
||||
name = 'header'
|
||||
type = 'label'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
type = 'box'
|
||||
|
||||
[[end.popup.widgets.widgets]]
|
||||
class = 'power-btn'
|
||||
on_click = '!shutdown now'
|
||||
label = '''<span font-size='40pt'></span>'''
|
||||
type = 'button'
|
||||
|
||||
[[end.popup.widgets.widgets]]
|
||||
class = 'power-btn'
|
||||
on_click = '!reboot'
|
||||
label = '''<span font-size='40pt'></span>'''
|
||||
type = 'button'
|
||||
|
||||
[[end.popup.widgets]]
|
||||
label = '''Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}'''
|
||||
name = 'uptime'
|
||||
type = 'label'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: clock
|
||||
- bar:
|
||||
- on_click: popup:toggle
|
||||
label:
|
||||
name: power-btn
|
||||
type: button
|
||||
class: power-menu
|
||||
popup:
|
||||
- orientation: vertical
|
||||
type: box
|
||||
widgets:
|
||||
- label: Power menu
|
||||
name: header
|
||||
type: label
|
||||
- type: box
|
||||
widgets:
|
||||
- class: power-btn
|
||||
on_click: '!shutdown now'
|
||||
label: <span font-size='40pt'></span>
|
||||
type: button
|
||||
- class: power-btn
|
||||
on_click: '!reboot'
|
||||
label: <span font-size='40pt'></span>
|
||||
type: button
|
||||
- label: 'Uptime: {{30000:uptime -p | cut -d '' '' -f2-}}'
|
||||
name: uptime
|
||||
type: label
|
||||
type: custom
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
let {
|
||||
$power_menu = {
|
||||
type = "custom"
|
||||
class = "power-menu"
|
||||
|
||||
bar = [ { type = "button" name="power-btn" label = "" on_click = "popup:toggle" } ]
|
||||
|
||||
popup = [ {
|
||||
type = "box"
|
||||
orientation = "vertical"
|
||||
widgets = [
|
||||
{ type = "label" name = "header" label = "Power menu" }
|
||||
{
|
||||
type = "box"
|
||||
widgets = [
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!shutdown now" }
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!reboot" }
|
||||
]
|
||||
}
|
||||
{ type = "label" name = "uptime" label = "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" }
|
||||
]
|
||||
} ]
|
||||
}
|
||||
} in {
|
||||
end = [ $power_menu ]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
Since the widgets are all custom, you can target them using `#name` and `.class`.
|
||||
|
||||
| Selector | Description |
|
||||
|-----------|-------------------------|
|
||||
| `#custom` | Custom widget container |
|
||||
90
docs/modules/Focused.md
Normal file
90
docs/modules/Focused.md
Normal file
@@ -0,0 +1,90 @@
|
||||
Displays the title and/or icon of the currently focused window.
|
||||
|
||||

|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `focused`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|--------------|-----------|---------|---------------------------------|
|
||||
| `show_icon` | `boolean` | `true` | Whether to show the app's icon |
|
||||
| `show_title` | `boolean` | `true` | Whether to show the app's title |
|
||||
| `icon_size` | `integer` | `32` | Size of icon in pixels |
|
||||
| `icon_theme` | `string` | `null` | GTK icon theme to use |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "focused",
|
||||
"show_icon": true,
|
||||
"show_title": true,
|
||||
"icon_size": 32,
|
||||
"icon_theme": "Paper"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = "focused"
|
||||
show_icon = true
|
||||
show_title = true
|
||||
icon_size = 32
|
||||
icon_theme = "Paper"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: "focused"
|
||||
show_icon: true
|
||||
show_title: true
|
||||
icon_size: 32
|
||||
icon_theme: "Paper"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "focused"
|
||||
show_icon = true
|
||||
show_title = true
|
||||
icon_size = 32
|
||||
icon_theme = "Paper"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|--------------------------|--------------------|
|
||||
| `#focused` | Focused widget box |
|
||||
| `#focused #icon` | App icon |
|
||||
| `#focused #label` | App name |
|
||||
103
docs/modules/Launcher.md
Normal file
103
docs/modules/Launcher.md
Normal file
@@ -0,0 +1,103 @@
|
||||
Windows-style taskbar that displays running windows, grouped by program.
|
||||
Hovering over a program with multiple windows open shows a popup with each window.
|
||||
Clicking an icon/popup item focuses or launches the program.
|
||||
Optionally displays a launchable set of favourites.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `launcher`
|
||||
|
||||
| | Type | Default | Description |
|
||||
|--------------|------------|---------|-----------------------------------------------------------------------------------------------------|
|
||||
| `favorites` | `string[]` | `[]` | List of app IDs (or classes) to always show at the start of the launcher |
|
||||
| `show_names` | `boolean` | `false` | Whether to show app names on the button label. Names will still show on tooltips when set to false. |
|
||||
| `show_icons` | `boolean` | `true` | Whether to show app icons on the button. |
|
||||
| `icon_theme` | `string` | `null` | GTK icon theme to use. |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"start": [
|
||||
{
|
||||
"type": "launcher",
|
||||
"favourites": [
|
||||
"firefox",
|
||||
"discord"
|
||||
],
|
||||
"show_names": false,
|
||||
"show_icons": true,
|
||||
"icon_theme": "Paper"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[start]]
|
||||
type = "launcher"
|
||||
favorites = ["firefox", "discord"]
|
||||
show_names = false
|
||||
show_icons = true
|
||||
icon_theme = "Paper"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
start:
|
||||
- type: "launcher"
|
||||
favorites:
|
||||
- firefox
|
||||
- discord
|
||||
show_names: false
|
||||
show_icons: true
|
||||
icon_theme: "Paper"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
start = [
|
||||
{
|
||||
type = "launcher"
|
||||
favorites = ["firefox" "discord"]
|
||||
show_names = false
|
||||
show_icons = true
|
||||
icon_theme = "Paper"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|-------------------------------|--------------------------|
|
||||
| `#launcher` | Launcher widget box |
|
||||
| `#launcher .item` | App button |
|
||||
| `#launcher .item.open` | App button (open app) |
|
||||
| `#launcher .item.focused` | App button (focused app) |
|
||||
| `#launcher .item.urgent` | App button (urgent app) |
|
||||
| `#launcher-popup` | Popup container |
|
||||
| `#launcher-popup .popup-item` | Window button in popup |
|
||||
131
docs/modules/MPD.md
Normal file
131
docs/modules/MPD.md
Normal file
@@ -0,0 +1,131 @@
|
||||
Displays currently playing song from MPD.
|
||||
Clicking on the widget opens a popout displaying info about the current song, album art
|
||||
and playback controls.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `mpd`
|
||||
|
||||
| | Type | Default | Description |
|
||||
|----------------|----------|-----------------------------|-----------------------------------------------------------------------|
|
||||
| `host` | `string` | `localhost:6600` | TCP or Unix socket for the MPD server. |
|
||||
| `format` | `string` | `{icon} {title} / {artist}` | Format string for the widget. More info below. |
|
||||
| `icons.play` | `string` | `` | Icon to show when playing. |
|
||||
| `icons.pause` | `string` | `` | Icon to show when paused. |
|
||||
| `icons.volume` | `string` | `墳` | Icon to show under popup volume slider. |
|
||||
| `music_dir` | `string` | `$HOME/Music` | Path to MPD server's music directory on disc. Required for album art. |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"start": [
|
||||
{
|
||||
"type": "mpd",
|
||||
"format": "{icon} {title} / {artist}",
|
||||
"icons": {
|
||||
"play": "",
|
||||
"pause": ""
|
||||
},
|
||||
"music_dir": "/home/jake/Music"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[start]]
|
||||
type = "mpd"
|
||||
format = "{icon} {title} / {artist}"
|
||||
music_dir = "/home/jake/Music"
|
||||
|
||||
[[start.icons]]
|
||||
play = ""
|
||||
pause = ""
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
start:
|
||||
- type: "mpd"
|
||||
format: "{icon} {title} / {artist}"
|
||||
icons:
|
||||
play: ""
|
||||
pause: ""
|
||||
music_dir: "/home/jake/Music"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
start = [
|
||||
{
|
||||
type = "mpd"
|
||||
format = "{icon} {title} / {artist}"
|
||||
icons.play = ""
|
||||
icons.pause = ""
|
||||
music_dir = "/home/jake/Music"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Formatting Tokens
|
||||
|
||||
The following tokens can be used in the `format` config option,
|
||||
and will be replaced with values from the currently playing track:
|
||||
|
||||
| Token | Description |
|
||||
|--------------|--------------------------------------|
|
||||
| `{icon}` | Either `icons.play` or `icons.pause` |
|
||||
| `{title}` | Title |
|
||||
| `{album}` | Album name |
|
||||
| `{artist}` | Artist name |
|
||||
| `{date}` | Release date |
|
||||
| `{track}` | Track number |
|
||||
| `{disc}` | Disc number |
|
||||
| `{genre}` | Genre |
|
||||
| `{duration}` | Duration in `mm:ss` |
|
||||
| `{elapsed}` | Time elapsed in `mm:ss` |
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|----------------------------------------|------------------------------------------|
|
||||
| `#mpd` | Tray widget button |
|
||||
| `#popup-mpd` | Popup box |
|
||||
| `#popup-mpd #album-art` | Album art image inside popup box |
|
||||
| `#popup-mpd #title` | Track title container inside popup box |
|
||||
| `#popup-mpd #title .icon` | Track title icon label inside popup box |
|
||||
| `#popup-mpd #title .label` | Track title label inside popup box |
|
||||
| `#popup-mpd #album` | Track album container inside popup box |
|
||||
| `#popup-mpd #album .icon` | Track album icon label inside popup box |
|
||||
| `#popup-mpd #album .label` | Track album label inside popup box |
|
||||
| `#popup-mpd #artist` | Track artist container inside popup box |
|
||||
| `#popup-mpd #artist .icon` | Track artist icon label inside popup box |
|
||||
| `#popup-mpd #artist .label` | Track artist label inside popup box |
|
||||
| `#popup-mpd #controls` | Controls container inside popup box |
|
||||
| `#popup-mpd #controls #btn-prev` | Previous button inside popup box |
|
||||
| `#popup-mpd #controls #btn-play-pause` | Play/pause button inside popup box |
|
||||
| `#popup-mpd #controls #btn-next` | Next button inside popup box |
|
||||
| `#popup-mpd #volume` | Volume container inside popup box |
|
||||
| `#popup-mpd #volume #slider` | Volume slider popup box |
|
||||
| `#popup-mpd #volume .icon` | Volume icon label inside popup box |
|
||||
87
docs/modules/Script.md
Normal file
87
docs/modules/Script.md
Normal file
@@ -0,0 +1,87 @@
|
||||
Executes a script and shows the result of `stdout` on a label.
|
||||
Pango markup is supported.
|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `script`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|------------|-----------------------|---------|---------------------------------------------------------|
|
||||
| `cmd` | `string` | `null` | Path to the script on disk |
|
||||
| `mode` | `'poll'` or `'watch'` | `poll` | See [#modes](#modes) |
|
||||
| `interval` | `number` | `5000` | Number of milliseconds to wait between executing script |
|
||||
|
||||
### Modes
|
||||
|
||||
- Use `poll` to run the script wait for it to exit. On exit, the label is updated to show everything the script wrote to `stdout`.
|
||||
- Use `watch` to start a long-running script. Every time the script writes to `stdout`, the label is updated to show the latest line.
|
||||
Note this does not work for all programs as they may use block-buffering instead of line-buffering when they detect output being piped.
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "script",
|
||||
"cmd": "/home/jake/.local/bin/phone-battery",
|
||||
"mode": "poll",
|
||||
"interval": 5000
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = "script"
|
||||
cmd = "/home/jake/.local/bin/phone-battery"
|
||||
mode = "poll"
|
||||
interval = 5000
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: "script"
|
||||
cmd: "/home/jake/.local/bin/phone-battery"
|
||||
mode: 'poll'
|
||||
interval : 5000
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "script"
|
||||
cmd = "/home/jake/.local/bin/phone-battery"
|
||||
mode = "poll"
|
||||
interval = 5000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|---------------|---------------------|
|
||||
| `#script` | Script widget label |
|
||||
176
docs/modules/Sys-Info.md
Normal file
176
docs/modules/Sys-Info.md
Normal file
@@ -0,0 +1,176 @@
|
||||
Displays one or more labels containing system information.
|
||||
|
||||
Separating information across several labels allows for styling each one independently.
|
||||
Pango markup is supported.
|
||||
|
||||

|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `sys_info`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|--------------------|--------------------|---------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `format` | `string[]` | `null` | Array of strings including formatting tokens. For available tokens see below. |
|
||||
| `interval` | `integer` or `Map` | `5` | Seconds between refreshing. Can be a single value for all data or a map of individual refresh values for different data types. |
|
||||
| `interval.memory` | `integer` | `5` | Seconds between refreshing memory data |
|
||||
| `interval.cpu` | `integer` | `5` | Seconds between refreshing cpu data |
|
||||
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data |
|
||||
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data |
|
||||
| `interval.network` | `integer` | `5` | Seconds between refreshing network data |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"format": [
|
||||
" {cpu_percent}% | {temp_c:k10temp_Tccd1}°C",
|
||||
" {memory_used} / {memory_total} GB ({memory_percent}%)",
|
||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
|
||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
|
||||
"李 {net_down:enp39s0} / {net_up:enp39s0} Mbps",
|
||||
"猪 {load_average:1} | {load_average:5} | {load_average:15}",
|
||||
" {uptime}"
|
||||
],
|
||||
"interval": {
|
||||
"cpu": 1,
|
||||
"disks": 300,
|
||||
"memory": 30,
|
||||
"networks": 3,
|
||||
"temps": 5
|
||||
},
|
||||
"type": "sys_info"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = 'sys_info'
|
||||
format = [
|
||||
' {cpu_percent}% | {temp_c:k10temp_Tccd1}°C',
|
||||
' {memory_used} / {memory_total} GB ({memory_percent}%)',
|
||||
'| {swap_used} / {swap_total} GB ({swap_percent}%)',
|
||||
' {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)',
|
||||
'李 {net_down:enp39s0} / {net_up:enp39s0} Mbps',
|
||||
'猪 {load_average:1} | {load_average:5} | {load_average:15}',
|
||||
' {uptime}',
|
||||
]
|
||||
|
||||
[end.interval]
|
||||
cpu = 1
|
||||
disks = 300
|
||||
memory = 30
|
||||
networks = 3
|
||||
temps = 5
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- format:
|
||||
- ' {cpu_percent}% | {temp_c:k10temp_Tccd1}°C'
|
||||
- ' {memory_used} / {memory_total} GB ({memory_percent}%)'
|
||||
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
|
||||
- ' {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)'
|
||||
- '李 {net_down:enp39s0} / {net_up:enp39s0} Mbps'
|
||||
- '猪 {load_average:1} | {load_average:5} | {load_average:15}'
|
||||
- ' {uptime}'
|
||||
interval:
|
||||
cpu: 1
|
||||
disks: 300
|
||||
memory: 30
|
||||
networks: 3
|
||||
temps: 5
|
||||
type: sys_info
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "sys_info"
|
||||
|
||||
interval.memory = 30
|
||||
interval.cpu = 1
|
||||
interval.temps = 5
|
||||
interval.disks = 300
|
||||
interval.networks = 3
|
||||
|
||||
format = [
|
||||
" {cpu_percent}% | {temp_c:k10temp_Tccd1}°C"
|
||||
" {memory_used} / {memory_total} GB ({memory_percent}%)"
|
||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
|
||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
|
||||
"李 {net_down:enp39s0} / {net_up:enp39s0} Mbps"
|
||||
"猪 {load_average:1} | {load_average:5} | {load_average:15}"
|
||||
" {uptime}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Formatting Tokens
|
||||
|
||||
The following tokens can be used in the `format` configuration option:
|
||||
|
||||
| Token | Description |
|
||||
|--------------------------|------------------------------------------------------------------------------------|
|
||||
| **CPU** | |
|
||||
| `{cpu_percent}` | Total CPU utilisation percentage |
|
||||
| **Memory** | |
|
||||
| `{memory_free}` | Memory free in GB. |
|
||||
| `{memory_used}` | Memory used in GB. |
|
||||
| `{memory_total}` | Memory total in GB. |
|
||||
| `{memory_percent}` | Memory utilisation percentage. |
|
||||
| `{swap_free}` | Swap free in GB. |
|
||||
| `{swap_used}` | Swap used in GB. |
|
||||
| `{swap_total}` | Swap total in GB. |
|
||||
| `{swap_percent}` | Swap utilisation percentage. |
|
||||
| **Temperature** | |
|
||||
| `{temp_c:[sensor]}` | Temperature in degrees C. Replace `[sensor]` with the sensor label. |
|
||||
| `{temp_f:[sensor]}` | Temperature in degrees F. Replace `[sensor]` with the sensor label. |
|
||||
| **Disk** | |
|
||||
| `{disk_free:[mount]}` | Disk free space in GB. Replace `[mount]` with the disk mountpoint. |
|
||||
| `{disk_used:[mount]}` | Disk used space in GB. Replace `[mount]` with the disk mountpoint. |
|
||||
| `{disk_total:[mount]}` | Disk total space in GB. Replace `[mount]` with the disk mountpoint. |
|
||||
| `{disk_percent:[mount]}` | Disk utilisation percentage. Replace `[mount]` with the disk mountpoint. |
|
||||
| **Network** | |
|
||||
| `{net_down:[adapter]}` | Average network download speed in Mbps. Replace `[adapter]` with the adapter name. |
|
||||
| `{net_up:[adapter]}` | Average network upload speed in Mbps. Replace `[adapter]` with the adapter name. |
|
||||
| **System** | |
|
||||
| `{load_average:1}` | 1-minute load average. |
|
||||
| `{load_average:5}` | 5-minute load average. |
|
||||
| `{load_average:15}` | 15-minute load average. |
|
||||
| `{uptime}` | System uptime formatted as `HH:mm`. |
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|------------------|------------------------------|
|
||||
| `#sysinfo` | Sysinfo widget box |
|
||||
| `#sysinfo #item` | Individual information label |
|
||||
64
docs/modules/Tray.md
Normal file
64
docs/modules/Tray.md
Normal file
@@ -0,0 +1,64 @@
|
||||
Displays a fully interactive icon tray using the KDE `libappindicator` protocol.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `tray`
|
||||
|
||||
***This module provides no configuration options.***
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "tray"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = "tray"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: "tray"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{ type = "tray" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|---------------|------------------|
|
||||
| `#tray` | Tray widget box |
|
||||
| `#tray .item` | Tray icon button |
|
||||
94
docs/modules/Workspaces.md
Normal file
94
docs/modules/Workspaces.md
Normal file
@@ -0,0 +1,94 @@
|
||||
> ⚠ **This module is currently only supported on Sway**
|
||||
|
||||
Shows all current Sway workspaces. Clicking a workspace changes focus to it.
|
||||
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
> Type: `workspaces`
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|----------------|-----------------------|---------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| `name_map` | `Map<string, string>` | `{}` | A map of actual workspace names to their display labels. Workspaces use their actual name if not present in the map. |
|
||||
| `all_monitors` | `boolean` | `false` | Whether to display workspaces from all monitors. When `false`, only shows workspaces on the current monitor. |
|
||||
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"end": [
|
||||
{
|
||||
"type": "workspaces",
|
||||
"name_map": {
|
||||
"1": "",
|
||||
"2": "",
|
||||
"3": ""
|
||||
},
|
||||
"all_monitors": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TOML</summary>
|
||||
|
||||
```toml
|
||||
[[end]]
|
||||
type = "workspaces"
|
||||
all_monitors = false
|
||||
|
||||
[[end.name_map]]
|
||||
1 = ""
|
||||
2 = ""
|
||||
3 = ""
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>YAML</summary>
|
||||
|
||||
```yaml
|
||||
end:
|
||||
- type: "workspaces"
|
||||
name_map:
|
||||
1: ""
|
||||
2: ""
|
||||
3: ""
|
||||
all_monitors: false
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Corn</summary>
|
||||
|
||||
```corn
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "workspaces",
|
||||
name_map.1 = ""
|
||||
name_map.2 = ""
|
||||
name_map.3 = ""
|
||||
all_monitors = false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Styling
|
||||
|
||||
| Selector | Description |
|
||||
|-----------------------------|--------------------------------------|
|
||||
| `#workspaces` | Workspaces widget box |
|
||||
| `#workspaces .item` | Workspace button |
|
||||
| `#workspaces .item.focused` | Workspace button (workspace focused) |
|
||||
@@ -23,20 +23,56 @@ let {
|
||||
$mpd_server = { type = "mpd" host = "chloe:6600" }
|
||||
|
||||
$sys_info = {
|
||||
type = "sys-info"
|
||||
format = ["{cpu-percent}% " "{memory-percent}% "]
|
||||
type = "sys_info"
|
||||
format = ["{cpu_percent}% " "{memory_percent}% "]
|
||||
}
|
||||
|
||||
$tray = { type = "tray" }
|
||||
$clock = { type = "clock" }
|
||||
$clock = {
|
||||
type = "clock"
|
||||
// show-if = "500:[ $(($(date +%s) % 2)) -eq 0 ]"
|
||||
show_if.cmd = "exit 0"
|
||||
show_if.interval = 500
|
||||
}
|
||||
|
||||
$phone_battery = {
|
||||
type = "script"
|
||||
path = "/home/jake/bin/phone-battery"
|
||||
cmd = "/home/jake/bin/phone-battery"
|
||||
}
|
||||
|
||||
$log_tail = {
|
||||
type = "script"
|
||||
path = "tail -f /home/jake/.local/share/ironbar/error.log"
|
||||
mode = "watch"
|
||||
}
|
||||
|
||||
$power_menu = {
|
||||
type = "custom"
|
||||
class = "power-menu"
|
||||
|
||||
bar = [ { type = "button" name="power-btn" label = "" on_click = "popup:toggle" } ]
|
||||
|
||||
popup = [ {
|
||||
type = "box"
|
||||
orientation = "vertical"
|
||||
widgets = [
|
||||
{ type = "label" name = "header" label = "Power menu" }
|
||||
{
|
||||
type = "box"
|
||||
widgets = [
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!shutdown now" }
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!reboot" }
|
||||
]
|
||||
}
|
||||
{ type = "label" name = "uptime" label = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}" }
|
||||
]
|
||||
} ]
|
||||
|
||||
tooltip = "Up: {{30000:uptime -p | cut -d ' ' -f2-}}"
|
||||
}
|
||||
|
||||
$left = [ $workspaces $launcher ]
|
||||
$right = [ $mpd_local $mpd_server $phone_battery $sys_info $clock ]
|
||||
$right = [ $mpd_local $mpd_server $phone_battery $sys_info $power_menu $clock ]
|
||||
}
|
||||
in {
|
||||
anchor_to_edges = true
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
},
|
||||
{
|
||||
"format": [
|
||||
"{cpu-percent}% ",
|
||||
"{memory-percent}% "
|
||||
"{cpu_percent}% ",
|
||||
"{memory_percent}% "
|
||||
],
|
||||
"type": "sys-info"
|
||||
"type": "sys_info"
|
||||
},
|
||||
{
|
||||
"type": "tray"
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
let {
|
||||
$button = { type = "button" name="power-btn" label = "" on_click = "popup:toggle" }
|
||||
|
||||
$popup = {
|
||||
type = "box"
|
||||
orientation = "vertical"
|
||||
widgets = [
|
||||
{ type = "label" name = "header" label = "Power menu" }
|
||||
{
|
||||
type = "box"
|
||||
widgets = [
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!shutdown now" }
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" on_click = "!reboot" }
|
||||
]
|
||||
}
|
||||
{ type = "label" name = "uptime" label = "Uptime: {{30000:uptime -p | cut -d ' ' -f2-}}" }
|
||||
]
|
||||
}
|
||||
|
||||
$power_menu = {
|
||||
type = "custom"
|
||||
class = "power-menu"
|
||||
|
||||
bar = [ { type = "button" name="power-btn" label = "" exec = "popup:toggle" } ]
|
||||
|
||||
popup = [ {
|
||||
type = "box"
|
||||
orientation = "vertical"
|
||||
widgets = [
|
||||
{ type = "label" name = "header" label = "Power menu" }
|
||||
{
|
||||
type = "box"
|
||||
widgets = [
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" exec = "!shutdown now" }
|
||||
{ type = "button" class="power-btn" label = "<span font-size='40pt'></span>" exec = "!reboot" }
|
||||
]
|
||||
}
|
||||
]
|
||||
} ]
|
||||
bar = [ $button ]
|
||||
popup = [ $popup ]
|
||||
}
|
||||
} in {
|
||||
end = [ { type = "clock" } $power_menu ]
|
||||
end = [ $power_menu { type = "clock" } ]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
end = [
|
||||
{
|
||||
type = "sys-info"
|
||||
type = "sys_info"
|
||||
|
||||
interval.memory = 30
|
||||
interval.cpu = 1
|
||||
@@ -10,12 +10,12 @@
|
||||
interval.networks = 3
|
||||
|
||||
format = [
|
||||
" {cpu-percent}% | {temp-c:k10temp-Tccd1}°C"
|
||||
" {memory-used} / {memory-total} GB ({memory-percent}%)"
|
||||
"| {swap-used} / {swap-total} GB ({swap-percent}%)"
|
||||
" {disk-used:/} / {disk-total:/} GB ({disk-percent:/}%)"
|
||||
"李 {net-down:enp39s0} / {net-up:enp39s0} Mbps"
|
||||
"猪 {load-average:1} | {load-average:5} | {load-average:15}"
|
||||
" {cpu_percent}% | {temp_c:k10temp_Tccd1}°C"
|
||||
" {memory_used} / {memory_total} GB ({memory_percent}%)"
|
||||
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
|
||||
" {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
|
||||
"李 {net_down:enp39s0} / {net_up:enp39s0} Mbps"
|
||||
"猪 {load_average:1} | {load_average:5} | {load_average:15}"
|
||||
" {uptime}"
|
||||
]
|
||||
}
|
||||
|
||||
64
flake.lock
generated
Normal file
64
flake.lock
generated
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1668994630,
|
||||
"narHash": "sha256-1lqx6HLyw6fMNX/hXrrETG1vMvZRGm2XVC9O/Jt0T6c=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "af50806f7c6ab40df3e6b239099e8f8385f6c78b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1669084742,
|
||||
"narHash": "sha256-aLYwYVnrmEE1LVqd17v99CuqVmAZQrlgi2DVTAs4wFg=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "9652ef34c7439eca9f86cee11e94dbef5c9adb09",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
142
flake.nix
Normal file
142
flake.nix
Normal file
@@ -0,0 +1,142 @@
|
||||
{
|
||||
description = "Nix Flake for ironbar";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
#nci.url = "github:yusdacra/nix-cargo-integration";
|
||||
#nci.inputs.nixpkgs.follows = "nixpkgs";
|
||||
#nci.inputs.rust-overlay.follows = "rust-overlay";
|
||||
};
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
rust-overlay,
|
||||
...
|
||||
}: let
|
||||
inherit (nixpkgs) lib;
|
||||
genSystems = lib.genAttrs [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
];
|
||||
pkgsFor = system:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
overlays = [
|
||||
self.overlays.default
|
||||
rust-overlay.overlays.default
|
||||
];
|
||||
};
|
||||
mkRustToolchain = pkgs: pkgs.rust-bin.stable.latest.default;
|
||||
in {
|
||||
overlays.default = final: prev: let
|
||||
rust = mkRustToolchain final;
|
||||
|
||||
rustPlatform = prev.makeRustPlatform {
|
||||
cargo = rust;
|
||||
rustc = rust;
|
||||
};
|
||||
in {
|
||||
ironbar = rustPlatform.buildRustPackage {
|
||||
pname = "ironbar";
|
||||
version = self.rev or "dirty";
|
||||
src = builtins.path {
|
||||
name = "ironbar";
|
||||
path = prev.lib.cleanSource ./.;
|
||||
};
|
||||
cargoDeps = rustPlatform.importCargoLock {lockFile = ./Cargo.lock;};
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
nativeBuildInputs = with prev; [pkg-config];
|
||||
buildInputs = with prev; [gtk3 gdk-pixbuf gtk-layer-shell libxkbcommon];
|
||||
};
|
||||
};
|
||||
packages = genSystems (
|
||||
system: let
|
||||
pkgs = pkgsFor system;
|
||||
in
|
||||
(self.overlays.default pkgs pkgs)
|
||||
// {
|
||||
default = self.packages.${system}.ironbar;
|
||||
}
|
||||
);
|
||||
devShells = genSystems (system: let
|
||||
pkgs = pkgsFor system;
|
||||
rust = mkRustToolchain pkgs;
|
||||
in {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
rust
|
||||
rust-analyzer-unwrapped
|
||||
gcc
|
||||
gtk3
|
||||
gtk-layer-shell
|
||||
pkg-config
|
||||
];
|
||||
|
||||
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";
|
||||
};
|
||||
});
|
||||
homeManagerModules.default = {
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
cfg = config.programs.ironbar;
|
||||
defaultIronbarPackage = self.packages.${pkgs.system}.default;
|
||||
jsonFormat = pkgs.formats.json {};
|
||||
in {
|
||||
options.programs.ironbar = {
|
||||
enable = lib.mkEnableOption "ironbar status bar";
|
||||
package = lib.mkOption {
|
||||
type = with lib.types; package;
|
||||
default = defaultIronbarPackage;
|
||||
description = "The package for ironbar to use";
|
||||
};
|
||||
systemd = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = pkgs.stdenv.isLinux;
|
||||
description = "Whether to enable to systemd service for ironbar";
|
||||
};
|
||||
style = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
default = "";
|
||||
description = "The stylesheet to apply to ironbar";
|
||||
};
|
||||
config = lib.mkOption {
|
||||
type = jsonFormat.type;
|
||||
default = {};
|
||||
description = "The config to pass to ironbar";
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [cfg.package];
|
||||
xdg.configFile = {
|
||||
"ironbar/config.json" = lib.mkIf (cfg.config != "") {
|
||||
source = jsonFormat.generate "ironbar-config" cfg.config;
|
||||
};
|
||||
"ironbar/style.css" = lib.mkIf (cfg.style != "") {
|
||||
text = cfg.style;
|
||||
};
|
||||
};
|
||||
systemd.user.services.ironbar = lib.mkIf cfg.systemd {
|
||||
Unit = {
|
||||
Description = "Systemd service for Ironbar";
|
||||
Requires = ["graphical-session.target"];
|
||||
};
|
||||
Service = {
|
||||
Type = "simple";
|
||||
ExecStart = "${cfg.package}/bin/ironbar";
|
||||
};
|
||||
Install.WantedBy = [
|
||||
(lib.mkIf config.wayland.windowManager.hyprland.systemdIntegration "hyprland-session.target")
|
||||
(lib.mkIf config.wayland.windowManager.sway.systemdIntegration "sway-session.target")
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
67
src/bar.rs
67
src/bar.rs
@@ -1,12 +1,14 @@
|
||||
use crate::bridge_channel::BridgeChannel;
|
||||
use crate::config::{BarPosition, ModuleConfig};
|
||||
use crate::dynamic_string::DynamicString;
|
||||
use crate::modules::custom::ExecEvent;
|
||||
use crate::modules::launcher::{ItemEvent, LauncherUpdate};
|
||||
use crate::modules::mpd::{PlayerCommand, SongUpdate};
|
||||
use crate::modules::workspaces::WorkspaceUpdate;
|
||||
use crate::modules::{Module, ModuleInfoBuilder, ModuleLocation, ModuleUpdateEvent, WidgetContext};
|
||||
use crate::popup::Popup;
|
||||
use crate::Config;
|
||||
use crate::script::{OutputStream, Script};
|
||||
use crate::{await_sync, Config};
|
||||
use chrono::{DateTime, Local};
|
||||
use color_eyre::Result;
|
||||
use gtk::gdk::Monitor;
|
||||
@@ -16,8 +18,9 @@ use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use stray::message::NotifierItemCommand;
|
||||
use stray::NotifierItemMessage;
|
||||
use tokio::spawn;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, info};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
/// Creates a new window for a bar,
|
||||
/// sets it up and adds its widgets.
|
||||
@@ -81,7 +84,11 @@ pub fn create_bar(
|
||||
});
|
||||
|
||||
debug!("Showing bar");
|
||||
win.show_all();
|
||||
start.show();
|
||||
center.show();
|
||||
end.show();
|
||||
content.show();
|
||||
win.show();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -155,11 +162,63 @@ fn add_modules(
|
||||
controller_tx: ui_tx,
|
||||
};
|
||||
|
||||
let common = $module.common.clone();
|
||||
|
||||
let widget = $module.into_widget(context, &info)?;
|
||||
|
||||
content.add(&widget.widget);
|
||||
let container = gtk::EventBox::new();
|
||||
container.add(&widget.widget);
|
||||
|
||||
content.add(&container);
|
||||
widget.widget.set_widget_name(info.module_name);
|
||||
|
||||
if let Some(show_if) = common.show_if {
|
||||
let script = Script::new_polling(show_if);
|
||||
let container = container.clone();
|
||||
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
spawn(async move {
|
||||
script
|
||||
.run(|(_, success)| {
|
||||
tx.send(success)
|
||||
.expect("Failed to send widget visibility toggle message");
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
rx.attach(None, move |success| {
|
||||
if success {
|
||||
container.show_all()
|
||||
} else {
|
||||
container.hide()
|
||||
};
|
||||
Continue(true)
|
||||
});
|
||||
} else {
|
||||
container.show_all();
|
||||
}
|
||||
|
||||
if let Some(on_click) = common.on_click {
|
||||
let script = Script::new_polling(on_click);
|
||||
container.connect_button_press_event(move |_, _| {
|
||||
trace!("Running on-click script");
|
||||
match await_sync(async { script.get_output().await }) {
|
||||
Ok((OutputStream::Stderr(out), _)) => error!("{out}"),
|
||||
Err(err) => error!("{err:?}"),
|
||||
_ => {}
|
||||
}
|
||||
Inhibit(false)
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(tooltip) = common.tooltip {
|
||||
DynamicString::new(&tooltip, move |string| {
|
||||
container.set_tooltip_text(Some(&string));
|
||||
Continue(true)
|
||||
});
|
||||
}
|
||||
|
||||
let has_popup = widget.popup.is_some();
|
||||
if let Some(popup_content) = widget.popup {
|
||||
popup
|
||||
|
||||
4
src/clients/mod.rs
Normal file
4
src/clients/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod mpd;
|
||||
pub mod sway;
|
||||
pub mod system_tray;
|
||||
pub mod wayland;
|
||||
@@ -141,10 +141,9 @@ async fn try_get_mpd_conn(host: &str) -> Result<Connection, MpdProtocolError> {
|
||||
fn is_unix_socket(host: &str) -> bool {
|
||||
let path = PathBuf::from(host);
|
||||
path.exists()
|
||||
&& match path.metadata() {
|
||||
Ok(metadata) => metadata.file_type().is_socket(),
|
||||
Err(_) => false,
|
||||
}
|
||||
&& path
|
||||
.metadata()
|
||||
.map_or(false, |metadata| metadata.file_type().is_socket())
|
||||
}
|
||||
|
||||
async fn connect_unix(host: &str) -> Result<Connection, MpdProtocolError> {
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::toplevel::{ToplevelEvent, ToplevelInfo};
|
||||
use super::toplevel_manager::listen_for_toplevels;
|
||||
use super::ToplevelChange;
|
||||
use super::{Env, ToplevelHandler};
|
||||
use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo};
|
||||
use crate::wayland::toplevel_manager::listen_for_toplevels;
|
||||
use crate::wayland::ToplevelChange;
|
||||
use color_eyre::Report;
|
||||
use indexmap::IndexMap;
|
||||
use smithay_client_toolkit::environment::Environment;
|
||||
@@ -4,11 +4,10 @@ mod toplevel_manager;
|
||||
|
||||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use self::toplevel_manager::ToplevelHandler;
|
||||
pub use crate::wayland::toplevel::{ToplevelChange, ToplevelEvent, ToplevelInfo};
|
||||
use crate::wayland::toplevel_manager::{ToplevelHandling, ToplevelStatusListener};
|
||||
use async_once::AsyncOnce;
|
||||
use lazy_static::lazy_static;
|
||||
pub use toplevel::{ToplevelChange, ToplevelEvent, ToplevelInfo};
|
||||
use toplevel_manager::{ToplevelHandler, ToplevelHandling, ToplevelStatusListener};
|
||||
use wayland_client::{Attached, DispatchData, Interface};
|
||||
use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{
|
||||
zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1,
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::wayland::toplevel::{Toplevel, ToplevelEvent};
|
||||
use crate::wayland::LazyGlobal;
|
||||
use super::toplevel::{Toplevel, ToplevelEvent};
|
||||
use super::LazyGlobal;
|
||||
use smithay_client_toolkit::environment::{Environment, GlobalHandler};
|
||||
use std::cell::RefCell;
|
||||
use std::rc;
|
||||
@@ -7,6 +7,7 @@ use crate::modules::script::ScriptModule;
|
||||
use crate::modules::sysinfo::SysInfoModule;
|
||||
use crate::modules::tray::TrayModule;
|
||||
use crate::modules::workspaces::WorkspacesModule;
|
||||
use crate::script::ScriptInput;
|
||||
use color_eyre::eyre::{Context, ContextCompat};
|
||||
use color_eyre::{eyre, Help, Report};
|
||||
use dirs::config_dir;
|
||||
@@ -19,7 +20,14 @@ use std::{env, fs};
|
||||
use tracing::instrument;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||
pub struct CommonConfig {
|
||||
pub show_if: Option<ScriptInput>,
|
||||
pub on_click: Option<ScriptInput>,
|
||||
pub tooltip: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ModuleConfig {
|
||||
Clock(ClockModule),
|
||||
Mpd(MpdModule),
|
||||
@@ -40,7 +48,7 @@ pub enum MonitorConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BarPosition {
|
||||
Top,
|
||||
Bottom,
|
||||
@@ -101,20 +109,21 @@ impl Config {
|
||||
/// parse it and return a new instance of `Self`.
|
||||
#[instrument]
|
||||
pub fn load() -> Result<Self> {
|
||||
let config_path = if let Ok(config_path) = env::var("IRONBAR_CONFIG") {
|
||||
let path = PathBuf::from(config_path);
|
||||
if path.exists() {
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(Report::msg(format!(
|
||||
"Specified config file does not exist: {}",
|
||||
path.display()
|
||||
))
|
||||
.note("Config file was specified using `IRONBAR_CONFIG` environment variable"))
|
||||
}
|
||||
} else {
|
||||
Self::try_find_config()
|
||||
}?;
|
||||
let config_path = env::var("IRONBAR_CONFIG").map_or_else(
|
||||
|_| Self::try_find_config(),
|
||||
|config_path| {
|
||||
let path = PathBuf::from(config_path);
|
||||
if path.exists() {
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(Report::msg(format!(
|
||||
"Specified config file does not exist: {}",
|
||||
path.display()
|
||||
))
|
||||
.note("Config file was specified using `IRONBAR_CONFIG` environment variable"))
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
Self::load_file(&config_path)
|
||||
}
|
||||
@@ -141,13 +150,15 @@ impl Config {
|
||||
}
|
||||
});
|
||||
|
||||
match file {
|
||||
Some(file) => Ok(file),
|
||||
None => Err(Report::msg("Could not find config file")
|
||||
.suggestion("Ironbar does not include a configuration out of the box")
|
||||
.suggestion("A guide on writing a config can be found on the wiki:")
|
||||
.suggestion("https://github.com/JakeStanger/ironbar/wiki/configuration-guide")),
|
||||
}
|
||||
file.map_or_else(
|
||||
|| {
|
||||
Err(Report::msg("Could not find config file")
|
||||
.suggestion("Ironbar does not include a configuration out of the box")
|
||||
.suggestion("A guide on writing a config can be found on the wiki:")
|
||||
.suggestion("https://github.com/JakeStanger/ironbar/wiki/configuration-guide"))
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
/// Loads the config file at the specified path
|
||||
@@ -164,14 +175,7 @@ impl Config {
|
||||
"json" => serde_json::from_slice(&file).wrap_err("Invalid JSON config"),
|
||||
"toml" => toml::from_slice(&file).wrap_err("Invalid TOML config"),
|
||||
"yaml" | "yml" => serde_yaml::from_slice(&file).wrap_err("Invalid YAML config"),
|
||||
"corn" => {
|
||||
// corn doesn't support deserialization yet
|
||||
// so serialize the interpreted result then deserialize that
|
||||
let file =
|
||||
String::from_utf8(file).wrap_err("Config file contains invalid UTF-8")?;
|
||||
let config = libcorn::parse(&file).wrap_err("Invalid corn config")?.value;
|
||||
Ok(serde_json::from_str(&serde_json::to_string(&config)?)?)
|
||||
}
|
||||
"corn" => libcorn::from_slice(&file).wrap_err("Invalid Corn config"),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
143
src/dynamic_string.rs
Normal file
143
src/dynamic_string.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use crate::script::{OutputStream, Script};
|
||||
use gtk::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::spawn;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DynamicStringSegment {
|
||||
Static(String),
|
||||
Dynamic(Script),
|
||||
}
|
||||
|
||||
pub struct DynamicString {
|
||||
// pub label: gtk::Label,
|
||||
}
|
||||
|
||||
impl DynamicString {
|
||||
pub fn new<F>(input: &str, f: F) -> Self
|
||||
where
|
||||
F: FnMut(String) -> Continue + 'static,
|
||||
{
|
||||
let mut segments = vec![];
|
||||
|
||||
let mut chars = input.chars().collect::<Vec<_>>();
|
||||
while !chars.is_empty() {
|
||||
let char = &chars[..=1];
|
||||
|
||||
let (token, skip) = if let ['{', '{'] = char {
|
||||
const SKIP_BRACKETS: usize = 4;
|
||||
|
||||
let str = chars
|
||||
.iter()
|
||||
.skip(2)
|
||||
.enumerate()
|
||||
.take_while(|(i, &c)| c != '}' && chars[i + 1] != '}')
|
||||
.map(|(_, c)| c)
|
||||
.collect::<String>();
|
||||
|
||||
let len = str.len();
|
||||
|
||||
(
|
||||
DynamicStringSegment::Dynamic(Script::from(str.as_str())),
|
||||
len + SKIP_BRACKETS,
|
||||
)
|
||||
} else {
|
||||
let str = chars
|
||||
.iter()
|
||||
.enumerate()
|
||||
.take_while(|(i, &c)| !(c == '{' && chars[i + 1] == '{'))
|
||||
.map(|(_, c)| c)
|
||||
.collect::<String>();
|
||||
|
||||
let len = str.len();
|
||||
|
||||
(DynamicStringSegment::Static(str), len)
|
||||
};
|
||||
|
||||
assert_ne!(skip, 0);
|
||||
|
||||
segments.push(token);
|
||||
chars.drain(..skip);
|
||||
}
|
||||
|
||||
let label_parts = Arc::new(Mutex::new(IndexMap::new()));
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
for (i, segment) in segments.into_iter().enumerate() {
|
||||
match segment {
|
||||
DynamicStringSegment::Static(str) => {
|
||||
label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts")
|
||||
.insert(i, str);
|
||||
}
|
||||
DynamicStringSegment::Dynamic(script) => {
|
||||
let tx = tx.clone();
|
||||
let label_parts = label_parts.clone();
|
||||
|
||||
spawn(async move {
|
||||
script
|
||||
.run(|(out, _)| {
|
||||
if let OutputStream::Stdout(out) = out {
|
||||
let mut label_parts = label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts");
|
||||
|
||||
label_parts
|
||||
// .lock()
|
||||
// .expect("Failed to get lock on label parts")
|
||||
.insert(i, out);
|
||||
|
||||
let string = label_parts
|
||||
.iter()
|
||||
.map(|(_, part)| part.as_str())
|
||||
.collect::<String>();
|
||||
|
||||
tx.send(string).expect("Failed to send update");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize
|
||||
{
|
||||
let label_parts = label_parts
|
||||
.lock()
|
||||
.expect("Failed to get lock on label parts")
|
||||
.iter()
|
||||
.map(|(_, part)| part.as_str())
|
||||
.collect::<String>();
|
||||
|
||||
tx.send(label_parts).expect("Failed to send update");
|
||||
}
|
||||
|
||||
rx.attach(None, f);
|
||||
|
||||
// Self { label }
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test() {
|
||||
// TODO: see if we can run gtk tests in ci
|
||||
if gtk::init().is_ok() {
|
||||
let label = gtk::Label::new(None);
|
||||
DynamicString::new(
|
||||
"Uptime: {{1000:uptime -p | cut -d ' ' -f2-}}",
|
||||
move |string| {
|
||||
label.set_label(&string);
|
||||
Continue(true)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/icon.rs
23
src/icon.rs
@@ -68,17 +68,12 @@ fn parse_desktop_file(path: PathBuf) -> io::Result<HashMap<String, String>> {
|
||||
|
||||
/// Attempts to get the icon name from the app's `.desktop` file.
|
||||
fn get_desktop_icon_name(app_id: &str) -> Option<String> {
|
||||
match find_desktop_file(app_id) {
|
||||
Some(file) => {
|
||||
let map = parse_desktop_file(file);
|
||||
|
||||
match map {
|
||||
Ok(map) => map.get("Icon").map(std::string::ToString::to_string),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
find_desktop_file(app_id).and_then(|file| {
|
||||
let map = parse_desktop_file(file);
|
||||
map.map_or(None, |map| {
|
||||
map.get("Icon").map(std::string::ToString::to_string)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
enum IconLocation {
|
||||
@@ -137,11 +132,7 @@ pub fn get_icon(theme: &IconTheme, app_id: &str, size: i32) -> Option<Pixbuf> {
|
||||
match icon_location {
|
||||
Some(IconLocation::Theme(icon_name)) => {
|
||||
let icon = theme.load_icon(&icon_name, size, IconLookupFlags::FORCE_SIZE);
|
||||
|
||||
match icon {
|
||||
Ok(icon) => icon,
|
||||
Err(_) => None,
|
||||
}
|
||||
icon.map_or(None, |icon| icon)
|
||||
}
|
||||
Some(IconLocation::File(path)) => Pixbuf::from_file_at_scale(path, size, size, true).ok(),
|
||||
None => None,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
mod bar;
|
||||
mod bridge_channel;
|
||||
mod clients;
|
||||
mod config;
|
||||
mod dynamic_string;
|
||||
mod icon;
|
||||
mod logging;
|
||||
mod modules;
|
||||
mod popup;
|
||||
mod script;
|
||||
mod style;
|
||||
mod sway;
|
||||
mod wayland;
|
||||
|
||||
use crate::bar::create_bar;
|
||||
use crate::config::{Config, MonitorConfig};
|
||||
@@ -27,8 +27,8 @@ use tokio::runtime::Handle;
|
||||
use tokio::task::block_in_place;
|
||||
|
||||
use crate::logging::install_tracing;
|
||||
use clients::wayland::{self, WaylandClient};
|
||||
use tracing::{debug, error, info};
|
||||
use wayland::WaylandClient;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::popup::Popup;
|
||||
use chrono::{DateTime, Local};
|
||||
@@ -18,7 +19,10 @@ pub struct ClockModule {
|
||||
/// Detail on available tokens can be found here:
|
||||
/// <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>
|
||||
#[serde(default = "default_format")]
|
||||
pub(crate) format: String,
|
||||
format: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
fn default_format() -> String {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::config::CommonConfig;
|
||||
use crate::dynamic_string::DynamicString;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::popup::{ButtonGeometry, Popup};
|
||||
use crate::script::exec_command;
|
||||
use crate::script::Script;
|
||||
use color_eyre::{Report, Result};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, Label, Orientation};
|
||||
@@ -17,6 +19,9 @@ pub struct CustomModule {
|
||||
bar: Vec<Widget>,
|
||||
/// Widgets to add to the popup container
|
||||
popup: Option<Vec<Widget>>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
/// Attempts to parse an `Orientation` from `String`
|
||||
@@ -38,13 +43,13 @@ pub struct Widget {
|
||||
label: Option<String>,
|
||||
name: Option<String>,
|
||||
class: Option<String>,
|
||||
exec: Option<String>,
|
||||
on_click: Option<String>,
|
||||
orientation: Option<String>,
|
||||
}
|
||||
|
||||
/// Supported GTK widget types
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WidgetType {
|
||||
Box,
|
||||
Label,
|
||||
@@ -99,15 +104,23 @@ impl Widget {
|
||||
|
||||
let label = builder.build();
|
||||
|
||||
if let Some(text) = self.label {
|
||||
label.set_markup(&text);
|
||||
}
|
||||
|
||||
if let Some(class) = self.class {
|
||||
label.style_context().add_class(&class);
|
||||
}
|
||||
|
||||
let text = self.label.map_or_else(String::new, |text| text);
|
||||
|
||||
{
|
||||
let label = label.clone();
|
||||
DynamicString::new(&text, move |string| {
|
||||
label.set_label(&string);
|
||||
Continue(true)
|
||||
});
|
||||
}
|
||||
|
||||
label
|
||||
|
||||
// DynamicString::new(label, &text)
|
||||
}
|
||||
|
||||
/// Creates a `gtk::Button` from this widget
|
||||
@@ -131,7 +144,7 @@ impl Widget {
|
||||
button.style_context().add_class(&class);
|
||||
}
|
||||
|
||||
if let Some(exec) = self.exec {
|
||||
if let Some(exec) = self.on_click {
|
||||
button.connect_clicked(move |button| {
|
||||
tx.try_send(ExecEvent {
|
||||
cmd: exec.clone(),
|
||||
@@ -164,8 +177,11 @@ impl Module<gtk::Box> for CustomModule {
|
||||
spawn(async move {
|
||||
while let Some(event) = rx.recv().await {
|
||||
if event.cmd.starts_with('!') {
|
||||
debug!("executing command: '{}'", &event.cmd[1..]);
|
||||
if let Err(err) = exec_command(&event.cmd[1..]) {
|
||||
let script = Script::from(&event.cmd[1..]);
|
||||
|
||||
debug!("executing command: '{}'", script.cmd);
|
||||
// TODO: Migrate to use script.run
|
||||
if let Err(err) = script.get_output().await {
|
||||
error!("{err:?}");
|
||||
}
|
||||
} else if event.cmd == "popup:toggle" {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::clients::wayland::{self, ToplevelChange};
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::wayland::ToplevelChange;
|
||||
use crate::{await_sync, icon, wayland};
|
||||
use crate::{await_sync, icon};
|
||||
use color_eyre::Result;
|
||||
use glib::Continue;
|
||||
use gtk::prelude::*;
|
||||
@@ -23,6 +24,9 @@ pub struct FocusedModule {
|
||||
icon_size: i32,
|
||||
/// GTK icon theme to use.
|
||||
icon_theme: Option<String>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
const fn default_icon_size() -> i32 {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::open_state::OpenState;
|
||||
use crate::clients::wayland::ToplevelInfo;
|
||||
use crate::icon::get_icon;
|
||||
use crate::modules::launcher::{ItemEvent, LauncherUpdate};
|
||||
use crate::modules::ModuleUpdateEvent;
|
||||
use crate::popup::Popup;
|
||||
use crate::wayland::ToplevelInfo;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, IconTheme, Image, Orientation};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
@@ -3,10 +3,10 @@ mod open_state;
|
||||
|
||||
use self::item::{Item, ItemButton, Window};
|
||||
use self::open_state::OpenState;
|
||||
use crate::clients::wayland::{self, ToplevelChange};
|
||||
use crate::config::CommonConfig;
|
||||
use crate::icon::find_desktop_file;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::wayland;
|
||||
use crate::wayland::ToplevelChange;
|
||||
use color_eyre::{Help, Report};
|
||||
use glib::Continue;
|
||||
use gtk::prelude::*;
|
||||
@@ -34,6 +34,9 @@ pub struct LauncherModule {
|
||||
|
||||
/// Name of the GTK icon theme to use.
|
||||
icon_theme: Option<String>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -81,18 +84,20 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>,
|
||||
mut rx: Receiver<Self::ReceiveMessage>,
|
||||
) -> crate::Result<()> {
|
||||
let items = match &self.favorites {
|
||||
Some(favorites) => favorites
|
||||
.iter()
|
||||
.map(|app_id| {
|
||||
(
|
||||
app_id.to_string(),
|
||||
Item::new(app_id.to_string(), OpenState::Closed, true),
|
||||
)
|
||||
})
|
||||
.collect::<IndexMap<_, _>>(),
|
||||
None => IndexMap::new(),
|
||||
};
|
||||
let items = self
|
||||
.favorites
|
||||
.as_ref()
|
||||
.map_or_else(IndexMap::new, |favorites| {
|
||||
favorites
|
||||
.iter()
|
||||
.map(|app_id| {
|
||||
(
|
||||
app_id.to_string(),
|
||||
Item::new(app_id.to_string(), OpenState::Closed, true),
|
||||
)
|
||||
})
|
||||
.collect::<IndexMap<_, _>>()
|
||||
});
|
||||
|
||||
let items = Arc::new(Mutex::new(items));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::wayland::ToplevelInfo;
|
||||
use crate::clients::wayland::ToplevelInfo;
|
||||
|
||||
/// Open state for a launcher item, or item window.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
mod client;
|
||||
|
||||
use crate::modules::mpd::client::MpdConnectionError;
|
||||
use crate::modules::mpd::client::{get_client, get_duration, get_elapsed};
|
||||
use crate::clients::mpd::{get_client, get_duration, get_elapsed, MpdConnectionError};
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::popup::Popup;
|
||||
use color_eyre::Result;
|
||||
@@ -68,6 +66,9 @@ pub struct MpdModule {
|
||||
/// Path to root of music directory.
|
||||
#[serde(default = "default_music_dir")]
|
||||
music_dir: PathBuf,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
fn default_socket() -> String {
|
||||
@@ -97,10 +98,7 @@ fn default_music_dir() -> PathBuf {
|
||||
/// Attempts to read the first value for a tag
|
||||
/// (since the MPD client returns a vector of tags, or None)
|
||||
pub fn try_get_first_tag(vec: Option<&Vec<String>>) -> Option<&str> {
|
||||
match vec {
|
||||
Some(vec) => vec.first().map(String::as_str),
|
||||
None => None,
|
||||
}
|
||||
vec.and_then(|vec| vec.first().map(String::as_str))
|
||||
}
|
||||
|
||||
/// Formats a duration given in seconds
|
||||
@@ -369,11 +367,14 @@ impl Module<Button> for MpdModule {
|
||||
.join("cover.jpg"),
|
||||
);
|
||||
|
||||
if let Ok(pixbuf) = Pixbuf::from_file_at_scale(cover_path, 128, 128, true) {
|
||||
album_image.set_from_pixbuf(Some(&pixbuf));
|
||||
} else {
|
||||
album_image.set_from_pixbuf(None);
|
||||
}
|
||||
Pixbuf::from_file_at_scale(cover_path, 128, 128, true).map_or_else(
|
||||
|_| {
|
||||
album_image.set_from_pixbuf(None);
|
||||
},
|
||||
|pixbuf| {
|
||||
album_image.set_from_pixbuf(Some(&pixbuf));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
title_label
|
||||
@@ -1,21 +1,32 @@
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::script::exec_command;
|
||||
use color_eyre::Result;
|
||||
use crate::script::{OutputStream, Script, ScriptMode};
|
||||
use color_eyre::{Help, Report, Result};
|
||||
use gtk::prelude::*;
|
||||
use gtk::Label;
|
||||
use serde::Deserialize;
|
||||
use tokio::spawn;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tokio::time::sleep;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct ScriptModule {
|
||||
/// Path to script to execute.
|
||||
path: String,
|
||||
cmd: String,
|
||||
/// Script execution mode
|
||||
#[serde(default = "default_mode")]
|
||||
mode: ScriptMode,
|
||||
/// Time in milliseconds between executions.
|
||||
#[serde(default = "default_interval")]
|
||||
interval: u64,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
/// `Mode::Poll`
|
||||
const fn default_mode() -> ScriptMode {
|
||||
ScriptMode::Poll
|
||||
}
|
||||
|
||||
/// 5000ms
|
||||
@@ -23,6 +34,16 @@ const fn default_interval() -> u64 {
|
||||
5000
|
||||
}
|
||||
|
||||
impl From<&ScriptModule> for Script {
|
||||
fn from(module: &ScriptModule) -> Self {
|
||||
Self {
|
||||
mode: module.mode,
|
||||
cmd: module.cmd.clone(),
|
||||
interval: module.interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Module<Label> for ScriptModule {
|
||||
type SendMessage = String;
|
||||
type ReceiveMessage = ();
|
||||
@@ -33,20 +54,21 @@ impl Module<Label> for ScriptModule {
|
||||
tx: Sender<ModuleUpdateEvent<Self::SendMessage>>,
|
||||
_rx: Receiver<Self::ReceiveMessage>,
|
||||
) -> Result<()> {
|
||||
let interval = self.interval;
|
||||
let path = self.path.clone();
|
||||
spawn(async move {
|
||||
loop {
|
||||
match exec_command(&path) {
|
||||
Ok(stdout) => tx
|
||||
.send(ModuleUpdateEvent::Update(stdout))
|
||||
.await
|
||||
.expect("Failed to send stdout"),
|
||||
Err(err) => error!("{:?}", err),
|
||||
}
|
||||
let script: Script = self.into();
|
||||
|
||||
sleep(tokio::time::Duration::from_millis(interval)).await;
|
||||
}
|
||||
spawn(async move {
|
||||
script.run(move |(out, _)| match out {
|
||||
OutputStream::Stdout(stdout) => {
|
||||
tx.try_send(ModuleUpdateEvent::Update(stdout))
|
||||
.expect("Failed to send stdout"); }
|
||||
OutputStream::Stderr(stderr) => {
|
||||
error!("{:?}", Report::msg(stderr)
|
||||
.wrap_err("Watched script error:")
|
||||
.suggestion("Check the path to your script")
|
||||
.suggestion("Check the script for errors")
|
||||
.suggestion("If you expect the script to write to stderr, consider redirecting its output to /dev/null to suppress these messages"));
|
||||
}
|
||||
}).await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use color_eyre::Result;
|
||||
use gtk::prelude::*;
|
||||
@@ -19,6 +20,9 @@ pub struct SysInfoModule {
|
||||
/// Number of seconds between refresh
|
||||
#[serde(default = "Interval::default")]
|
||||
interval: Interval,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Copy, Clone)]
|
||||
@@ -289,19 +293,19 @@ fn refresh_memory_tokens(format_info: &mut HashMap<String, String>, sys: &mut Sy
|
||||
let memory_percent = actual_used_memory as f64 / total_memory as f64 * 100.0;
|
||||
|
||||
format_info.insert(
|
||||
String::from("memory-free"),
|
||||
String::from("memory_free"),
|
||||
(bytes_to_gigabytes(available_memory)).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("memory-used"),
|
||||
String::from("memory_used"),
|
||||
(bytes_to_gigabytes(actual_used_memory)).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("memory-total"),
|
||||
String::from("memory_total"),
|
||||
(bytes_to_gigabytes(total_memory)).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("memory-percent"),
|
||||
String::from("memory_percent"),
|
||||
format!("{:0>2.0}", memory_percent),
|
||||
);
|
||||
|
||||
@@ -309,19 +313,19 @@ fn refresh_memory_tokens(format_info: &mut HashMap<String, String>, sys: &mut Sy
|
||||
let total_swap = sys.total_swap();
|
||||
|
||||
format_info.insert(
|
||||
String::from("swap-free"),
|
||||
String::from("swap_free"),
|
||||
(bytes_to_gigabytes(sys.free_swap())).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("swap-used"),
|
||||
String::from("swap_used"),
|
||||
(bytes_to_gigabytes(used_swap)).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("swap-total"),
|
||||
String::from("swap_total"),
|
||||
(bytes_to_gigabytes(total_swap)).to_string(),
|
||||
);
|
||||
format_info.insert(
|
||||
String::from("swap-percent"),
|
||||
String::from("swap_percent"),
|
||||
format!("{:0>2.0}", used_swap as f64 / total_swap as f64 * 100.0),
|
||||
);
|
||||
}
|
||||
@@ -333,7 +337,7 @@ fn refresh_cpu_tokens(format_info: &mut HashMap<String, String>, sys: &mut Syste
|
||||
let cpu_percent = cpu_info.cpu_usage();
|
||||
|
||||
format_info.insert(
|
||||
String::from("cpu-percent"),
|
||||
String::from("cpu_percent"),
|
||||
format!("{:0>2.0}", cpu_percent),
|
||||
);
|
||||
}
|
||||
@@ -346,8 +350,8 @@ fn refresh_temp_tokens(format_info: &mut HashMap<String, String>, sys: &mut Syst
|
||||
let key = component.label().replace(' ', "-");
|
||||
let temp = component.temperature();
|
||||
|
||||
format_info.insert(format!("temp-c:{key}"), format!("{temp:.0}"));
|
||||
format_info.insert(format!("temp-f:{key}"), format!("{:.0}", c_to_f(temp)));
|
||||
format_info.insert(format!("temp_c:{key}"), format!("{temp:.0}"));
|
||||
format_info.insert(format!("temp_f:{key}"), format!("{:.0}", c_to_f(temp)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +363,7 @@ fn refresh_disk_tokens(format_info: &mut HashMap<String, String>, sys: &mut Syst
|
||||
let key = disk
|
||||
.mount_point()
|
||||
.to_str()
|
||||
.map(|s| s.replace('{', "").replace('}', ""));
|
||||
.map(|s| s.replace(['{', '}'], ""));
|
||||
|
||||
if let Some(key) = key {
|
||||
let total = disk.total_space();
|
||||
@@ -367,22 +371,22 @@ fn refresh_disk_tokens(format_info: &mut HashMap<String, String>, sys: &mut Syst
|
||||
let used = total - available;
|
||||
|
||||
format_info.insert(
|
||||
format!("disk-free:{key}"),
|
||||
format!("disk_free:{key}"),
|
||||
bytes_to_gigabytes(available).to_string(),
|
||||
);
|
||||
|
||||
format_info.insert(
|
||||
format!("disk-used:{key}"),
|
||||
format!("disk_used:{key}"),
|
||||
bytes_to_gigabytes(used).to_string(),
|
||||
);
|
||||
|
||||
format_info.insert(
|
||||
format!("disk-total:{key}"),
|
||||
format!("disk_total:{key}"),
|
||||
bytes_to_gigabytes(total).to_string(),
|
||||
);
|
||||
|
||||
format_info.insert(
|
||||
format!("disk-percent:{key}"),
|
||||
format!("disk_percent:{key}"),
|
||||
format!("{:0>2.0}", used as f64 / total as f64 * 100.0),
|
||||
);
|
||||
}
|
||||
@@ -398,12 +402,12 @@ fn refresh_network_tokens(
|
||||
|
||||
for (iface, network) in sys.networks() {
|
||||
format_info.insert(
|
||||
format!("net-down:{iface}"),
|
||||
format!("net_down:{iface}"),
|
||||
format!("{:0>2.0}", bytes_to_megabits(network.received()) / interval),
|
||||
);
|
||||
|
||||
format_info.insert(
|
||||
format!("net-up:{iface}"),
|
||||
format!("net_up:{iface}"),
|
||||
format!(
|
||||
"{:0>2.0}",
|
||||
bytes_to_megabits(network.transmitted()) / interval
|
||||
@@ -416,15 +420,15 @@ fn refresh_system_tokens(format_info: &mut HashMap<String, String>, sys: &System
|
||||
// no refresh required for these tokens
|
||||
|
||||
let load_average = sys.load_average();
|
||||
format_info.insert(String::from("load-average:1"), load_average.one.to_string());
|
||||
format_info.insert(String::from("load_average:1"), load_average.one.to_string());
|
||||
|
||||
format_info.insert(
|
||||
String::from("load-average:5"),
|
||||
String::from("load_average:5"),
|
||||
load_average.five.to_string(),
|
||||
);
|
||||
|
||||
format_info.insert(
|
||||
String::from("load-average:15"),
|
||||
String::from("load_average:15"),
|
||||
load_average.fifteen.to_string(),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
mod client;
|
||||
|
||||
use crate::await_sync;
|
||||
use crate::modules::tray::client::get_tray_event_client;
|
||||
use crate::clients::system_tray::get_tray_event_client;
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use color_eyre::Result;
|
||||
use gtk::prelude::*;
|
||||
@@ -16,14 +15,17 @@ use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct TrayModule;
|
||||
pub struct TrayModule {
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
/// Gets a GTK `Image` component
|
||||
/// for the status notifier item's icon.
|
||||
fn get_icon(item: &StatusNotifierItem) -> Option<Image> {
|
||||
item.icon_theme_path.as_ref().and_then(|path| {
|
||||
let theme = IconTheme::new();
|
||||
theme.append_search_path(&path);
|
||||
theme.append_search_path(path);
|
||||
|
||||
item.icon_name.as_ref().and_then(|icon_name| {
|
||||
let icon_info = theme.lookup_icon(icon_name, 16, IconLookupFlags::empty());
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::await_sync;
|
||||
use crate::clients::sway::{get_client, get_sub_client};
|
||||
use crate::config::CommonConfig;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::sway::{get_client, get_sub_client};
|
||||
use color_eyre::{Report, Result};
|
||||
use gtk::prelude::*;
|
||||
use gtk::Button;
|
||||
@@ -19,6 +20,9 @@ pub struct WorkspacesModule {
|
||||
/// Whether to display buttons for all monitors.
|
||||
#[serde(default = "crate::config::default_false")]
|
||||
all_monitors: bool,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub common: CommonConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
375
src/script.rs
375
src/script.rs
@@ -1,35 +1,354 @@
|
||||
use color_eyre::eyre::WrapErr;
|
||||
use color_eyre::{Help, Report, Result};
|
||||
use std::process::Command;
|
||||
use tracing::instrument;
|
||||
use color_eyre::{Report, Result};
|
||||
use serde::Deserialize;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::process::Stdio;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::time::sleep;
|
||||
use tokio::{select, spawn};
|
||||
use tracing::{error, warn};
|
||||
|
||||
/// Attempts to execute a given command.
|
||||
/// If the command returns status 0,
|
||||
/// the `stdout` is returned.
|
||||
/// Otherwise, an `Err` variant
|
||||
/// containing the `stderr` is returned.
|
||||
#[instrument]
|
||||
pub fn exec_command(command: &str) -> Result<String> {
|
||||
let output = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.output()
|
||||
.wrap_err("Failed to get script output")?;
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum ScriptInput {
|
||||
String(String),
|
||||
Struct(Script),
|
||||
}
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map(|output| output.trim().to_string())
|
||||
.wrap_err("Script stdout not valid UTF-8")?;
|
||||
#[derive(Debug, Deserialize, Clone, Copy, Eq, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScriptMode {
|
||||
Poll,
|
||||
Watch,
|
||||
}
|
||||
|
||||
Ok(stdout)
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr)
|
||||
.map(|output| output.trim().to_string())
|
||||
.wrap_err("Script stderr not valid UTF-8")?;
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutputStream {
|
||||
Stdout(String),
|
||||
Stderr(String),
|
||||
}
|
||||
|
||||
Err(Report::msg(stderr)
|
||||
.wrap_err("Script returned non-zero error code")
|
||||
.suggestion("Check the path to your script")
|
||||
.suggestion("Check the script for errors"))
|
||||
impl From<&str> for ScriptMode {
|
||||
fn from(str: &str) -> Self {
|
||||
match str {
|
||||
"poll" | "p" => Self::Poll,
|
||||
"watch" | "w" => Self::Watch,
|
||||
_ => {
|
||||
warn!("Invalid script mode: '{str}', falling back to polling");
|
||||
ScriptMode::Poll
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScriptMode {
|
||||
fn default() -> Self {
|
||||
Self::Poll
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ScriptMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ScriptMode::Poll => "poll",
|
||||
ScriptMode::Watch => "watch",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptMode {
|
||||
fn try_parse(str: &str) -> Result<Self> {
|
||||
match str {
|
||||
"poll" | "p" => Ok(Self::Poll),
|
||||
"watch" | "w" => Ok(Self::Watch),
|
||||
_ => Err(Report::msg(format!("Invalid script mode: {str}"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Script {
|
||||
#[serde(default = "ScriptMode::default")]
|
||||
pub(crate) mode: ScriptMode,
|
||||
pub cmd: String,
|
||||
#[serde(default = "default_interval")]
|
||||
pub(crate) interval: u64,
|
||||
}
|
||||
|
||||
const fn default_interval() -> u64 {
|
||||
5000
|
||||
}
|
||||
|
||||
impl Default for Script {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: ScriptMode::default(),
|
||||
interval: default_interval(),
|
||||
cmd: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ScriptInput> for Script {
|
||||
fn from(input: ScriptInput) -> Self {
|
||||
match input {
|
||||
ScriptInput::String(string) => Self::from(string.as_str()),
|
||||
ScriptInput::Struct(script) => script,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ScriptInputToken {
|
||||
Mode(ScriptMode),
|
||||
Interval(u64),
|
||||
Cmd(String),
|
||||
Colon,
|
||||
}
|
||||
|
||||
impl From<&str> for Script {
|
||||
fn from(str: &str) -> Self {
|
||||
let mut script = Self::default();
|
||||
let mut tokens = vec![];
|
||||
|
||||
let mut chars = str.chars().collect::<Vec<_>>();
|
||||
while !chars.is_empty() {
|
||||
let char = chars[0];
|
||||
|
||||
let (token, skip) = match char {
|
||||
':' => (ScriptInputToken::Colon, 1),
|
||||
// interval
|
||||
'0'..='9' => {
|
||||
let interval_str = chars
|
||||
.iter()
|
||||
.take_while(|c| c.is_ascii_digit())
|
||||
.collect::<String>();
|
||||
(
|
||||
ScriptInputToken::Interval(
|
||||
interval_str.parse::<u64>().expect("Invalid interval"),
|
||||
),
|
||||
interval_str.len(),
|
||||
)
|
||||
}
|
||||
// watching or polling
|
||||
'w' | 'p' => {
|
||||
let mode_str = chars.iter().take_while(|&c| c != &':').collect::<String>();
|
||||
let len = mode_str.len();
|
||||
|
||||
let token = ScriptMode::try_parse(&mode_str)
|
||||
.map_or(ScriptInputToken::Cmd(mode_str), |mode| {
|
||||
ScriptInputToken::Mode(mode)
|
||||
});
|
||||
|
||||
(token, len)
|
||||
}
|
||||
_ => {
|
||||
let cmd_str = chars.iter().take_while(|_| true).collect::<String>();
|
||||
let len = cmd_str.len();
|
||||
(ScriptInputToken::Cmd(cmd_str), len)
|
||||
}
|
||||
};
|
||||
|
||||
tokens.push(token);
|
||||
chars.drain(..skip);
|
||||
}
|
||||
|
||||
for token in tokens {
|
||||
match token {
|
||||
ScriptInputToken::Mode(mode) => script.mode = mode,
|
||||
ScriptInputToken::Interval(interval) => script.interval = interval,
|
||||
ScriptInputToken::Cmd(cmd) => script.cmd = cmd,
|
||||
ScriptInputToken::Colon => {}
|
||||
}
|
||||
}
|
||||
|
||||
script
|
||||
}
|
||||
}
|
||||
|
||||
impl Script {
|
||||
pub fn new_polling(input: ScriptInput) -> Self {
|
||||
let mut script = Self::from(input);
|
||||
script.mode = ScriptMode::Poll;
|
||||
script
|
||||
}
|
||||
|
||||
pub async fn run<F>(&self, callback: F)
|
||||
where
|
||||
F: Fn((OutputStream, bool)),
|
||||
{
|
||||
loop {
|
||||
match self.mode {
|
||||
ScriptMode::Poll => match self.get_output().await {
|
||||
Ok(output) => callback(output),
|
||||
Err(err) => error!("{err:?}"),
|
||||
},
|
||||
ScriptMode::Watch => match self.spawn().await {
|
||||
Ok(mut rx) => {
|
||||
while let Some(msg) = rx.recv().await {
|
||||
callback((msg, true));
|
||||
}
|
||||
}
|
||||
Err(err) => error!("{err:?}"),
|
||||
},
|
||||
};
|
||||
|
||||
sleep(tokio::time::Duration::from_millis(self.interval)).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to execute a given command,
|
||||
/// waiting for it to finish.
|
||||
/// If the command returns status 0,
|
||||
/// the `stdout` is returned.
|
||||
/// Otherwise, an `Err` variant
|
||||
/// containing the `stderr` is returned.
|
||||
pub async fn get_output(&self) -> Result<(OutputStream, bool)> {
|
||||
let output = Command::new("sh")
|
||||
.args(["-c", &self.cmd])
|
||||
.output()
|
||||
.await
|
||||
.wrap_err("Failed to get script output")?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map(|output| output.trim().to_string())
|
||||
.wrap_err("Script stdout not valid UTF-8")?;
|
||||
|
||||
Ok((OutputStream::Stdout(stdout), true))
|
||||
} else {
|
||||
let stderr = String::from_utf8(output.stderr)
|
||||
.map(|output| output.trim().to_string())
|
||||
.wrap_err("Script stderr not valid UTF-8")?;
|
||||
|
||||
Ok((OutputStream::Stderr(stderr), false))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn(&self) -> Result<mpsc::Receiver<OutputStream>> {
|
||||
let mut handle = Command::new("sh")
|
||||
.args(["-c", &self.cmd])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.stdin(Stdio::null())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdout_lines = BufReader::new(
|
||||
handle
|
||||
.stdout
|
||||
.take()
|
||||
.expect("Failed to take script handle stdout"),
|
||||
)
|
||||
.lines();
|
||||
|
||||
let mut stderr_lines = BufReader::new(
|
||||
handle
|
||||
.stderr
|
||||
.take()
|
||||
.expect("Failed to take script handle stderr"),
|
||||
)
|
||||
.lines();
|
||||
|
||||
let (tx, rx) = mpsc::channel(32);
|
||||
|
||||
spawn(async move {
|
||||
loop {
|
||||
select! {
|
||||
_ = handle.wait() => break,
|
||||
Ok(Some(line)) = stdout_lines.next_line() => {
|
||||
tx.send(OutputStream::Stdout(line)).await.expect("Failed to send stdout");
|
||||
}
|
||||
Ok(Some(line)) = stderr_lines.next_line() => {
|
||||
tx.send(OutputStream::Stderr(line)).await.expect("Failed to send stderr");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(rx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_basic() {
|
||||
let cmd = "echo 'hello'";
|
||||
let script = Script::from(cmd);
|
||||
|
||||
assert_eq!(script.cmd, cmd);
|
||||
assert_eq!(script.interval, default_interval());
|
||||
assert_eq!(script.mode, ScriptMode::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_full() {
|
||||
let cmd = "echo 'hello'";
|
||||
let mode = ScriptMode::Watch;
|
||||
let interval = 300;
|
||||
|
||||
let full_cmd = format!("{mode}:{interval}:{cmd}");
|
||||
let script = Script::from(full_cmd.as_str());
|
||||
|
||||
assert_eq!(script.cmd, cmd);
|
||||
assert_eq!(script.mode, mode);
|
||||
assert_eq!(script.interval, interval);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_interval_and_cmd() {
|
||||
let cmd = "echo 'hello'";
|
||||
let interval = 300;
|
||||
|
||||
let full_cmd = format!("{interval}:{cmd}");
|
||||
let script = Script::from(full_cmd.as_str());
|
||||
|
||||
assert_eq!(script.cmd, cmd);
|
||||
assert_eq!(script.interval, interval);
|
||||
assert_eq!(script.mode, ScriptMode::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mode_and_cmd() {
|
||||
let cmd = "echo 'hello'";
|
||||
let mode = ScriptMode::Watch;
|
||||
|
||||
let full_cmd = format!("{mode}:{cmd}");
|
||||
let script = Script::from(full_cmd.as_str());
|
||||
|
||||
assert_eq!(script.cmd, cmd);
|
||||
assert_eq!(script.interval, default_interval());
|
||||
assert_eq!(script.mode, mode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_cmd_with_colon() {
|
||||
let cmd = "uptime | awk '{print \"Uptime: \" $1}'";
|
||||
let script = Script::from(cmd);
|
||||
|
||||
assert_eq!(script.cmd, cmd);
|
||||
assert_eq!(script.interval, default_interval());
|
||||
assert_eq!(script.mode, ScriptMode::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_cmd() {
|
||||
let mode = ScriptMode::Watch;
|
||||
let interval = 300;
|
||||
|
||||
let full_cmd = format!("{mode}:{interval}");
|
||||
let script = Script::from(full_cmd.as_str());
|
||||
|
||||
assert_eq!(script.cmd, ""); // TODO: Probably better handle this case
|
||||
assert_eq!(script.interval, interval);
|
||||
assert_eq!(script.mode, mode);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user