mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
Merge branch 'master' into feat/volume-icon
This commit is contained in:
commit
9ea49202b3
55 changed files with 1161 additions and 556 deletions
60
.github/workflows/deploy.yml
vendored
60
.github/workflows/deploy.yml
vendored
|
@ -6,20 +6,13 @@ on:
|
||||||
- v[0-9]+.[0-9]+.[0-9]+
|
- v[0-9]+.[0-9]+.[0-9]+
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
release:
|
||||||
|
name: 'Create Release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Install build deps
|
|
||||||
run: ./.github/scripts/ubuntu_setup.sh
|
|
||||||
|
|
||||||
- name: Update CHANGELOG
|
- name: Update CHANGELOG
|
||||||
id: changelog
|
id: changelog
|
||||||
uses: Requarks/changelog-action@v1
|
uses: Requarks/changelog-action@v1
|
||||||
|
@ -41,9 +34,54 @@ jobs:
|
||||||
uses: stefanzweifel/git-auto-commit-action@v4
|
uses: stefanzweifel/git-auto-commit-action@v4
|
||||||
with:
|
with:
|
||||||
branch: master
|
branch: master
|
||||||
commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]'
|
commit_message: 'chore: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]'
|
||||||
file_pattern: CHANGELOG.md
|
file_pattern: CHANGELOG.md
|
||||||
|
|
||||||
- uses: katyo/publish-crates@v1
|
|
||||||
|
publish-crate:
|
||||||
|
name: 'Publish Crate'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Install build deps
|
||||||
|
run: ./.github/scripts/ubuntu_setup.sh
|
||||||
|
|
||||||
|
- name: Publish crate
|
||||||
|
uses: katyo/publish-crates@v1
|
||||||
with:
|
with:
|
||||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
publish-schema:
|
||||||
|
name: 'Publish Schema'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
name: Cache dependencies
|
||||||
|
|
||||||
|
- name: Install build deps
|
||||||
|
run: ./.github/scripts/ubuntu_setup.sh
|
||||||
|
|
||||||
|
- name: Build schema
|
||||||
|
run: cargo build --features schema -- --print-schema > target/schema-${{ github.ref_name }}.json
|
||||||
|
|
||||||
|
- name: Copy file via SSH
|
||||||
|
uses: appleboy/scp-action@v0.1.7
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
source: "target/schema-${{ github.ref_name }}.json"
|
||||||
|
target: /storage/Public/github/ironbar
|
||||||
|
strip_components: 1
|
41
.github/workflows/schema.yml
vendored
Normal file
41
.github/workflows/schema.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
name: Publish Schema
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUSTFLAGS: '-Dwarnings'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-schema:
|
||||||
|
name: 'Publish Schema'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
name: Cache dependencies
|
||||||
|
|
||||||
|
- name: Install build deps
|
||||||
|
run: ./.github/scripts/ubuntu_setup.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --features schema
|
||||||
|
|
||||||
|
- name: Print schema
|
||||||
|
run: cargo run --features schema -- --print-schema > target/schema.json
|
||||||
|
|
||||||
|
- name: Copy file via SSH
|
||||||
|
uses: appleboy/scp-action@v0.1.7
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
port: ${{ secrets.SSH_PORT }}
|
||||||
|
username: ${{ secrets.SSH_USERNAME }}
|
||||||
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
source: "target/schema.json"
|
||||||
|
target: /storage/Public/github/ironbar
|
||||||
|
strip_components: 1
|
211
Cargo.lock
generated
211
Cargo.lock
generated
|
@ -437,9 +437,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg_aliases"
|
name = "cfg_aliases"
|
||||||
version = "0.1.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
|
@ -456,9 +456,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.4"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -466,9 +466,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -478,9 +478,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.4"
|
version = "4.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -761,6 +761,12 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
|
@ -891,9 +897,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.1.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
@ -1446,6 +1452,23 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.27.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -1542,9 +1565,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-bidi",
|
"unicode-bidi",
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
|
@ -1637,10 +1660,11 @@ dependencies = [
|
||||||
"mlua",
|
"mlua",
|
||||||
"mpd-utils",
|
"mpd-utils",
|
||||||
"mpris",
|
"mpris",
|
||||||
"nix 0.28.0",
|
"nix 0.29.0",
|
||||||
"notify",
|
"notify",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
|
@ -1875,9 +1899,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.9.8"
|
version = "0.9.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e340c022072f3208a4105458286f4985ba5355bfe243c3073afe45cbe9ecf491"
|
checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"mlua-sys",
|
"mlua-sys",
|
||||||
|
@ -1888,9 +1912,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mlua-sys"
|
name = "mlua-sys"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5552e7e4e22ada0463dfdeee6caf6dc057a189fdc83136408a8f950a5e5c5540"
|
checksum = "a088ed0723df7567f569ba018c5d48c23c501f3878b190b04144dfa5ebfa8abc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -1991,9 +2015,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.28.0"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -2203,9 +2227,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pest"
|
name = "pest"
|
||||||
|
@ -2490,9 +2514,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.4"
|
version = "1.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -2534,9 +2558,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.4"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
|
checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.0",
|
"base64 0.22.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -2547,6 +2571,7 @@ dependencies = [
|
||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
|
@ -2572,6 +2597,20 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
@ -2592,9 +2631,9 @@ checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
|
@ -2632,6 +2671,19 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "2.1.2"
|
version = "2.1.2"
|
||||||
|
@ -2648,6 +2700,17 @@ version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247"
|
checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.102.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.13"
|
version = "1.0.13"
|
||||||
|
@ -2672,6 +2735,30 @@ dependencies = [
|
||||||
"windows-sys 0.42.0",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"schemars_derive",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars_derive"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"serde_derive_internals",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -2715,18 +2802,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive_internals"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.35",
|
"quote 1.0.35",
|
||||||
|
@ -2899,6 +2997,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2926,6 +3030,12 @@ version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swayipc-async"
|
name = "swayipc-async"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
@ -2986,9 +3096,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sync_wrapper"
|
name = "sync_wrapper"
|
||||||
version = "0.1.2"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synom"
|
name = "synom"
|
||||||
|
@ -3127,9 +3237,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.37.0"
|
version = "1.38.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -3146,9 +3256,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.35",
|
"quote 1.0.35",
|
||||||
|
@ -3165,6 +3275,17 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
|
@ -3445,6 +3566,12 @@ version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "upower_dbus"
|
name = "upower_dbus"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -3458,9 +3585,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.3.1"
|
version = "2.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
|
@ -4102,6 +4229,12 @@ dependencies = [
|
||||||
"zvariant",
|
"zvariant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "3.15.0"
|
version = "3.15.0"
|
||||||
|
|
23
Cargo.toml
23
Cargo.toml
|
@ -79,12 +79,14 @@ workspaces = ["futures-lite"]
|
||||||
"workspaces+sway" = ["workspaces", "swayipc-async"]
|
"workspaces+sway" = ["workspaces", "swayipc-async"]
|
||||||
"workspaces+hyprland" = ["workspaces", "hyprland"]
|
"workspaces+hyprland" = ["workspaces", "hyprland"]
|
||||||
|
|
||||||
|
schema = ["dep:schemars"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# core
|
# core
|
||||||
gtk = "0.18.1"
|
gtk = "0.18.1"
|
||||||
gtk-layer-shell = "0.8.0"
|
gtk-layer-shell = "0.8.0"
|
||||||
glib = "0.18.5"
|
glib = "0.18.5"
|
||||||
tokio = { version = "1.37.0", features = [
|
tokio = { version = "1.38.0", features = [
|
||||||
"macros",
|
"macros",
|
||||||
"rt-multi-thread",
|
"rt-multi-thread",
|
||||||
"time",
|
"time",
|
||||||
|
@ -99,7 +101,7 @@ tracing-error = { version = "0.2.0" , default-features = false }
|
||||||
tracing-appender = "0.2.3"
|
tracing-appender = "0.2.3"
|
||||||
strip-ansi-escapes = "0.2.0"
|
strip-ansi-escapes = "0.2.0"
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
serde = { version = "1.0.202", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
indexmap = "2.2.6"
|
indexmap = "2.2.6"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
@ -109,29 +111,29 @@ wayland-protocols-wlr = { version = "0.2.0", features = ["client"] }
|
||||||
smithay-client-toolkit = { version = "0.18.1", default-features = false, features = [
|
smithay-client-toolkit = { version = "0.18.1", default-features = false, features = [
|
||||||
"calloop",
|
"calloop",
|
||||||
] }
|
] }
|
||||||
universal-config = { version = "0.5.0", default_features = false }
|
universal-config = { version = "0.5.0", default-features = false }
|
||||||
ctrlc = "3.4.2"
|
ctrlc = "3.4.2"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
# cli
|
# cli
|
||||||
clap = { version = "4.5.4", optional = true, features = ["derive"] }
|
clap = { version = "4.5.7", optional = true, features = ["derive"] }
|
||||||
|
|
||||||
# ipc
|
# ipc
|
||||||
serde_json = { version = "1.0.117", optional = true }
|
serde_json = { version = "1.0.117", optional = true }
|
||||||
|
|
||||||
# http
|
# http
|
||||||
reqwest = { version = "0.12.4", default_features = false, features = ["default-tls", "http2"], optional = true }
|
reqwest = { version = "0.12.5", default-features = false, features = ["default-tls", "http2"], optional = true }
|
||||||
|
|
||||||
# cairo
|
# cairo
|
||||||
lua-src = { version = "546.0.2", optional = true }
|
lua-src = { version = "546.0.2", optional = true }
|
||||||
mlua = { version = "0.9.8", optional = true, features = ["luajit"] }
|
mlua = { version = "0.9.9", optional = true, features = ["luajit"] }
|
||||||
cairo-rs = { version = "0.18.5", optional = true, features = ["png"] }
|
cairo-rs = { version = "0.18.5", optional = true, features = ["png"] }
|
||||||
|
|
||||||
# clipboard
|
# clipboard
|
||||||
nix = { version = "0.28.0", optional = true, features = ["event", "fs"] }
|
nix = { version = "0.29.0", optional = true, features = ["event", "fs"] }
|
||||||
|
|
||||||
# clock
|
# clock
|
||||||
chrono = { version = "0.4.38", optional = true, default_features = false, features = ["clock", "unstable-locales"] }
|
chrono = { version = "0.4.38", optional = true, default-features = false, features = ["clock", "unstable-locales"] }
|
||||||
|
|
||||||
# music
|
# music
|
||||||
mpd-utils = { version = "0.2.1", optional = true }
|
mpd-utils = { version = "0.2.1", optional = true }
|
||||||
|
@ -159,7 +161,10 @@ futures-util = { version = "0.3.30", optional = true }
|
||||||
|
|
||||||
# shared
|
# shared
|
||||||
futures-lite = { version = "2.3.0", optional = true } # networkmanager, upower, workspaces
|
futures-lite = { version = "2.3.0", optional = true } # networkmanager, upower, workspaces
|
||||||
regex = { version = "1.10.4", default-features = false, features = [
|
regex = { version = "1.10.5", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
], optional = true } # music, sys_info
|
], optional = true } # music, sys_info
|
||||||
zbus = { version = "3.15.2", default-features = false, features = ["tokio"], optional = true } # networkmanager, notifications, upower
|
zbus = { version = "3.15.2", default-features = false, features = ["tokio"], optional = true } # networkmanager, notifications, upower
|
||||||
|
|
||||||
|
# schema
|
||||||
|
schemars = { version = "0.8.21", optional = true }
|
||||||
|
|
|
@ -136,6 +136,15 @@ A flake is included with the repo which can be used with Home Manager.
|
||||||
CI builds are automatically cached by Garnix.
|
CI builds are automatically cached by Garnix.
|
||||||
You can use their binary cache by following the steps [here](https://garnix.io/docs/caching).
|
You can use their binary cache by following the steps [here](https://garnix.io/docs/caching).
|
||||||
|
|
||||||
|
### Fedora
|
||||||
|
|
||||||
|
[fedora package](https://copr.fedorainfracloud.org/coprs/victorvintorez/tilingtools/packages/)
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
dnf copr enable victorvintorez/tilingtools
|
||||||
|
dnf install ironbar
|
||||||
|
```
|
||||||
|
|
||||||
### Void Linux
|
### Void Linux
|
||||||
|
|
||||||
[void package](https://github.com/void-linux/void-packages/tree/master/srcpkgs/ironbar)
|
[void package](https://github.com/void-linux/void-packages/tree/master/srcpkgs/ironbar)
|
||||||
|
|
|
@ -101,6 +101,9 @@ cargo build --release --no-default-features \
|
||||||
| workspaces+all | Enables the `workspaces` module with support for all compositors. |
|
| workspaces+all | Enables the `workspaces` module with support for all compositors. |
|
||||||
| workspaces+sway | Enables the `workspaces` module with support for Sway. |
|
| workspaces+sway | Enables the `workspaces` module with support for Sway. |
|
||||||
| workspaces+hyprland | Enables the `workspaces` module with support for Hyprland. |
|
| workspaces+hyprland | Enables the `workspaces` module with support for Hyprland. |
|
||||||
|
| **Other** | |
|
||||||
|
| schema | Enables JSON schema support and the CLI `--print-schema` flag. |
|
||||||
|
|
||||||
|
|
||||||
## Speeding up compiling
|
## Speeding up compiling
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,18 @@ Ironbar supports a range of configuration formats, so you can pick your favourit
|
||||||
- `config.json`
|
- `config.json`
|
||||||
- `config.toml`
|
- `config.toml`
|
||||||
- `config.yaml`
|
- `config.yaml`
|
||||||
- `config.corn` (Experimental, includes variable support for re-using blocks.
|
- `config.corn` (Includes variable support for re-using blocks.
|
||||||
See [here](https://github.com/jakestanger/corn) for info)
|
See [here](https://github.com/jakestanger/corn) for info)
|
||||||
|
|
||||||
You can also override the default config path using the `IRONBAR_CONFIG` environment variable.
|
You can also override the default config path using the `IRONBAR_CONFIG` environment variable.
|
||||||
|
|
||||||
|
A hosted schema is available for the latest Git version ~~and each versioned release~~.
|
||||||
|
JSON and YAML both support schema checking by adding the `$schema` key
|
||||||
|
to the top level of your config.
|
||||||
|
|
||||||
|
- master: `https://f.jstanger.dev/github/ironbar/schema.json`
|
||||||
|
- ~~release: `https://f.jstanger.dev/github/ironbar/schema-v0.16.0.json`~~ *(Not released yet)*
|
||||||
|
|
||||||
## 2. Pick your use-case
|
## 2. Pick your use-case
|
||||||
|
|
||||||
Ironbar gives you a few ways to configure the bar to suit your needs.
|
Ironbar gives you a few ways to configure the bar to suit your needs.
|
||||||
|
@ -288,23 +295,25 @@ The following table lists each of the top-level bar config options:
|
||||||
|
|
||||||
The following table lists each of the bar-level bar config options:
|
The following table lists each of the bar-level bar config options:
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|-------------------|----------------------------------------|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------|
|
|-------------------|------------------------------------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `name` | `string` | `bar-<n>` | A unique identifier for the bar, used for controlling it over IPC. If not set, uses a generated integer suffix. |
|
| `name` | `string` | `bar-<n>` | A unique identifier for the bar, used for controlling it over IPC. If not set, uses a generated integer suffix. |
|
||||||
| `position` | `top` or `bottom` or `left` or `right` | `bottom` | The bar's position on screen. |
|
| `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. |
|
| `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. |
|
| `height` | `integer` | `42` | The bar's height in pixels. |
|
||||||
| `popup_gap` | `integer` | `5` | The gap between the bar and popup window. |
|
| `margin.top` | `integer` | `0` | The margin on the top of the bar |
|
||||||
| `margin.top` | `integer` | `0` | The margin on the top of the bar |
|
| `margin.bottom` | `integer` | `0` | The margin on the bottom of the bar |
|
||||||
| `margin.bottom` | `integer` | `0` | The margin on the bottom of the bar |
|
| `margin.left` | `integer` | `0` | The margin on the left of the bar |
|
||||||
| `margin.left` | `integer` | `0` | The margin on the left of the bar |
|
| `margin.right` | `integer` | `0` | The margin on the right of the bar |
|
||||||
| `margin.right` | `integer` | `0` | The margin on the right of the bar |
|
| `layer` | `background` or `bottom` or `top` or `overlay` | `top` | The layer-shell layer to place the bar on. |
|
||||||
| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. |
|
| `exclusive_zone` | `boolean` | `true` unless `start_hidden` is enabled. | Whether the bar should reserve an exclusive zone around it. |
|
||||||
| `start_hidden` | `boolean` | `false`, or `true` if `autohide` set | Whether the bar should be hidden when the application starts. Enabled by default when `autohide` is set. |
|
| `popup_gap` | `integer` | `5` | The gap between the bar and popup window. |
|
||||||
| `autohide` | `integer` | `null` | The duration in milliseconds before the bar is hidden after the cursor leaves. Leave unset to disable auto-hide behaviour. |
|
| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. |
|
||||||
| `start` | `Module[]` | `[]` | Array of left or top modules. |
|
| `start_hidden` | `boolean` | `false`, or `true` if `autohide` set | Whether the bar should be hidden when the application starts. Enabled by default when `autohide` is set. |
|
||||||
| `center` | `Module[]` | `[]` | Array of center modules. |
|
| `autohide` | `integer` | `null` | The duration in milliseconds before the bar is hidden after the cursor leaves. Leave unset to disable auto-hide behaviour. |
|
||||||
| `end` | `Module[]` | `[]` | Array of right or bottom modules. |
|
| `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
|
### 3.2 Module-level options
|
||||||
|
|
||||||
|
|
|
@ -5,28 +5,38 @@ It also includes a command line interface, which can be used for interacting wit
|
||||||
# CLI
|
# CLI
|
||||||
|
|
||||||
This is shipped as part of the `ironbar` binary. To view commands, you can use `ironbar --help`.
|
This is shipped as part of the `ironbar` binary. To view commands, you can use `ironbar --help`.
|
||||||
You can also view help per-command, for example using `ironbar set --help`.
|
You can also view help per sub-command or command, for example using `ironbar var --help` or `ironbar var set --help`.
|
||||||
|
|
||||||
Responses are handled by writing their type to stdout, followed by any value starting on the next line.
|
The CLI supports plaintext and JSON output. Plaintext will:
|
||||||
Error responses are written to stderr in the same format.
|
|
||||||
|
- Print `ok` for empty success responses
|
||||||
|
- Print the returned body for success responses
|
||||||
|
- Print `error` to followed by the error on the next line for error responses. This is printed to `stderr`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ironbar set subject world
|
$ ironbar var set subject world
|
||||||
ok
|
ok
|
||||||
|
|
||||||
$ ironbar get subject
|
$ ironbar var get subject
|
||||||
ok
|
|
||||||
world
|
world
|
||||||
|
|
||||||
|
$ ironbar var get foo
|
||||||
|
error
|
||||||
|
Variable not found
|
||||||
```
|
```
|
||||||
|
|
||||||
|
All error responses will cause the CLI to exit code 3.
|
||||||
|
|
||||||
# IPC
|
# IPC
|
||||||
|
|
||||||
The server listens on a Unix socket.
|
The server listens on a Unix socket.
|
||||||
This can usually be found at `/run/user/$UID/ironbar-ipc.sock`.
|
The path is printed on startup, and can usually be found at `/run/user/$UID/ironbar-ipc.sock`.
|
||||||
|
|
||||||
Commands and responses are sent as JSON objects, denoted by their `type` key.
|
Commands and responses are sent as JSON objects.
|
||||||
|
|
||||||
|
Commands will have a `command` key, and a `subcommand` key when part of a sub-command.
|
||||||
|
|
||||||
The message buffer is currently limited to `1024` bytes.
|
The message buffer is currently limited to `1024` bytes.
|
||||||
Particularly large messages will be truncated or cause an error.
|
Particularly large messages will be truncated or cause an error.
|
||||||
|
@ -47,7 +57,7 @@ Responds with `ok`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "ping"
|
"command": "ping"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -59,7 +69,7 @@ Responds with `ok`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "inspect"
|
"command": "inspect"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -73,48 +83,7 @@ Responds with `ok`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "reload"
|
"command": "reload"
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `get`
|
|
||||||
|
|
||||||
Gets an [ironvar](ironvars) value.
|
|
||||||
|
|
||||||
Responds with `ok_value` if the value exists, otherwise `error`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "get",
|
|
||||||
"key": "foo"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `set`
|
|
||||||
|
|
||||||
Sets an [ironvar](ironvars) value.
|
|
||||||
|
|
||||||
Responds with `ok`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "set",
|
|
||||||
"key": "foo",
|
|
||||||
"value": "bar"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### list
|
|
||||||
|
|
||||||
Gets a list of all [ironvar](ironvars) values.
|
|
||||||
|
|
||||||
Responds with `ok_value`.
|
|
||||||
|
|
||||||
Each key/value pair is on its own `\n` separated newline. The key and value are separated by a colon and space `: `.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "list"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -126,26 +95,113 @@ Responds with `ok` if the stylesheet exists, otherwise `error`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "load_css",
|
"command": "load_css",
|
||||||
"path": "/path/to/style.css"
|
"path": "/path/to/style.css"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `set_visible`
|
### `var`
|
||||||
|
|
||||||
Sets a bar's visibility.
|
Subcommand for controlling Ironvars.
|
||||||
|
|
||||||
|
#### `get`
|
||||||
|
|
||||||
|
Gets an [ironvar](ironvars) value.
|
||||||
|
|
||||||
|
Responds with `ok_value` if the value exists, otherwise `error`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "var",
|
||||||
|
"subcommand": "get",
|
||||||
|
"key": "foo"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `set`
|
||||||
|
|
||||||
|
Sets an [ironvar](ironvars) value.
|
||||||
|
|
||||||
|
Responds with `ok`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "var",
|
||||||
|
"subcommand": "set",
|
||||||
|
"key": "foo",
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `list`
|
||||||
|
|
||||||
|
Gets a list of all [ironvar](ironvars) values.
|
||||||
|
|
||||||
|
Responds with `ok_value`.
|
||||||
|
|
||||||
|
Each key/value pair is on its own `\n` separated newline. The key and value are separated by a colon and space `: `.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "var",
|
||||||
|
"subcommand": "list"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `bar`
|
||||||
|
|
||||||
|
#### `show`
|
||||||
|
|
||||||
|
Forces a bar to be shown, regardless of the current visibility state.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "show",
|
||||||
|
"name": "bar-123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `hide`
|
||||||
|
|
||||||
|
Forces a bar to be hidden, regardless of the current visibility state.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "hide",
|
||||||
|
"name": "bar-123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `set_visible`
|
||||||
|
|
||||||
|
Sets a bar's visibility to one of shown/hidden.
|
||||||
|
|
||||||
Responds with `ok` if the bar exists, otherwise `error`.
|
Responds with `ok` if the bar exists, otherwise `error`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "set_visible",
|
"command": "bar",
|
||||||
"bar_name": "bar-123",
|
"subcommand": "set_visible",
|
||||||
|
"name": "bar-123",
|
||||||
"visible": true
|
"visible": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_visible`
|
#### `toggle_visible`
|
||||||
|
|
||||||
|
Toggles the current visibility state of a bar between shown and hidden.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "toggle_visible",
|
||||||
|
"name": "bar-123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `get_visible`
|
||||||
|
|
||||||
Gets a bar's visibility.
|
Gets a bar's visibility.
|
||||||
|
|
||||||
|
@ -153,54 +209,98 @@ Responds with `ok_value` and the visibility (`true`/`false`) if the bar exists,
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "get_visible",
|
"command": "bar",
|
||||||
"bar_name": "bar-123"
|
"subcommand": "get_visible",
|
||||||
|
"name": "bar-123"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `toggle_popup`
|
#### `show_popup`
|
||||||
|
|
||||||
Toggles the open/closed state for a module's popup.
|
|
||||||
Since each bar only has a single popup, any open popup on the bar is closed.
|
|
||||||
|
|
||||||
Responds with `ok` if the popup exists, otherwise `error`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "toggle_popup",
|
|
||||||
"bar_name": "bar-123",
|
|
||||||
"name": "clock"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `open_popup`
|
|
||||||
|
|
||||||
Sets a module's popup open, regardless of its current state.
|
Sets a module's popup open, regardless of its current state.
|
||||||
Since each bar only has a single popup, any open popup on the bar is closed.
|
Since each bar only has a single popup, any open popup on the bar is closed.
|
||||||
|
|
||||||
Responds with `ok` if the popup exists, otherwise `error`.
|
Responds with `ok` if the bar and widget exist, otherwise `error`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "open_popup",
|
"command": "bar",
|
||||||
"bar_name": "bar-123",
|
"subcommand": "show_popup",
|
||||||
"name": "clock"
|
"name": "bar-123",
|
||||||
|
"widget_name": "clock"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `close_popup`
|
#### `hide_popup`
|
||||||
|
|
||||||
Sets the popup on a bar closed, regardless of which module it is open for.
|
Sets the popup on a bar closed, regardless of which module it is open for.
|
||||||
|
|
||||||
Responds with `ok` if the popup exists, otherwise `error`.
|
Responds with `ok` if the bar and widget exist, otherwise `error`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "close_popup",
|
"command": "bar",
|
||||||
|
"subcommand": "hide_popup",
|
||||||
"bar_name": "bar-123"
|
"bar_name": "bar-123"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `set_popup_visible`
|
||||||
|
|
||||||
|
Sets a popup's visibility to one of shown/hidden.
|
||||||
|
|
||||||
|
Responds with `ok` if the bar and widget exist, otherwise `error`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "set_popup_visible",
|
||||||
|
"name": "bar-123",
|
||||||
|
"widget_name": "clock",
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `toggle_popup`
|
||||||
|
|
||||||
|
Toggles the open/closed state for a module's popup.
|
||||||
|
Since each bar only has a single popup, any open popup on the bar is closed.
|
||||||
|
|
||||||
|
Responds with `ok` if the bar and widget exist, otherwise `error`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "toggle_popup",
|
||||||
|
"bar_name": "bar-123",
|
||||||
|
"widget_name": "clock"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `get_popup_visible`
|
||||||
|
|
||||||
|
Gets the popup's current visibility state.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "get_popup_visible",
|
||||||
|
"bar_name": "bar-123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `set_exclusive`
|
||||||
|
|
||||||
|
Sets whether the bar reserves an exclusive zone.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "bar",
|
||||||
|
"subcommand": "set_exclusive",
|
||||||
|
"exclusive": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Responses
|
## Responses
|
||||||
|
|
||||||
### `ok`
|
### `ok`
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"$schema": "https://f.jstanger.dev/github/ironbar/schema.json",
|
||||||
"anchor_to_edges": true,
|
"anchor_to_edges": true,
|
||||||
"position": "bottom",
|
"position": "bottom",
|
||||||
"icon_theme": "Paper",
|
"icon_theme": "Paper",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
$schema: https://f.jstanger.dev/github/ironbar/schema.json
|
||||||
anchor_to_edges: true
|
anchor_to_edges: true
|
||||||
position: bottom
|
position: bottom
|
||||||
icon_theme: Paper
|
icon_theme: Paper
|
||||||
|
|
30
flake.lock
generated
30
flake.lock
generated
|
@ -7,11 +7,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1713979152,
|
"lastModified": 1717025063,
|
||||||
"narHash": "sha256-apdecPuh8SOQnkEET/kW/UcfjCRb8JbV5BKjoH+DcP4=",
|
"narHash": "sha256-dIubLa56W9sNNz0e8jGxrX3CAkPXsq7snuFA/Ie6dn8=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "a5eca68a2cf11adb32787fc141cddd29ac8eb79c",
|
"rev": "480dff0be03dac0e51a8dfc26e882b0d123a450e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1713520724,
|
"lastModified": 1717067539,
|
||||||
"narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=",
|
"narHash": "sha256-oIs5EF+6VpHJRvvpVWuqCYJMMVW/6h59aYUv9lABLtY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49",
|
"rev": "fa19d8c135e776dc97f4dcca08656a0eeb28d5c0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -58,11 +58,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714314149,
|
"lastModified": 1717112898,
|
||||||
"narHash": "sha256-yNAevSKF4krRWacmLUsLK7D7PlfuY3zF0lYnGYNi9vQ=",
|
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "cf8cc1201be8bc71b7cbbbdaf349b22f4f99c7ae",
|
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -72,11 +72,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714253743,
|
"lastModified": 1716948383,
|
||||||
"narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
|
"narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
|
"rev": "ad57eef4ef0659193044870c731987a6df5cf56b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -102,11 +102,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714443211,
|
"lastModified": 1717121863,
|
||||||
"narHash": "sha256-lKTA3XqRo4aVgkyTSCtpcALpGXdmkilHTtN00eRg0QU=",
|
"narHash": "sha256-/3sxIe7MZqF/jw1RTQCSmgTjwVod43mmrk84m50MJQ4=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "ce35c36f58f82cee6ec959e0d44c587d64281b6f",
|
"rev": "2a7b53172ed08f856b8382d7dcfd36a4e0cbd866",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
57
src/bar.rs
57
src/bar.rs
|
@ -120,27 +120,28 @@ impl Bar {
|
||||||
self.name, self.monitor_name
|
self.name, self.monitor_name
|
||||||
);
|
);
|
||||||
|
|
||||||
self.setup_layer_shell(
|
|
||||||
&self.window,
|
|
||||||
true,
|
|
||||||
config.anchor_to_edges,
|
|
||||||
config.margin,
|
|
||||||
monitor,
|
|
||||||
);
|
|
||||||
|
|
||||||
let start_hidden = config
|
let start_hidden = config
|
||||||
.start_hidden
|
.start_hidden
|
||||||
.unwrap_or_else(|| config.autohide.is_some());
|
.unwrap_or_else(|| config.autohide.is_some());
|
||||||
|
|
||||||
|
self.setup_layer_shell(
|
||||||
|
&self.window,
|
||||||
|
config.exclusive_zone.unwrap_or(!start_hidden),
|
||||||
|
config.anchor_to_edges,
|
||||||
|
config.margin,
|
||||||
|
config.layer,
|
||||||
|
monitor,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(autohide) = config.autohide {
|
if let Some(autohide) = config.autohide {
|
||||||
let hotspot_window = Window::new(WindowType::Toplevel);
|
let hotspot_window = Window::new(WindowType::Toplevel);
|
||||||
|
|
||||||
Self::setup_autohide(&self.window, &hotspot_window, autohide);
|
Self::setup_autohide(&self.window, &hotspot_window, autohide);
|
||||||
self.setup_layer_shell(
|
self.setup_layer_shell(
|
||||||
&hotspot_window,
|
&hotspot_window,
|
||||||
false,
|
false,
|
||||||
config.anchor_to_edges,
|
config.anchor_to_edges,
|
||||||
config.margin,
|
config.margin,
|
||||||
|
gtk_layer_shell::Layer::Top,
|
||||||
monitor,
|
monitor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -166,43 +167,46 @@ impl Bar {
|
||||||
exclusive_zone: bool,
|
exclusive_zone: bool,
|
||||||
anchor_to_edges: bool,
|
anchor_to_edges: bool,
|
||||||
margin: MarginConfig,
|
margin: MarginConfig,
|
||||||
|
layer: gtk_layer_shell::Layer,
|
||||||
monitor: &Monitor,
|
monitor: &Monitor,
|
||||||
) {
|
) {
|
||||||
|
use gtk_layer_shell::Edge;
|
||||||
|
|
||||||
let position = self.position;
|
let position = self.position;
|
||||||
|
|
||||||
win.init_layer_shell();
|
win.init_layer_shell();
|
||||||
win.set_monitor(monitor);
|
win.set_monitor(monitor);
|
||||||
win.set_layer(gtk_layer_shell::Layer::Top);
|
win.set_layer(layer);
|
||||||
win.set_namespace(env!("CARGO_PKG_NAME"));
|
win.set_namespace(env!("CARGO_PKG_NAME"));
|
||||||
|
|
||||||
if exclusive_zone {
|
if exclusive_zone {
|
||||||
win.auto_exclusive_zone_enable();
|
win.auto_exclusive_zone_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
win.set_layer_shell_margin(gtk_layer_shell::Edge::Top, margin.top);
|
win.set_layer_shell_margin(Edge::Top, margin.top);
|
||||||
win.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, margin.bottom);
|
win.set_layer_shell_margin(Edge::Bottom, margin.bottom);
|
||||||
win.set_layer_shell_margin(gtk_layer_shell::Edge::Left, margin.left);
|
win.set_layer_shell_margin(Edge::Left, margin.left);
|
||||||
win.set_layer_shell_margin(gtk_layer_shell::Edge::Right, margin.right);
|
win.set_layer_shell_margin(Edge::Right, margin.right);
|
||||||
|
|
||||||
let bar_orientation = position.orientation();
|
let bar_orientation = position.orientation();
|
||||||
|
|
||||||
win.set_anchor(
|
win.set_anchor(
|
||||||
gtk_layer_shell::Edge::Top,
|
Edge::Top,
|
||||||
position == BarPosition::Top
|
position == BarPosition::Top
|
||||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||||
);
|
);
|
||||||
win.set_anchor(
|
win.set_anchor(
|
||||||
gtk_layer_shell::Edge::Bottom,
|
Edge::Bottom,
|
||||||
position == BarPosition::Bottom
|
position == BarPosition::Bottom
|
||||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||||
);
|
);
|
||||||
win.set_anchor(
|
win.set_anchor(
|
||||||
gtk_layer_shell::Edge::Left,
|
Edge::Left,
|
||||||
position == BarPosition::Left
|
position == BarPosition::Left
|
||||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||||
);
|
);
|
||||||
win.set_anchor(
|
win.set_anchor(
|
||||||
gtk_layer_shell::Edge::Right,
|
Edge::Right,
|
||||||
position == BarPosition::Right
|
position == BarPosition::Right
|
||||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||||
);
|
);
|
||||||
|
@ -320,6 +324,23 @@ impl Bar {
|
||||||
Inner::Loaded { popup } => popup.clone(),
|
Inner::Loaded { popup } => popup.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn visible(&self) -> bool {
|
||||||
|
self.window.is_visible()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the window visibility status
|
||||||
|
pub fn set_visible(&self, visible: bool) {
|
||||||
|
self.window.set_visible(visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_exclusive(&self, exclusive: bool) {
|
||||||
|
if exclusive {
|
||||||
|
self.window.auto_exclusive_zone_enable();
|
||||||
|
} else {
|
||||||
|
self.window.set_exclusive_zone(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `gtk::Box` container to place widgets inside.
|
/// Creates a `gtk::Box` container to place widgets inside.
|
||||||
|
|
48
src/cli.rs
48
src/cli.rs
|
@ -1,7 +1,9 @@
|
||||||
|
use crate::error::ExitCode;
|
||||||
use crate::ipc::commands::Command;
|
use crate::ipc::commands::Command;
|
||||||
use crate::ipc::responses::Response;
|
use crate::ipc::responses::Response;
|
||||||
use clap::Parser;
|
use clap::{Parser, ValueEnum};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
#[derive(Parser, Debug, Serialize, Deserialize)]
|
#[derive(Parser, Debug, Serialize, Deserialize)]
|
||||||
#[command(version)]
|
#[command(version)]
|
||||||
|
@ -9,16 +11,50 @@ pub struct Args {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<Command>,
|
pub command: Option<Command>,
|
||||||
|
|
||||||
|
/// Prints the config JSON schema to `stdout`
|
||||||
|
/// and exits.
|
||||||
|
#[cfg(feature = "schema")]
|
||||||
|
#[arg(long("print-schema"))]
|
||||||
|
pub print_schema: bool,
|
||||||
|
|
||||||
|
/// Print debug information to stderr
|
||||||
|
/// TODO: Make bar follow this too
|
||||||
|
#[arg(long)]
|
||||||
|
pub debug: bool,
|
||||||
|
|
||||||
|
/// Format to output the response as.
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub format: Option<Format>,
|
||||||
|
|
||||||
/// `bar_id` argument passed by `swaybar_command`.
|
/// `bar_id` argument passed by `swaybar_command`.
|
||||||
/// Not used.
|
/// Not used.
|
||||||
#[arg(short('b'), hide(true))]
|
#[arg(short('b'), hide(true))]
|
||||||
sway_bar_id: Option<String>,
|
sway_bar_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_response(response: Response) {
|
#[derive(Debug, Serialize, Deserialize, Default, ValueEnum, Clone, Copy)]
|
||||||
match response {
|
pub enum Format {
|
||||||
Response::Ok => println!("ok"),
|
#[default]
|
||||||
Response::OkValue { value } => println!("ok\n{value}"),
|
Plain,
|
||||||
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
|
Json,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_response(response: Response, format: Format) {
|
||||||
|
let is_err = matches!(response, Response::Err { .. });
|
||||||
|
|
||||||
|
match format {
|
||||||
|
Format::Plain => match response {
|
||||||
|
Response::Ok => println!("ok"),
|
||||||
|
Response::OkValue { value } => println!("{value}"),
|
||||||
|
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
|
||||||
|
},
|
||||||
|
Format::Json => println!(
|
||||||
|
"{}",
|
||||||
|
serde_json::to_string(&response).expect("to be valid json")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_err {
|
||||||
|
exit(ExitCode::IpcResponseError as i32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub struct Event {
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
pub dnd: bool,
|
pub dnd: bool,
|
||||||
pub cc_open: bool,
|
pub cc_open: bool,
|
||||||
pub inhibited: bool,
|
// pub inhibited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetSubscribeData = (bool, bool, u32, bool);
|
type GetSubscribeData = (bool, bool, u32, bool);
|
||||||
|
@ -22,12 +22,12 @@ type GetSubscribeData = (bool, bool, u32, bool);
|
||||||
/// Converts the data returned from
|
/// Converts the data returned from
|
||||||
/// `get_subscribe_data` into an event for convenience.
|
/// `get_subscribe_data` into an event for convenience.
|
||||||
impl From<GetSubscribeData> for Event {
|
impl From<GetSubscribeData> for Event {
|
||||||
fn from((dnd, cc_open, count, inhibited): (bool, bool, u32, bool)) -> Self {
|
fn from((dnd, cc_open, count, _inhibited): (bool, bool, u32, bool)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
count,
|
count,
|
||||||
dnd,
|
dnd,
|
||||||
cc_open,
|
cc_open,
|
||||||
inhibited,
|
// inhibited,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ mod macros;
|
||||||
mod wl_output;
|
mod wl_output;
|
||||||
mod wl_seat;
|
mod wl_seat;
|
||||||
|
|
||||||
use crate::error::ERR_CHANNEL_RECV;
|
use crate::error::{ExitCode, ERR_CHANNEL_RECV};
|
||||||
use crate::{arc_mut, lock, register_client, send, spawn, spawn_blocking};
|
use crate::{arc_mut, lock, register_client, send, spawn, spawn_blocking};
|
||||||
|
use std::process::exit;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use calloop_channel::Event::Msg;
|
use calloop_channel::Event::Msg;
|
||||||
|
@ -305,6 +306,8 @@ impl Environment {
|
||||||
"{:?}",
|
"{:?}",
|
||||||
Report::new(err).wrap_err("Failed to dispatch pending wayland events")
|
Report::new(err).wrap_err("Failed to dispatch pending wayland events")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
exit(ExitCode::WaylandDispatchError as i32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ impl ToplevelHandleData {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ToplevelHandleDataInner {
|
pub struct ToplevelHandleDataInner {
|
||||||
initial_done: bool,
|
initial_done: bool,
|
||||||
|
closed: bool,
|
||||||
output: Option<WlOutput>,
|
output: Option<WlOutput>,
|
||||||
|
|
||||||
current_info: Option<ToplevelInfo>,
|
current_info: Option<ToplevelInfo>,
|
||||||
|
@ -137,14 +138,17 @@ where
|
||||||
}
|
}
|
||||||
Event::OutputEnter { output } => lock!(data.inner).output = Some(output),
|
Event::OutputEnter { output } => lock!(data.inner).output = Some(output),
|
||||||
Event::OutputLeave { output: _ } => lock!(data.inner).output = None,
|
Event::OutputLeave { output: _ } => lock!(data.inner).output = None,
|
||||||
Event::Closed => state.remove_handle(
|
Event::Closed => {
|
||||||
conn,
|
lock!(data.inner).closed = true;
|
||||||
qh,
|
state.remove_handle(
|
||||||
ToplevelHandle {
|
conn,
|
||||||
handle: handle.clone(),
|
qh,
|
||||||
},
|
ToplevelHandle {
|
||||||
),
|
handle: handle.clone(),
|
||||||
Event::Done => {
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Event::Done if !lock!(data.inner).closed => {
|
||||||
{
|
{
|
||||||
let pending_info = lock!(data.inner).pending_info.clone();
|
let pending_info = lock!(data.inner).pending_info.clone();
|
||||||
lock!(data.inner).current_info = Some(pending_info);
|
lock!(data.inner).current_info = Some(pending_info);
|
||||||
|
|
|
@ -16,6 +16,7 @@ use tracing::trace;
|
||||||
/// see [here](script).
|
/// see [here](script).
|
||||||
/// For information on styling, please see the [styling guide](styling-guide).
|
/// For information on styling, please see the [styling guide](styling-guide).
|
||||||
#[derive(Debug, Default, Deserialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct CommonConfig {
|
pub struct CommonConfig {
|
||||||
/// Sets the unique widget name,
|
/// Sets the unique widget name,
|
||||||
/// allowing you to target it in CSS using `#name`.
|
/// allowing you to target it in CSS using `#name`.
|
||||||
|
@ -160,6 +161,7 @@ pub struct CommonConfig {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum TransitionType {
|
pub enum TransitionType {
|
||||||
None,
|
None,
|
||||||
Crossfade,
|
Crossfade,
|
||||||
|
@ -169,6 +171,7 @@ pub enum TransitionType {
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Clone, Copy)]
|
#[derive(Debug, Default, Deserialize, Clone, Copy)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum ModuleOrientation {
|
pub enum ModuleOrientation {
|
||||||
#[default]
|
#[default]
|
||||||
#[serde(alias = "h")]
|
#[serde(alias = "h")]
|
||||||
|
|
|
@ -35,6 +35,37 @@ impl<'de> Deserialize<'de> for MonitorConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_layer<'de, D>(deserializer: D) -> Result<gtk_layer_shell::Layer, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use gtk_layer_shell::Layer;
|
||||||
|
|
||||||
|
let value = Option::<String>::deserialize(deserializer)?;
|
||||||
|
value
|
||||||
|
.map(|v| match v.as_str() {
|
||||||
|
"background" => Ok(Layer::Background),
|
||||||
|
"bottom" => Ok(Layer::Bottom),
|
||||||
|
"top" => Ok(Layer::Top),
|
||||||
|
"overlay" => Ok(Layer::Overlay),
|
||||||
|
_ => Err(serde::de::Error::custom("invalid value for orientation")),
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(Layer::Top))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "schema")]
|
||||||
|
pub fn schema_layer(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
|
||||||
|
schema.enum_values = Some(vec![
|
||||||
|
"background".into(),
|
||||||
|
"bottom".into(),
|
||||||
|
"top".into(),
|
||||||
|
"overlay".into(),
|
||||||
|
]);
|
||||||
|
schema.into()
|
||||||
|
}
|
||||||
|
|
||||||
impl BarPosition {
|
impl BarPosition {
|
||||||
/// Gets the orientation the bar and widgets should use
|
/// Gets the orientation the bar and widgets should use
|
||||||
/// based on this position.
|
/// based on this position.
|
||||||
|
|
|
@ -38,11 +38,15 @@ use color_eyre::Result;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[cfg(feature = "schema")]
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
pub use self::common::{CommonConfig, ModuleOrientation, TransitionType};
|
pub use self::common::{CommonConfig, ModuleOrientation, TransitionType};
|
||||||
pub use self::truncate::TruncateMode;
|
pub use self::truncate::TruncateMode;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(tag = "type", rename_all = "snake_case")]
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub enum ModuleConfig {
|
pub enum ModuleConfig {
|
||||||
#[cfg(feature = "cairo")]
|
#[cfg(feature = "cairo")]
|
||||||
Cairo(Box<CairoModule>),
|
Cairo(Box<CairoModule>),
|
||||||
|
@ -123,6 +127,7 @@ impl ModuleConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub enum MonitorConfig {
|
pub enum MonitorConfig {
|
||||||
Single(BarConfig),
|
Single(BarConfig),
|
||||||
Multiple(Vec<BarConfig>),
|
Multiple(Vec<BarConfig>),
|
||||||
|
@ -130,6 +135,7 @@ pub enum MonitorConfig {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub enum BarPosition {
|
pub enum BarPosition {
|
||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
|
@ -144,6 +150,7 @@ impl Default for BarPosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Deserialize, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub struct MarginConfig {
|
pub struct MarginConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub bottom: i32,
|
pub bottom: i32,
|
||||||
|
@ -162,6 +169,7 @@ pub struct MarginConfig {
|
||||||
/// depending on your [use-case](#2-pick-your-use-case).
|
/// depending on your [use-case](#2-pick-your-use-case).
|
||||||
///
|
///
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub struct BarConfig {
|
pub struct BarConfig {
|
||||||
/// A unique identifier for the bar, used for controlling it over IPC.
|
/// A unique identifier for the bar, used for controlling it over IPC.
|
||||||
/// If not set, uses a generated integer suffix.
|
/// If not set, uses a generated integer suffix.
|
||||||
|
@ -214,6 +222,36 @@ pub struct BarConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub margin: MarginConfig,
|
pub margin: MarginConfig,
|
||||||
|
|
||||||
|
/// The layer-shell layer to place the bar on.
|
||||||
|
///
|
||||||
|
/// Taken from the
|
||||||
|
/// [wlr_layer_shell](https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_shell_v1:enum:layer) definition:
|
||||||
|
///
|
||||||
|
/// > These values indicate which layers a surface can be rendered in.
|
||||||
|
/// > They are ordered by z depth, bottom-most first.
|
||||||
|
/// > Traditional shell surfaces will typically be rendered between the bottom and top layers.
|
||||||
|
/// > Fullscreen shell surfaces are typically rendered at the top layer.
|
||||||
|
/// > Multiple surfaces can share a single layer, and ordering within a single layer is undefined.
|
||||||
|
///
|
||||||
|
/// **Valid options**: `background`, `bottom`, `top`, `overlay`
|
||||||
|
/// <br>
|
||||||
|
/// **Default**: `top`
|
||||||
|
#[serde(
|
||||||
|
default = "default_layer",
|
||||||
|
deserialize_with = "r#impl::deserialize_layer"
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "schema", schemars(schema_with = "r#impl::schema_layer"))]
|
||||||
|
pub layer: gtk_layer_shell::Layer,
|
||||||
|
|
||||||
|
/// Whether the bar should reserve an exclusive zone around it.
|
||||||
|
///
|
||||||
|
/// When true, this prevents windows from rendering in the same space
|
||||||
|
/// as the bar, causing them to shift.
|
||||||
|
///
|
||||||
|
/// **Default**: `true` unless `start_hidden` is set.
|
||||||
|
#[serde(default)]
|
||||||
|
pub exclusive_zone: Option<bool>,
|
||||||
|
|
||||||
/// The size of the gap in pixels
|
/// The size of the gap in pixels
|
||||||
/// between the bar and the popup window.
|
/// between the bar and the popup window.
|
||||||
///
|
///
|
||||||
|
@ -280,9 +318,11 @@ impl Default for BarConfig {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
position: BarPosition::default(),
|
position: BarPosition::default(),
|
||||||
height: default_bar_height(),
|
|
||||||
margin: MarginConfig::default(),
|
margin: MarginConfig::default(),
|
||||||
name: None,
|
name: None,
|
||||||
|
layer: default_layer(),
|
||||||
|
exclusive_zone: None,
|
||||||
|
height: default_bar_height(),
|
||||||
start_hidden: None,
|
start_hidden: None,
|
||||||
autohide: None,
|
autohide: None,
|
||||||
icon_theme: None,
|
icon_theme: None,
|
||||||
|
@ -298,6 +338,7 @@ impl Default for BarConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// A map of [ironvar](ironvar) keys and values
|
/// A map of [ironvar](ironvar) keys and values
|
||||||
/// to initialize Ironbar with on startup.
|
/// to initialize Ironbar with on startup.
|
||||||
|
@ -337,6 +378,10 @@ pub struct Config {
|
||||||
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn default_layer() -> gtk_layer_shell::Layer {
|
||||||
|
gtk_layer_shell::Layer::Top
|
||||||
|
}
|
||||||
|
|
||||||
const fn default_bar_height() -> i32 {
|
const fn default_bar_height() -> i32 {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum EllipsizeMode {
|
pub enum EllipsizeMode {
|
||||||
Start,
|
Start,
|
||||||
Middle,
|
Middle,
|
||||||
|
@ -28,6 +29,7 @@ impl From<EllipsizeMode> for GtkEllipsizeMode {
|
||||||
///
|
///
|
||||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum TruncateMode {
|
pub enum TruncateMode {
|
||||||
/// Auto mode lets GTK decide when to ellipsize.
|
/// Auto mode lets GTK decide when to ellipsize.
|
||||||
///
|
///
|
||||||
|
|
|
@ -8,6 +8,7 @@ use tokio::sync::mpsc;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum DynamicBool {
|
pub enum DynamicBool {
|
||||||
/// Either a script or variable, to be determined.
|
/// Either a script or variable, to be determined.
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
pub enum ExitCode {
|
pub enum ExitCode {
|
||||||
GtkDisplay = 1,
|
GtkDisplay = 1,
|
||||||
CreateBars = 2,
|
CreateBars = 2,
|
||||||
|
IpcResponseError = 3,
|
||||||
|
WaylandDispatchError = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ERR_MUTEX_LOCK: &str = "Failed to get lock on Mutex";
|
pub const ERR_MUTEX_LOCK: &str = "Failed to get lock on Mutex";
|
||||||
|
|
|
@ -8,7 +8,7 @@ use tokio::net::UnixStream;
|
||||||
impl Ipc {
|
impl Ipc {
|
||||||
/// Sends a command to the IPC server.
|
/// Sends a command to the IPC server.
|
||||||
/// The server response is returned.
|
/// The server response is returned.
|
||||||
pub async fn send(&self, command: Command) -> Result<Response> {
|
pub async fn send(&self, command: Command, debug: bool) -> Result<Response> {
|
||||||
let mut stream = match UnixStream::connect(&self.path).await {
|
let mut stream = match UnixStream::connect(&self.path).await {
|
||||||
Ok(stream) => Ok(stream),
|
Ok(stream) => Ok(stream),
|
||||||
Err(err) => Err(Report::new(err)
|
Err(err) => Err(Report::new(err)
|
||||||
|
@ -17,6 +17,11 @@ impl Ipc {
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let write_buffer = serde_json::to_vec(&command)?;
|
let write_buffer = serde_json::to_vec(&command)?;
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
eprintln!("REQUEST JSON: {}", serde_json::to_string(&command)?);
|
||||||
|
}
|
||||||
|
|
||||||
stream.write_all(&write_buffer).await?;
|
stream.write_all(&write_buffer).await?;
|
||||||
|
|
||||||
let mut read_buffer = vec![0; 1024];
|
let mut read_buffer = vec![0; 1024];
|
||||||
|
|
|
@ -1,20 +1,39 @@
|
||||||
|
use clap::ArgAction;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Subcommand;
|
use clap::{Args, Subcommand};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Subcommand, Debug, Serialize, Deserialize)]
|
#[derive(Subcommand, Debug, Serialize, Deserialize)]
|
||||||
#[serde(tag = "type", rename_all = "snake_case")]
|
#[serde(tag = "command", rename_all = "snake_case")]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Return "ok"
|
/// Pong
|
||||||
Ping,
|
Ping,
|
||||||
|
|
||||||
/// Open the GTK inspector
|
/// Open the GTK inspector.
|
||||||
Inspect,
|
Inspect,
|
||||||
|
|
||||||
/// Reload the config
|
/// Reload the config.
|
||||||
Reload,
|
Reload,
|
||||||
|
|
||||||
|
/// Load an additional CSS stylesheet.
|
||||||
|
/// The sheet is automatically hot-reloaded.
|
||||||
|
LoadCss {
|
||||||
|
/// The path to the sheet.
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Get and set reactive Ironvar values.
|
||||||
|
#[command(subcommand)]
|
||||||
|
Var(IronvarCommand),
|
||||||
|
|
||||||
|
/// Interact with a specific bar.
|
||||||
|
Bar(BarCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "subcommand", rename_all = "snake_case")]
|
||||||
|
pub enum IronvarCommand {
|
||||||
/// Set an `ironvar` value.
|
/// Set an `ironvar` value.
|
||||||
/// This creates it if it does not already exist, and updates it if it does.
|
/// This creates it if it does not already exist, and updates it if it does.
|
||||||
/// Any references to this variable are automatically and immediately updated.
|
/// Any references to this variable are automatically and immediately updated.
|
||||||
|
@ -34,49 +53,80 @@ pub enum Command {
|
||||||
|
|
||||||
/// Gets the current value of all `ironvar`s.
|
/// Gets the current value of all `ironvar`s.
|
||||||
List,
|
List,
|
||||||
|
}
|
||||||
|
|
||||||
/// Load an additional CSS stylesheet.
|
#[derive(Args, Debug, Serialize, Deserialize)]
|
||||||
/// The sheet is automatically hot-reloaded.
|
pub struct BarCommand {
|
||||||
LoadCss {
|
/// The name of the bar.
|
||||||
/// The path to the sheet.
|
pub name: String,
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Set the visibility of the bar with the given name.
|
#[command(subcommand)]
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub subcommand: BarCommandType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "subcommand", rename_all = "snake_case")]
|
||||||
|
pub enum BarCommandType {
|
||||||
|
// == Visibility == \\
|
||||||
|
/// Force the bar to be shown, regardless of current visibility state.
|
||||||
|
Show,
|
||||||
|
/// Force the bar to be hidden, regardless of current visibility state.
|
||||||
|
Hide,
|
||||||
|
/// Set the bar's visibility state via an argument.
|
||||||
SetVisible {
|
SetVisible {
|
||||||
///Bar name to target.
|
/// The new visibility state.
|
||||||
bar_name: String,
|
#[clap(
|
||||||
/// The visibility status.
|
num_args(1),
|
||||||
#[arg(short, long)]
|
require_equals(true),
|
||||||
|
action = ArgAction::Set,
|
||||||
|
)]
|
||||||
visible: bool,
|
visible: bool,
|
||||||
},
|
},
|
||||||
|
/// Toggle the current visibility state between shown and hidden.
|
||||||
|
ToggleVisible,
|
||||||
|
/// Get the bar's visibility state.
|
||||||
|
GetVisible,
|
||||||
|
|
||||||
/// Get the visibility of the bar with the given name.
|
// == Popup visibility == \\
|
||||||
GetVisible {
|
/// Open a popup, regardless of current state.
|
||||||
/// Bar name to target.
|
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
|
||||||
bar_name: String,
|
ShowPopup {
|
||||||
|
/// The configured name of the widget.
|
||||||
|
widget_name: String,
|
||||||
},
|
},
|
||||||
|
/// Close a popup, regardless of current state.
|
||||||
|
HidePopup,
|
||||||
|
/// Set the popup's visibility state via an argument.
|
||||||
|
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
|
||||||
|
SetPopupVisible {
|
||||||
|
/// The configured name of the widget.
|
||||||
|
widget_name: String,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
num_args(1),
|
||||||
|
require_equals(true),
|
||||||
|
action = ArgAction::Set,
|
||||||
|
)]
|
||||||
|
visible: bool,
|
||||||
|
},
|
||||||
/// Toggle a popup open/closed.
|
/// Toggle a popup open/closed.
|
||||||
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
|
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
|
||||||
TogglePopup {
|
TogglePopup {
|
||||||
/// The name of the monitor the bar is located on.
|
/// The configured name of the widget.
|
||||||
bar_name: String,
|
widget_name: String,
|
||||||
/// The name of the widget.
|
|
||||||
name: String,
|
|
||||||
},
|
},
|
||||||
|
/// Get the popup's current visibility state.
|
||||||
|
GetPopupVisible,
|
||||||
|
|
||||||
/// Open a popup, regardless of current state.
|
// == Exclusivity == \\
|
||||||
OpenPopup {
|
/// Set whether the bar reserves an exclusive zone.
|
||||||
/// The name of the monitor the bar is located on.
|
SetExclusive {
|
||||||
bar_name: String,
|
#[clap(
|
||||||
/// The name of the widget.
|
num_args(1),
|
||||||
name: String,
|
require_equals(true),
|
||||||
},
|
action = ArgAction::Set,
|
||||||
|
)]
|
||||||
/// Close a popup, regardless of current state.
|
exclusive: bool,
|
||||||
ClosePopup {
|
|
||||||
/// The name of the monitor the bar is located on.
|
|
||||||
bar_name: String,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ mod server;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
pub use commands::Command;
|
pub use commands::*;
|
||||||
pub use responses::Response;
|
pub use responses::Response;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1,297 +0,0 @@
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use color_eyre::{Report, Result};
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk::Application;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
use tokio::net::{UnixListener, UnixStream};
|
|
||||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
|
||||||
use tracing::{debug, error, info, warn};
|
|
||||||
|
|
||||||
use crate::ipc::{Command, Response};
|
|
||||||
use crate::modules::PopupButton;
|
|
||||||
use crate::style::load_css;
|
|
||||||
use crate::{glib_recv_mpsc, read_lock, send_async, spawn, try_send, write_lock, Ironbar};
|
|
||||||
|
|
||||||
use super::Ipc;
|
|
||||||
|
|
||||||
impl Ipc {
|
|
||||||
/// Starts the IPC server on its socket.
|
|
||||||
///
|
|
||||||
/// Once started, the server will begin accepting connections.
|
|
||||||
pub fn start(&self, application: &Application, ironbar: Rc<Ironbar>) {
|
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel(32);
|
|
||||||
let (res_tx, mut res_rx) = mpsc::channel(32);
|
|
||||||
|
|
||||||
let path = self.path.clone();
|
|
||||||
|
|
||||||
if path.exists() {
|
|
||||||
warn!("Socket already exists. Did Ironbar exit abruptly?");
|
|
||||||
warn!("Attempting IPC shutdown to allow binding to address");
|
|
||||||
Self::shutdown(&path);
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn(async move {
|
|
||||||
info!("Starting IPC on {}", path.display());
|
|
||||||
|
|
||||||
let listener = match UnixListener::bind(&path) {
|
|
||||||
Ok(listener) => listener,
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"{:?}",
|
|
||||||
Report::new(err).wrap_err("Unable to start IPC server")
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match listener.accept().await {
|
|
||||||
Ok((stream, _addr)) => {
|
|
||||||
if let Err(err) =
|
|
||||||
Self::handle_connection(stream, &cmd_tx, &mut res_rx).await
|
|
||||||
{
|
|
||||||
error!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let application = application.clone();
|
|
||||||
glib_recv_mpsc!(cmd_rx, command => {
|
|
||||||
let res = Self::handle_command(command, &application, &ironbar);
|
|
||||||
try_send!(res_tx, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes an incoming connections,
|
|
||||||
/// reads the command message, and sends the response.
|
|
||||||
///
|
|
||||||
/// The connection is closed once the response has been written.
|
|
||||||
async fn handle_connection(
|
|
||||||
mut stream: UnixStream,
|
|
||||||
cmd_tx: &Sender<Command>,
|
|
||||||
res_rx: &mut Receiver<Response>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let (mut stream_read, mut stream_write) = stream.split();
|
|
||||||
|
|
||||||
let mut read_buffer = vec![0; 1024];
|
|
||||||
let bytes = stream_read.read(&mut read_buffer).await?;
|
|
||||||
|
|
||||||
let command = serde_json::from_slice::<Command>(&read_buffer[..bytes])?;
|
|
||||||
|
|
||||||
debug!("Received command: {command:?}");
|
|
||||||
|
|
||||||
send_async!(cmd_tx, command);
|
|
||||||
let res = res_rx
|
|
||||||
.recv()
|
|
||||||
.await
|
|
||||||
.unwrap_or(Response::Err { message: None });
|
|
||||||
let res = serde_json::to_vec(&res)?;
|
|
||||||
|
|
||||||
stream_write.write_all(&res).await?;
|
|
||||||
stream_write.shutdown().await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes an input command, runs it and returns with the appropriate response.
|
|
||||||
///
|
|
||||||
/// This runs on the main thread, allowing commands to interact with GTK.
|
|
||||||
fn handle_command(
|
|
||||||
command: Command,
|
|
||||||
application: &Application,
|
|
||||||
ironbar: &Rc<Ironbar>,
|
|
||||||
) -> Response {
|
|
||||||
match command {
|
|
||||||
Command::Inspect => {
|
|
||||||
gtk::Window::set_interactive_debugging(true);
|
|
||||||
Response::Ok
|
|
||||||
}
|
|
||||||
Command::Reload => {
|
|
||||||
info!("Closing existing bars");
|
|
||||||
ironbar.bars.borrow_mut().clear();
|
|
||||||
|
|
||||||
let windows = application.windows();
|
|
||||||
for window in windows {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
let wl = ironbar.clients.borrow_mut().wayland();
|
|
||||||
let outputs = wl.output_info_all();
|
|
||||||
|
|
||||||
ironbar.reload_config();
|
|
||||||
|
|
||||||
for output in outputs {
|
|
||||||
match crate::load_output_bars(ironbar, application, &output) {
|
|
||||||
Ok(mut bars) => ironbar.bars.borrow_mut().append(&mut bars),
|
|
||||||
Err(err) => error!("{err:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Ok
|
|
||||||
}
|
|
||||||
Command::Set { key, value } => {
|
|
||||||
let variable_manager = Ironbar::variable_manager();
|
|
||||||
let mut variable_manager = write_lock!(variable_manager);
|
|
||||||
match variable_manager.set(key, value) {
|
|
||||||
Ok(()) => Response::Ok,
|
|
||||||
Err(err) => Response::error(&format!("{err}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Get { key } => {
|
|
||||||
let variable_manager = Ironbar::variable_manager();
|
|
||||||
let value = read_lock!(variable_manager).get(&key);
|
|
||||||
match value {
|
|
||||||
Some(value) => Response::OkValue { value },
|
|
||||||
None => Response::error("Variable not found"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::List => {
|
|
||||||
let variable_manager = Ironbar::variable_manager();
|
|
||||||
|
|
||||||
let mut values = read_lock!(variable_manager)
|
|
||||||
.get_all()
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| format!("{k}: {}", v.get().unwrap_or_default()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
values.sort();
|
|
||||||
let value = values.join("\n");
|
|
||||||
|
|
||||||
Response::OkValue { value }
|
|
||||||
}
|
|
||||||
Command::LoadCss { path } => {
|
|
||||||
if path.exists() {
|
|
||||||
load_css(path);
|
|
||||||
Response::Ok
|
|
||||||
} else {
|
|
||||||
Response::error("File not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::TogglePopup { bar_name, name } => {
|
|
||||||
let bar = ironbar.bar_by_name(&bar_name);
|
|
||||||
|
|
||||||
match bar {
|
|
||||||
Some(bar) => {
|
|
||||||
let popup = bar.popup();
|
|
||||||
let current_widget = popup.current_widget();
|
|
||||||
|
|
||||||
popup.hide();
|
|
||||||
|
|
||||||
let data = popup
|
|
||||||
.container_cache
|
|
||||||
.borrow()
|
|
||||||
.iter()
|
|
||||||
.find(|(_, value)| value.name == name)
|
|
||||||
.map(|(id, value)| (*id, value.content.buttons.first().cloned()));
|
|
||||||
|
|
||||||
match data {
|
|
||||||
Some((id, Some(button))) if current_widget != Some(id) => {
|
|
||||||
let button_id = button.popup_id();
|
|
||||||
|
|
||||||
if popup.is_visible() {
|
|
||||||
popup.hide();
|
|
||||||
} else {
|
|
||||||
popup.show(id, button_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Ok
|
|
||||||
}
|
|
||||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
|
||||||
Some(_) => Response::Ok,
|
|
||||||
None => Response::error("Invalid module name"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Response::error("Invalid bar name"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::OpenPopup { bar_name, name } => {
|
|
||||||
let bar = ironbar.bar_by_name(&bar_name);
|
|
||||||
|
|
||||||
match bar {
|
|
||||||
Some(bar) => {
|
|
||||||
let popup = bar.popup();
|
|
||||||
|
|
||||||
// only one popup per bar, so hide if open for another widget
|
|
||||||
popup.hide();
|
|
||||||
|
|
||||||
let data = popup
|
|
||||||
.container_cache
|
|
||||||
.borrow()
|
|
||||||
.iter()
|
|
||||||
.find(|(_, value)| value.name == name)
|
|
||||||
.map(|(id, value)| (*id, value.content.buttons.first().cloned()));
|
|
||||||
|
|
||||||
match data {
|
|
||||||
Some((id, Some(button))) => {
|
|
||||||
let button_id = button.popup_id();
|
|
||||||
popup.show(id, button_id);
|
|
||||||
|
|
||||||
Response::Ok
|
|
||||||
}
|
|
||||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
|
||||||
None => Response::error("Invalid module name"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Response::error("Invalid bar name"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::ClosePopup { bar_name } => {
|
|
||||||
let bar = ironbar.bar_by_name(&bar_name);
|
|
||||||
|
|
||||||
match bar {
|
|
||||||
Some(bar) => {
|
|
||||||
let popup = bar.popup();
|
|
||||||
popup.hide();
|
|
||||||
|
|
||||||
Response::Ok
|
|
||||||
}
|
|
||||||
None => Response::error("Invalid bar name"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Ping => Response::Ok,
|
|
||||||
Command::SetVisible { bar_name, visible } => {
|
|
||||||
let windows = application.windows();
|
|
||||||
let found = windows
|
|
||||||
.iter()
|
|
||||||
.find(|window| window.widget_name() == bar_name);
|
|
||||||
|
|
||||||
if let Some(window) = found {
|
|
||||||
window.set_visible(visible);
|
|
||||||
Response::Ok
|
|
||||||
} else {
|
|
||||||
Response::error("Bar not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::GetVisible { bar_name } => {
|
|
||||||
let windows = application.windows();
|
|
||||||
let found = windows
|
|
||||||
.iter()
|
|
||||||
.find(|window| window.widget_name() == bar_name);
|
|
||||||
|
|
||||||
if let Some(window) = found {
|
|
||||||
Response::OkValue {
|
|
||||||
value: window.is_visible().to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::error("Bar not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shuts down the IPC server,
|
|
||||||
/// removing the socket file in the process.
|
|
||||||
///
|
|
||||||
/// Note this is static as the `Ipc` struct is not `Send`.
|
|
||||||
pub fn shutdown<P: AsRef<Path>>(path: P) {
|
|
||||||
fs::remove_file(&path).ok();
|
|
||||||
}
|
|
||||||
}
|
|
89
src/ipc/server/bar.rs
Normal file
89
src/ipc/server/bar.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use super::Response;
|
||||||
|
use crate::bar::Bar;
|
||||||
|
use crate::ipc::{BarCommand, BarCommandType};
|
||||||
|
use crate::modules::PopupButton;
|
||||||
|
use crate::Ironbar;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub fn handle_command(command: BarCommand, ironbar: &Rc<Ironbar>) -> Response {
|
||||||
|
let bar = ironbar.bar_by_name(&command.name);
|
||||||
|
let Some(bar) = bar else {
|
||||||
|
return Response::error("Invalid bar name");
|
||||||
|
};
|
||||||
|
|
||||||
|
use BarCommandType::*;
|
||||||
|
match command.subcommand {
|
||||||
|
Show => set_visible(&bar, true),
|
||||||
|
Hide => set_visible(&bar, false),
|
||||||
|
SetVisible { visible } => set_visible(&bar, visible),
|
||||||
|
ToggleVisible => set_visible(&bar, !bar.visible()),
|
||||||
|
GetVisible => Response::OkValue {
|
||||||
|
value: bar.visible().to_string(),
|
||||||
|
},
|
||||||
|
|
||||||
|
ShowPopup { widget_name } => show_popup(&bar, widget_name),
|
||||||
|
HidePopup => hide_popup(&bar),
|
||||||
|
SetPopupVisible {
|
||||||
|
widget_name,
|
||||||
|
visible,
|
||||||
|
} => {
|
||||||
|
if visible {
|
||||||
|
show_popup(&bar, widget_name)
|
||||||
|
} else {
|
||||||
|
hide_popup(&bar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TogglePopup { widget_name } => {
|
||||||
|
if bar.popup().visible() {
|
||||||
|
hide_popup(&bar)
|
||||||
|
} else {
|
||||||
|
show_popup(&bar, widget_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GetPopupVisible => Response::OkValue {
|
||||||
|
value: bar.popup().visible().to_string(),
|
||||||
|
},
|
||||||
|
SetExclusive { exclusive } => {
|
||||||
|
bar.set_exclusive(exclusive);
|
||||||
|
|
||||||
|
Response::Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_visible(bar: &Bar, visible: bool) -> Response {
|
||||||
|
bar.set_visible(visible);
|
||||||
|
Response::Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_popup(bar: &Bar, widget_name: String) -> Response {
|
||||||
|
let popup = bar.popup();
|
||||||
|
|
||||||
|
// only one popup per bar, so hide if open for another widget
|
||||||
|
popup.hide();
|
||||||
|
|
||||||
|
let data = popup
|
||||||
|
.container_cache
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
|
.find(|(_, value)| value.name == widget_name)
|
||||||
|
.map(|(id, value)| (*id, value.content.buttons.first().cloned()));
|
||||||
|
|
||||||
|
match data {
|
||||||
|
Some((id, Some(button))) => {
|
||||||
|
let button_id = button.popup_id();
|
||||||
|
popup.show(id, button_id);
|
||||||
|
|
||||||
|
Response::Ok
|
||||||
|
}
|
||||||
|
Some((_, None)) => Response::error("Module has no popup functionality"),
|
||||||
|
None => Response::error("Invalid module name"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_popup(bar: &Bar) -> Response {
|
||||||
|
let popup = bar.popup();
|
||||||
|
popup.hide();
|
||||||
|
|
||||||
|
Response::Ok
|
||||||
|
}
|
38
src/ipc/server/ironvar.rs
Normal file
38
src/ipc/server/ironvar.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::ipc::commands::IronvarCommand;
|
||||||
|
use crate::ipc::Response;
|
||||||
|
use crate::{read_lock, write_lock, Ironbar};
|
||||||
|
|
||||||
|
pub fn handle_command(command: IronvarCommand) -> Response {
|
||||||
|
match command {
|
||||||
|
IronvarCommand::Set { key, value } => {
|
||||||
|
let variable_manager = Ironbar::variable_manager();
|
||||||
|
let mut variable_manager = write_lock!(variable_manager);
|
||||||
|
match variable_manager.set(key, value) {
|
||||||
|
Ok(()) => Response::Ok,
|
||||||
|
Err(err) => Response::error(&format!("{err}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IronvarCommand::Get { key } => {
|
||||||
|
let variable_manager = Ironbar::variable_manager();
|
||||||
|
let value = read_lock!(variable_manager).get(&key);
|
||||||
|
match value {
|
||||||
|
Some(value) => Response::OkValue { value },
|
||||||
|
None => Response::error("Variable not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IronvarCommand::List => {
|
||||||
|
let variable_manager = Ironbar::variable_manager();
|
||||||
|
|
||||||
|
let mut values = read_lock!(variable_manager)
|
||||||
|
.get_all()
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{k}: {}", v.get().unwrap_or_default()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
values.sort();
|
||||||
|
let value = values.join("\n");
|
||||||
|
|
||||||
|
Response::OkValue { value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
164
src/ipc/server/mod.rs
Normal file
164
src/ipc/server/mod.rs
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
mod bar;
|
||||||
|
mod ironvar;
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk::Application;
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use tokio::net::{UnixListener, UnixStream};
|
||||||
|
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
use crate::ipc::{Command, Response};
|
||||||
|
use crate::style::load_css;
|
||||||
|
use crate::{glib_recv_mpsc, send_async, spawn, try_send, Ironbar};
|
||||||
|
|
||||||
|
use super::Ipc;
|
||||||
|
|
||||||
|
impl Ipc {
|
||||||
|
/// Starts the IPC server on its socket.
|
||||||
|
///
|
||||||
|
/// Once started, the server will begin accepting connections.
|
||||||
|
pub fn start(&self, application: &Application, ironbar: Rc<Ironbar>) {
|
||||||
|
let (cmd_tx, cmd_rx) = mpsc::channel(32);
|
||||||
|
let (res_tx, mut res_rx) = mpsc::channel(32);
|
||||||
|
|
||||||
|
let path = self.path.clone();
|
||||||
|
|
||||||
|
if path.exists() {
|
||||||
|
warn!("Socket already exists. Did Ironbar exit abruptly?");
|
||||||
|
warn!("Attempting IPC shutdown to allow binding to address");
|
||||||
|
Self::shutdown(&path);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
info!("Starting IPC on {}", path.display());
|
||||||
|
|
||||||
|
let listener = match UnixListener::bind(&path) {
|
||||||
|
Ok(listener) => listener,
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"{:?}",
|
||||||
|
Report::new(err).wrap_err("Unable to start IPC server")
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match listener.accept().await {
|
||||||
|
Ok((stream, _addr)) => {
|
||||||
|
if let Err(err) =
|
||||||
|
Self::handle_connection(stream, &cmd_tx, &mut res_rx).await
|
||||||
|
{
|
||||||
|
error!("{err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("{err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let application = application.clone();
|
||||||
|
glib_recv_mpsc!(cmd_rx, command => {
|
||||||
|
let res = Self::handle_command(command, &application, &ironbar);
|
||||||
|
try_send!(res_tx, res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes an incoming connections,
|
||||||
|
/// reads the command message, and sends the response.
|
||||||
|
///
|
||||||
|
/// The connection is closed once the response has been written.
|
||||||
|
async fn handle_connection(
|
||||||
|
mut stream: UnixStream,
|
||||||
|
cmd_tx: &Sender<Command>,
|
||||||
|
res_rx: &mut Receiver<Response>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (mut stream_read, mut stream_write) = stream.split();
|
||||||
|
|
||||||
|
let mut read_buffer = vec![0; 1024];
|
||||||
|
let bytes = stream_read.read(&mut read_buffer).await?;
|
||||||
|
|
||||||
|
// FIXME: Error on invalid command
|
||||||
|
let command = serde_json::from_slice::<Command>(&read_buffer[..bytes])?;
|
||||||
|
|
||||||
|
debug!("Received command: {command:?}");
|
||||||
|
|
||||||
|
send_async!(cmd_tx, command);
|
||||||
|
let res = res_rx
|
||||||
|
.recv()
|
||||||
|
.await
|
||||||
|
.unwrap_or(Response::Err { message: None });
|
||||||
|
let res = serde_json::to_vec(&res)?;
|
||||||
|
|
||||||
|
stream_write.write_all(&res).await?;
|
||||||
|
stream_write.shutdown().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes an input command, runs it and returns with the appropriate response.
|
||||||
|
///
|
||||||
|
/// This runs on the main thread, allowing commands to interact with GTK.
|
||||||
|
fn handle_command(
|
||||||
|
command: Command,
|
||||||
|
application: &Application,
|
||||||
|
ironbar: &Rc<Ironbar>,
|
||||||
|
) -> Response {
|
||||||
|
match command {
|
||||||
|
Command::Ping => Response::Ok,
|
||||||
|
Command::Inspect => {
|
||||||
|
gtk::Window::set_interactive_debugging(true);
|
||||||
|
Response::Ok
|
||||||
|
}
|
||||||
|
Command::Reload => {
|
||||||
|
info!("Closing existing bars");
|
||||||
|
ironbar.bars.borrow_mut().clear();
|
||||||
|
|
||||||
|
let windows = application.windows();
|
||||||
|
for window in windows {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
let wl = ironbar.clients.borrow_mut().wayland();
|
||||||
|
let outputs = wl.output_info_all();
|
||||||
|
|
||||||
|
ironbar.reload_config();
|
||||||
|
|
||||||
|
for output in outputs {
|
||||||
|
match crate::load_output_bars(ironbar, application, &output) {
|
||||||
|
Ok(mut bars) => ironbar.bars.borrow_mut().append(&mut bars),
|
||||||
|
Err(err) => error!("{err:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::Ok
|
||||||
|
}
|
||||||
|
Command::LoadCss { path } => {
|
||||||
|
if path.exists() {
|
||||||
|
load_css(path);
|
||||||
|
Response::Ok
|
||||||
|
} else {
|
||||||
|
Response::error("File not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Var(cmd) => ironvar::handle_command(cmd),
|
||||||
|
Command::Bar(cmd) => bar::handle_command(cmd, ironbar),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shuts down the IPC server,
|
||||||
|
/// removing the socket file in the process.
|
||||||
|
///
|
||||||
|
/// Note this is static as the `Ipc` struct is not `Send`.
|
||||||
|
pub fn shutdown<P: AsRef<Path>>(path: P) {
|
||||||
|
fs::remove_file(&path).ok();
|
||||||
|
}
|
||||||
|
}
|
22
src/main.rs
22
src/main.rs
|
@ -76,13 +76,30 @@ fn main() {
|
||||||
fn run_with_args() {
|
fn run_with_args() {
|
||||||
let args = cli::Args::parse();
|
let args = cli::Args::parse();
|
||||||
|
|
||||||
|
#[cfg(feature = "schema")]
|
||||||
|
if args.print_schema {
|
||||||
|
let schema = schemars::schema_for!(Config);
|
||||||
|
println!("{}", serde_json::to_string_pretty(&schema).unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Some(command) => {
|
Some(command) => {
|
||||||
|
if args.debug {
|
||||||
|
eprintln!("REQUEST: {command:?}")
|
||||||
|
}
|
||||||
|
|
||||||
let rt = create_runtime();
|
let rt = create_runtime();
|
||||||
rt.block_on(async move {
|
rt.block_on(async move {
|
||||||
let ipc = ipc::Ipc::new();
|
let ipc = ipc::Ipc::new();
|
||||||
match ipc.send(command).await {
|
match ipc.send(command, args.debug).await {
|
||||||
Ok(res) => cli::handle_response(res),
|
Ok(res) => {
|
||||||
|
if args.debug {
|
||||||
|
eprintln!("RESPONSE: {res:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
cli::handle_response(res, args.format.unwrap_or_default())
|
||||||
|
}
|
||||||
Err(err) => error!("{err:?}"),
|
Err(err) => error!("{err:?}"),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -232,6 +249,7 @@ impl Ironbar {
|
||||||
|
|
||||||
/// Gets a `usize` ID value that is unique to the entire Ironbar instance.
|
/// Gets a `usize` ID value that is unique to the entire Ironbar instance.
|
||||||
/// This is just a static `AtomicUsize` that increments every time this function is called.
|
/// This is just a static `AtomicUsize` that increments every time this function is called.
|
||||||
|
#[must_use]
|
||||||
pub fn unique_id() -> usize {
|
pub fn unique_id() -> usize {
|
||||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||||
|
|
|
@ -18,6 +18,7 @@ use tokio::time::sleep;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct CairoModule {
|
pub struct CairoModule {
|
||||||
/// The path to the Lua script to load.
|
/// The path to the Lua script to load.
|
||||||
/// This can be absolute, or relative to the working directory.
|
/// This can be absolute, or relative to the working directory.
|
||||||
|
|
|
@ -17,6 +17,7 @@ use tokio::sync::{broadcast, mpsc};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ClipboardModule {
|
pub struct ClipboardModule {
|
||||||
/// The icon to show on the bar widget button.
|
/// The icon to show on the bar widget button.
|
||||||
/// Supports [image](images) icons.
|
/// Supports [image](images) icons.
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::modules::{
|
||||||
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
use crate::{glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ClockModule {
|
pub struct ClockModule {
|
||||||
/// The format string to use for the date/time shown on the bar.
|
/// The format string to use for the date/time shown on the bar.
|
||||||
/// Pango markup is supported.
|
/// Pango markup is supported.
|
||||||
|
|
|
@ -6,6 +6,7 @@ use gtk::prelude::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct BoxWidget {
|
pub struct BoxWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::{build, try_send};
|
||||||
use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig};
|
use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ButtonWidget {
|
pub struct ButtonWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::image::ImageProvider;
|
||||||
use super::{CustomWidget, CustomWidgetContext};
|
use super::{CustomWidget, CustomWidgetContext};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ImageWidget {
|
pub struct ImageWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::dynamic_value::dynamic_string;
|
||||||
use super::{CustomWidget, CustomWidgetContext};
|
use super::{CustomWidget, CustomWidgetContext};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct LabelWidget {
|
pub struct LabelWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -28,6 +28,7 @@ use tokio::sync::{broadcast, mpsc};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct CustomModule {
|
pub struct CustomModule {
|
||||||
/// Modules and widgets to add to the bar container.
|
/// Modules and widgets to add to the bar container.
|
||||||
///
|
///
|
||||||
|
@ -45,6 +46,7 @@ pub struct CustomModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct WidgetConfig {
|
pub struct WidgetConfig {
|
||||||
/// One of a custom module native Ironbar module.
|
/// One of a custom module native Ironbar module.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -57,6 +59,7 @@ pub struct WidgetConfig {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum WidgetOrModule {
|
pub enum WidgetOrModule {
|
||||||
/// A custom-module specific basic widget
|
/// A custom-module specific basic widget
|
||||||
Widget(Widget),
|
Widget(Widget),
|
||||||
|
@ -67,6 +70,7 @@ pub enum WidgetOrModule {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(tag = "type", rename_all = "snake_case")]
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum Widget {
|
pub enum Widget {
|
||||||
/// A container to place nested widgets inside.
|
/// A container to place nested widgets inside.
|
||||||
Box(BoxWidget),
|
Box(BoxWidget),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::{build, glib_recv_mpsc, spawn, try_send};
|
||||||
use super::{CustomWidget, CustomWidgetContext};
|
use super::{CustomWidget, CustomWidgetContext};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ProgressWidget {
|
pub struct ProgressWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::{build, glib_recv_mpsc, spawn, try_send};
|
||||||
use super::{CustomWidget, CustomWidgetContext, ExecEvent};
|
use super::{CustomWidget, CustomWidgetContext, ExecEvent};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct SliderWidget {
|
pub struct SliderWidget {
|
||||||
/// Widget name.
|
/// Widget name.
|
||||||
///
|
///
|
||||||
|
|
|
@ -12,6 +12,7 @@ use tokio::sync::mpsc;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct FocusedModule {
|
pub struct FocusedModule {
|
||||||
/// Whether to show icon on the bar.
|
/// Whether to show icon on the bar.
|
||||||
///
|
///
|
||||||
|
|
|
@ -9,6 +9,7 @@ use serde::Deserialize;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct LabelModule {
|
pub struct LabelModule {
|
||||||
/// The text to show on the label.
|
/// The text to show on the label.
|
||||||
/// This is a [Dynamic String](dynamic-values#dynamic-string).
|
/// This is a [Dynamic String](dynamic-values#dynamic-string).
|
||||||
|
|
|
@ -19,6 +19,7 @@ use tokio::sync::{broadcast, mpsc};
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct LauncherModule {
|
pub struct LauncherModule {
|
||||||
/// List of app IDs (or classes) to always show regardless of open state,
|
/// List of app IDs (or classes) to always show regardless of open state,
|
||||||
/// in the order specified.
|
/// in the order specified.
|
||||||
|
|
|
@ -384,7 +384,7 @@ impl ModuleFactory for BarModuleFactory {
|
||||||
}
|
}
|
||||||
ModuleUpdateEvent::TogglePopup(button_id) if !disable_popup => {
|
ModuleUpdateEvent::TogglePopup(button_id) if !disable_popup => {
|
||||||
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
if popup.is_visible() && popup.current_widget().unwrap_or_default() == id {
|
if popup.visible() && popup.current_widget().unwrap_or_default() == id {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
} else {
|
} else {
|
||||||
popup.show(id, button_id);
|
popup.show(id, button_id);
|
||||||
|
@ -457,7 +457,7 @@ impl ModuleFactory for PopupModuleFactory {
|
||||||
}
|
}
|
||||||
ModuleUpdateEvent::TogglePopup(_) if !disable_popup => {
|
ModuleUpdateEvent::TogglePopup(_) if !disable_popup => {
|
||||||
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
debug!("Toggling popup for {} [#{}] (button id: {button_id})", name, id);
|
||||||
if popup.is_visible() && popup.current_widget().unwrap_or_default() == id {
|
if popup.visible() && popup.current_widget().unwrap_or_default() == id {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
} else {
|
} else {
|
||||||
popup.show(id, button_id);
|
popup.show(id, button_id);
|
||||||
|
|
|
@ -4,6 +4,7 @@ use serde::Deserialize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct Icons {
|
pub struct Icons {
|
||||||
/// Icon to display when playing.
|
/// Icon to display when playing.
|
||||||
///
|
///
|
||||||
|
@ -71,6 +72,7 @@ impl Default for Icons {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum PlayerType {
|
pub enum PlayerType {
|
||||||
Mpd,
|
Mpd,
|
||||||
Mpris,
|
Mpris,
|
||||||
|
@ -83,6 +85,7 @@ impl Default for PlayerType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct MusicModule {
|
pub struct MusicModule {
|
||||||
/// Type of player to connect to
|
/// Type of player to connect to
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use glib::{Propagation, PropertySet};
|
use glib::{markup_escape_text, Propagation, PropertySet};
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Button, IconTheme, Label, Orientation, Scale};
|
use gtk::{Button, IconTheme, Label, Orientation, Scale};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -531,6 +531,7 @@ fn get_token_value(song: &Track, token: &str) -> String {
|
||||||
"track" => song.track.map(|x| x.to_string()),
|
"track" => song.track.map(|x| x.to_string()),
|
||||||
_ => Some(token.to_string()),
|
_ => Some(token.to_string()),
|
||||||
}
|
}
|
||||||
|
.map(|str| markup_escape_text(str.as_str()).to_string())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use tokio::sync::mpsc::Receiver;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct NotificationsModule {
|
pub struct NotificationsModule {
|
||||||
/// Whether to show the current notification count.
|
/// Whether to show the current notification count.
|
||||||
///
|
///
|
||||||
|
@ -29,6 +30,7 @@ pub struct NotificationsModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
struct Icons {
|
struct Icons {
|
||||||
/// Icon to show when the panel is closed, with no notifications.
|
/// Icon to show when the panel is closed, with no notifications.
|
||||||
///
|
///
|
||||||
|
@ -193,6 +195,7 @@ impl Module<Overlay> for NotificationsModule {
|
||||||
if self.show_count {
|
if self.show_count {
|
||||||
label.add_class("count");
|
label.add_class("count");
|
||||||
overlay.add_overlay(&label);
|
overlay.add_overlay(&label);
|
||||||
|
overlay.set_overlay_pass_through(&label, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx = context.controller_tx.clone();
|
let ctx = context.controller_tx.clone();
|
||||||
|
|
|
@ -10,6 +10,7 @@ use tokio::sync::mpsc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct ScriptModule {
|
pub struct ScriptModule {
|
||||||
/// Path to script to execute.
|
/// Path to script to execute.
|
||||||
///
|
///
|
||||||
|
|
|
@ -14,6 +14,7 @@ use tokio::sync::mpsc;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct SysInfoModule {
|
pub struct SysInfoModule {
|
||||||
/// List of strings including formatting tokens.
|
/// List of strings including formatting tokens.
|
||||||
/// For available tokens, see [below](#formatting-tokens).
|
/// For available tokens, see [below](#formatting-tokens).
|
||||||
|
@ -51,6 +52,7 @@ pub struct SysInfoModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Copy, Clone)]
|
#[derive(Debug, Deserialize, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct Intervals {
|
pub struct Intervals {
|
||||||
/// The number of seconds between refreshing memory data.
|
/// The number of seconds between refreshing memory data.
|
||||||
///
|
///
|
||||||
|
@ -91,6 +93,7 @@ pub struct Intervals {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Copy, Clone)]
|
#[derive(Debug, Deserialize, Copy, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum Interval {
|
pub enum Interval {
|
||||||
All(u64),
|
All(u64),
|
||||||
Individual(Intervals),
|
Individual(Intervals),
|
||||||
|
|
|
@ -19,6 +19,7 @@ use tokio::sync::mpsc;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct TrayModule {
|
pub struct TrayModule {
|
||||||
/// Requests that icons from the theme be used over the item-provided item.
|
/// Requests that icons from the theme be used over the item-provided item.
|
||||||
/// Most items only provide one or the other so this will have no effect in most circumstances.
|
/// Most items only provide one or the other so this will have no effect in most circumstances.
|
||||||
|
@ -38,7 +39,8 @@ pub struct TrayModule {
|
||||||
/// **Valid options**: `top_to_bottom`, `bottom_to_top`, `left_to_right`, `right_to_left`
|
/// **Valid options**: `top_to_bottom`, `bottom_to_top`, `left_to_right`, `right_to_left`
|
||||||
/// <br>
|
/// <br>
|
||||||
/// **Default**: `left_to_right` if bar is horizontal, `top_to_bottom` if bar is vertical
|
/// **Default**: `left_to_right` if bar is horizontal, `top_to_bottom` if bar is vertical
|
||||||
#[serde(default, deserialize_with = "deserialize_orientation")]
|
#[serde(default, deserialize_with = "deserialize_pack_direction")]
|
||||||
|
#[cfg_attr(feature = "schema", schemars(schema_with = "schema_pack_direction"))]
|
||||||
direction: Option<PackDirection>,
|
direction: Option<PackDirection>,
|
||||||
|
|
||||||
/// See [common options](module-level-options#common-options).
|
/// See [common options](module-level-options#common-options).
|
||||||
|
@ -50,7 +52,7 @@ const fn default_icon_size() -> u32 {
|
||||||
16
|
16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_orientation<'de, D>(deserializer: D) -> Result<Option<PackDirection>, D::Error>
|
fn deserialize_pack_direction<'de, D>(deserializer: D) -> Result<Option<PackDirection>, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
@ -61,11 +63,24 @@ where
|
||||||
"right_to_left" => Ok(PackDirection::Rtl),
|
"right_to_left" => Ok(PackDirection::Rtl),
|
||||||
"top_to_bottom" => Ok(PackDirection::Ttb),
|
"top_to_bottom" => Ok(PackDirection::Ttb),
|
||||||
"bottom_to_top" => Ok(PackDirection::Btt),
|
"bottom_to_top" => Ok(PackDirection::Btt),
|
||||||
_ => Err(serde::de::Error::custom("invalid value for orientation")),
|
_ => Err(serde::de::Error::custom("invalid value for direction")),
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "schema")]
|
||||||
|
fn schema_pack_direction(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
|
||||||
|
schema.enum_values = Some(vec![
|
||||||
|
"top_to_bottom".into(),
|
||||||
|
"bottom_to_top".into(),
|
||||||
|
"left_to_right".into(),
|
||||||
|
"right_to_left".into(),
|
||||||
|
]);
|
||||||
|
schema.into()
|
||||||
|
}
|
||||||
|
|
||||||
impl Module<MenuBar> for TrayModule {
|
impl Module<MenuBar> for TrayModule {
|
||||||
type SendMessage = Event;
|
type SendMessage = Event;
|
||||||
type ReceiveMessage = ActivateRequest;
|
type ReceiveMessage = ActivateRequest;
|
||||||
|
|
|
@ -22,6 +22,7 @@ const HOUR: i64 = 60 * 60;
|
||||||
const MINUTE: i64 = 60;
|
const MINUTE: i64 = 60;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct UpowerModule {
|
pub struct UpowerModule {
|
||||||
/// The format string to use for the widget button label.
|
/// The format string to use for the widget button label.
|
||||||
/// For available tokens, see [below](#formatting-tokens).
|
/// For available tokens, see [below](#formatting-tokens).
|
||||||
|
|
|
@ -18,6 +18,7 @@ use std::collections::HashMap;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct VolumeModule {
|
pub struct VolumeModule {
|
||||||
/// Maximum value to allow volume sliders to reach.
|
/// Maximum value to allow volume sliders to reach.
|
||||||
/// Pulse supports values > 100 but this may result in distortion.
|
/// Pulse supports values > 100 but this may result in distortion.
|
||||||
|
|
|
@ -15,6 +15,7 @@ use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, Copy, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum SortOrder {
|
pub enum SortOrder {
|
||||||
/// Shows workspaces in the order they're added
|
/// Shows workspaces in the order they're added
|
||||||
Added,
|
Added,
|
||||||
|
@ -31,6 +32,7 @@ impl Default for SortOrder {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum Favorites {
|
pub enum Favorites {
|
||||||
ByMonitor(HashMap<String, Vec<String>>),
|
ByMonitor(HashMap<String, Vec<String>>),
|
||||||
Global(Vec<String>),
|
Global(Vec<String>),
|
||||||
|
@ -43,6 +45,7 @@ impl Default for Favorites {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct WorkspacesModule {
|
pub struct WorkspacesModule {
|
||||||
/// Map of actual workspace names to custom names.
|
/// Map of actual workspace names to custom names.
|
||||||
///
|
///
|
||||||
|
|
|
@ -229,7 +229,7 @@ impl Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the popup is currently visible
|
/// Checks if the popup is currently visible
|
||||||
pub fn is_visible(&self) -> bool {
|
pub fn visible(&self) -> bool {
|
||||||
self.window.is_visible()
|
self.window.is_visible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum ScriptInput {
|
pub enum ScriptInput {
|
||||||
String(String),
|
String(String),
|
||||||
Struct(Script),
|
Struct(Script),
|
||||||
|
@ -21,6 +22,7 @@ pub enum ScriptInput {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, Copy, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub enum ScriptMode {
|
pub enum ScriptMode {
|
||||||
Poll,
|
Poll,
|
||||||
Watch,
|
Watch,
|
||||||
|
@ -75,6 +77,7 @@ impl ScriptMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
pub struct Script {
|
pub struct Script {
|
||||||
#[serde(default = "ScriptMode::default")]
|
#[serde(default = "ScriptMode::default")]
|
||||||
pub(crate) mode: ScriptMode,
|
pub(crate) mode: ScriptMode,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue