1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-08-16 06:11:03 +02:00

Compare commits

...

537 commits

Author SHA1 Message Date
26686739a0
WIP update of networkmanager & volume modules 2025-08-10 16:37:55 +02:00
c42024d48a
Merge branch 'master' into develop
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	src/clients/networkmanager.rs
#	src/modules/networkmanager.rs
#	src/modules/volume.rs
2025-07-14 14:44:53 +02:00
b980d74a9a
Merge pull request #1082 from JakeStanger/dependabot/cargo/tokio-1.46.1
build(deps): bump tokio from 1.45.1 to 1.46.1
2025-07-08 16:18:00 +01:00
3fce815fa9
Merge pull request #1080 from JakeStanger/dependabot/cargo/reqwest-0.12.22
build(deps): bump reqwest from 0.12.20 to 0.12.22
2025-07-08 16:17:24 +01:00
cc11e9df52
Merge pull request #1079 from JakeStanger/dependabot/cargo/notify-8.1.0
build(deps): bump notify from 8.0.0 to 8.1.0
2025-07-08 16:16:59 +01:00
dependabot[bot]
6c41b9c336
build(deps): bump tokio from 1.45.1 to 1.46.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.45.1 to 1.46.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.45.1...tokio-1.46.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.46.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 17:36:28 +00:00
dependabot[bot]
74e19848f3
build(deps): bump reqwest from 0.12.20 to 0.12.22
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.20 to 0.12.22.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.20...v0.12.22)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-version: 0.12.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 17:17:34 +00:00
dependabot[bot]
b6d8d73858
build(deps): bump notify from 8.0.0 to 8.1.0
Bumps [notify](https://github.com/notify-rs/notify) from 8.0.0 to 8.1.0.
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/notify-8.0.0...notify-8.1.0)

---
updated-dependencies:
- dependency-name: notify
  dependency-version: 8.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 17:04:08 +00:00
6d4170d3a6
Merge pull request #1073 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-07-01 10:46:17 +01:00
15a933e887
Merge pull request #1070 from JakeStanger/dependabot/cargo/indexmap-2.10.0
build(deps): bump indexmap from 2.9.0 to 2.10.0
2025-07-01 10:45:29 +01:00
github-actions[bot]
a4cc46b2d8 flake.lock: Update
Flake lock file updates:

• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/59138c7667b7970d205d6a05a8bfa2d78caa3643?narHash=sha256-7gGa49iB9nCnFk4h/g9zwjlQAyjtpgcFkODjcOQS0Es%3D' (2025-05-31)
  → 'github:NixOS/nixpkgs/b95255df2360a45ddbb03817a68869d5cb01bf96?narHash=sha256-IJWIzZSkBsDzS7iS/iwSwur%2BxFkWqeLYC4kdf8ObtOM%3D' (2025-06-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102?narHash=sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg%3D' (2025-05-28)
  → 'github:nixos/nixpkgs/30e2e2857ba47844aa71991daa6ed1fc678bcbb7?narHash=sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM%3D' (2025-06-27)
2025-07-01 00:19:15 +00:00
dependabot[bot]
4bc41415fa
build(deps): bump indexmap from 2.9.0 to 2.10.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.9.0 to 2.10.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.9.0...2.10.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-30 17:24:37 +00:00
d45eb2a69a
Merge pull request #1063 from JakeStanger/dependabot/cargo/lua-src-548.1.1
build(deps): bump lua-src from 548.1.0 to 548.1.1
2025-06-23 23:23:38 +01:00
dependabot[bot]
411fa53071
build(deps): bump lua-src from 548.1.0 to 548.1.1
Bumps [lua-src](https://github.com/mlua-rs/lua-src-rs) from 548.1.0 to 548.1.1.
- [Commits](https://github.com/mlua-rs/lua-src-rs/compare/v548.1.0...v548.1.1)

---
updated-dependencies:
- dependency-name: lua-src
  dependency-version: 548.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-23 16:30:59 +00:00
bb7e927309
Merge pull request #1060 from Username404-59/master
Add a launch_command setting
2025-06-22 17:39:51 +01:00
Username404-59
d891893101
Rename launch_command to open_program & move it to desktop_file.rs 2025-06-22 18:31:22 +02:00
452bd89e98
Merge pull request #1061 from JakeStanger/fix/controller-crash
fix: panic when module controller errors
2025-06-22 16:36:38 +01:00
673b05e337
fix: panic when module controller errors
If an individual controller fails, it will now error independently and allow other modules to continue loading.

Fixes #1057
2025-06-22 16:26:19 +01:00
Username404-59
8dfca4303d
Update docs to mention {app_name} in launch_command 2025-06-22 16:53:43 +02:00
Username404-59
d2f46a59b4
Make launch_command more flexible with {app_name} 2025-06-22 16:45:17 +02:00
Username404-59
a6f63b3bf1
Improve launch_command 2025-06-22 16:15:33 +02:00
Username404-59
a253a23d57
Remove now unused imports in menu 2025-06-21 19:12:22 +02:00
Username404-59
c2d55a1963
Add documentation for launch_command 2025-06-21 14:47:53 +02:00
Username404-59
931807e326
Add a launch_command setting
(and put duplicate code in a function)
2025-06-21 14:21:54 +02:00
Preston Corless
81ce2c169f
fix(upower): round battery percent to whole number (#1058) 2025-06-20 18:20:27 +01:00
f125058e79
Merge pull request #1056 from JakeStanger/dependabot/cargo/reqwest-0.12.20
build(deps): bump reqwest from 0.12.19 to 0.12.20
2025-06-17 20:56:16 +01:00
dc7c109d08
Merge pull request #1053 from JakeStanger/dependabot/cargo/clap_complete-4.5.54
build(deps): bump clap_complete from 4.5.52 to 4.5.54
2025-06-17 20:55:44 +01:00
dependabot[bot]
0fb7199140
build(deps): bump reqwest from 0.12.19 to 0.12.20
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.19 to 0.12.20.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.19...v0.12.20)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-version: 0.12.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 16:46:42 +00:00
dependabot[bot]
4adf15ca0d
build(deps): bump clap_complete from 4.5.52 to 4.5.54
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.52 to 4.5.54.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.52...clap_complete-v4.5.54)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.54
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 16:15:54 +00:00
326c036acd
Merge pull request #1054 from JakeStanger/dependabot/cargo/swayipc-async-2.1.0
build(deps): bump swayipc-async from 2.0.4 to 2.1.0
2025-06-16 17:14:48 +01:00
4b9d783026
Merge pull request #1052 from JakeStanger/dependabot/cargo/lua-src-548.1.0
build(deps): bump lua-src from 548.0.0 to 548.1.0
2025-06-16 17:14:27 +01:00
f1e07d496b
Merge pull request #1051 from JakeStanger/dependabot/cargo/clap-4.5.40
build(deps): bump clap from 4.5.39 to 4.5.40
2025-06-16 17:14:04 +01:00
a00f38bfe9
Merge pull request #1055 from JakeStanger/dependabot/cargo/cfg-if-1.0.1
build(deps): bump cfg-if from 1.0.0 to 1.0.1
2025-06-16 17:13:46 +01:00
dependabot[bot]
687b3c60fb
build(deps): bump cfg-if from 1.0.0 to 1.0.1
Bumps [cfg-if](https://github.com/rust-lang/cfg-if) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/rust-lang/cfg-if/releases)
- [Changelog](https://github.com/rust-lang/cfg-if/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cfg-if/compare/1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: cfg-if
  dependency-version: 1.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 15:53:17 +00:00
dependabot[bot]
fc1b54c26b
build(deps): bump swayipc-async from 2.0.4 to 2.1.0
Bumps [swayipc-async](https://github.com/jaycefayne/swayipc-rs) from 2.0.4 to 2.1.0.
- [Commits](https://github.com/jaycefayne/swayipc-rs/compare/swayipc-async-2.0.4...swayipc-async-2.1.0)

---
updated-dependencies:
- dependency-name: swayipc-async
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 15:51:51 +00:00
dependabot[bot]
e54a6f087a
build(deps): bump lua-src from 548.0.0 to 548.1.0
Bumps [lua-src](https://github.com/mlua-rs/lua-src-rs) from 548.0.0 to 548.1.0.
- [Commits](https://github.com/mlua-rs/lua-src-rs/compare/v548.0.0...v548.1.0)

---
updated-dependencies:
- dependency-name: lua-src
  dependency-version: 548.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 15:45:27 +00:00
dependabot[bot]
ffb087fe0b
build(deps): bump clap from 4.5.39 to 4.5.40
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.39 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.39...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.40
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 15:37:26 +00:00
923fc4b5af
Merge pull request #1048 from postsolar/feat/music-popup-truncate
feat(music): Add `truncate_popup_{artist,album,title}` to `music`
2025-06-16 11:40:42 +01:00
postsolar
69ae4c2ac6 Add truncate_popup_{artist,album,title} to music 2025-06-16 13:36:28 +03:00
ff9f4477bb
Merge pull request #1047 from postsolar/feat/volume-input-truncate
feat(volume): Add `truncate` option for `volume` popup
2025-06-16 09:55:08 +01:00
d4c2ac1dc2
Merge pull request #1046 from postsolar/fix/hm-settings
fix(nix): default HM module `config` option to `null`
2025-06-16 09:31:26 +01:00
postsolar
f9ff267aa2 Add truncate option for volume popup 2025-06-14 05:43:42 +03:00
postsolar
9b0c0dd0fe fix: default HM module config option to null
This makes it more semantically approachable IMO.

I would expect this behavior:
- Default value is `null`
- Config is created if the value is not `null`

Currently it's this behavior (makes little sense to me):
- Default value is `{}`
- Config is created if the values is not `""`
2025-06-14 04:29:47 +03:00
4fad5a4d18
Merge pull request #1041 from JakeStanger/dependabot/cargo/lua-src-548.0.0
build(deps): bump lua-src from 547.1.0 to 548.0.0
2025-06-10 22:21:20 +01:00
1b40bf6450
Merge pull request #1040 from JakeStanger/dependabot/cargo/sysinfo-0.35.2
build(deps): bump sysinfo from 0.35.1 to 0.35.2
2025-06-10 22:21:05 +01:00
dependabot[bot]
268a34f504
build(deps): bump lua-src from 547.1.0 to 548.0.0
Bumps [lua-src](https://github.com/mlua-rs/lua-src-rs) from 547.1.0 to 548.0.0.
- [Commits](https://github.com/mlua-rs/lua-src-rs/commits)

---
updated-dependencies:
- dependency-name: lua-src
  dependency-version: 548.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 14:39:33 +00:00
dependabot[bot]
a438a6c322
build(deps): bump sysinfo from 0.35.1 to 0.35.2
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.35.1 to 0.35.2.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/compare/v0.35.1...v0.35.2)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-version: 0.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 14:38:48 +00:00
199c86d945
Merge pull request #1019 from atagen/nix-fix
chore(nix): simplify packaging and outputs
2025-06-06 09:08:39 +01:00
atagen
3b80b43582 chore(nix): simplify packaging and outputs 2025-06-04 13:05:17 +10:00
098df1ee26
Merge pull request #1024 from JakeStanger/dependabot/cargo/clap-4.5.39
build(deps): bump clap from 4.5.38 to 4.5.39
2025-06-02 21:07:17 +01:00
dependabot[bot]
1e8e902781
build(deps): bump clap from 4.5.38 to 4.5.39
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.39.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.38...clap_complete-v4.5.39)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.39
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 15:58:00 +00:00
1467c19a18
Merge pull request #1027 from JakeStanger/dependabot/cargo/clap_complete-4.5.52
build(deps): bump clap_complete from 4.5.50 to 4.5.52
2025-06-02 16:56:18 +01:00
da37126dd2
Merge pull request #1026 from JakeStanger/dependabot/cargo/color-eyre-0.6.5
build(deps): bump color-eyre from 0.6.3 to 0.6.5
2025-06-02 16:56:06 +01:00
f06af2e7d0
Merge pull request #1023 from JakeStanger/dependabot/cargo/reqwest-0.12.19
build(deps): bump reqwest from 0.12.15 to 0.12.19
2025-06-02 16:50:10 +01:00
dependabot[bot]
8afc797918
build(deps): bump clap_complete from 4.5.50 to 4.5.52
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.50 to 4.5.52.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.50...clap_complete-v4.5.52)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 15:25:34 +00:00
dependabot[bot]
a4ed8dc7be
build(deps): bump color-eyre from 0.6.3 to 0.6.5
Bumps [color-eyre](https://github.com/eyre-rs/eyre) from 0.6.3 to 0.6.5.
- [Commits](https://github.com/eyre-rs/eyre/compare/color-eyre-v0.6.3...color-eyre@0.6.5)

---
updated-dependencies:
- dependency-name: color-eyre
  dependency-version: 0.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 15:25:09 +00:00
dependabot[bot]
026fff6430
build(deps): bump reqwest from 0.12.15 to 0.12.19
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.15 to 0.12.19.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.15...v0.12.19)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-version: 0.12.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 15:09:07 +00:00
ba8506b41f
Merge pull request #1021 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-06-01 11:24:25 +01:00
github-actions[bot]
51d2f5129f flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/efd36682371678e2b6da3f108fdb5c613b3ec598?narHash=sha256-oLvmxOnsEKGtwczxp/CwhrfmQUG2ym24OMWowcoRhH8%3D' (2025-04-24)
  → 'github:ipetkov/crane/b718a78696060df6280196a6f992d04c87a16aef?narHash=sha256-t0qLLqb4C1rdtiY8IFRH5KIapTY/n3Lqt57AmxEv9mk%3D' (2025-05-24)
• Updated input 'flake-compat':
    'github:edolstra/flake-compat/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec?narHash=sha256-NeCCThCEP3eCl2l/%2B27kNNK7QrwZB1IJCrXfrbv5oqU%3D' (2024-12-04)
  → 'github:edolstra/flake-compat/9100a0f413b0c601e0533d1d94ffd501ce2e7885?narHash=sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX%2BfjA8Xf8PUmqCY%3D' (2025-05-12)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/423d2df5b04b4ee7688c3d71396e872afa236a89?narHash=sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U%3D' (2025-04-30)
  → 'github:NixOS/nixpkgs/59138c7667b7970d205d6a05a8bfa2d78caa3643?narHash=sha256-7gGa49iB9nCnFk4h/g9zwjlQAyjtpgcFkODjcOQS0Es%3D' (2025-05-31)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29)
  → 'github:nixos/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102?narHash=sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg%3D' (2025-05-28)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6?narHash=sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4%3D' (2025-04-30)
  → 'github:oxalica/rust-overlay/fc82ce758cc5df6a6d5d24e75710321cdbdc787a?narHash=sha256-F%2BnGITu6D7RswJlm8qCuU1PCuOSgDeAqaDKWW1n1jmQ%3D' (2025-05-31)
2025-06-01 00:20:23 +00:00
b62d0d58cb
refactor: fix clippy warnings 2025-05-27 13:33:05 +01:00
3c4b6a0fdb
refactor: remove unnecessary Clone constraint from recv_glib methods 2025-05-27 13:31:22 +01:00
448f461943
fix: regression - image prefixes not handled correctly
Fixes #1015
2025-05-27 11:38:08 +01:00
93489d6924
Merge pull request #1007 from JakeStanger/dependabot/cargo/mlua-0.10.5
build(deps): bump mlua from 0.10.3 to 0.10.5
2025-05-26 23:12:13 +01:00
3b0527c78a
Merge pull request #1009 from JakeStanger/dependabot/cargo/tokio-1.45.1
build(deps): bump tokio from 1.45.0 to 1.45.1
2025-05-26 23:05:23 +01:00
dependabot[bot]
ecf6a21cd4
build(deps): bump tokio from 1.45.0 to 1.45.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.45.0 to 1.45.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.45.0...tokio-1.45.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.45.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 21:56:24 +00:00
e035b8eda7
Merge pull request #1008 from JakeStanger/dependabot/cargo/zbus-5.7.1
build(deps): bump zbus from 5.7.0 to 5.7.1
2025-05-26 22:54:46 +01:00
b27c601733
Merge pull request #1010 from JakeStanger/refactor/glib-deps
refactor: `recv_glib` dependency arrays
2025-05-26 22:54:04 +01:00
9d18ce52f5
refactor: recv_glib dependency arrays
Adds a dependency array system to `recv_glib` which internally clones the passed deps and then passes by reference to the callback.

This cleans up a lot of the big `{}` blocks full of `widget.clone()` and removes a lot of boilerplate. Yay!
2025-05-26 22:46:25 +01:00
beab26a37e
docs: add missing menu sidebar link 2025-05-26 16:38:15 +01:00
333796a9ae
Merge pull request #609 from ClaireNeveu/menu-widget
feat: Add Menu module
2025-05-26 16:37:15 +01:00
Claire Neveu
96e10fe139
feat: add menu module
Adds a new Menu module which allows users to create XDG or custom menus that open after clicking on a button.

Resolves #534

Co-authored-by: Jake Stanger <mail@jstanger.dev>
2025-05-26 16:23:49 +01:00
dependabot[bot]
190045b13b
build(deps): bump zbus from 5.7.0 to 5.7.1
Bumps [zbus](https://github.com/dbus2/zbus) from 5.7.0 to 5.7.1.
- [Release notes](https://github.com/dbus2/zbus/releases)
- [Commits](https://github.com/dbus2/zbus/compare/zbus-5.7.0...zbus-5.7.1)

---
updated-dependencies:
- dependency-name: zbus
  dependency-version: 5.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 14:28:54 +00:00
dependabot[bot]
ee2ac6f3b0
build(deps): bump mlua from 0.10.3 to 0.10.5
Bumps [mlua](https://github.com/mlua-rs/mlua) from 0.10.3 to 0.10.5.
- [Release notes](https://github.com/mlua-rs/mlua/releases)
- [Changelog](https://github.com/mlua-rs/mlua/blob/v0.10.5/CHANGELOG.md)
- [Commits](https://github.com/mlua-rs/mlua/compare/v0.10.3...v0.10.5)

---
updated-dependencies:
- dependency-name: mlua
  dependency-version: 0.10.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 14:28:32 +00:00
2ae2e78407
Merge pull request #1005 from JakeStanger/fix/icon-label-markup
fix: unable to use markup in icon labels
2025-05-25 23:31:12 +01:00
9ed0d1270a
fix: unable to use markup in icon labels 2025-05-25 23:27:03 +01:00
e99a04923d
Merge pull request #1003 from JakeStanger/refactor/image
Overhaul `.desktop` and image resolver code
2025-05-25 19:29:07 +01:00
9270225db8
chore(intellij): add test run config 2025-05-25 16:24:17 +01:00
3a0e102afc
feat: change icon_overrides to apply to all resolved images 2025-05-25 16:24:17 +01:00
3e55d87c3a
refactor: overhaul .desktop and image resolver systems
Rewrites the desktop file parser code and image resolver code to introduce caching system and make fully async. They should be much faster now.

BREAKING CHANGE: The `icon_theme` setting has been moved from per-bar to top-level
2025-05-25 16:24:17 +01:00
ca524f19f6
refactor: fix some strict clippy warnings 2025-05-25 12:14:24 +01:00
bd9b3af5bc
Merge pull request #999 from postsolar/fix-calendar-date
fix(clock): reset selected calendar date on each popup open
2025-05-20 16:55:09 +01:00
b8d87eb9a9
Merge pull request #998 from JakeStanger/fix/launcher-favourites
fix(launcher): regression - favourites in wrong order
2025-05-20 16:03:48 +01:00
postsolar
c4b29e199c Calendar: reset selected date on each popup open 2025-05-20 17:50:09 +03:00
ff359f61c2
fix(launcher): regression - favourites in wrong order
Fixes #997
2025-05-20 15:42:07 +01:00
0914ea3972
Merge pull request #994 from JakeStanger/dependabot/cargo/ctrlc-3.4.7
build(deps): bump ctrlc from 3.4.6 to 3.4.7
2025-05-20 10:59:19 +01:00
dependabot[bot]
cf65691d6b
build(deps): bump ctrlc from 3.4.6 to 3.4.7
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.4.6 to 3.4.7.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.4.6...3.4.7)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-version: 3.4.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 15:06:56 +00:00
60b42c4d94
Merge pull request #993 from JakeStanger/dependabot/cargo/zbus-5.7.0
build(deps): bump zbus from 5.6.0 to 5.7.0
2025-05-19 16:05:13 +01:00
423ba56d3f
Merge pull request #995 from JakeStanger/dependabot/cargo/sysinfo-0.35.1
build(deps): bump sysinfo from 0.35.0 to 0.35.1
2025-05-19 16:04:50 +01:00
a8decbd152
Merge pull request #996 from JakeStanger/refactor/into-popup
refactor(module): remove redundant `into_popup` parameters
2025-05-19 16:03:57 +01:00
8575300044
refactor(module): remove redundant into_popup parameters 2025-05-19 15:57:44 +01:00
dependabot[bot]
5be6892458
build(deps): bump sysinfo from 0.35.0 to 0.35.1
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.35.0 to 0.35.1.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/compare/v0.35.0...v0.35.1)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-version: 0.35.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 14:45:08 +00:00
dependabot[bot]
aafa60ba58
build(deps): bump zbus from 5.6.0 to 5.7.0
Bumps [zbus](https://github.com/dbus2/zbus) from 5.6.0 to 5.7.0.
- [Release notes](https://github.com/dbus2/zbus/releases)
- [Commits](https://github.com/dbus2/zbus/compare/zbus-5.6.0...zbus-5.7.0)

---
updated-dependencies:
- dependency-name: zbus
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 14:43:19 +00:00
09ba3816d6
refactor: couple of tiny changes 2025-05-19 15:33:08 +01:00
7b220cb2ae
fix: issues with bar appearing on wrong monitor
Due to a GTK bug, the bar relied on GDK and the compositor reporting monitors in the same order. This broke down when monitors were connected, or occasionally on reload.

This reverts the workaround put in place for the bug, since it has since been patched upstream
2025-05-19 15:29:26 +01:00
Brandon
b13c725f67
feat(custom): add halign and valign options to box widget (#988) 2025-05-19 09:20:59 +01:00
e4c0a1ba92
Merge pull request #983 from postsolar/hide-empty-bindmode
fix(bindmode): hide bindmode label if it's empty
2025-05-18 22:26:55 +01:00
postsolar
aa2d82469f Send a dummy event on init so that the widget starts hidden 2025-05-18 23:09:26 +03:00
postsolar
f6598f46d5 Hide bindmode label if it's empty 2025-05-18 22:53:27 +03:00
75b60ec23e
Merge pull request #991 from JakeStanger/fix/niri-reload
fix(workspaces): reload broken on niri
2025-05-18 20:00:24 +01:00
375ceb81ce
fix(workspaces): reload broken on niri
Fixes #930
Fixes #981
2025-05-18 19:54:42 +01:00
760db3067b
Merge pull request #823 from JakeStanger/refactor/remove-macros
refactor: replace channel macros with ext trait methods
2025-05-18 17:22:03 +01:00
f929aef2d9
refactor: replace channel macros with ext trait methods 2025-05-18 15:22:58 +01:00
d5744f597c
Merge pull request #990 from JakeStanger/build/feature-fix
build: fix issues with compositor feature flags
2025-05-18 14:33:26 +01:00
999d173eff
build: fix issues with compositor feature flags 2025-05-18 14:29:40 +01:00
da5164c8a4
docs(gtk4): fix launcher icon 2025-05-16 22:34:04 +01:00
c6f3af4142
refactor: improvements to feature flags 2025-05-16 22:33:54 +01:00
88190f0d4a
Merge pull request #984 from JakeStanger/fix/hyprland-grimblast-crash
refactor: update hyprland-rs, remove `expect`s from hyprland client code
2025-05-16 22:32:49 +01:00
1b6aa10423
refactor: update hyprland-rs, remove expects from hyprland client code
Fixes #960
2025-05-16 22:29:25 +01:00
f3ae57bfe5
Merge pull request #980 from JakeStanger/fix/cairo-leak
fix(cairo): huge memory leak
2025-05-14 22:35:55 +01:00
e240ab7acb
fix(cairo): huge memory leak
Fixes increasing the reference count of the `Context` without ever decreasing it.

Fixes #972
2025-05-14 22:29:43 +01:00
fe1d28cda8
Merge pull request #977 from JakeStanger/dependabot/cargo/clap_complete-4.5.50
build(deps): bump clap_complete from 4.5.47 to 4.5.50
2025-05-12 17:02:06 +01:00
f72e83a3ea
Merge pull request #974 from JakeStanger/dependabot/cargo/chrono-0.4.41
build(deps): bump chrono from 0.4.40 to 0.4.41
2025-05-12 17:01:56 +01:00
postsolar
040e7e64ee
fix(mpris): use set_position instead of seek (#978) 2025-05-12 16:06:29 +01:00
dependabot[bot]
3638670aeb
build(deps): bump clap_complete from 4.5.47 to 4.5.50
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.47 to 4.5.50.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.47...clap_complete-v4.5.50)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.50
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:36:09 +00:00
ffbc851f0e
Merge pull request #975 from JakeStanger/dependabot/cargo/tokio-1.45.0
build(deps): bump tokio from 1.44.2 to 1.45.0
2025-05-12 15:34:27 +01:00
23bb068d81
Merge pull request #973 from JakeStanger/dependabot/cargo/clap-4.5.38
build(deps): bump clap from 4.5.37 to 4.5.38
2025-05-12 15:34:01 +01:00
dependabot[bot]
533385f3b1
build(deps): bump tokio from 1.44.2 to 1.45.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.44.2 to 1.45.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.44.2...tokio-1.45.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:19:05 +00:00
dependabot[bot]
532a7b6bf6
build(deps): bump chrono from 0.4.40 to 0.4.41
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.40 to 0.4.41.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.40...v0.4.41)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:18:54 +00:00
dependabot[bot]
b2ff2de5f1
build(deps): bump clap from 4.5.37 to 4.5.38
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.37 to 4.5.38.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.37...clap_complete-v4.5.38)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 14:18:44 +00:00
28136f62bf
Merge pull request #971 from postsolar/bug-report-template
chore: update bug report template
2025-05-12 09:43:30 +01:00
postsolar
8b30901b0b chore: update bug report template 2025-05-12 11:37:41 +03:00
postsolar
4d92af7ced
fix(music): hours not being displayed in track duration (#968) 2025-05-12 09:16:06 +01:00
d31ac29db0
Merge pull request #953 from 74k1/update-flake
feat(nix)!: standardize home-manager options & restructure flake
2025-05-06 21:03:13 +01:00
reo101
89073dc0cf feat(nix): use lib.fileset for src
- Filters out more unnecessary files (like `nix` ones)
2025-05-06 21:47:27 +02:00
reo101
d97d07e98e chore(nix): reformat package.nix with alejandra 2025-05-06 21:47:27 +02:00
74k1
322fc0cdcc
feat(nix)!: standardize home-manager module options
- reload ironbar on config change
- update docs to reflect breaking change

BREAKING CHANGE: `programs.ironbar.systemd` is not enabled by default anymore

Co-authored-by: reo101 <pavel.atanasov2001@gmail.com>
2025-05-06 21:47:27 +02:00
74k1
5affad3400
refactor(nix): use flake-parts and flake-compat
- redefine `{default,shell}.nix` to derive from `flake.nix` (using
  `flake-compat`)
- use `flake-parts`' `_module.args.pkgs` machinery to apply overlays
  (instead of `pkgsFor`)
- extract `homeManagerModule` to `nix/module.nix`
- rename package at `nix/default.nix` to `nix/package.nix`

Co-authored-by: reo101 <pavel.atanasov2001@gmail.com>
2025-05-06 21:46:12 +02:00
95669fcf38
Merge pull request #958 from JakeStanger/dependabot/cargo/lua-src-547.1.0
build(deps): bump lua-src from 547.0.0 to 547.1.0
2025-05-05 18:47:35 +01:00
2e29f8a323
Merge pull request #957 from JakeStanger/dependabot/cargo/wayland-protocols-wlr-0.3.8
build(deps): bump wayland-protocols-wlr from 0.3.6 to 0.3.8
2025-05-05 18:47:15 +01:00
b17eb612f6
Merge pull request #956 from JakeStanger/dependabot/cargo/zbus-5.6.0
build(deps): bump zbus from 5.5.0 to 5.6.0
2025-05-05 18:47:00 +01:00
dependabot[bot]
0543820078
build(deps): bump zbus from 5.5.0 to 5.6.0
Bumps [zbus](https://github.com/dbus2/zbus) from 5.5.0 to 5.6.0.
- [Release notes](https://github.com/dbus2/zbus/releases)
- [Commits](https://github.com/dbus2/zbus/compare/zbus-5.5.0...zbus-5.6.0)

---
updated-dependencies:
- dependency-name: zbus
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 16:01:02 +00:00
68665e5107
Merge pull request #955 from JakeStanger/dependabot/cargo/sysinfo-0.35.0
build(deps): bump sysinfo from 0.34.2 to 0.35.0
2025-05-05 16:58:45 +01:00
1a9207f8b3
Merge pull request #954 from JakeStanger/dependabot/cargo/rustix-1.0.7
build(deps): bump rustix from 1.0.5 to 1.0.7
2025-05-05 16:57:58 +01:00
dependabot[bot]
f05553c6dc
build(deps): bump lua-src from 547.0.0 to 547.1.0
Bumps [lua-src](https://github.com/mlua-rs/lua-src-rs) from 547.0.0 to 547.1.0.
- [Commits](https://github.com/mlua-rs/lua-src-rs/compare/v547.0.0...v547.1.0)

---
updated-dependencies:
- dependency-name: lua-src
  dependency-version: 547.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 15:01:29 +00:00
dependabot[bot]
3e0f1395d6
build(deps): bump wayland-protocols-wlr from 0.3.6 to 0.3.8
Bumps [wayland-protocols-wlr](https://github.com/smithay/wayland-rs) from 0.3.6 to 0.3.8.
- [Release notes](https://github.com/smithay/wayland-rs/releases)
- [Changelog](https://github.com/Smithay/wayland-rs/blob/master/historical_changelog.md)
- [Commits](https://github.com/smithay/wayland-rs/commits)

---
updated-dependencies:
- dependency-name: wayland-protocols-wlr
  dependency-version: 0.3.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:54:43 +00:00
dependabot[bot]
cd434f68a0
build(deps): bump sysinfo from 0.34.2 to 0.35.0
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.34.2 to 0.35.0.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits/v0.35.0)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-version: 0.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:44:01 +00:00
dependabot[bot]
c773f2b001
build(deps): bump rustix from 1.0.5 to 1.0.7
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 1.0.5 to 1.0.7.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v1.0.5...v1.0.7)

---
updated-dependencies:
- dependency-name: rustix
  dependency-version: 1.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 14:43:05 +00:00
5c2f290fb1
Merge pull request #952 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-05-01 10:15:04 +01:00
github-actions[bot]
c5b0fa1ab8 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/70947c1908108c0c551ddfd73d4f750ff2ea67cd?narHash=sha256-vVOAp9ahvnU%2BfQoKd4SEXB2JG2wbENkpqcwlkIXgUC0%3D' (2025-03-19)
  → 'github:ipetkov/crane/efd36682371678e2b6da3f108fdb5c613b3ec598?narHash=sha256-oLvmxOnsEKGtwczxp/CwhrfmQUG2ym24OMWowcoRhH8%3D' (2025-04-24)
• Updated input 'naersk':
    'github:nix-community/naersk/a75c0584b0d69de943babc899530e9c70c642b42?narHash=sha256-o9dXcpfXpBm6%2B/SRqcQW0GzRkJymwyEAu5UD5LMDd3s%3D' (2025-03-25)
  → 'github:nix-community/naersk/38bc60bbc157ae266d4a0c96671c6c742ee17a5f?narHash=sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0%3D' (2025-04-29)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/63158b9cbb6ec93d26255871c447b0f01da81619?narHash=sha256-FurMxmjEEqEMld11eX2vgfAx0Rz0JhoFm8UgxbfCZa8%3D' (2025-03-30)
  → 'github:NixOS/nixpkgs/423d2df5b04b4ee7688c3d71396e872afa236a89?narHash=sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U%3D' (2025-04-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30)
  → 'github:nixos/nixpkgs/46e634be05ce9dc6d4db8e664515ba10b78151ae?narHash=sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ%2B5dck%3D' (2025-04-29)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/011de3c895927300651d9c2cb8e062adf17aa665?narHash=sha256-OBcNE%2B2/TD1AMgq8HKMotSQF8ZPJEFGZdRoBJ7t/HIc%3D' (2025-03-31)
  → 'github:oxalica/rust-overlay/7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6?narHash=sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4%3D' (2025-04-30)
2025-05-01 00:17:37 +00:00
e19af2bdd3
docs(gtk4): update launcher as partially ported, update popups/ipc 2025-04-25 23:49:14 +01:00
a9508e6541
Merge pull request #855 from Rodrigodd/feat/module-bindmode
feat: rename sway_mode to bindmode and add Hyprland support
2025-04-22 23:40:47 +01:00
304fa03d90
docs(gtk4): update music to ported, popup state 2025-04-22 22:32:59 +01:00
0c84c0c583
docs(gtk4): update workspaces to be ported 2025-04-21 23:26:19 +01:00
Rodrigodd
ca3a2e31ef ci: add bindmode to feature-checks matrix 2025-04-21 19:13:59 -03:00
Rodrigodd
638f2a20d2 refactor: avoid explicity cast with long type
Aparently Rust doesn't implicitly cast a `Result<Arc<T>>` to
`Result<Arc<dyn T>>`, so the `Result` was being mapped with a explicity
cast.

But unwrapping the `Result` with a `?` and then wrapping it back into
`Ok` make the implicit cast work. This avoid typing the entire type,
which was breaking the code in multiple lines.
2025-04-21 19:13:59 -03:00
Rodrigodd
5a5b110c7a feat: rename sway_mode to bindmode and add Hyprland support
Signed-off-by: Rodrigodd <rodrigobatsmoraes@hotmail.com>
2025-04-21 19:13:59 -03:00
1e501d99d2
Merge pull request #946 from JakeStanger/fix/wayland-panic
fix(wayland): panicking on compositors without protocol support
2025-04-21 21:11:31 +01:00
e1e8af8cf2
Merge pull request #945 from JakeStanger/dependabot/cargo/clap-4.5.37
build(deps): bump clap from 4.5.36 to 4.5.37
2025-04-21 20:50:28 +01:00
681310e215
Merge pull request #944 from JakeStanger/dependabot/cargo/libpulse-binding-2.30.1
build(deps): bump libpulse-binding from 2.29.0 to 2.30.1
2025-04-21 20:50:02 +01:00
8c76e957f9
ci: fix paths not globbing 2025-04-21 20:13:17 +01:00
963a450ff5
fix(wayland): panicking on compositors without protocol support
This allows Ironbar to run on a wider range of compositors, where support for the `wlr_foreign_toplevel` protocol used by focused/launcher, and the `wlr_data_control_device` protocol used by clipboard is missing
2025-04-21 20:11:47 +01:00
53317dbada
ci(schema): fix incorrect trigger path for own file 2025-04-21 20:09:45 +01:00
dependabot[bot]
53ce2ffb73
build(deps): bump clap from 4.5.36 to 4.5.37
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.36 to 4.5.37.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.36...clap_complete-v4.5.37)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 14:46:07 +00:00
dependabot[bot]
596fcadf2b
build(deps): bump libpulse-binding from 2.29.0 to 2.30.1
Bumps [libpulse-binding](https://github.com/jnqnfe/pulse-binding-rust) from 2.29.0 to 2.30.1.
- [Commits](https://github.com/jnqnfe/pulse-binding-rust/compare/v2.29.0...v2.30.1)

---
updated-dependencies:
- dependency-name: libpulse-binding
  dependency-version: 2.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 14:40:54 +00:00
7e82e0ff20
Merge pull request #939 from JakeStanger/ci/feature-check
ci(build): check each feature individually
2025-04-16 23:43:21 +01:00
4aff6bcbbc
ci: restrict trigger paths
this should hopefully reduce unnecessary job runs
2025-04-16 23:39:30 +01:00
e46fdce14c
ci: use custom build image 2025-04-16 23:35:37 +01:00
75301868de
ci(build): check each feature individually 2025-04-16 23:35:36 +01:00
5f5791cbe4
Merge pull request #941 from JakeStanger/ci/dockerfile-clippy
ci(docker): add clippy
2025-04-16 23:31:16 +01:00
8b71ff3cb6
ci(docker): add clippy 2025-04-16 23:30:08 +01:00
2b22b4a77b
Merge pull request #940 from JakeStanger/ci/dockerfile
ci: add dockerfile for build image
2025-04-16 23:14:15 +01:00
8d5ec1d114
ci: add dockerfile for build image 2025-04-16 23:11:51 +01:00
Rodrigo Batista de Moraes
cff6b1fc83
build: fix compilation for many feature combinations (#932)
* Fix compilation for many feature combinations

* fix: keep Mpris as the default music player type

* fix: update futures-lite comment

* fix: remove redundant "clap" feature dependency from "cli"

* fix: don't make IPC a dependency of sysinfo module

* refactor: move serde_json feature to "shared" section

* refactor: avoid cfgs inside listen_workspace_events

by splitting list_keyboards_events out of it, which manages its own
connection to the Hyprland IPC socket.

* refactor: sort multiline cfg any's

* Revert "refactor: avoid cfgs inside listen_workspace_events"

This reverts commit 1b4202ed80c9483c609ada8c4436e0fec26a9eef.

* refactor: split listen_workspace_events in more functions

* style: fix broken identation

Not sure why rustfmt didn't catch this.
2025-04-16 22:43:14 +01:00
5744929931
build: update universal-config 2025-04-14 20:52:55 +01:00
0a88b8e3d2
Merge pull request #937 from JakeStanger/dependabot/cargo/clap-4.5.36
build(deps): bump clap from 4.5.35 to 4.5.36
2025-04-14 16:04:36 +01:00
dependabot[bot]
d912b23248
build(deps): bump clap from 4.5.35 to 4.5.36
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.35 to 4.5.36.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.35...clap_complete-v4.5.36)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.36
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 14:20:47 +00:00
e3742f822c
Merge pull request #936 from JakeStanger/dependabot/cargo/cargo-ea4584273b
build(deps): bump crossbeam-channel from 0.5.14 to 0.5.15 in the cargo group
2025-04-10 16:45:39 +01:00
dependabot[bot]
1ddf42dfd9
build(deps): bump crossbeam-channel in the cargo group
Bumps the cargo group with 1 update: [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam).


Updates `crossbeam-channel` from 0.5.14 to 0.5.15
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.14...crossbeam-channel-0.5.15)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.15
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-10 14:47:29 +00:00
ffd4447951
chore: update bug report template
Quick fix to change the label - more work coming soon
2025-04-08 23:30:31 +01:00
d639d0602b
Merge pull request #927 from JakeStanger/dependabot/cargo/indexmap-2.9.0
build(deps): bump indexmap from 2.8.0 to 2.9.0
2025-04-08 09:39:14 +01:00
f3f11fc80c
Merge pull request #926 from JakeStanger/dependabot/cargo/wayland-protocols-wlr-0.3.6
build(deps): bump wayland-protocols-wlr from 0.2.0 to 0.3.6
2025-04-08 09:38:35 +01:00
d260d0bf52
Merge pull request #925 from JakeStanger/dependabot/cargo/sysinfo-0.34.2
build(deps): bump sysinfo from 0.34.1 to 0.34.2
2025-04-08 09:37:36 +01:00
0e8ed7a82f
Merge pull request #924 from JakeStanger/dependabot/cargo/ctrlc-3.4.6
build(deps): bump ctrlc from 3.4.5 to 3.4.6
2025-04-08 09:36:50 +01:00
97f0902f96
Merge pull request #923 from JakeStanger/dependabot/cargo/tokio-1.44.2
build(deps): bump tokio from 1.44.1 to 1.44.2
2025-04-08 09:35:19 +01:00
dependabot[bot]
77458967fd
build(deps): bump indexmap from 2.8.0 to 2.9.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.8.0 to 2.9.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 16:01:34 +00:00
dependabot[bot]
c334aac812
build(deps): bump wayland-protocols-wlr from 0.2.0 to 0.3.6
Bumps [wayland-protocols-wlr](https://github.com/smithay/wayland-rs) from 0.2.0 to 0.3.6.
- [Release notes](https://github.com/smithay/wayland-rs/releases)
- [Changelog](https://github.com/Smithay/wayland-rs/blob/master/historical_changelog.md)
- [Commits](https://github.com/smithay/wayland-rs/commits)

---
updated-dependencies:
- dependency-name: wayland-protocols-wlr
  dependency-version: 0.3.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 16:00:26 +00:00
dependabot[bot]
9ffdc26b49
build(deps): bump sysinfo from 0.34.1 to 0.34.2
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.34.1 to 0.34.2.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-version: 0.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 15:57:55 +00:00
dependabot[bot]
48b7f034e0
build(deps): bump ctrlc from 3.4.5 to 3.4.6
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.4.5 to 3.4.6.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.4.5...3.4.6)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-version: 3.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 15:53:31 +00:00
dependabot[bot]
4b40a722ee
build(deps): bump tokio from 1.44.1 to 1.44.2
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.44.1 to 1.44.2.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.44.1...tokio-1.44.2)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.44.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 15:53:15 +00:00
f2bc33ec26
Merge pull request #922 from JakeStanger/dependabot/cargo/cargo-7b9612cc80
build(deps): bump the cargo group with 2 updates
2025-04-04 23:01:21 +01:00
dependabot[bot]
fc19f785a9
build(deps): bump the cargo group with 2 updates
Bumps the cargo group with 2 updates: [openssl](https://github.com/sfackler/rust-openssl) and [ring](https://github.com/briansmith/ring).


Updates `openssl` from 0.10.71 to 0.10.72
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.71...openssl-v0.10.72)

Updates `ring` from 0.17.11 to 0.17.14
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.72
  dependency-type: indirect
  dependency-group: cargo
- dependency-name: ring
  dependency-version: 0.17.14
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-04 20:56:13 +00:00
226048a2e3
Merge pull request #920 from JakeStanger/refactor/update-lua
refactor: update `mlua` crate
2025-04-04 20:37:52 +01:00
fc249497aa
refactor: update mlua crate 2025-04-04 20:34:33 +01:00
28a0e0fb99
Merge pull request #846 from JakeStanger/refactor/keys/colpetto
refactor(keys): switch to `colpetto`
2025-04-04 20:06:12 +01:00
ff9ba7b5f5
fix(clipboard): failing to paste in some applications
Fixed by ensuring the internal mime type is always last
2025-04-04 19:32:32 +01:00
5bb1c88c12
refactor(clipboard): switch from nix to rustix 2025-04-01 22:21:02 +01:00
a0231559d0
refactor(keys): switch to colpetto
Switches from the `input` crate to new async `colpetto`
2025-04-01 22:20:46 +01:00
f204b24ba0
Merge pull request #918 from JakeStanger/dependabot/cargo/clap-4.5.34
build(deps): bump clap from 4.5.32 to 4.5.34
2025-04-01 08:28:17 +01:00
9e35f3c404
Merge pull request #919 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-04-01 08:27:13 +01:00
github-actions[bot]
d4ce2de500 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/19de14aaeb869287647d9461cbd389187d8ecdb7?narHash=sha256-x4syUjNUuRblR07nDPeLDP7DpphaBVbUaSoeZkFbGSk%3D' (2025-02-19)
  → 'github:ipetkov/crane/70947c1908108c0c551ddfd73d4f750ff2ea67cd?narHash=sha256-vVOAp9ahvnU%2BfQoKd4SEXB2JG2wbENkpqcwlkIXgUC0%3D' (2025-03-19)
• Updated input 'naersk':
    'github:nix-community/naersk/e5130d37369bfa600144c2424270c96f0ef0e11d?narHash=sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg%3D' (2025-02-17)
  → 'github:nix-community/naersk/a75c0584b0d69de943babc899530e9c70c642b42?narHash=sha256-o9dXcpfXpBm6%2B/SRqcQW0GzRkJymwyEAu5UD5LMDd3s%3D' (2025-03-25)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/3a05eebede89661660945da1f151959900903b6a?narHash=sha256-Ly2fBL1LscV%2BKyCqPRufUBuiw%2BzmWrlJzpWOWbahplg%3D' (2025-02-26)
  → 'github:NixOS/nixpkgs/63158b9cbb6ec93d26255871c447b0f01da81619?narHash=sha256-FurMxmjEEqEMld11eX2vgfAx0Rz0JhoFm8UgxbfCZa8%3D' (2025-03-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/5135c59491985879812717f4c9fea69604e7f26f?narHash=sha256-Vr3Qi346M%2B8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic%3D' (2025-02-26)
  → 'github:nixos/nixpkgs/52faf482a3889b7619003c0daec593a1912fddc1?narHash=sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om%2BD4UnDhlDW9BE%3D' (2025-03-30)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/b4270835bf43c6f80285adac6f66a26d83f0f277?narHash=sha256-4dF%2B%2BMXIXna/AwlZWDKr7bgUmY4xoEwvkF1GewjNrt0%3D' (2025-02-28)
  → 'github:oxalica/rust-overlay/011de3c895927300651d9c2cb8e062adf17aa665?narHash=sha256-OBcNE%2B2/TD1AMgq8HKMotSQF8ZPJEFGZdRoBJ7t/HIc%3D' (2025-03-31)
2025-04-01 00:17:35 +00:00
dependabot[bot]
40515f707b
build(deps): bump clap from 4.5.32 to 4.5.34
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.34.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.34)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 18:20:20 +00:00
eef4918b4c
Merge pull request #917 from JakeStanger/dependabot/cargo/reqwest-0.12.15
build(deps): bump reqwest from 0.12.14 to 0.12.15
2025-03-31 19:19:49 +01:00
3e08a2b056
Merge pull request #916 from JakeStanger/dependabot/cargo/sysinfo-0.34.1
build(deps): bump sysinfo from 0.33.1 to 0.34.1
2025-03-31 19:19:00 +01:00
ab912c1d06
Merge pull request #915 from JakeStanger/dependabot/cargo/clap_complete-4.5.47
build(deps): bump clap_complete from 4.5.46 to 4.5.47
2025-03-31 19:17:46 +01:00
dependabot[bot]
b82fcbf36c
build(deps): bump reqwest from 0.12.14 to 0.12.15
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.14 to 0.12.15.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.14...v0.12.15)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 14:43:52 +00:00
dependabot[bot]
58e7bd9719
build(deps): bump sysinfo from 0.33.1 to 0.34.1
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.33.1 to 0.34.1.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 14:43:39 +00:00
dependabot[bot]
d17875feea
build(deps): bump clap_complete from 4.5.46 to 4.5.47
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.46 to 4.5.47.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.46...clap_complete-v4.5.47)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-31 14:43:27 +00:00
f4f5910f61
docs(gtk4): update clipboard, custom, keyboard, script, sysinfo to be ported 2025-03-28 21:24:10 +00:00
bfb9f04afd
build: fix even more coupling issues with keyboard/workspace/sway
damn it
2025-03-28 20:51:26 +00:00
557ef4c898
build: fix more coupling issues with keyboard/workspace/sway 2025-03-28 20:47:26 +00:00
caa288656c
docs(gtk4): add text format status 2025-03-28 20:25:07 +00:00
41d29d84c8
docs(gtk4): update notifications to ported 2025-03-27 14:34:16 +00:00
3e5fcdc86c
docs: add info on gtk 4 port 2025-03-26 23:35:39 +00:00
355cf39446
Merge pull request #912 from JakeStanger/build/feature-flags
build: add feature flags for `custom`, `label`, `script` modules
2025-03-24 22:17:40 +00:00
15177d707e
build: add feature flags for custom, label, script modules 2025-03-24 22:11:24 +00:00
MapoMagpie
3320cf27b2
fix(workspaces): niri workspaces not respecting added sort (#911)
* refactor: sort niri workspaces before init

* chore: comment for ordered_niri_workspace changing
2025-03-24 14:16:23 +00:00
66bdac52d6
Merge pull request #910 from JakeStanger/build/workspace-keyboard
build: decouple `workspace` and `keyboard` modules
2025-03-24 12:26:50 +00:00
a4eb149816
build: decouple workspace and keyboard modules
Fixes #862
2025-03-24 12:22:02 +00:00
157e34ceb8
Merge pull request #909 from JakeStanger/fix/custom-button-script
fix(custom): buttons lock up on long running commands
2025-03-24 12:06:02 +00:00
e8da00b850
fix(custom): buttons lock up on long running commands
Fixes #899
2025-03-24 12:01:59 +00:00
29e00b4f6b
Merge pull request #908 from JakeStanger/chore/volume-logging
chore: add trace logging to volume module
2025-03-24 11:54:45 +00:00
76ca6115a3
chore: add trace logging to volume module 2025-03-24 11:51:26 +00:00
5a7ad5675d
Merge pull request #907 from JakeStanger/feat/widget-rotation
feat: fully implement orientation/justify options
2025-03-22 21:47:43 +00:00
c20feb77b7
feat: fully implement orientation/justify options
Adds `orientation` and `justify` options to all modules and custom
widgets where it makes sense to do so.

Any modules without support document this. Widgets fully document the
options inline where present for now.

Resolves #296
2025-03-22 19:24:41 +00:00
0855039bce
Merge pull request #905 from JakeStanger/dependabot/cargo/indexmap-2.8.0
build(deps): bump indexmap from 2.7.1 to 2.8.0
2025-03-17 22:24:35 +00:00
4ebcc3fced
Merge pull request #904 from JakeStanger/dependabot/cargo/reqwest-0.12.14
build(deps): bump reqwest from 0.12.12 to 0.12.14
2025-03-17 22:23:17 +00:00
0b5c495bc2
Merge pull request #903 from JakeStanger/dependabot/cargo/tokio-1.44.1
build(deps): bump tokio from 1.44.0 to 1.44.1
2025-03-17 22:22:37 +00:00
d7335b8286
Merge pull request #902 from JakeStanger/dependabot/cargo/clap-4.5.32
build(deps): bump clap from 4.5.31 to 4.5.32
2025-03-17 22:21:26 +00:00
0cd62d8ddd
Merge pull request #901 from JakeStanger/dependabot/cargo/libc-0.2.171
build(deps): bump libc from 0.2.170 to 0.2.171
2025-03-17 22:21:17 +00:00
dependabot[bot]
82094e15da
build(deps): bump indexmap from 2.7.1 to 2.8.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.1 to 2.8.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.1...2.8.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 14:43:18 +00:00
dependabot[bot]
770bfa5494
build(deps): bump reqwest from 0.12.12 to 0.12.14
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.12 to 0.12.14.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/v0.12.14/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.12...v0.12.14)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 14:43:03 +00:00
dependabot[bot]
9969e42201
build(deps): bump tokio from 1.44.0 to 1.44.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.44.0 to 1.44.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.44.0...tokio-1.44.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 14:42:50 +00:00
dependabot[bot]
da9a78bcae
build(deps): bump clap from 4.5.31 to 4.5.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.31 to 4.5.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.31...clap_complete-v4.5.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 14:42:40 +00:00
dependabot[bot]
ea07f85ba2
build(deps): bump libc from 0.2.170 to 0.2.171
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.170 to 0.2.171.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.171/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.170...0.2.171)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 14:42:29 +00:00
9c7a562469
docs(tray): correct to direction instead of orientation
Fixes #886
2025-03-10 17:30:32 +00:00
a503cabc18
Merge pull request #892 from JakeStanger/dependabot/cargo/serde-1.0.219
build(deps): bump serde from 1.0.218 to 1.0.219
2025-03-10 16:53:32 +00:00
dependabot[bot]
a242ee4de1
build(deps): bump serde from 1.0.218 to 1.0.219
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.218 to 1.0.219.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.218...v1.0.219)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 16:44:36 +00:00
a043724e35
Merge pull request #893 from JakeStanger/dependabot/cargo/serde_json-1.0.140
build(deps): bump serde_json from 1.0.138 to 1.0.140
2025-03-10 16:43:17 +00:00
307b254dde
Merge pull request #891 from JakeStanger/dependabot/cargo/libpulse-binding-2.29.0
build(deps): bump libpulse-binding from 2.28.2 to 2.29.0
2025-03-10 16:34:49 +00:00
dependabot[bot]
84d600f377
build(deps): bump serde_json from 1.0.138 to 1.0.140
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.138 to 1.0.140.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.138...v1.0.140)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 16:28:35 +00:00
27844a7a89
Merge pull request #894 from JakeStanger/dependabot/cargo/clap-4.5.31
build(deps): bump clap from 4.5.29 to 4.5.31
2025-03-10 16:27:20 +00:00
5ea217f7ed
Merge pull request #895 from JakeStanger/dependabot/cargo/tokio-1.44.0
build(deps): bump tokio from 1.43.0 to 1.44.0
2025-03-10 16:25:59 +00:00
dependabot[bot]
5f0d5f53cb
build(deps): bump tokio from 1.43.0 to 1.44.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.43.0 to 1.44.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.43.0...tokio-1.44.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 14:55:23 +00:00
dependabot[bot]
ed470a5993
build(deps): bump clap from 4.5.29 to 4.5.31
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.29 to 4.5.31.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.29...v4.5.31)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 14:55:12 +00:00
dependabot[bot]
2870a18085
build(deps): bump libpulse-binding from 2.28.2 to 2.29.0
Bumps [libpulse-binding](https://github.com/jnqnfe/pulse-binding-rust) from 2.28.2 to 2.29.0.
- [Commits](https://github.com/jnqnfe/pulse-binding-rust/compare/v2.28.2...v2.29.0)

---
updated-dependencies:
- dependency-name: libpulse-binding
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 14:54:35 +00:00
00e5ac8191
Merge pull request #872 from JakeStanger/refactor/rust-2024
refactor: update to rust 2024, fix strict clippy warnings
2025-03-03 21:32:22 +00:00
202c19efd4
refactor: update to rust 2024, fix strict clippy warnings 2025-03-03 21:28:29 +00:00
79c917857c
Merge pull request #874 from JakeStanger/feat/sysinfo-ironvar
feat(sysinfo): expose values as ironvars
2025-03-03 21:23:21 +00:00
934b4584fc
Merge pull request #884 from JakeStanger/dependabot/cargo/libc-0.2.170
build(deps): bump libc from 0.2.169 to 0.2.170
2025-03-03 16:06:20 +00:00
d061ee904a
Merge pull request #883 from JakeStanger/dependabot/cargo/serde-1.0.218
build(deps): bump serde from 1.0.217 to 1.0.218
2025-03-03 16:04:27 +00:00
c190e1de13
Merge pull request #882 from JakeStanger/dependabot/cargo/schemars-0.8.22
build(deps): bump schemars from 0.8.21 to 0.8.22
2025-03-03 15:59:33 +00:00
dependabot[bot]
a951212cea
build(deps): bump serde from 1.0.217 to 1.0.218
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.217 to 1.0.218.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.217...v1.0.218)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 15:31:26 +00:00
dependabot[bot]
aeedc8190d
build(deps): bump schemars from 0.8.21 to 0.8.22
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.21 to 0.8.22.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.21...v0.8.22)

---
updated-dependencies:
- dependency-name: schemars
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 15:31:23 +00:00
84eb5628f4
Merge pull request #881 from JakeStanger/dependabot/cargo/chrono-0.4.40
build(deps): bump chrono from 0.4.39 to 0.4.40
2025-03-03 15:30:47 +00:00
61b0e3b3a5
Merge pull request #880 from JakeStanger/dependabot/cargo/clap_complete-4.5.46
build(deps): bump clap_complete from 4.5.44 to 4.5.46
2025-03-03 15:30:13 +00:00
dependabot[bot]
e340f37799
build(deps): bump libc from 0.2.169 to 0.2.170
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.169 to 0.2.170.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.170/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.169...0.2.170)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 14:11:05 +00:00
dependabot[bot]
c4964acb63
build(deps): bump chrono from 0.4.39 to 0.4.40
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.39 to 0.4.40.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.39...v0.4.40)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 14:10:30 +00:00
dependabot[bot]
19faecd3a2
build(deps): bump clap_complete from 4.5.44 to 4.5.46
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.44 to 4.5.46.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.44...clap_complete-v4.5.46)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-03 14:10:18 +00:00
b90cf36fbf
Merge pull request #877 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-03-01 10:43:49 +00:00
github-actions[bot]
2a5c8366e4 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/6fe74265bbb6d016d663b1091f015e2976c4a527?narHash=sha256-ivVXYaYlShxYoKfSo5%2By5930qMKKJ8CLcAoIBPQfJ6s%3D' (2025-01-24)
  → 'github:ipetkov/crane/19de14aaeb869287647d9461cbd389187d8ecdb7?narHash=sha256-x4syUjNUuRblR07nDPeLDP7DpphaBVbUaSoeZkFbGSk%3D' (2025-02-19)
• Updated input 'naersk':
    'github:nix-community/naersk/0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce?narHash=sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI%3D' (2025-01-09)
  → 'github:nix-community/naersk/e5130d37369bfa600144c2424270c96f0ef0e11d?narHash=sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg%3D' (2025-02-17)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/9189ac18287c599860e878e905da550aa6dec1cd?narHash=sha256-AYvaFBzt8dU0fcSK2jKD0Vg23K2eIRxfsVXIPCW9a0E%3D' (2025-01-31)
  → 'github:NixOS/nixpkgs/3a05eebede89661660945da1f151959900903b6a?narHash=sha256-Ly2fBL1LscV%2BKyCqPRufUBuiw%2BzmWrlJzpWOWbahplg%3D' (2025-02-26)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/9d3ae807ebd2981d593cddd0080856873139aa40?narHash=sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9%2BWC4%3D' (2025-01-29)
  → 'github:nixos/nixpkgs/5135c59491985879812717f4c9fea69604e7f26f?narHash=sha256-Vr3Qi346M%2B8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic%3D' (2025-02-26)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/b031b584125d33d23a0182f91ddbaf3ab4880236?narHash=sha256-YKOHUmc0Clm4tMV8grnxYL4IIwtjTayoq/3nqk0QM7k%3D' (2025-01-31)
  → 'github:oxalica/rust-overlay/b4270835bf43c6f80285adac6f66a26d83f0f277?narHash=sha256-4dF%2B%2BMXIXna/AwlZWDKr7bgUmY4xoEwvkF1GewjNrt0%3D' (2025-02-28)
2025-03-01 00:15:53 +00:00
b83f26cb1a
feat(sysinfo): expose values as ironvars 2025-02-23 16:51:30 +00:00
9b67719cac
Merge pull request #873 from JakeStanger/feat/launcher-paginate
Launcher pagination and label truncation options
2025-02-22 13:03:35 +00:00
ba5ec8015d
feat(label): add truncate to module and custom widget 2025-02-22 12:59:00 +00:00
183ca402d4
feat(launcher): pagination controls when item count is reached
Resolves #633
2025-02-22 12:59:00 +00:00
5049226289
Merge pull request #871 from JakeStanger/feat/niri-workspaces
feat(workspaces): niri support
2025-02-21 16:18:23 +00:00
Anant Sharma
02a8ddabf0
feat(workspaces): niri support
Co-authored-by: Jake Stanger <mail@jstanger.dev>
2025-02-21 16:12:05 +00:00
57bfab1dcc
Merge pull request #870 from JakeStanger/dependabot/cargo/clap-4.5.29
build(deps): bump clap from 4.5.28 to 4.5.29
2025-02-17 15:34:02 +00:00
dependabot[bot]
6e549d406f
build(deps): bump clap from 4.5.28 to 4.5.29
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.28 to 4.5.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.28...clap_complete-v4.5.29)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 15:19:10 +00:00
quietvoid
551e1ce691
fix(keyboard): panic when layout update channel lags behind (#869)
Fixes #866
2025-02-16 23:01:21 +00:00
452e85f5f6
build: update swayipc 2025-02-15 23:35:32 +00:00
f39c4c430e
refactor: fix clippy warnings 2025-02-15 23:18:57 +00:00
1d29d61628
fix: exit 0 on ipc connection error
Will now return code 3

Fixes #868
2025-02-15 23:16:05 +00:00
7c7b6c7323
Merge pull request #831 from JakeStanger/feat/sysinfo-overhaul
feat(sysinfo): overhaul to add aggregate/unit/formatting support
2025-02-15 23:10:51 +00:00
01de0ac6f5
feat(sysinfo): overhaul to add aggregate/unit/formatting support
This completely reworks the sysinfo module to add support for aggregate functions, better support for working with individual devices, the ability to specify units, and some string formatting support.

Several new tokens have also been added, and performance should be marginally improved.

BREAKING CHANGE: Use of the `sys_info` module in your config will need to be updated to use the new token format. See the wiki page for more info.
2025-02-15 22:58:17 +00:00
49ab7e0c7b
Merge pull request #837 from BowDown097/master
feat: icon overrides
2025-02-15 22:43:46 +00:00
2e59bc9b27
Merge pull request #865 from JakeStanger/refactor/zbus-5
refactor: upgrade to zbus v5
2025-02-15 22:41:36 +00:00
39153e04b3
Merge pull request #867 from JakeStanger/dependabot/cargo/clap-4.5.28
build(deps): bump clap from 4.5.27 to 4.5.28
2025-02-12 11:41:39 +00:00
dependabot[bot]
b5b1f3cff7
build(deps): bump clap from 4.5.27 to 4.5.28
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.27 to 4.5.28.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.27...clap_complete-v4.5.28)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 15:06:35 +00:00
BowDown097
c2beda852d refactor: ensure one copy of icon_overrides 2025-02-08 04:45:35 -08:00
63f5954837
refactor: upgrade to zbus v5
Also drops the deprecated `upower-dbus` crate
2025-02-08 01:56:01 +00:00
Merlijn
75375aa341
fix(clipboard): possible deadlock when copying (#863)
* Change clipboard calloop event loop with blocking read to tokio async readers.

Co-authored-by: Ridan Vandenbergh <ridanvandenbergh@gmail.com>

* cargo fmt

---------

Co-authored-by: Ridan Vandenbergh <ridanvandenbergh@gmail.com>
2025-02-06 16:29:33 +00:00
BowDown097
2fd49e0f3a refactor: address requested changes (3) 2025-02-05 09:11:28 -08:00
BowDown097
d5e4e08863 refactor: address requested changes (2) 2025-02-05 07:40:54 -08:00
68ccc7ac4d
Merge pull request #861 from JakeStanger/fix/volume-markup
feat(volume): add pango support to button
2025-02-04 19:18:56 +00:00
BowDown097
40d449e011 refactor: address requested changes 2025-02-04 09:48:10 -08:00
814fbc2d8f
docs(keyboard): add missing show_layout option 2025-02-04 15:00:18 +00:00
5e7e741842
feat(volume): add pango support to button
Resolves #852
2025-02-03 22:48:25 +00:00
kuzy000
03e6f10141
feat(keyboard): ability to display and switch kb layout (#836)
This extends the existing `keys` module to be able to show the current keyboard layout, and cycle between layouts (using the `next` command) by clicking. The `keys` module has been renamed to `keyboard` to more accurately reflect its extended featureset.
2025-02-03 21:19:30 +00:00
ee19176a2c
Merge pull request #860 from JakeStanger/dependabot/cargo/cargo-2c68f0befb
build(deps): bump openssl from 0.10.66 to 0.10.70 in the cargo group
2025-02-03 19:21:11 +00:00
dependabot[bot]
2ba97bbb5c
build(deps): bump openssl from 0.10.66 to 0.10.70 in the cargo group
Bumps the cargo group with 1 update: [openssl](https://github.com/sfackler/rust-openssl).


Updates `openssl` from 0.10.66 to 0.10.70
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.66...openssl-v0.10.70)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 18:46:22 +00:00
7288f5fb80
Merge pull request #859 from JakeStanger/dependabot/cargo/clap_complete-4.5.44
build(deps): bump clap_complete from 4.5.42 to 4.5.44
2025-02-03 16:22:10 +00:00
dependabot[bot]
fa02fdff64
build(deps): bump clap_complete from 4.5.42 to 4.5.44
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.42 to 4.5.44.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.42...clap_complete-v4.5.44)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 15:39:11 +00:00
e129e5f9b2
Merge pull request #858 from JakeStanger/dependabot/cargo/serde_json-1.0.138
build(deps): bump serde_json from 1.0.137 to 1.0.138
2025-02-03 15:37:57 +00:00
dependabot[bot]
48627a687b
build(deps): bump serde_json from 1.0.137 to 1.0.138
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.137 to 1.0.138.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.137...v1.0.138)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 14:34:58 +00:00
dd36891f5b
Merge pull request #856 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-02-01 00:43:26 +00:00
github-actions[bot]
112bfad6e3 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/72e2d02dbac80c8c86bf6bf3e785536acf8ee926?narHash=sha256-3aH/0Y6ajIlfy7j52FGZ%2Bs4icVX0oHhqBzRdlOeztqg%3D' (2024-12-21)
  → 'github:ipetkov/crane/6fe74265bbb6d016d663b1091f015e2976c4a527?narHash=sha256-ivVXYaYlShxYoKfSo5%2By5930qMKKJ8CLcAoIBPQfJ6s%3D' (2025-01-24)
• Updated input 'naersk':
    'github:nix-community/naersk/378614f37a6bee5a3f2ef4f825a73d948d3ae921?narHash=sha256-a4WZp1xQkrnA4BbnKrzJNr%2BdYoQr5Xneh2syJoddFyE%3D' (2024-12-04)
  → 'github:nix-community/naersk/0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce?narHash=sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI%3D' (2025-01-09)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/0e82ab234249d8eee3e8c91437802b32c74bb3fd?narHash=sha256-zExSA1i/b%2B1NMRhGGLtNfFGXgLtgo%2BdcuzHzaWA6w3Q%3D' (2024-12-30)
  → 'github:NixOS/nixpkgs/9189ac18287c599860e878e905da550aa6dec1cd?narHash=sha256-AYvaFBzt8dU0fcSK2jKD0Vg23K2eIRxfsVXIPCW9a0E%3D' (2025-01-31)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29)
  → 'github:nixos/nixpkgs/9d3ae807ebd2981d593cddd0080856873139aa40?narHash=sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9%2BWC4%3D' (2025-01-29)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/d199142e84bfaae476ffb4e09a70879d7918784d?narHash=sha256-rsjojgfPUf9tWuMXuuo2KAIoUZ49XGZQJSjFGOO8Cq4%3D' (2024-12-31)
  → 'github:oxalica/rust-overlay/b031b584125d33d23a0182f91ddbaf3ab4880236?narHash=sha256-YKOHUmc0Clm4tMV8grnxYL4IIwtjTayoq/3nqk0QM7k%3D' (2025-01-31)
2025-02-01 00:15:03 +00:00
141f03f6ca
Merge pull request #853 from JakeStanger/dependabot/cargo/clap-4.5.27
build(deps): bump clap from 4.5.26 to 4.5.27
2025-01-27 16:04:03 +00:00
dependabot[bot]
a695b875cc
build(deps): bump clap from 4.5.26 to 4.5.27
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.26 to 4.5.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.26...clap_complete-v4.5.27)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 14:27:25 +00:00
dbdff91c3c
Merge pull request #850 from JakeStanger/dependabot/cargo/serde_json-1.0.137
build(deps): bump serde_json from 1.0.135 to 1.0.137
2025-01-20 22:27:55 +00:00
0ba4a9b1ba
Merge pull request #851 from JakeStanger/dependabot/cargo/indexmap-2.7.1
build(deps): bump indexmap from 2.7.0 to 2.7.1
2025-01-20 15:59:28 +00:00
dependabot[bot]
6b9861bad2
build(deps): bump indexmap from 2.7.0 to 2.7.1
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.0 to 2.7.1.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.0...2.7.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 15:29:49 +00:00
9c2d77c0f5
Merge pull request #849 from JakeStanger/dependabot/cargo/dirs-6.0.0
build(deps): bump dirs from 5.0.1 to 6.0.0
2025-01-20 15:28:38 +00:00
dependabot[bot]
de8e45b9a4
build(deps): bump serde_json from 1.0.135 to 1.0.137
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.135 to 1.0.137.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.135...v1.0.137)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 15:24:35 +00:00
eb5439e411
Merge pull request #847 from JakeStanger/dependabot/cargo/clap-4.5.26
build(deps): bump clap from 4.5.23 to 4.5.26
2025-01-20 15:23:25 +00:00
dependabot[bot]
d2dde7d766
build(deps): bump dirs from 5.0.1 to 6.0.0
Bumps [dirs](https://github.com/soc/dirs-rs) from 5.0.1 to 6.0.0.
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 15:04:08 +00:00
dependabot[bot]
3c44ada6c6
build(deps): bump clap from 4.5.23 to 4.5.26
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.23 to 4.5.26.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.23...clap_complete-v4.5.26)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 15:03:34 +00:00
BowDown097
d1b4af4710 refactor: make icon overrides bar-level and apply to focused module 2025-01-16 21:39:06 -08:00
11581380ad
Merge pull request #844 from JakeStanger/dependabot/cargo/cargo-0aefbba9fd
build(deps): bump the cargo group with 2 updates
2025-01-14 09:24:44 +00:00
250777f9b3
Merge pull request #842 from JakeStanger/dependabot/cargo/tokio-1.43.0
build(deps): bump tokio from 1.42.0 to 1.43.0
2025-01-14 09:24:21 +00:00
ad07a8a637
Merge pull request #843 from JakeStanger/dependabot/cargo/futures-lite-2.6.0
build(deps): bump futures-lite from 2.5.0 to 2.6.0
2025-01-14 09:23:38 +00:00
a9004382b5
Merge pull request #841 from JakeStanger/dependabot/cargo/clap_complete-4.5.42
build(deps): bump clap_complete from 4.5.40 to 4.5.42
2025-01-13 22:51:51 +00:00
e445b4020d
Merge pull request #840 from JakeStanger/dependabot/cargo/notify-8.0.0
build(deps): bump notify from 7.0.0 to 8.0.0
2025-01-13 22:47:03 +00:00
dependabot[bot]
2da0e9d17c
build(deps): bump the cargo group with 2 updates
Bumps the cargo group with 2 updates: [anstream](https://github.com/rust-cli/anstyle) and [hashbrown](https://github.com/rust-lang/hashbrown).


Updates `anstream` from 0.6.7 to 0.6.18
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.7...anstream-v0.6.18)

Updates `hashbrown` from 0.15.0 to 0.15.2
- [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/hashbrown/commits)

---
updated-dependencies:
- dependency-name: anstream
  dependency-type: indirect
  dependency-group: cargo
- dependency-name: hashbrown
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 22:39:59 +00:00
dependabot[bot]
e4caf2dcc8
build(deps): bump clap_complete from 4.5.40 to 4.5.42
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.40 to 4.5.42.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.40...clap_complete-v4.5.42)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 22:39:42 +00:00
f1279188ee
Merge pull request #839 from JakeStanger/dependabot/cargo/serde_json-1.0.135
build(deps): bump serde_json from 1.0.134 to 1.0.135
2025-01-13 22:38:31 +00:00
dependabot[bot]
42dbd988ce
build(deps): bump futures-lite from 2.5.0 to 2.6.0
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.5.0...v2.6.0)

---
updated-dependencies:
- dependency-name: futures-lite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 14:57:42 +00:00
dependabot[bot]
1631135ac8
build(deps): bump tokio from 1.42.0 to 1.43.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.42.0 to 1.43.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.42.0...tokio-1.43.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 14:57:31 +00:00
dependabot[bot]
545b478db0
build(deps): bump notify from 7.0.0 to 8.0.0
Bumps [notify](https://github.com/notify-rs/notify) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/notify-7.0.0...notify-8.0.0)

---
updated-dependencies:
- dependency-name: notify
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 14:57:02 +00:00
dependabot[bot]
84f70ba4d6
build(deps): bump serde_json from 1.0.134 to 1.0.135
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.134 to 1.0.135.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.134...v1.0.135)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 14:56:51 +00:00
BowDown097
87c680122b feat(launcher): icon overrides 2025-01-10 20:28:05 -08:00
b296726aa4
Merge pull request #835 from JakeStanger/fix/keys-cpu
fix(keys): high cpu usage
2025-01-06 22:19:38 +00:00
e0bae2c0af
fix(keys): high cpu usage
Fixes #834
2025-01-06 22:13:57 +00:00
0b4d868134
Merge pull request #833 from JakeStanger/dependabot/cargo/reqwest-0.12.12
build(deps): bump reqwest from 0.12.11 to 0.12.12
2025-01-06 14:52:30 +00:00
f9443026e5
Merge pull request #832 from JakeStanger/dependabot/cargo/system-tray-0.6.0
build(deps): bump system-tray from 0.5.0 to 0.6.0
2025-01-06 14:52:04 +00:00
dependabot[bot]
ff3def7a7b
build(deps): bump reqwest from 0.12.11 to 0.12.12
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.11 to 0.12.12.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.11...v0.12.12)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 14:28:28 +00:00
dependabot[bot]
5efe423a29
build(deps): bump system-tray from 0.5.0 to 0.6.0
Bumps [system-tray](https://github.com/jakestanger/system-tray) from 0.5.0 to 0.6.0.
- [Commits](https://github.com/jakestanger/system-tray/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: system-tray
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 14:28:09 +00:00
998e5cecc0
Merge pull request #829 from BowDown097/master
feat: justify property for clock and custom label
2025-01-02 15:46:25 +00:00
BowDown097
59b5ddcc69 refactor(clock): move justify above common 2025-01-02 07:01:28 -08:00
BowDown097
27c14d2da8
Merge branch 'JakeStanger:master' into master 2025-01-02 06:40:52 -08:00
db6b46abb0
Merge pull request #827 from JakeStanger/dependabot/cargo/clap_complete-4.5.40
build(deps): bump clap_complete from 4.5.8 to 4.5.40
2025-01-02 09:36:39 +00:00
79ea315295
Merge pull request #828 from JakeStanger/update_flake_lock_action
Update flake.lock
2025-01-02 09:36:16 +00:00
BowDown097
9cb1dfc1b1 refactor: enum/struct formatting 2025-01-01 12:27:33 -08:00
BowDown097
708118d266 feat: justify property for clock and custom label 2025-01-01 12:16:04 -08:00
github-actions[bot]
ff9709601f flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/da87d1af7e4e09fd0271432340a5cadf3eb96005?narHash=sha256-CgEHGXSzUdlRI1MzsZmWUwW8%2B6MKYqtCBIDrD/5H5/o%3D' (2024-11-30)
  → 'github:ipetkov/crane/72e2d02dbac80c8c86bf6bf3e785536acf8ee926?narHash=sha256-3aH/0Y6ajIlfy7j52FGZ%2Bs4icVX0oHhqBzRdlOeztqg%3D' (2024-12-21)
• Updated input 'naersk':
    'github:nix-community/naersk/3fb418eaf352498f6b6c30592e3beb63df42ef11?narHash=sha256-r/xppY958gmZ4oTfLiHN0ZGuQ%2BRSTijDblVgVLFi1mw%3D' (2024-07-23)
  → 'github:nix-community/naersk/378614f37a6bee5a3f2ef4f825a73d948d3ae921?narHash=sha256-a4WZp1xQkrnA4BbnKrzJNr%2BdYoQr5Xneh2syJoddFyE%3D' (2024-12-04)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/4703b8d2c708e13a8cab03d865f90973536dcdf5?narHash=sha256-B5pYT%2BIVaqcrfOekkwKvx/iToDnuQWzc2oyDxzzBDc4%3D' (2024-11-30)
  → 'github:NixOS/nixpkgs/0e82ab234249d8eee3e8c91437802b32c74bb3fd?narHash=sha256-zExSA1i/b%2B1NMRhGGLtNfFGXgLtgo%2BdcuzHzaWA6w3Q%3D' (2024-12-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28)
  → 'github:nixos/nixpkgs/88195a94f390381c6afcdaa933c2f6ff93959cb4?narHash=sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs%3D' (2024-12-29)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/c65e91d4a33abc3bc4a892d3c5b5b378bad64ea1?narHash=sha256-dge02pUSe2QeC/B3PriA0R8eAX%2BEU3aDoXj9FcS3XDw%3D' (2024-11-30)
  → 'github:oxalica/rust-overlay/d199142e84bfaae476ffb4e09a70879d7918784d?narHash=sha256-rsjojgfPUf9tWuMXuuo2KAIoUZ49XGZQJSjFGOO8Cq4%3D' (2024-12-31)
2025-01-01 00:15:48 +00:00
dependabot[bot]
087a1845cf
build(deps): bump clap_complete from 4.5.8 to 4.5.40
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.8 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.8...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 14:20:06 +00:00
5136637752
refactor: fix some strict clippy warnings 2024-12-29 00:40:12 +00:00
87a6523367
build: remove unused deps 2024-12-29 00:35:32 +00:00
b2194d01c9
Merge pull request #683 from donovanglover/feat/man-pages-shell-completions
feat: add shell completions
2024-12-29 00:24:54 +00:00
Donovan Glover
4381fd505d
feat: add shell completions
Auto-generated with clap.
2024-12-29 00:19:02 +00:00
df55cdfa9f
Merge pull request #748 from JakeStanger/fix/workspaces
Workspaces module rewrite
2024-12-28 15:07:19 +00:00
03136e7c70
feat(workspaces): new sorting options
Renames existing `alphanumeric` sorting method to `label` and adds a new method called `name` which uses the real workspace name, akin to behaviour before #799.

BREAKING CHANGE: The workspace `sort` config option valid values have changed. Where `alphanumeric` is explicitly set, this will need changing to one of `label` or `name`.
2024-12-28 14:01:30 +00:00
fa6f27d4b9
fix(workspaces): rewrite module to fix several small issues
Rewrites the module code to be better structured, in a similar pattern to the launcher. The code is now more robust and more maintainable, yay!

Fixes #705

Fixes an issue with moving favourite workspaces.

Fixes an issue with workspace visible state being incorrect.

Fixes an issue where the `inactive` class looked at hidden instead of closed favourites.
2024-12-28 13:38:03 +00:00
8cdbe7e083
refactor(hyprland): improve logging 2024-12-28 12:58:29 +00:00
42e18da126
chore: comment out doc comment for unused func 2024-12-28 12:58:05 +00:00
2840ff66db
chore: update lockfile 2024-12-28 12:57:50 +00:00
1099a111f5
Merge pull request #824 from JakeStanger/docs/fix-keys
docs: add keys page to sidebar
2024-12-28 01:14:48 +00:00
63a9a427bb
docs: add keys page to sidebar 2024-12-28 01:14:08 +00:00
67426bd8e2
Merge pull request #783 from JakeStanger/feat/libinput
feat: libinput `keys` module
2024-12-27 19:49:36 +00:00
ccfe73f6a7
feat: libinput keys module
Adds a new module which shows the status of toggle mod keys (capslock, num lock, scroll lock).

Resolves #700
2024-12-27 19:44:25 +00:00
353ee92d48
Merge pull request #821 from JakeStanger/dependabot/cargo/system-tray-0.5.0
build(deps): bump system-tray from 0.4.0 to 0.5.0
2024-12-24 12:53:39 +00:00
178f2cc850
Merge pull request #822 from JakeStanger/dependabot/cargo/serde_json-1.0.134
build(deps): bump serde_json from 1.0.133 to 1.0.134
2024-12-24 12:53:15 +00:00
dependabot[bot]
4625db77d4
build(deps): bump serde_json from 1.0.133 to 1.0.134
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.133 to 1.0.134.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.133...v1.0.134)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 14:13:05 +00:00
dependabot[bot]
4995c15f3b
build(deps): bump system-tray from 0.4.0 to 0.5.0
Bumps [system-tray](https://github.com/jakestanger/system-tray) from 0.4.0 to 0.5.0.
- [Commits](https://github.com/jakestanger/system-tray/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: system-tray
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 14:12:38 +00:00
cfaa99830f
Merge pull request #814 from BowDown097/master
feat(launcher): open new window on middle-click
2024-12-18 22:19:30 +00:00
4e6db91090
Merge pull request #819 from JakeStanger/dependabot/cargo/time-0.3.37
build(deps): bump time from 0.3.36 to 0.3.37
2024-12-16 16:34:00 +00:00
9bf4de05b6
Merge pull request #818 from JakeStanger/dependabot/cargo/serde-1.0.216
build(deps): bump serde from 1.0.215 to 1.0.216
2024-12-16 16:33:16 +00:00
58ffb76a4d
Merge pull request #817 from JakeStanger/dependabot/cargo/gtk-0.18.2
build(deps): bump gtk from 0.18.1 to 0.18.2
2024-12-16 16:32:15 +00:00
dependabot[bot]
a5dbf69137
build(deps): bump time from 0.3.36 to 0.3.37
Bumps [time](https://github.com/time-rs/time) from 0.3.36 to 0.3.37.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.36...v0.3.37)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 14:47:05 +00:00
dependabot[bot]
0d57295fc3
build(deps): bump serde from 1.0.215 to 1.0.216
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.215 to 1.0.216.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.215...v1.0.216)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 14:46:48 +00:00
dependabot[bot]
2f12ee35c4
build(deps): bump gtk from 0.18.1 to 0.18.2
Bumps [gtk](https://github.com/gtk-rs/gtk3-rs) from 0.18.1 to 0.18.2.
- [Release notes](https://github.com/gtk-rs/gtk3-rs/releases)
- [Commits](https://github.com/gtk-rs/gtk3-rs/commits)

---
updated-dependencies:
- dependency-name: gtk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 14:46:14 +00:00
BowDown097
7bff8c0a7f
docs(launcher): note middle click functionality 2024-12-15 12:44:36 -08:00
BowDown097
7f71a383c4
feat(launcher): open new window on middle-click 2024-12-15 07:00:42 -08:00
8a06ec04b0
Merge pull request #810 from noah427/master
Remove nixpkgs override for crane
2024-12-11 15:24:17 +00:00
noah427
53c98cebad chore: remove nixpkgs override for crane 2024-12-11 10:18:08 -05:00
2a869480eb
Merge pull request #807 from JakeStanger/dependabot/cargo/tracing-subscriber-0.3.19
build(deps): bump tracing-subscriber from 0.3.18 to 0.3.19
2024-12-09 14:27:51 +00:00
10bd80f1ff
Merge pull request #806 from JakeStanger/dependabot/cargo/tokio-1.42.0
build(deps): bump tokio from 1.41.1 to 1.42.0
2024-12-09 14:27:15 +00:00
8c372ba147
Merge pull request #805 from JakeStanger/dependabot/cargo/gtk-layer-shell-0.8.2
build(deps): bump gtk-layer-shell from 0.8.1 to 0.8.2
2024-12-09 14:26:11 +00:00
34fe812a4e
Merge pull request #804 from JakeStanger/dependabot/cargo/clap-4.5.23
build(deps): bump clap from 4.5.21 to 4.5.23
2024-12-09 14:24:25 +00:00
819911728a
Merge pull request #803 from JakeStanger/dependabot/cargo/chrono-0.4.39
build(deps): bump chrono from 0.4.38 to 0.4.39
2024-12-09 14:22:19 +00:00
dependabot[bot]
7d77bce5f8
build(deps): bump tracing-subscriber from 0.3.18 to 0.3.19
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.18 to 0.3.19.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.18...tracing-subscriber-0.3.19)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 14:18:38 +00:00
dependabot[bot]
414386839c
build(deps): bump tokio from 1.41.1 to 1.42.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.41.1 to 1.42.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.41.1...tokio-1.42.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 14:18:26 +00:00
dependabot[bot]
95b19ea0a7
build(deps): bump gtk-layer-shell from 0.8.1 to 0.8.2
Bumps [gtk-layer-shell](https://github.com/pentamassiv/gtk-layer-shell-gir) from 0.8.1 to 0.8.2.
- [Commits](https://github.com/pentamassiv/gtk-layer-shell-gir/commits)

---
updated-dependencies:
- dependency-name: gtk-layer-shell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 14:18:15 +00:00
dependabot[bot]
fb8fa8b683
build(deps): bump clap from 4.5.21 to 4.5.23
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.21 to 4.5.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.21...clap_complete-v4.5.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 14:18:05 +00:00
dependabot[bot]
0a89f4adb3
build(deps): bump chrono from 0.4.38 to 0.4.39
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.38 to 0.4.39.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.38...v0.4.39)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 14:17:55 +00:00
quietvoid
9f7c3918c4
feat(sway): support workspace rename events (#799)
And reorder based on label with fallback to widget name
2024-12-05 12:04:22 +00:00
13c2f8fa8b
Merge pull request #801 from JakeStanger/dependabot/cargo/cargo-657ccba0a8
build(deps): bump anstream from 0.6.7 to 0.6.18 in the cargo group
2024-12-04 23:45:36 +00:00
dependabot[bot]
7a846b0bb1
build(deps): bump anstream from 0.6.7 to 0.6.18 in the cargo group
Bumps the cargo group with 1 update: [anstream](https://github.com/rust-cli/anstyle).


Updates `anstream` from 0.6.7 to 0.6.18
- [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.6.7...anstream-v0.6.18)

---
updated-dependencies:
- dependency-name: anstream
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-04 23:31:30 +00:00
557005df7e
Merge pull request #800 from JakeStanger/dependabot/cargo/cargo-a5df8d2751
build(deps): bump hashbrown from 0.15.0 to 0.15.2 in the cargo group
2024-12-04 23:30:28 +00:00
dependabot[bot]
935cbb7e75
build(deps): bump hashbrown from 0.15.0 to 0.15.2 in the cargo group
Bumps the cargo group with 1 update: [hashbrown](https://github.com/rust-lang/hashbrown).


Updates `hashbrown` from 0.15.0 to 0.15.2
- [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/hashbrown/commits)

---
updated-dependencies:
- dependency-name: hashbrown
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-04 18:30:49 +00:00
e0654f11ac
Merge pull request #797 from JakeStanger/dependabot/cargo/tracing-error-0.2.1
build(deps): bump tracing-error from 0.2.0 to 0.2.1
2024-12-04 09:40:47 +00:00
84e21652a2
Merge pull request #796 from JakeStanger/dependabot/cargo/indexmap-2.7.0
build(deps): bump indexmap from 2.6.0 to 2.7.0
2024-12-04 09:39:55 +00:00
249f11ac18
Merge pull request #795 from JakeStanger/dependabot/cargo/libpulse-binding-2.28.2
build(deps): bump libpulse-binding from 2.28.1 to 2.28.2
2024-12-04 09:39:26 +00:00
dependabot[bot]
2338f29940
build(deps): bump tracing-error from 0.2.0 to 0.2.1
Bumps [tracing-error](https://github.com/tokio-rs/tracing) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-error-0.2.0...tracing-error-0.2.1)

---
updated-dependencies:
- dependency-name: tracing-error
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-04 09:37:09 +00:00
5018c25424
Merge pull request #794 from JakeStanger/dependabot/cargo/tracing-0.1.41
build(deps): bump tracing from 0.1.40 to 0.1.41
2024-12-04 09:35:56 +00:00
dependabot[bot]
679d4ee91a
build(deps): bump indexmap from 2.6.0 to 2.7.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.6.0 to 2.7.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.6.0...2.7.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 16:31:25 +00:00
dependabot[bot]
7c2bcdecd1
build(deps): bump libpulse-binding from 2.28.1 to 2.28.2
Bumps [libpulse-binding](https://github.com/jnqnfe/pulse-binding-rust) from 2.28.1 to 2.28.2.
- [Commits](https://github.com/jnqnfe/pulse-binding-rust/commits)

---
updated-dependencies:
- dependency-name: libpulse-binding
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 16:31:15 +00:00
dependabot[bot]
f8dbcf43c7
build(deps): bump tracing from 0.1.40 to 0.1.41
Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.40 to 0.1.41.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.40...tracing-0.1.41)

---
updated-dependencies:
- dependency-name: tracing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 16:31:00 +00:00
58c878a3cc
Merge pull request #792 from JakeStanger/update_flake_lock_action
Update flake.lock
2024-12-01 10:04:10 +00:00
github-actions[bot]
e711cd9caf flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/498d9f122c413ee1154e8131ace5a35a80d8fa76?narHash=sha256-RMgSVkZ9H03sxC%2BVh4jxtLTCzSjPq18UWpiM0gq6shQ%3D' (2024-10-27)
  → 'github:ipetkov/crane/da87d1af7e4e09fd0271432340a5cadf3eb96005?narHash=sha256-CgEHGXSzUdlRI1MzsZmWUwW8%2B6MKYqtCBIDrD/5H5/o%3D' (2024-11-30)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53?narHash=sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z%2Bw%3D' (2024-10-30)
  → 'github:NixOS/nixpkgs/4703b8d2c708e13a8cab03d865f90973536dcdf5?narHash=sha256-B5pYT%2BIVaqcrfOekkwKvx/iToDnuQWzc2oyDxzzBDc4%3D' (2024-11-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd?narHash=sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU%3D' (2024-10-29)
  → 'github:nixos/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370?narHash=sha256-jNRNr49UiuIwaarqijgdTR2qLPifxsVhlJrKzQ8XUIE%3D' (2024-11-28)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/815d1b3ee71716fc91a7bd149801e1f04d45fbc5?narHash=sha256-RFaeY7EWzXOmAL2IQEACbnrEza3TgD5UQApHR4hGHhY%3D' (2024-10-31)
  → 'github:oxalica/rust-overlay/c65e91d4a33abc3bc4a892d3c5b5b378bad64ea1?narHash=sha256-dge02pUSe2QeC/B3PriA0R8eAX%2BEU3aDoXj9FcS3XDw%3D' (2024-11-30)
2024-12-01 00:18:05 +00:00
a181d4e257 fix(networkmanager, upower): widget not vertical on left/right bars 2024-11-25 11:12:23 +00:00
e7ffc3704c
Merge pull request #769 from pachliopta/minimize
feat: Minimize focused window option for launcher
2024-11-24 21:49:54 +00:00
pachliopta
64b953ce5e
feat(launcher): add option to minimize window if focused 2024-11-24 21:46:45 +00:00
9c13e534b7
Merge pull request #735 from Rodrigodd/feat/urgent
feat: add `.urgent` workspace css class
2024-11-24 21:40:52 +00:00
abf1c1207d
Merge pull request #651 from JakeStanger/feat/launcher-truncate
Launcher truncate options & related refactors
2024-11-24 21:39:49 +00:00
da13b9d500
feat(launcher): truncate and truncate_popup config options 2024-11-24 21:35:38 +00:00
8b05ed526d
refactor: move label truncate function to ext trait
 Conflicts:
	src/gtk_helpers.rs
	src/modules/music/mod.rs
2024-11-24 21:35:31 +00:00
cc6f21ed68
refactor: take reference into image provider 2024-11-24 21:30:54 +00:00
df4bfc83d0
feat(truncate): explicit off mode 2024-11-24 21:30:54 +00:00
39f02a1023
chore: add note to changelog [skip ci] 2024-11-24 16:13:44 +00:00
JakeStanger
5aa81abf05 chore: update CHANGELOG.md for v0.16.1 [skip ci] 2024-11-24 16:09:59 +00:00
f31c1710d6
chore(release): v0.16.1 2024-11-24 16:08:28 +00:00
230dd8b13b
fix(workspaces): clicking currently focused workspace attempts to focus it
Resolves #731
2024-11-19 22:23:57 +00:00
Christian Meissl
ce48fc9d0d
fix(tray): prevent widget buttons from piling up (#788)
each call to set_menu_widget registered a new event handler,
resulting in multiple popups to be created. each popup tried
to issue a grab with the same serial.
prevent this by disconnecting any previously registered handler.
2024-11-19 22:07:52 +00:00
944006f859
build(nix): fix deprecated pkg path warning 2024-11-18 21:27:05 +00:00
e1f3b1bb72
feat: route gtk logging through tracing
Yay consistent logging, especially now the tray uses a library that likes to vomit warnings
2024-11-18 21:25:57 +00:00
5afe5c19b9
Merge pull request #786 from JakeStanger/dependabot/cargo/serde-1.0.215
build(deps): bump serde from 1.0.214 to 1.0.215
2024-11-18 20:54:22 +00:00
dependabot[bot]
31a11ea8e0
build(deps): bump serde from 1.0.214 to 1.0.215
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.214 to 1.0.215.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.214...v1.0.215)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 20:49:07 +00:00
ebcf38f2ea
Merge pull request #785 from JakeStanger/dependabot/cargo/clap-4.5.21
build(deps): bump clap from 4.5.20 to 4.5.21
2024-11-18 20:47:45 +00:00
dependabot[bot]
7538332872
build(deps): bump clap from 4.5.20 to 4.5.21
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.20 to 4.5.21.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.20...clap_complete-v4.5.21)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 20:19:02 +00:00
097b548734
Merge pull request #784 from JakeStanger/dependabot/cargo/serde_json-1.0.133
build(deps): bump serde_json from 1.0.132 to 1.0.133
2024-11-18 20:17:49 +00:00
dependabot[bot]
7db732641b
build(deps): bump serde_json from 1.0.132 to 1.0.133
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.132 to 1.0.133.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.132...v1.0.133)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 14:48:19 +00:00
bdf6b3b1b3
Merge pull request #782 from JakeStanger/fix/tray-fixes2
More tray fixes
2024-11-17 14:52:01 +00:00
f364bb64fb
fix(tray): tray icons not disappearing on close 2024-11-17 14:47:50 +00:00
5aa9f37fe4
fix(tray): menus not attaching to secondary bars 2024-11-17 14:47:42 +00:00
48dc65f60b
chore(tray): remove unneeded png image code 2024-11-17 14:47:15 +00:00
Ridan Vandenbergh
42e25f5ef2
fix(ipc): support querying against duplicate bar names
It is possible/valid to define multiple bars by the same name by setting `name` on the top-level bar object, but not specifying monitors. This updates IPC to support this scenario.

Allow IPC to act on multiple bars by the same name (#777)
2024-11-16 20:48:12 +00:00
e7c56ee09b
Merge pull request #780 from JakeStanger/fix/tray-focus
Fix tray focus issues on Sway
2024-11-16 20:39:16 +00:00
f161429dfc
docs(clock): align table columns 2024-11-16 20:36:28 +00:00
e4e9632caa
fix(tray): menu causing bar to lose focus on sway
BREAKING CHANGE: The `direction` option has been changed to only accept `horizontal` or `vertical`
2024-11-16 20:36:20 +00:00
3f8afa998d
Merge pull request #779 from JakeStanger/refactor/tray-menu
refactor(tray): switch over to `libdbusmenu-gtk3`
2024-11-16 17:38:00 +00:00
30de23dc64
refactor(tray): switch over to libdbusmenu-gtk3
Also adds tooltips
2024-11-16 17:35:29 +00:00
ff3f541183
docs(tray): fix formatting 2024-11-16 17:19:23 +00:00
c04387fa11
ci(clippy): fail on unwrap 2024-11-16 17:19:14 +00:00
a76957216b
Merge pull request #773 from JakeStanger/dependabot/cargo/futures-lite-2.5.0
build(deps): bump futures-lite from 2.4.0 to 2.5.0
2024-11-11 22:28:01 +00:00
4f775c92a1
Merge pull request #772 from JakeStanger/dependabot/cargo/tokio-1.41.1
build(deps): bump tokio from 1.41.0 to 1.41.1
2024-11-11 22:27:18 +00:00
dependabot[bot]
99124a2224
build(deps): bump futures-lite from 2.4.0 to 2.5.0
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.4.0...v2.5.0)

---
updated-dependencies:
- dependency-name: futures-lite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 14:51:00 +00:00
dependabot[bot]
ddaa758df0
build(deps): bump tokio from 1.41.0 to 1.41.1
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.41.0 to 1.41.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.41.0...tokio-1.41.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 14:50:49 +00:00
05530cf776
fix: regression caused by #652 2024-11-10 17:35:47 +00:00
ed338e948c
Merge pull request #766 from JakeStanger/fix/tray-fixes
A whole load of tray fixes 🎉
2024-11-09 19:08:43 +00:00
f1e87830ed
chore: tidy main 2024-11-08 01:40:25 +00:00
cf38c37fe3
refactor(tray): move some debug logging to trace logging 2024-11-08 01:40:25 +00:00
486beff8a5
refactor: put in basic placeholders for menu icon diffs 2024-11-08 01:40:25 +00:00
662ddb6946
feat(tray): image support in menu items 2024-11-08 01:40:24 +00:00
fb1799531b
fix(tray): image updates lag 1 behind 2024-11-08 01:35:35 +00:00
55c0940e1d
fix(tray): update system-tray dep to bring in a whole load of fixes 2024-11-08 01:35:04 +00:00
e53a9067b0
fix(tray): cannot activate with mixed left/right click
Fixes #513
2024-11-05 13:20:34 +00:00
81c48fecad
fix(clipboard): crash when unsupported image type 2024-11-05 13:15:42 +00:00
3cd2fce333
Merge pull request #765 from JakeStanger/fix/style-reload
fix: not properly redrawing on style reload
2024-11-05 13:10:12 +00:00
80403e3ca9
fix: not properly redrawing on style reload
Fixes #456
2024-11-05 13:06:23 +00:00
66ff849c56
Merge pull request #762 from JakeStanger/dependabot/cargo/reqwest-0.12.9
build(deps): bump reqwest from 0.12.8 to 0.12.9
2024-11-04 16:03:49 +00:00
51bddb0765
Merge pull request #761 from JakeStanger/dependabot/cargo/serde-1.0.214
build(deps): bump serde from 1.0.213 to 1.0.214
2024-11-04 16:02:54 +00:00
3c0003640a
Merge pull request #759 from JakeStanger/dependabot/cargo/futures-lite-2.4.0
build(deps): bump futures-lite from 2.3.0 to 2.4.0
2024-11-04 16:01:06 +00:00
dependabot[bot]
61f55fe517
build(deps): bump serde from 1.0.213 to 1.0.214
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.213 to 1.0.214.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.213...v1.0.214)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 15:56:42 +00:00
dependabot[bot]
2f03b66ba7
build(deps): bump futures-lite from 2.3.0 to 2.4.0
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: futures-lite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 15:56:35 +00:00
8b065fa50f
Revert "build: update workspaces deps"
This reverts commit f06885cea6.
2024-11-04 15:55:22 +00:00
f06885cea6
build: update workspaces deps 2024-11-04 15:30:24 +00:00
dependabot[bot]
acc68eb0b7
build(deps): bump reqwest from 0.12.8 to 0.12.9
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.8 to 0.12.9.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.8...v0.12.9)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 14:11:52 +00:00
3b3d86b812
Merge pull request #758 from JakeStanger/fix/trackpad-scroll
fix: `on_scroll` events broken on touchpad
2024-11-04 13:08:38 +00:00
d87888d173
fix: on_scroll events broken on touchpad
Fixes #652
2024-11-04 13:05:18 +00:00
c7cf896164
Merge pull request #757 from JakeStanger/update_flake_lock_action
Update flake.lock
2024-11-01 08:23:16 +00:00
github-actions[bot]
f993cce5f6 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/5b03654ce046b5167e7b0bccbd8244cb56c16f0e?narHash=sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE%3D' (2024-09-26)
  → 'github:ipetkov/crane/498d9f122c413ee1154e8131ace5a35a80d8fa76?narHash=sha256-RMgSVkZ9H03sxC%2BVh4jxtLTCzSjPq18UWpiM0gq6shQ%3D' (2024-10-27)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/b5b22b42c0d10c7d2463e90a546c394711e3a724?narHash=sha256-uMVkVHL4r3QmlZ1JM%2BUoJwxqa46cgHnIfqGzVlw5ca4%3D' (2024-09-30)
  → 'github:NixOS/nixpkgs/2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53?narHash=sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z%2Bw%3D' (2024-10-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/06cf0e1da4208d3766d898b7fdab6513366d45b9?narHash=sha256-S5kVU7U82LfpEukbn/ihcyNt2%2BEvG7Z5unsKW9H/yFA%3D' (2024-09-29)
  → 'github:nixos/nixpkgs/807e9154dcb16384b1b765ebe9cd2bba2ac287fd?narHash=sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU%3D' (2024-10-29)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/c2099c6c7599ea1980151b8b6247a8f93e1806ee?narHash=sha256-83j/GrHsx8GFUcQofKh%2BPRPz6pz8sxAsZyT/HCNdey8%3D' (2024-09-30)
  → 'github:oxalica/rust-overlay/815d1b3ee71716fc91a7bd149801e1f04d45fbc5?narHash=sha256-RFaeY7EWzXOmAL2IQEACbnrEza3TgD5UQApHR4hGHhY%3D' (2024-10-31)
2024-11-01 01:11:16 +00:00
9aaf5fca2e
Merge pull request #756 from JakeStanger/dependabot/cargo/regex-1.11.1
build(deps): bump regex from 1.11.0 to 1.11.1
2024-10-30 13:24:03 +00:00
1aebbbe5f5
Merge pull request #755 from JakeStanger/dependabot/cargo/notify-7.0.0
build(deps): bump notify from 6.1.1 to 7.0.0
2024-10-30 10:44:26 +00:00
39b0d8ae9f
Merge pull request #753 from JakeStanger/dependabot/cargo/tokio-1.41.0
build(deps): bump tokio from 1.40.0 to 1.41.0
2024-10-30 10:36:08 +00:00
f47e4910e2
Merge pull request #752 from JakeStanger/dependabot/cargo/serde_json-1.0.132
build(deps): bump serde_json from 1.0.128 to 1.0.132
2024-10-29 22:35:47 +00:00
d99ee0cc6e
Merge pull request #754 from JakeStanger/dependabot/cargo/serde-1.0.213
build(deps): bump serde from 1.0.210 to 1.0.213
2024-10-29 22:23:09 +00:00
dependabot[bot]
cfd96ef935
build(deps): bump regex from 1.11.0 to 1.11.1
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.0...1.11.1)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 14:46:37 +00:00
dependabot[bot]
4212905780
build(deps): bump notify from 6.1.1 to 7.0.0
Bumps [notify](https://github.com/notify-rs/notify) from 6.1.1 to 7.0.0.
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/notify-6.1.1...notify-7.0.0)

---
updated-dependencies:
- dependency-name: notify
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 14:46:25 +00:00
dependabot[bot]
3e3f6ac592
build(deps): bump serde from 1.0.210 to 1.0.213
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.210 to 1.0.213.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.210...v1.0.213)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 14:46:08 +00:00
dependabot[bot]
2fd987ecb1
build(deps): bump tokio from 1.40.0 to 1.41.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.40.0 to 1.41.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.40.0...tokio-1.41.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 14:45:57 +00:00
dependabot[bot]
aec05d44a1
build(deps): bump serde_json from 1.0.128 to 1.0.132
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.128 to 1.0.132.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/1.0.128...1.0.132)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 14:13:52 +00:00
6240b4b4fd
Merge pull request #746 from JakeStanger/fix/markup-escape
fix: markup escape issues
2024-10-15 22:14:15 +01:00
b2db7b0bb5
fix: markup escape issues
Fixes #629
2024-10-15 22:11:15 +01:00
56153f189a
docs(dynamic values): fix missing backtick 2024-10-15 21:47:11 +01:00
8c57d2be44
chore(intellij): add git toolbox blame config 2024-10-15 21:46:53 +01:00
1f2f966a75
Merge pull request #745 from JakeStanger/dependabot/cargo/clap-4.5.20
build(deps): bump clap from 4.5.19 to 4.5.20
2024-10-14 16:52:45 +01:00
dependabot[bot]
4a9249ca68
build(deps): bump clap from 4.5.19 to 4.5.20
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.19 to 4.5.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.19...clap_complete-v4.5.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-14 14:39:19 +00:00
8a44b0fd29
Merge pull request #740 from JakeStanger/dependabot/cargo/clap-4.5.19
build(deps): bump clap from 4.5.18 to 4.5.19
2024-10-08 12:34:38 +01:00
dependabot[bot]
667914057d
build(deps): bump clap from 4.5.18 to 4.5.19
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.18 to 4.5.19.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.18...clap_complete-v4.5.19)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-08 10:31:58 +00:00
be2c1bc55a
Merge pull request #739 from JakeStanger/dependabot/cargo/reqwest-0.12.8
build(deps): bump reqwest from 0.12.7 to 0.12.8
2024-10-08 11:30:52 +01:00
0fcf70c36f
Merge pull request #738 from JakeStanger/dependabot/cargo/indexmap-2.6.0
build(deps): bump indexmap from 2.5.0 to 2.6.0
2024-10-08 11:30:19 +01:00
63df227d3f
Merge pull request #741 from JakeStanger/dependabot/cargo/futures-util-0.3.31
build(deps): bump futures-util from 0.3.30 to 0.3.31
2024-10-08 11:29:35 +01:00
dependabot[bot]
dcc4b8b5ca
build(deps): bump futures-util from 0.3.30 to 0.3.31
Bumps [futures-util](https://github.com/rust-lang/futures-rs) from 0.3.30 to 0.3.31.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.30...0.3.31)

---
updated-dependencies:
- dependency-name: futures-util
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-07 14:51:31 +00:00
dependabot[bot]
f4e360b6a8
build(deps): bump reqwest from 0.12.7 to 0.12.8
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.7 to 0.12.8.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.7...v0.12.8)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-07 14:51:11 +00:00
dependabot[bot]
0e87a5901f
build(deps): bump indexmap from 2.5.0 to 2.6.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.5.0 to 2.6.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-07 14:50:55 +00:00
83c4f77bbd
Merge pull request #736 from JakeStanger/dependabot/cargo/regex-1.11.0
build(deps): bump regex from 1.10.6 to 1.11.0
2024-10-01 09:20:24 +01:00
9d430b1382
Merge pull request #737 from JakeStanger/update_flake_lock_action
Update flake.lock
2024-10-01 09:20:07 +01:00
github-actions[bot]
e539c03734 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/96fd12c7100e9e05fa1a0a5bd108525600ce282f?narHash=sha256-CB20rDD5eHikF6mMTTJdwPP1qvyoiyyw1RDUzwIaIF8%3D' (2024-08-31)
  → 'github:ipetkov/crane/5b03654ce046b5167e7b0bccbd8244cb56c16f0e?narHash=sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE%3D' (2024-09-26)
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe?narHash=sha256-CHgumPZaC7z%2BWYx72WgaLt2XF0yUVzJS60rO4GZ7ytY%3D' (2024-08-31)
  → 'github:NixOS/nixpkgs/b5b22b42c0d10c7d2463e90a546c394711e3a724?narHash=sha256-uMVkVHL4r3QmlZ1JM%2BUoJwxqa46cgHnIfqGzVlw5ca4%3D' (2024-09-30)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28)
  → 'github:nixos/nixpkgs/06cf0e1da4208d3766d898b7fdab6513366d45b9?narHash=sha256-S5kVU7U82LfpEukbn/ihcyNt2%2BEvG7Z5unsKW9H/yFA%3D' (2024-09-29)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/87b6cffc276795b46ef544d7ed8d7fed6ad9c8e4?narHash=sha256-BC6MUq0CTdmAu/cueVcdWTI%2BS95s0mJcn19SoEgd7gU%3D' (2024-08-30)
  → 'github:oxalica/rust-overlay/c2099c6c7599ea1980151b8b6247a8f93e1806ee?narHash=sha256-83j/GrHsx8GFUcQofKh%2BPRPz6pz8sxAsZyT/HCNdey8%3D' (2024-09-30)
2024-10-01 01:11:25 +00:00
dependabot[bot]
a77c150137
build(deps): bump regex from 1.10.6 to 1.11.0
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.6 to 1.11.0.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.6...1.11.0)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 15:03:31 +00:00
Rodrigo Batista de Moraes
afe534ccd9 feat: add .urgent workspace css class 2024-09-25 22:44:12 -03:00
Leshuguita
e08027fe69
fix(workspaces): prevent crash when clicking current workspace (#733)
* fix(workspaces): prevent crash on hyprland error

* fix(workspaces): typo in warning message

* refactor: fix format

---------

Co-authored-by: Leshu <leshuguita.saiyan@gmail.com>
2024-09-24 21:42:13 +01:00
1a4f1575dd
Merge pull request #734 from JakeStanger/dependabot/cargo/clap-4.5.18
build(deps): bump clap from 4.5.17 to 4.5.18
2024-09-24 21:39:48 +01:00
dependabot[bot]
119e249cf9
build(deps): bump clap from 4.5.17 to 4.5.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.17 to 4.5.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 14:06:04 +00:00
32e6fc5dc2
Merge pull request #730 from JakeStanger/dependabot/cargo/serde_json-1.0.128
build(deps): bump serde_json from 1.0.127 to 1.0.128
2024-09-17 17:15:41 +01:00
22a3235100
Merge pull request #729 from JakeStanger/dependabot/cargo/clap-4.5.17
build(deps): bump clap from 4.5.16 to 4.5.17
2024-09-17 17:15:17 +01:00
dependabot[bot]
282406d98c
build(deps): bump serde_json from 1.0.127 to 1.0.128
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.127 to 1.0.128.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/1.0.127...1.0.128)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 15:03:00 +00:00
dependabot[bot]
ead142560b
build(deps): bump clap from 4.5.16 to 4.5.17
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.16 to 4.5.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.16...clap_complete-v4.5.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 15:02:49 +00:00
8bae17a24a
Merge pull request #727 from JakeStanger/dependabot/cargo/serde-1.0.210
build(deps): bump serde from 1.0.209 to 1.0.210
2024-09-11 10:11:02 +01:00
dependabot[bot]
358547841e
build(deps): bump serde from 1.0.209 to 1.0.210
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.209 to 1.0.210.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.209...v1.0.210)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 09:02:13 +00:00
b9c6f72e0a
Merge pull request #723 from JakeStanger/dependabot/cargo/indexmap-2.5.0
build(deps): bump indexmap from 2.4.0 to 2.5.0
2024-09-11 10:01:02 +01:00
825f1ebdef
Merge pull request #722 from JakeStanger/dependabot/cargo/hyprland-0.4.0-alpha.3
build(deps): bump hyprland from 0.4.0-alpha.2 to 0.4.0-alpha.3
2024-09-11 10:00:41 +01:00
2986be9e30
Merge pull request #721 from JakeStanger/dependabot/cargo/tokio-1.40.0
build(deps): bump tokio from 1.39.3 to 1.40.0
2024-09-11 10:00:18 +01:00
bd93c2b565
Merge pull request #719 from JakeStanger/update_flake_lock_action
Update flake.lock
2024-09-11 09:59:36 +01:00
dependabot[bot]
6d2cdf3432
build(deps): bump indexmap from 2.4.0 to 2.5.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.4.0 to 2.5.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.4.0...2.5.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 14:32:33 +00:00
dependabot[bot]
4052243c20
build(deps): bump hyprland from 0.4.0-alpha.2 to 0.4.0-alpha.3
Bumps [hyprland](https://github.com/hyprland-community/hyprland-rs) from 0.4.0-alpha.2 to 0.4.0-alpha.3.
- [Release notes](https://github.com/hyprland-community/hyprland-rs/releases)
- [Commits](https://github.com/hyprland-community/hyprland-rs/compare/0.4.0-alpha.2...0.4.0-alpha.3)

---
updated-dependencies:
- dependency-name: hyprland
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 14:32:12 +00:00
dependabot[bot]
e7aee5baab
build(deps): bump tokio from 1.39.3 to 1.40.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.39.3 to 1.40.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.39.3...tokio-1.40.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 14:31:54 +00:00
github-actions[bot]
ac6759bf80 flake.lock: Update
Flake lock file updates:

• Updated input 'crane':
    'github:ipetkov/crane/852a59f9672c3413d75bca2b3e9cb4c661cacfc3?narHash=sha256-lcut8uZMSa80z%2BaWpxg%2B9nM8BKWtpU59rtcpMXtHd1Q%3D' (2024-08-03)
  → 'github:ipetkov/crane/96fd12c7100e9e05fa1a0a5bd108525600ce282f?narHash=sha256-CB20rDD5eHikF6mMTTJdwPP1qvyoiyyw1RDUzwIaIF8%3D' (2024-08-31)
• Removed input 'crane/nixpkgs'
• Updated input 'naersk/nixpkgs':
    'github:NixOS/nixpkgs/81610abc161d4021b29199aa464d6a1a521e0cc9?narHash=sha256-TcXjLVNd3VeH1qKPH335Tc4RbFDbZQX%2Bd7rqnDUoRaY%3D' (2024-08-02)
  → 'github:NixOS/nixpkgs/5629520edecb69630a3f4d17d3d33fc96c13f6fe?narHash=sha256-CHgumPZaC7z%2BWYx72WgaLt2XF0yUVzJS60rO4GZ7ytY%3D' (2024-08-31)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/d04953086551086b44b6f3c6b7eeb26294f207da?narHash=sha256-hMyG9/WlUi0Ho9VkRrrez7SeNlDzLxalm9FwY7n/Noo%3D' (2024-08-02)
  → 'github:nixos/nixpkgs/71e91c409d1e654808b2621f28a327acfdad8dc2?narHash=sha256-GnR7/ibgIH1vhoy8cYdmXE6iyZqKqFxQSVkFgosBh6w%3D' (2024-08-28)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/27ec296d93cb4b2d03e8cbd019b1b4cde8c34280?narHash=sha256-cWD5pCs9AYb%2B512/yCx9D0Pl5KcmyuXHeJpsDw/D1vs%3D' (2024-08-04)
  → 'github:oxalica/rust-overlay/87b6cffc276795b46ef544d7ed8d7fed6ad9c8e4?narHash=sha256-BC6MUq0CTdmAu/cueVcdWTI%2BS95s0mJcn19SoEgd7gU%3D' (2024-08-30)
2024-09-01 01:11:10 +00:00
0daa6bf5c2
Merge pull request #718 from JakeStanger/dependabot/cargo/serde-1.0.209
build(deps): bump serde from 1.0.206 to 1.0.209
2024-08-27 17:40:19 +01:00
dependabot[bot]
83401465e1
build(deps): bump serde from 1.0.206 to 1.0.209
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.206 to 1.0.209.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.206...v1.0.209)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-27 16:34:58 +00:00
de1cbb4880
Merge pull request #716 from JakeStanger/dependabot/cargo/serde_json-1.0.127
build(deps): bump serde_json from 1.0.125 to 1.0.127
2024-08-27 17:34:00 +01:00
5efcd4f207
Merge pull request #715 from JakeStanger/dependabot/cargo/reqwest-0.12.7
build(deps): bump reqwest from 0.12.5 to 0.12.7
2024-08-27 17:33:48 +01:00
dependabot[bot]
e806d9c60c
build(deps): bump serde_json from 1.0.125 to 1.0.127
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.125 to 1.0.127.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/1.0.125...1.0.127)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 15:20:30 +00:00
dependabot[bot]
77673a305d
build(deps): bump reqwest from 0.12.5 to 0.12.7
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.5 to 0.12.7.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.5...v0.12.7)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 15:20:18 +00:00
eff371ee08
Merge pull request #713 from JakeStanger/dependabot/cargo/ctrlc-3.4.5
build(deps): bump ctrlc from 3.4.4 to 3.4.5
2024-08-20 11:06:55 +01:00
6652adb58d
Merge pull request #712 from JakeStanger/dependabot/cargo/serde_json-1.0.125
build(deps): bump serde_json from 1.0.124 to 1.0.125
2024-08-20 11:05:47 +01:00
dependabot[bot]
a9c79a88df
build(deps): bump serde_json from 1.0.124 to 1.0.125
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.124...1.0.125)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-20 10:00:55 +00:00
dependabot[bot]
a8046a9ef4
build(deps): bump ctrlc from 3.4.4 to 3.4.5
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.4.4 to 3.4.5.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.4.4...3.4.5)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-20 10:00:48 +00:00
68b173af53
Merge pull request #711 from JakeStanger/dependabot/cargo/tokio-1.39.3
build(deps): bump tokio from 1.39.2 to 1.39.3
2024-08-20 11:00:27 +01:00
092f33558f
Merge pull request #710 from JakeStanger/dependabot/cargo/indexmap-2.4.0
build(deps): bump indexmap from 2.3.0 to 2.4.0
2024-08-20 10:59:14 +01:00
553d22b1b2
Merge pull request #709 from JakeStanger/dependabot/cargo/clap-4.5.16
build(deps): bump clap from 4.5.15 to 4.5.16
2024-08-20 10:58:47 +01:00
dependabot[bot]
28028dc705
build(deps): bump tokio from 1.39.2 to 1.39.3
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.39.2 to 1.39.3.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.39.2...tokio-1.39.3)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 14:57:19 +00:00
dependabot[bot]
dbbd9c158c
build(deps): bump indexmap from 2.3.0 to 2.4.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.3.0 to 2.4.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.3.0...2.4.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 14:57:04 +00:00
dependabot[bot]
a710748108
build(deps): bump clap from 4.5.15 to 4.5.16
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.15 to 4.5.16.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.15...clap_complete-v4.5.16)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 14:56:55 +00:00
7c07bbef57
Merge pull request #708 from JakeStanger/refactor/clippy
refactor: fix some pedantic clippy warnings
2024-08-14 21:52:20 +01:00
04f45ccae1
refactor: fix some pedantic clippy warnings 2024-08-14 21:26:40 +01:00
4d30819ff6
Merge pull request #707 from JakeStanger/fix/xwayland-popup
fix(launcher): showing xwayland menus and tooltips
2024-08-14 21:10:11 +01:00
d8b68fd378
fix(launcher): showing xwayland menus and tooltips
Fixes #606
2024-08-14 21:05:26 +01:00
73799291d5
Merge pull request #706 from JakeStanger/fix/mon-resize
fix(popup): incorrect pos when resolution changes
2024-08-14 20:54:47 +01:00
2aa55d8d66
fix(popup): incorrect pos when resolution changes
Popups will now instantly reposition themselves to be correctly placed on resolution change. This includes orientation and scale changes.

Fixes #610
2024-08-14 20:46:40 +01:00
5001295d7c
Merge pull request #704 from JakeStanger/dependabot/cargo/clap-4.5.15
build(deps): bump clap from 4.5.13 to 4.5.15
2024-08-12 17:00:25 +01:00
8abf1aa49f
Merge pull request #703 from JakeStanger/dependabot/cargo/serde-1.0.206
build(deps): bump serde from 1.0.204 to 1.0.206
2024-08-12 16:59:41 +01:00
dependabot[bot]
d798d0bcff
build(deps): bump clap from 4.5.13 to 4.5.15
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.13 to 4.5.15.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.13...v4.5.15)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 15:45:28 +00:00
dependabot[bot]
a5bf17f224
build(deps): bump serde from 1.0.204 to 1.0.206
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.204 to 1.0.206.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.204...v1.0.206)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 15:45:23 +00:00
a3ed719555
Merge pull request #702 from JakeStanger/dependabot/cargo/serde_json-1.0.124
build(deps): bump serde_json from 1.0.122 to 1.0.124
2024-08-12 16:44:16 +01:00
dependabot[bot]
a74fc21a98
build(deps): bump serde_json from 1.0.122 to 1.0.124
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.122 to 1.0.124.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.122...v1.0.124)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 14:22:36 +00:00
513e249ccd
Merge pull request #701 from JakeStanger/fix/workspace-name-map-focus
fix(workspaces): incorrectly checking focus using name_map value
2024-08-11 16:47:02 +01:00
82a6660c85
fix(workspaces): incorrectly checking focus using name_map value
Fixes #639
2024-08-11 16:43:41 +01:00
80729a6cfa
ci(deploy): fix issues with schema job 2024-08-11 16:30:02 +01:00
f302e7c6a6
chore: bump version to 0.16.1-pre 2024-08-11 16:29:49 +01:00
ed49d7cd01
Merge pull request #699 from GameDungeon/patch-1
Update flake.nix to match adwaita-icon-theme being moved to top-level
2024-08-10 22:13:45 +01:00
GameDungeon
37a9e4468d
Update flake.nix 2024-08-10 15:05:17 -05:00
83a3a70e57
chore: update breaking changes in changelog 2024-08-10 15:46:41 +01:00
JakeStanger
d25568c247 chore: update CHANGELOG.md for v0.16.0 [skip ci] 2024-08-10 14:43:51 +00:00
973819ca45
chore(release): v0.16.0 2024-08-10 15:43:10 +01:00
bba345a13b
Merge pull request #697 from JakeStanger/refactor/dbus-error
refactor(mpris): better logging, avoid panic on dbus error
2024-08-10 13:55:49 +01:00
189975791f
refactor(mpris): better logging, avoid panic on dbus error 2024-08-10 13:45:17 +01:00
860a6767f1
docs(upower): add note to make clear upower is required 2024-08-09 23:26:52 +01:00
e812889762
Merge pull request #696 from JakeStanger/fix/upower-crash
fix(upower): avoid panic on client init error
2024-08-09 23:19:23 +01:00
474e1fe364
fix(upower): avoid panic on client init error 2024-08-09 23:16:00 +01:00
9d125353c4
refactor: small tidy 2024-08-09 23:08:08 +01:00
1e0ab67aac
Merge pull request #695 from JakeStanger/fix/popup-scaling
fix: popups not accounting for monitor scaling
2024-08-09 23:02:42 +01:00
45d5bf5feb
fix: popups not accounting for monitor scaling
Fixes #657
2024-08-09 22:56:30 +01:00
7de0412399
Merge pull request #691 from JakeStanger/dependabot/cargo/futures-signals-0.3.34
build(deps): bump futures-signals from 0.3.33 to 0.3.34
2024-08-05 15:35:52 +01:00
dependabot[bot]
17291d2dc7
build(deps): bump futures-signals from 0.3.33 to 0.3.34
Bumps [futures-signals](https://github.com/Pauan/rust-signals) from 0.3.33 to 0.3.34.
- [Changelog](https://github.com/Pauan/rust-signals/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Pauan/rust-signals/commits)

---
updated-dependencies:
- dependency-name: futures-signals
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 14:15:06 +00:00
Rodrigo Batista de Moraes
e307e15dc4
feat: new sway-mode module (#671)
* feat: add sway-mode module

* refactor: Avoid making multiple connections to SwayIPC

Now `sway::Client` is store in `ironbar.clients`, and allow dynamically
registering event listeners, instead of hardcoding events for Workspace
updates.

Remove the use of `swayipc::Connection` from `sway-mode` module, and
replace it with the new event listener system.

#671
2024-08-05 13:22:01 +01:00
4f2f890c93
Merge pull request #685 from JakeStanger/fix/tray-empty-icon
fix(tray): crash when provided empty pixmap
2024-08-04 16:36:21 +01:00
2bc741d197
fix(tray): crash when provided empty pixmap
Fixes #675
2024-08-04 13:40:05 +01:00
164 changed files with 13842 additions and 6177 deletions

View file

@ -2,37 +2,38 @@
name: Bug report
about: Report an issue with the bar not working as expected
title: ''
labels: bug
labels: T:Bug
assignees: ''
---
**Describe the bug**
> A clear and concise description of what the bug is.
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
**To reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
1. Add module `clock`
2. Click popup
3. Observe X is broken
**Expected behavior**
> A clear and concise description of what you expected to happen.
<!-- A clear and concise description of what you expected to happen. -->
**System information:**
- Distro: [e.g. Arch Linux, Ubuntu 22.10]
- Distro: [e.g. Arch Linux, Ubuntu 22.10]
- Compositor: [e.g. Sway]
- Ironbar version: [e.g. 0.8.0]
- Ironbar version: [e.g. 0.16.1]
**Configuration**
> Share your bar configuration and stylesheet as applicable:
<!-- Share your bar configuration and stylesheet as applicable: -->
<details><summary>Config</summary>
```
```
</details>
@ -41,10 +42,11 @@ Steps to reproduce the behavior:
```css
```
</details>
**Additional context**
> Add any other context about the problem here.
<!-- Add any other context about the problem here. -->
**Screenshots**
> If applicable, add screenshots to help explain your problem.
<!-- If applicable, add screenshots to help explain your problem. -->

View file

@ -17,5 +17,7 @@ $SUDO apt-get update && $SUDO apt-get install --assume-yes \
libssl-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libgtk-3-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libgtk-layer-shell-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libinput-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libdbusmenu-gtk3-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libpulse-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH} \
libluajit-5.1-dev${CROSS_DEB_ARCH:+:$CROSS_DEB_ARCH}

View file

@ -4,8 +4,17 @@ on:
workflow_dispatch:
push:
branches: [ "master" ]
paths:
- 'src/**/*'
- 'Cargo.*'
- 'build.rs'
pull_request:
branches: [ "master" ]
paths:
- 'src/**/*'
- 'Cargo.*'
- 'build.rs'
- '.github/workflows/build.yml'
env:
CARGO_TERM_COLOR: always
@ -24,6 +33,7 @@ jobs:
clippy-base:
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
name: 'Clippy (Base features)'
steps:
- uses: actions/checkout@v4
@ -31,9 +41,6 @@ jobs:
- uses: Swatinem/rust-cache@v2
name: Cache dependencies
- name: Install build deps
run: ./.github/scripts/ubuntu_setup.sh
- name: Clippy
run: cargo clippy --no-default-features --features config+json
env:
@ -43,6 +50,7 @@ jobs:
clippy-all:
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
name: 'Clippy (All features)'
steps:
- uses: actions/checkout@v4
@ -50,16 +58,16 @@ jobs:
- uses: Swatinem/rust-cache@v2
name: Cache dependencies
- name: Install build deps
run: ./.github/scripts/ubuntu_setup.sh
- name: Clippy
run: cargo clippy --all-targets --all-features
env:
RUSTFLAGS: '-W clippy::unwrap_used'
build:
name: 'Build & Test'
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
steps:
- uses: actions/checkout@v4
@ -67,9 +75,6 @@ jobs:
- uses: Swatinem/rust-cache@v2
name: Cache dependencies
- name: Install build deps
run: ./.github/scripts/ubuntu_setup.sh
- name: Build
run: cargo build --verbose
@ -77,3 +82,62 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
feature-checks:
name: 'Check feature flag'
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
strategy:
matrix:
feature:
- http
- ipc
- cli
- config+all
- config+json
- config+yaml
- config+toml
- config+corn
- config+ron
- bindmode+all
- bindmode+sway
- bindmode+hyprland
- cairo
- clipboard
- clock
- custom
- focused
- keyboard+all
- keyboard+sway
- keyboard+hyprland
- label
- launcher
- menu
- music+all
- music+mpris
- music+mpd
- network_manager
- notifications
- sys_info
- script
- tray
- upower
- volume
- workspaces+all
- workspaces+sway
- workspaces+hyprland
- workspaces+niri
- schema
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
name: Cache dependencies
- name: Check
run: cargo check --no-default-features --features ${{ matrix.feature }}
env:
# Allow some warnings through as we'll never get it perfect
RUSTFLAGS: '-A unused-imports -A unused-variables -A unused-mut -A dead-code'

View file

@ -41,6 +41,7 @@ jobs:
publish-crate:
name: 'Publish Crate'
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
steps:
- uses: actions/checkout@v3
@ -50,9 +51,6 @@ jobs:
toolchain: stable
override: true
- name: Install build deps
run: ./.github/scripts/ubuntu_setup.sh
- name: Publish crate
uses: katyo/publish-crates@v1
with:
@ -62,6 +60,7 @@ jobs:
publish-schema:
name: 'Publish Schema'
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
steps:
- uses: actions/checkout@v3
@ -69,11 +68,11 @@ jobs:
- uses: Swatinem/rust-cache@v2
name: Cache dependencies
- name: Install build deps
run: ./.github/scripts/ubuntu_setup.sh
- name: Ensure target folder
run: mkdir -p target
- name: Build schema
run: cargo build --features schema -- --print-schema > target/schema-${{ github.ref_name }}.json
run: cargo run --features schema -- --print-schema > target/schema-${{ github.ref_name }}.json
- name: Copy file via SSH
uses: appleboy/scp-action@v0.1.7

105
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,105 @@
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
schedule:
- cron: '32 23 * * *'
push:
branches: [ "master" ]
paths:
- '.github/scripts/ubuntu_setup.sh'
- 'Dockerfile'
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "master" ]
paths:
- '.github/scripts/ubuntu_setup.sh'
- 'Dockerfile'
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}-build
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Install the cosign tool except on PR
# https://github.com/sigstore/cosign-installer
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
with:
cosign-release: 'v2.2.4'
# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
env:
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

View file

@ -4,6 +4,11 @@ on:
workflow_dispatch:
push:
branches: [ "master" ]
paths:
- 'src/**/*'
- 'Cargo.*'
- 'build.rs'
- '.github/workflows/schema.yml'
env:
CARGO_TERM_COLOR: always
@ -13,6 +18,7 @@ jobs:
publish-schema:
name: 'Publish Schema'
runs-on: ubuntu-latest
container: ghcr.io/jakestanger/ironbar-build:master
steps:
- uses: actions/checkout@v3
@ -20,9 +26,6 @@ jobs:
- 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

View file

@ -3,6 +3,8 @@ name: Sync Wiki
on:
push:
branches: [ "master" ]
paths:
- 'docs/**/*'
jobs:
build:

6
.idea/git_toolbox_blame.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

View file

@ -11,7 +11,7 @@
<option name="backtrace" value="SHORT" />
<envs>
<env name="PATH" value="/usr/local/bin:/usr/bin:$USER_HOME$/.local/share/npm/bin" />
<env name="RUST_LOG" value="debug" />
<env name="IRONBAR_LOG" value="debug" />
</envs>
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />

20
.idea/runConfigurations/Test.xml generated Normal file
View file

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="buildProfileId" value="test" />
<option name="command" value="test" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<option name="emulateTerminal" value="true" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="withSudo" value="false" />
<option name="buildTarget" value="REMOTE" />
<option name="backtrace" value="SHORT" />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>

View file

@ -4,6 +4,116 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v0.16.1] - 2024-11-24
### :boom: BREAKING CHANGES
- due to [`e4e9632`](https://github.com/JakeStanger/ironbar/commit/e4e9632caab66f6a8627ffb03b2f82cd5404003f) - menu causing bar to lose focus on sway *(commit by [@JakeStanger](https://github.com/JakeStanger))*:
The `direction` option has been changed to only accept `horizontal` or `vertical`
### :sparkles: New Features
- [`662ddb6`](https://github.com/JakeStanger/ironbar/commit/662ddb69464ab45546231d337f7f4f3e5efcdc98) - **tray**: image support in menu items *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`e1f3b1b`](https://github.com/JakeStanger/ironbar/commit/e1f3b1bb72f3aa6562f7c7d98a0ef8d131e86600) - route gtk logging through tracing *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### :bug: Bug Fixes
- [`82a6660`](https://github.com/JakeStanger/ironbar/commit/82a6660c8568fc6fe7661a7703173c0c8cd93085) - **workspaces**: incorrectly checking focus using name_map value *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`2aa55d8`](https://github.com/JakeStanger/ironbar/commit/2aa55d8d66c69ed02089811af4f3f2eaee11f2ee) - **popup**: incorrect pos when resolution changes *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`d8b68fd`](https://github.com/JakeStanger/ironbar/commit/d8b68fd378b4ece7260511386115b363ed8eec2e) - **launcher**: showing xwayland menus and tooltips *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`e08027f`](https://github.com/JakeStanger/ironbar/commit/e08027fe6992756f8cb4800ec19782024543b19b) - **workspaces**: prevent crash when clicking current workspace *(PR [#733](https://github.com/JakeStanger/ironbar/pull/733) by [@Leshuguita](https://github.com/Leshuguita))*
- [`b2db7b0`](https://github.com/JakeStanger/ironbar/commit/b2db7b0bb546f0fc4b642a83cbe6303213480723) - markup escape issues *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`d87888d`](https://github.com/JakeStanger/ironbar/commit/d87888d173b2737bc5a3ae5ff4ae192cca2a87c7) - `on_scroll` events broken on touchpad *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`80403e3`](https://github.com/JakeStanger/ironbar/commit/80403e3ca9b1261d8374baf29d6971236494929c) - not properly redrawing on style reload *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`81c48fe`](https://github.com/JakeStanger/ironbar/commit/81c48fecadb629a0f915d58f59c0523ab54af162) - **clipboard**: crash when unsupported image type *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`e53a906`](https://github.com/JakeStanger/ironbar/commit/e53a9067b03de002a07f85e5302e3774e53521f2) - **tray**: cannot activate with mixed left/right click *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`55c0940`](https://github.com/JakeStanger/ironbar/commit/55c0940e1dc90069018e40254a2b079b31bc2da2) - **tray**: update `system-tray` dep to bring in a whole load of fixes *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`fb17995`](https://github.com/JakeStanger/ironbar/commit/fb1799531b4638f8cd3a36ccb94425643aaa6082) - **tray**: image updates lag 1 behind *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`05530cf`](https://github.com/JakeStanger/ironbar/commit/05530cf7769a47a49a185d228afaa934d6df7575) - regression caused by [#652](https://github.com/JakeStanger/ironbar/pull/652) *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`e4e9632`](https://github.com/JakeStanger/ironbar/commit/e4e9632caab66f6a8627ffb03b2f82cd5404003f) - **tray**: menu causing bar to lose focus on sway *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`42e25f5`](https://github.com/JakeStanger/ironbar/commit/42e25f5ef2ce9886d8fafb42aff9ced7ef183726) - **ipc**: support querying against duplicate bar names *(commit by [@zeroeightysix](https://github.com/zeroeightysix))*
- [`5aa9f37`](https://github.com/JakeStanger/ironbar/commit/5aa9f37fe4ff76d9ef61e8c0aacc110ecb9a89c3) - **tray**: menus not attaching to secondary bars *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`f364bb6`](https://github.com/JakeStanger/ironbar/commit/f364bb64fbfaef31a55bdc3c9e4cbb6f90fcdab5) - **tray**: tray icons not disappearing on close *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`ce48fc9`](https://github.com/JakeStanger/ironbar/commit/ce48fc9d0d8e603e69f01e58f3a717fc845888f7) - **tray**: prevent widget buttons from piling up *(PR [#788](https://github.com/JakeStanger/ironbar/pull/788) by [@cmeissl](https://github.com/cmeissl))*
- [`230dd8b`](https://github.com/JakeStanger/ironbar/commit/230dd8b13b024eb039613c579f940802ea5857e5) - **workspaces**: clicking currently focused workspace attempts to focus it *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### :recycle: Refactors
- [`04f45cc`](https://github.com/JakeStanger/ironbar/commit/04f45ccae1498630a81edd34923c5920e864bacc) - fix some pedantic clippy warnings *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`486beff`](https://github.com/JakeStanger/ironbar/commit/486beff8a550e0bc757e79ea37c450cab3a810eb) - put in basic placeholders for menu icon diffs *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`cf38c37`](https://github.com/JakeStanger/ironbar/commit/cf38c37fe3223280bf89e50b6869e1912a8ed8bf) - **tray**: move some debug logging to trace logging *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`30de23d`](https://github.com/JakeStanger/ironbar/commit/30de23dc6487635c0e71edfb7a8780bea7ae23e1) - **tray**: switch over to `libdbusmenu-gtk3` *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### :memo: Documentation Changes
- [`56153f1`](https://github.com/JakeStanger/ironbar/commit/56153f189a6496d01cbc42cdc9086d52d69a235a) - **dynamic values**: fix missing backtick *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`ff3f541`](https://github.com/JakeStanger/ironbar/commit/ff3f541183260c786d28e5c9e0a01af0f45408a9) - **tray**: fix formatting *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`f161429`](https://github.com/JakeStanger/ironbar/commit/f161429dfc4e3faa815a00ee9f9afe01ccc959cc) - **clock**: align table columns *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### Note to maintainers
The tray module now depends on `libdbusmenu-gtk3` , which must be present at build time and runtime.
## [v0.16.0] - 2024-08-10
### :boom: BREAKING CHANGES
- due to [`9dd7112`](https://github.com/JakeStanger/ironbar/commit/9dd711235f21d9016fec240f1be5c8d6de1596df) - improve CLI structure, add new commands *(commit by [@JakeStanger](https://github.com/JakeStanger))*:
- `ok_value` responses will no longer print `ok` as the first line when using the CLI
- All IPC commands have changed. Namely, `type` has been changed to `command`, and bar/var related commands are now under a `subcommand`. The full spec can be found on the wiki.
- Several CLI commands are now located under the `var` and `bar` categories. Usage of any commands to get/set Ironvars or control bar visibility will need to be updated.
- The `open_popup` and `close_popup` IPC commands are now called `show_popup` and `hide_popup` respectively.
- The popup `name` argument has been renamed to `widget_name` on all IPC commands.
- The `set-visibility` CLI command now takes a `true`/`false` positional argument in place of the `-v` flag.
### :sparkles: New Features
- [`f11da3e`](https://github.com/JakeStanger/ironbar/commit/f11da3eca1b7d1bc5e1904266f285f0e28f290a0) - **music**: pango markup support *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`951576c`](https://github.com/JakeStanger/ironbar/commit/951576ce3c092d187fd6d1d2ff55b7dbf6198a25) - pango markup support in image icons *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`36d724f`](https://github.com/JakeStanger/ironbar/commit/36d724f148ed8ebe84cbb3c3e25cd4a361d94e66) - **config**: json schema support *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`7413f78`](https://github.com/JakeStanger/ironbar/commit/7413f78e04fe9b532397144e49b7545547980723) - **cli**: debug flag *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`a33e0a2`](https://github.com/JakeStanger/ironbar/commit/a33e0a241a8d5f65f7360b5c7e34a116f3f9f092) - **cli**: format flag, json output format *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`9dd7112`](https://github.com/JakeStanger/ironbar/commit/9dd711235f21d9016fec240f1be5c8d6de1596df) - improve CLI structure, add new commands *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`aa45396`](https://github.com/JakeStanger/ironbar/commit/aa4539606241cfd4d7b8e5512866d30ce92e432d) - ability to set bar layer and exclusive zone *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`92ae1a8`](https://github.com/JakeStanger/ironbar/commit/92ae1a8d73d68ebf51e008cd6322a9269cd10325) - **nix**: home-manager option to read style.css file *(commit by [@Alpha-Ursae-Minoris](https://github.com/Alpha-Ursae-Minoris))*
- [`6d0fe4c`](https://github.com/JakeStanger/ironbar/commit/6d0fe4c8ace9c8a4136fb65c9ff9cdb04e9b6408) - add networkmanager module *(commit by [@Zedfrigg](https://github.com/Zedfrigg))*
- [`e307e15`](https://github.com/JakeStanger/ironbar/commit/e307e15dc4462d1bdcaabff2375f5ac0c5a5df7b) - new sway-mode module *(PR [#671](https://github.com/JakeStanger/ironbar/pull/671) by [@Rodrigodd](https://github.com/Rodrigodd))*
### :bug: Bug Fixes
- [`5e7f576`](https://github.com/JakeStanger/ironbar/commit/5e7f576841f94bdfd89d401cb9a2ba1fabb45c1c) - **workspaces**: add support for hyprland rename event *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`c45ea02`](https://github.com/JakeStanger/ironbar/commit/c45ea02a7d39b30fece3986044a44a64aebf5e16) - **workspaces**: regression due to [#572](https://github.com/JakeStanger/ironbar/pull/572) *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`4a37429`](https://github.com/JakeStanger/ironbar/commit/4a37429634a32a2ffaeb1b591240bdb2a564cab9) - **launcher**: ghost windows in reload *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`520a94a`](https://github.com/JakeStanger/ironbar/commit/520a94abfa526c22df0bebecc42b9be8ae20881e) - all bars showing on same display due to GTK bug *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`4ad4b0e`](https://github.com/JakeStanger/ironbar/commit/4ad4b0e070cc4e271251763db7210e70857d68ca) - **ipc**: regression - reload not working due to [#592](https://github.com/JakeStanger/ironbar/pull/592) *(commit by [@SerraPi](https://github.com/SerraPi))*
- [`9a39420`](https://github.com/JakeStanger/ironbar/commit/9a39420ae28b185cb38a33817f1fc91b5c4e9f55) - **launcher**: favourites staying focused when closed in hyprland *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`8dda494`](https://github.com/JakeStanger/ironbar/commit/8dda49477b2ceb268b94c729aadc5986bdca8528) - **cli**: using zero exit code for error responses *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`9d8a3eb`](https://github.com/JakeStanger/ironbar/commit/9d8a3eb370195321d224c0a51a6752c62404ac1b) - correctly escape pango markup *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`277e6b6`](https://github.com/JakeStanger/ironbar/commit/277e6b62965608ae90defa9a2170d414e09d4c59) - **notifications**: unable to click through overlay *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`dbd385d`](https://github.com/JakeStanger/ironbar/commit/dbd385d225e27a7d732d60ba5a6d6f13c1184add) - **launcher**: apps with multiple windows stay focused when window closed *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`176af99`](https://github.com/JakeStanger/ironbar/commit/176af997f442833adcd7ada1919836d54530d7ef) - **music**: tokens with `&` not rendering *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`7e04e30`](https://github.com/JakeStanger/ironbar/commit/7e04e30171a1897de468592fe5c1f6082d12eb69) - **wayland**: exit on event dispatch error *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`461bee8`](https://github.com/JakeStanger/ironbar/commit/461bee8847590e769df186a2f24ab2ce957568f7) - **bar**: do not add start/center/end containers if empty *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`fb6ae42`](https://github.com/JakeStanger/ironbar/commit/fb6ae42f3bcc7ad35066e1182e617c739a8cfa8a) - crash due to clipboard fd incorrectly closed *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`2bc741d`](https://github.com/JakeStanger/ironbar/commit/2bc741d197867cd5f0c391b9532b4cf9c4d378f6) - **tray**: crash when provided empty pixmap *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`f819aec`](https://github.com/JakeStanger/ironbar/commit/f819aec259cfe418f050c57eb51a236a95039b57) - **notifications**: client broken by recent refactor *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`45d5bf5`](https://github.com/JakeStanger/ironbar/commit/45d5bf5feb88d0854a41faa5890b56188b3e051c) - popups not accounting for monitor scaling *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`474e1fe`](https://github.com/JakeStanger/ironbar/commit/474e1fe364ef70fa0afcff476034d5f307dcd54b) - **upower**: avoid panic on client init error *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### :recycle: Refactors
- [`04a694e`](https://github.com/JakeStanger/ironbar/commit/04a694e2ad5998e82de8dd121cc2b486432c0a70) - fix latest clippy warnings *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`c876904`](https://github.com/JakeStanger/ironbar/commit/c876904bda7bb51ef2d3ec1661281df75fad60be) - update `nix` crate to latest version *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`dedb89b`](https://github.com/JakeStanger/ironbar/commit/dedb89bb027c4477620410d9103d64c3f2668517) - **popup**: rename `is_visible` to `visible` *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`a0cb01a`](https://github.com/JakeStanger/ironbar/commit/a0cb01ae5f2121581eb90f73b8f661862da12b03) - make `Ironbar#unique_id` `must_use` *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`b8fdd85`](https://github.com/JakeStanger/ironbar/commit/b8fdd8531e5516598f81e869b9284b8888f1d06b) - explicitly use `set_text` on non-pango labels *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`9d12535`](https://github.com/JakeStanger/ironbar/commit/9d125353c45a7a8ce3fee43192364745a3fba931) - small tidy *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`1899757`](https://github.com/JakeStanger/ironbar/commit/189975791f6424eca85fcfd76b796e5e9f9fb47f) - **mpris**: better logging, avoid panic on dbus error *(commit by [@JakeStanger](https://github.com/JakeStanger))*
### :memo: Documentation Changes
- [`c25440c`](https://github.com/JakeStanger/ironbar/commit/c25440cd3274cb7adf4e8a1c97b4bc88a53841b4) - update CHANGELOG.md for v0.15.1 [skip ci] *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`f7f991b`](https://github.com/JakeStanger/ironbar/commit/f7f991b2e68a19ff66387913b54127fd8808cc21) - **compiling**: fix wrong fedora package for pulse libs *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`c7743b2`](https://github.com/JakeStanger/ironbar/commit/c7743b28c68919e5bb1d8b9c53d63fb53fd3b081) - add rustdoc comments to all module options *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`7d19106`](https://github.com/JakeStanger/ironbar/commit/7d191065fc20e64befca64e8814aa86b2c654a7c) - add notes about nerd fonts *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`9db0cbc`](https://github.com/JakeStanger/ironbar/commit/9db0cbcbdc561ba929c300cec92156c873c3c151) - **upower**: fix incorrect css selectors *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`057fdff`](https://github.com/JakeStanger/ironbar/commit/057fdffc5f3219b60bbc1f095f88a9d8e3e8f750) - **examples**: fix incorrect cpu sensor name *(commit by [@JakeStanger](https://github.com/JakeStanger))*
- [`076c2df`](https://github.com/JakeStanger/ironbar/commit/076c2df4a29bb3af2183dc1617f101e1e39d3fa4) - add fedora copr package to readme *(commit by [@victorvintorez](https://github.com/victorvintorez))*
- [`860a676`](https://github.com/JakeStanger/ironbar/commit/860a6767f144610d6c1809ddadd52e31c8d8d68d) - **upower**: add note to make clear upower is required *(commit by [@JakeStanger](https://github.com/JakeStanger))*
## [v0.15.1] - 2024-05-05
Release to bump hyprland-rs version due to Hyprland v0.40 socket path breaking change.
@ -575,3 +685,5 @@ It also requires `lua-lgi` as a runtime dependency.
[v0.14.1]: https://github.com/JakeStanger/ironbar/compare/v0.14.0...v0.14.1
[v0.15.0]: https://github.com/JakeStanger/ironbar/compare/v0.14.3...v0.15.0
[v0.15.1]: https://github.com/JakeStanger/ironbar/compare/v0.15.0...v0.15.1
[v0.16.0]: https://github.com/JakeStanger/ironbar/compare/v0.15.1...v0.16.0
[v0.16.1]: https://github.com/JakeStanger/ironbar/compare/v0.16.0...v0.16.1

3096
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
[package]
name = "ironbar"
version = "0.16.0-pre"
edition = "2021"
version = "0.16.1"
edition = "2024"
license = "MIT"
description = "Customisable GTK Layer Shell wlroots/sway bar"
repository = "https://github.com/jakestanger/ironbar"
@ -10,36 +10,47 @@ keywords = ["gtk", "bar", "wayland", "wlroots", "gtk-layer-shell"]
[features]
default = [
"bindmode+all",
"cli",
"cairo",
"clipboard",
"clock",
"config+all",
"custom",
"focused",
"http",
"ipc",
"keyboard+all",
"launcher",
"label",
"menu",
"music+all",
"network_manager",
"notifications",
"script",
"sys_info",
"tray",
"upower",
"volume",
"workspaces+all"
"workspaces+all",
]
cli = ["dep:clap", "ipc"]
ipc = ["dep:serde_json"]
cli = ["ipc"]
ipc = ["dep:serde_json", "dep:clap"]
http = ["dep:reqwest"]
bindmode = []
"bindmode+all" = ["bindmode+sway", "bindmode+hyprland"]
"bindmode+sway" = ["bindmode", "sway"]
"bindmode+hyprland" = ["bindmode", "hyprland"]
"config+all" = [
"config+json",
"config+yaml",
"config+toml",
"config+corn",
"config+ron",
"config+json",
"config+yaml",
"config+toml",
"config+corn",
"config+ron",
]
"config+json" = ["universal-config/json"]
"config+yaml" = ["universal-config/yaml"]
@ -49,15 +60,26 @@ http = ["dep:reqwest"]
cairo = ["lua-src", "mlua", "cairo-rs"]
clipboard = ["nix"]
clipboard = ["dep:rustix"]
clock = ["chrono"]
custom = []
focused = []
keyboard = ["dep:colpetto", "dep:evdev-rs", "dep:rustix", "futures-lite"]
"keyboard+all" = ["keyboard", "keyboard+sway", "keyboard+hyprland"]
"keyboard+sway" = ["keyboard", "sway"]
"keyboard+hyprland" = ["keyboard", "hyprland"]
label = []
launcher = []
music = ["regex"]
menu = []
music = ["dep:regex"]
"music+all" = ["music", "music+mpris", "music+mpd"]
"music+mpris" = ["music", "mpris"]
"music+mpd" = ["music", "mpd-utils"]
@ -66,109 +88,113 @@ network_manager = ["futures-lite", "futures-signals", "zbus"]
notifications = ["zbus"]
sys_info = ["sysinfo", "regex"]
script = []
sys_info = ["dep:sysinfo"]
tray = ["system-tray"]
upower = ["upower_dbus", "zbus", "futures-lite"]
upower = ["zbus", "futures-lite"]
volume = ["libpulse-binding"]
workspaces = ["futures-lite"]
"workspaces+all" = ["workspaces", "workspaces+sway", "workspaces+hyprland"]
"workspaces+sway" = ["workspaces", "swayipc-async"]
"workspaces+all" = ["workspaces", "workspaces+sway", "workspaces+hyprland", "workspaces+niri"]
"workspaces+sway" = ["workspaces", "sway"]
"workspaces+hyprland" = ["workspaces", "hyprland"]
"workspaces+niri" = ["workspaces", "niri"]
sway = ["swayipc-async", "futures-lite"]
niri = ["dep:serde_json"]
schema = ["dep:schemars"]
[dependencies]
# core
gtk = "0.18.1"
gtk-layer-shell = "0.8.1"
gtk = "0.18.2"
gtk-layer-shell = "0.8.2"
glib = "0.18.5"
tokio = { version = "1.39.2", features = [
"macros",
"rt-multi-thread",
"time",
"process",
"sync",
"io-util",
"net",
tokio = { version = "1.46.1", features = [
"macros",
"rt-multi-thread",
"time",
"process",
"sync",
"io-util",
"net",
"fs"
] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
tracing-error = { version = "0.2.0" , default-features = false }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-error = { version = "0.2.1", default-features = false }
tracing-appender = "0.2.3"
strip-ansi-escapes = "0.2.0"
color-eyre = "0.6.3"
serde = { version = "1.0.204", features = ["derive"] }
indexmap = "2.3.0"
dirs = "5.0.1"
color-eyre = "0.6.5"
serde = { version = "1.0.219", features = ["derive"] }
indexmap = "2.10.0"
dirs = "6.0.0"
walkdir = "2.5.0"
notify = { version = "6.1.1", default-features = false }
notify = { version = "8.1.0", default-features = false }
wayland-client = "0.31.1"
wayland-protocols-wlr = { version = "0.2.0", features = ["client"] }
wayland-protocols-wlr = { version = "0.3.8", features = ["client"] }
smithay-client-toolkit = { version = "0.18.1", default-features = false, features = [
"calloop",
"calloop",
] }
universal-config = { version = "0.5.0", default-features = false }
ctrlc = "3.4.4"
cfg-if = "1.0.0"
universal-config = { version = "0.5.1", default-features = false }
ctrlc = "3.4.7"
cfg-if = "1.0.1"
# cli
clap = { version = "4.5.13", optional = true, features = ["derive"] }
# ipc
serde_json = { version = "1.0.122", optional = true }
clap = { version = "4.5.40", optional = true, features = ["derive"] }
# http
reqwest = { version = "0.12.5", default-features = false, features = ["default-tls", "http2"], optional = true }
reqwest = { version = "0.12.22", default-features = false, features = ["default-tls", "http2"], optional = true }
# cairo
lua-src = { version = "547.0.0", optional = true }
mlua = { version = "0.9.9", optional = true, features = ["luajit"] }
lua-src = { version = "548.1.1", optional = true }
mlua = { version = "0.10.5", optional = true, features = ["luajit", "send"] }
cairo-rs = { version = "0.18.5", optional = true, features = ["png"] }
# clipboard
nix = { version = "0.29.0", optional = true, features = ["event", "fs"] }
# clock
chrono = { version = "0.4.38", optional = true, default-features = false, features = ["clock", "unstable-locales"] }
chrono = { version = "0.4.41", optional = true, default-features = false, features = ["clock", "unstable-locales"] }
# keyboard
colpetto = { version = "0.6.0", features = ["tokio", "tracing"], optional = true }
evdev-rs = { version = "0.6.1", optional = true }
# music
mpd-utils = { version = "0.2.1", optional = true }
mpris = { version = "2.0.1", optional = true }
regex = { version = "1.11.1", default-features = false, features = [
"std",
], optional = true }
# network_manager
futures-signals = { version = "0.3.33", optional = true }
futures-signals = { version = "0.3.34", optional = true }
# sys_info
sysinfo = { version = "0.29.11", optional = true }
sysinfo = { version = "0.35.2", optional = true }
# tray
system-tray = { version = "0.2.0", optional = true }
# upower
upower_dbus = { version = "0.3.2", optional = true }
system-tray = { version = "0.7.0", features = ["dbusmenu-gtk3"], optional = true }
# volume
libpulse-binding = { version = "2.28.1", optional = true }
# workspaces
swayipc-async = { version = "2.0.1", optional = true }
hyprland = { version = "0.4.0-alpha.2", features = ["silent"], optional = true }
futures-util = { version = "0.3.30", optional = true }
libpulse-binding = { version = "2.30.1", optional = true }
# shared
futures-lite = { version = "2.3.0", optional = true } # network_manager, upower, workspaces
regex = { version = "1.10.6", default-features = false, features = [
"std",
], optional = true } # music, sys_info
zbus = { version = "3.15.2", default-features = false, features = ["tokio"], optional = true } # network_manager, notifications, upower
futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces, keyboard
zbus = { version = "5.7.1", default-features = false, features = ["blocking-api", "tokio"], optional = true } # network_manager, notifications, upower
swayipc-async = { version = "2.1.0", optional = true } # workspaces, keyboard
hyprland = { version = "0.4.0-beta.2", optional = true } # workspaces, keyboard
rustix = { version = "1.0.7", default-features = false, features = ["std", "fs", "pipe", "event"], optional = true } # clipboard, input
serde_json = { version = "1.0.140", optional = true } # ipc, niri
# schema
schemars = { version = "0.8.21", optional = true }
schemars = { version = "0.8.22", optional = true }
# -- PATCH --
# temp fix for tracing-appender/time
time = "0.3.36"
[build-dependencies]
clap = { version = "4.5.40", features = ["derive"] }
clap_complete = "4.5.54"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"

6
Dockerfile Normal file
View file

@ -0,0 +1,6 @@
FROM rust:latest
COPY .github/scripts/ubuntu_setup.sh /scripts/ubuntu_setup.sh
RUN /scripts/ubuntu_setup.sh
RUN rustup component add clippy

View file

@ -48,10 +48,10 @@ Ironbar is designed to support anything from a lightweight bar to a full desktop
## Features
- First-class support for Sway and Hyprland
- First-class support for Sway and Hyprland, and partial support for Niri
- Fully themeable with hot-loaded CSS
- Popups to show rich content
- Ability to create custom widgets, run scripts and embed dynamic content
- Ability to create custom widgets, run scripts and embed dynamic content (including via Lua)
- Easy to configure anything from a single bar across all monitors, to multiple different unique bars per monitor
- Support for multiple config languages
@ -119,8 +119,35 @@ A flake is included with the repo which can be used with Home Manager.
# And configure
programs.ironbar = {
enable = true;
config = {};
style = "";
systemd = true;
config = {
# An example:
monitors = {
DP-1 = {
anchor_to_edges = true;
position = "top";
height = 16;
start = [
{ type = "clock"; }
];
end = [
{
type = "tray";
icon_size = 16;
}
];
};
};
};
style = /* css */ ''
/* An example */
* {
font-family: Noto Sans Nerd Font, sans-serif;
font-size: 16px;
border: none;
border-radius: 0;
}
'';
package = inputs.ironbar;
features = ["feature" "another_feature"];
};

53
build.rs Normal file
View file

@ -0,0 +1,53 @@
// Importing from Ironbar modules brings in lots of things not used by the build script
// we can just globally suppress those.
#![allow(unused, dead_code)]
#[path = "src/cli.rs"]
mod cli;
#[path = "src/error.rs"]
mod error;
#[path = "src/ipc"]
mod ipc {
#[path = "commands.rs"]
mod commands;
#[path = "responses.rs"]
mod responses;
pub use commands::Command;
pub use responses::Response;
}
use clap::Command;
use clap::CommandFactory;
use clap_complete::Shell::{Bash, Fish, Zsh};
use clap_complete::generate_to;
use cli::Args;
use std::fs;
use std::path::PathBuf;
const NAME: &str = "ironbar";
fn generate_shell_completions(mut cmd: Command) -> std::io::Result<()> {
const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");
let comp_dir = PathBuf::from(MANIFEST_DIR).join("target/completions");
fs::create_dir_all(&comp_dir)?;
for shell in [Bash, Fish, Zsh] {
generate_to(shell, &mut cmd, NAME, &comp_dir)?;
}
Ok(())
}
fn main() -> std::io::Result<()> {
let mut cmd = Args::command();
cmd.set_bin_name(NAME);
generate_shell_completions(cmd)?;
Ok(())
}

12
default.nix Normal file
View file

@ -0,0 +1,12 @@
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) {src = ./.;}).defaultNix

View file

@ -22,8 +22,12 @@ You also need rust; only the latest stable version is supported.
pacman -S gtk3 gtk-layer-shell
# for http support
pacman -S openssl
# for tray support
pacman -S libdbusmenu-gtk3
# for volume support
pacman -S libpulse
# for keyboard support
pacman -S libinput
# for lua/cairo support
pacman -S luajit lua51-lgi
```
@ -34,8 +38,12 @@ pacman -S luajit lua51-lgi
apt install build-essential libgtk-3-dev libgtk-layer-shell-dev
# for http support
apt install libssl-dev
# for tray support
apt install libdbusmenu-gtk3-dev
# for volume support
apt install libpulse-dev
# for keyboard support
apt install libinput-dev
# for lua/cairo support
apt install luajit-dev lua-lgi
```
@ -46,8 +54,12 @@ apt install luajit-dev lua-lgi
dnf install gtk3-devel gtk-layer-shell-devel
# for http support
dnf install openssl-devel
# for tray support
dnf install libdbusmenu-gtk3-devel
# for volume support
dnf install pulseaudio-libs-devel
# for keyboard support
dnf install libinput-devel
# for lua/cairo support
dnf install luajit-devel lua-lgi
```
@ -88,7 +100,13 @@ cargo build --release --no-default-features \
| cairo | Enables the `cairo` module |
| clipboard | Enables the `clipboard` module. |
| clock | Enables the `clock` module. |
| custom | Enables the `custom` module. |
| focused | Enables the `focused` module. |
| keyboard | Enables the `keyboard` module without keyboard layout support. |
| keyboard+all | Enables the `keyboard` module with keyboard layout support for all compositors. |
| keyboard+sway | Enables the `keyboard` module with keyboard layout support for Sway. |
| keyboard+hyprland | Enables the `keyboard` module with keyboard layout support for Hyprland. |
| label | Enables the `label` module. |
| launcher | Enables the `launcher` module. |
| music+all | Enables the `music` module with support for all player types. |
| music+mpris | Enables the `music` module with MPRIS support. |
@ -96,15 +114,37 @@ cargo build --release --no-default-features \
| network_manager | Enables the `network_manager` module. |
| notifications | Enables the `notiications` module. |
| sys_info | Enables the `sys_info` module. |
| script | Enables the `script` module. |
| tray | Enables the `tray` module. |
| upower | Enables the `upower` module. |
| volume | Enables the `volume` module. |
| workspaces+all | Enables the `workspaces` module with support for all compositors. |
| workspaces+sway | Enables the `workspaces` module with support for Sway. |
| workspaces+hyprland | Enables the `workspaces` module with support for Hyprland. |
| workspaces+niri | Enables the `workspaces` module with support for Niri. |
| **Other** | |
| schema | Enables JSON schema support and the CLI `--print-schema` flag. |
## Shell completions
Compiling Ironbar will produce shell completions for bash, zsh and fish; these can be found in `target/completions`.
You can install these as follows:
Bash:
```shell
install -Dm644 completions/ironbar.bash /usr/share/bash-completion/completions/ironbar
```
Zsh:
```shell
install -Dm644 completions/_ironbar /usr/share/zsh/site-functions/_ironbar
```
Fish:
```shell
install -Dm644 completions/ironbar.fish /usr/share/fish/vendor_completions.d/ironbar.fish
```
## Speeding up compiling
@ -164,4 +204,4 @@ codegen-backend = true
[profile.dev]
codegen-backend = "cranelift"
```
```

View file

@ -280,10 +280,12 @@ Check [here](config) for an example config file for a fully configured bar in ea
The following table lists each of the top-level bar config options:
| Name | Type | Default | Description |
|--------------------|-----------------------------------------|---------|---------------------------------------------------------------|
| `ironvar_defaults` | `Map<string, string>` | `{}` | Map of [ironvar](ironvars) keys against their default values. |
| `monitors` | `Map<string, BarConfig or BarConfig[]>` | `null` | Map of monitor names against bar configs. |
| Name | Type | Default | Description |
|--------------------|-----------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------|
| `ironvar_defaults` | `Map<string, string>` | `{}` | Map of [ironvar](ironvars) keys against their default values. |
| `monitors` | `Map<string, BarConfig or BarConfig[]>` | `null` | Map of monitor names against bar configs. |
| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. |
| `icon_overrides` | `Map<string, string>` | `{}` | Map of image inputs to override names. Usually used for app IDs (or classes) to icon names, overriding the app's default icon. |
> [!TIP]
> `monitors` is only required if you are following **2b** or **2c** (ie not the same bar across all monitors).
@ -308,7 +310,6 @@ The following table lists each of the bar-level bar config options:
| `layer` | `background` or `bottom` or `top` or `overlay` | `top` | The layer-shell layer to place the bar on. |
| `exclusive_zone` | `boolean` | `true` unless `start_hidden` is enabled. | Whether the bar should reserve an exclusive zone around it. |
| `popup_gap` | `integer` | `5` | The gap between the bar and popup window. |
| `icon_theme` | `string` | `null` | Name of the GTK icon theme to use. Leave blank to use default. |
| `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. |
| `autohide` | `integer` | `null` | The duration in milliseconds before the bar is hidden after the cursor leaves. Leave unset to disable auto-hide behaviour. |
| `start` | `Module[]` | `[]` | Array of left or top modules. |
@ -350,7 +351,14 @@ For information on the `Script` type, and embedding scripts in strings, see [her
| Name | Type | Default | Description |
|-----------|----------|---------|-----------------------------------------------------------------------------------|
| `tooltip` | `string` | `null` | Shows this text on hover. Supports embedding scripts between `{{double braces}}`. |
| `name` | `string` | `null` | Sets the unique widget name, allowing you to style it using `#name`. |
| `class` | `string` | `null` | Sets one or more CSS classes, allowing you to style it using `.class`. |
| `name` | `string` | `null` | The unique widget name, allowing you to style it using `#name`. |
| `class` | `string` | `null` | One or more CSS classes, allowing you to style it using `.class`. |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).
#### Formatting
| Name | Type | Default | Description |
|---------------|--------------------------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `orientation` | `horizontal` or `vertical` (shorthand: `'h'` or `'v'`) | `horizontal` or `vertical` | The direction in which the widget and its text are laid out. Some modules additionally provide a `direction` option to provide further control. |
| `justify` | `left`, `right`, `center`, `fill` | `left` | The justification (alignment) of the widget text shown on the bar. |

View file

@ -10,7 +10,8 @@ You can also view help per sub-command or command, for example using `ironbar va
The CLI supports plaintext and JSON output. Plaintext will:
- Print `ok` for empty success responses
- Print the returned body for success responses
- Print the returned body for each success response
- Some commands act on multiple objects, in which case the CLI will print one line for each body.
- Print `error` to followed by the error on the next line for error responses. This is printed to `stderr`.
Example:
@ -150,6 +151,9 @@ Each key/value pair is on its own `\n` separated newline. The key and value are
### `bar`
> [!NOTE]
> If there are multiple bars by the same name, the `bar` subcommand will act on all of them and return a `multi` response for commands that get a value.
#### `show`
Forces a bar to be shown, regardless of the current visibility state.
@ -324,6 +328,17 @@ The operation completed successfully, with response data.
}
```
### `multi`
The operation completed successfully on multiple objects, with response data.
```json
{
"type": "multi",
"values": ["lorem ipsum", "dolor sit"]
}
```
### `error`
The operation failed.
@ -335,4 +350,4 @@ Message is optional.
"type": "error",
"message": "lorem ipsum"
}
```
```

View file

@ -25,7 +25,7 @@ Dynamic booleans can use a single source of either a script or variable to contr
For scripts, you can just write these directly with no notation.
Only polling scripts are supported.
The script exit code is used, where `0` is `true` and any other code is `false.
The script exit code is used, where `0` is `true` and any other code is `false`.
For variables, use the standard `#name` notation.
An empty string, `0` and `false` are treated as false.

50
docs/GTK4.md Normal file
View file

@ -0,0 +1,50 @@
As the GTK3 and gtk-layer-shell crates are now deprecated, there is a need to move to GTK 4.
The `refactor/gtk-4` branch and PR [#112](https://github.com/JakeStanger/ironbar/pull/112) are tracking the code upgrade.
This page documents the port progress.
Assistance in the porting process is very much welcomed, no matter how small.
As many modules have not been ported, the default feature set will fail to compile.
It is therefore necessary to compile manually with `--no-default-features`, enabling only the working modules:
```shell
cargo run --no-default-features \
--features config+all,clock,cairo
```
A full list of feature flags can be found [here](Compiling#features).
## Core functionality
| Area | Status | Notes |
|-----------------|--------|-------------------------------------------------------------------------------------------------------|
| Bar | ✅ | |
| Popups | ✅ | Potential styling issues, otherwise working. |
| Theming - CSS | ✅ | |
| Theming - Icons | ⚠️ | GTK4 does not support icon theming - always uses default theme. Image scaling may be incorrect. |
| Config - Format | ❌ | Angle/justify properties have been removed from widgets and should now be controlled via CSS instead. |
| IPC | ⚠️ | Some popup-related commands not implemented. |
## Modules
| Module | Status | Notes |
|-----------------|--------|------------------------------------------------------------------------------------------------------------------------------------------|
| Bindmode | ❌ | |
| Cairo | ✅ | |
| Clipboard | ✅ | |
| Clock | ✅ | |
| Custom | ✅ | |
| Focused | ✅ | |
| Keyboard | ✅ | |
| Label | ✅ | |
| Launcher | ⚠️ | Popup behaviour may be a little strange. |
| Music | ✅ | |
| Network Manager | ❌ | |
| Notifications | ✅ | |
| Script | ✅ | |
| SysInfo | ✅ | |
| Tray | ❌ | GTK4 removes widgets required to move the tray. No `libdbusmenu-gtk4` either. will need to manually re-create menus with custom widgets. |
| UPower | ❌ | |
| Volume | ❌ | |
| Workspaces | ✅ | |

View file

@ -6,4 +6,19 @@ Any UTF-8 string is a valid value.
Reference values using `#my_variable`. These update as soon as the value changes.
You can set defaults using the `ironvar_defaults` key in your top-level config.
You can set defaults using the `ironvar_defaults` key in your top-level config.
Some modules (such as `sys_info`) expose their values over the Ironvar interface,
allowing you to build custom interfaces and integrate into scripts.
These present their values inside read-only namespaces.
Some examples below:
```shell
ironbar var list
ironbar var list sysinfo
ironbar var list sysinfo.disk_percent
ironbar var get sysinfo.disk_percent./home
ironbar var get sysinfo.disk_percent.mean
ironbar var get sysinfo.memory_percent
```

View file

@ -4,6 +4,7 @@
- [Configuration guide](configuration-guide)
- [Images](images)
- [Styling guide](styling-guide)
- [GTK 4 Port](gtk4)
# Dynamic content
@ -24,13 +25,16 @@
# Modules
- [Bindmode](bindmode)
- [Cairo](cairo)
- [Clipboard](clipboard)
- [Clock](clock)
- [Custom](custom)
- [Focused](focused)
- [Keyboard](keyboard)
- [Label](label)
- [Launcher](launcher)
- [Menu](menu)
- [Music](music)
- [Network Manager](network-manager)
- [Notifications](notifications)

78
docs/modules/Bindmode.md Normal file
View file

@ -0,0 +1,78 @@
> [!IMPORTANT]
> This module is currently only available on Sway and Hyprland.
Displays Sway's current binding mode or [Hyprland's current submap](https://wiki.hyprland.org/Configuring/Binds/#submaps)
in a label. Nothing is displayed if no binding mode is active.
## Configuration
> Type: `bindmode`
| Name | Type | Default | Description |
| --------------------- | ------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `truncate` | `'start'` or `'middle'` or `'end'` or `Map` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
<details>
<summary>JSON</summary>
```json
{
"end": [
{
"type": "bindmode",
"truncate": "start"
}
]
}
```
</details>
<details>
<summary>TOML</summary>
```toml
[[end]]
type = "bindmode"
truncate = "start"
```
</details>
<details>
<summary>YAML</summary>
```yaml
end:
- type: "bindmode"
truncate: "start"
```
</details>
<details>
<summary>Corn</summary>
```corn
{
end = [
{
type = "bindmode"
truncate = "start"
}
]
}
```
</details>
## Styling
| Selector | Description |
| ----------- | ---------------------- |
| `.bindmode` | Bind mode label widget |
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -12,15 +12,15 @@ Supports plain text and images.
> Type: `clipboard`
| Name | Type | Default | Description |
|-----------------------|---------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `icon` | `string` or [image](images) | `󰨸` | Icon to show on the widget button. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `max_items` | `integer` | `10` | Maximum number of items to show in the popup. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `Map` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| Name | Type | Default | Description |
|-----------------------|------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `icon` | `string` or [image](images) | `󰨸` | Icon to show on the widget button. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `max_items` | `integer` | `10` | Maximum number of items to show in the popup. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
<details>
<summary>JSON</summary>

View file

@ -8,12 +8,13 @@ Clicking on the widget opens a popup with the time and a calendar.
> Type: `clock`
| Name | Type | Default | Description |
|----------------|----------|------------------------------------|-------------------------------------------------------------------------------------|
| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Pango markup is supported. |
| `format_popup` | `string` | `%H:%M:%S` | Date/time format string to display in the popup header. Pango markup is supported. |
| `locale` | `string` | `$LC_TIME` or `$LANG` or `'POSIX'` | Locale to use (eg `en_GB`). Defaults to the system language (reading from env var). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the time on the clock button. |
| Name | Type | Default | Description |
|----------------|------------------------------------------------------------|------------------------------------|-------------------------------------------------------------------------------------|
| `format` | `string` | `%d/%m/%Y %H:%M` | Date/time format string. Pango markup is supported. |
| `format_popup` | `string` | `%H:%M:%S` | Date/time format string to display in the popup header. Pango markup is supported. |
| `locale` | `string` | `$LC_TIME` or `$LANG` or `'POSIX'` | Locale to use (eg `en_GB`). Defaults to the system language (reading from env var). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the time on the clock button. |
| `justify` | `'left'`', `'right'`, `'center'`, or `'fill'` | `'left'` | Justification (alignment) of the date/time shown on the bar. |
> Detail on available tokens can be found here: <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>

View file

@ -47,6 +47,8 @@ A container to place nested widgets inside.
|---------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------|
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Whether child widgets should be horizontally or vertically added. |
| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this box. |
| `halign` | `'start'` or `'center'` or `'end'` or `'fill'` | `'fill'` | The horizontal alignment of the box within its parent container. |
| `valign` | `'start'` or `'center'` or `'end'` or `'fill'` | `'fill'` | The vertical alignment of the box within its parent container. |
#### Label
@ -54,10 +56,16 @@ A text label. Pango markup is supported.
> Type `label`
| Name | Type | Default | Description |
|---------|-------------------------------------------------|---------|---------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the label. |
| Name | Type | Default | Description |
|-----------------------|------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the label text. |
| `justify` | `'left'`, `'right'`, `'center'`, or `'fill'` | `'left'` | Justification (alignment) of the label text. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
#### Button
@ -65,12 +73,13 @@ A clickable button, which can run a command when clicked.
> Type `button`
| Name | Type | Default | Description |
|------------|-------------------------------------------------|---------|--------------------------------------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. Ignored if `widgets` is set. |
| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this button. |
| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the button. |
| Name | Type | Default | Description |
|---------------|------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. Ignored if `widgets` is set. |
| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this button. |
| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the label text. |
| `justify` | `'left'`, `'right'`, `'center'`, or `'fill'` | `'left'` | Justification (alignment) of the label text. |
#### Image
@ -425,4 +434,4 @@ The following top-level selectors are always available:
| `.custom` | Custom widget container. |
| `.popup-custom` | Custom widget popup container. |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -10,15 +10,15 @@ Displays the title and/or icon of the currently focused window.
> Type: `focused`
| Name | Type | Default | Description |
|-----------------------|---------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `show_icon` | `boolean` | `true` | Whether to show the app's icon. |
| `show_title` | `boolean` | `true` | Whether to show the app's title. |
| `icon_size` | `integer` | `32` | Size of icon in pixels. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `Map` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| Name | Type | Default | Description |
|-----------------------|------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `show_icon` | `boolean` | `true` | Whether to show the app's icon. |
| `show_title` | `boolean` | `true` | Whether to show the app's title. |
| `icon_size` | `integer` | `32` | Size of icon in pixels. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
<details>
<summary>JSON</summary>

122
docs/modules/Keyboard.md Normal file
View file

@ -0,0 +1,122 @@
> [!NOTE]
> This module requires your user is in the `input` group.
> [!IMPORTANT]
> The keyboard layout feature is only available on Sway and Hyprland.
Displays the toggle state of the capslock, num lock and scroll lock keys, and the current keyboard layout.
![Screenshot of keyboard widget](https://f.jstanger.dev/github/ironbar/keys.png)
## Configuration
> Type: `keyboard`
| Name | Type | Default | Description |
| ------------------ | ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `show_caps` | `boolean` | `true` | Whether to show capslock indicator. |
| `show_num` | `boolean` | `true` | Whether to show num lock indicator. |
| `show_scroll` | `boolean` | `true` | Whether to show scroll lock indicator. |
| `show_layout` | `boolean` | `true` | Whether to show the keyboard layout button. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `icons.caps_on` | `string` or [image](images) | `󰪛` | Icon to show for enabled capslock indicator. |
| `icons.caps_off` | `string` or [image](images) | `''` | Icon to show for disabled capslock indicator. |
| `icons.num_on` | `string` or [image](images) | `` | Icon to show for enabled num lock indicator. |
| `icons.num_off` | `string` or [image](images) | `''` | Icon to show for disabled num lock indicator. |
| `icons.scroll_on` | `string` or [image](images) | `` | Icon to show for enabled scroll lock indicator. |
| `icons.scroll_off` | `string` or [image](images) | `''` | Icon to show for disabled scroll lock indicator. |
| `icons.layout_map` | `Map<string, string or image>` | `{}` | Map of icons or labels to show for a particular keyboard layout. Layouts use their actual name if not present in the map. |
| `seat` | `string` | `seat0` | ID of the Wayland seat to attach to. |
<details>
<summary>JSON</summary>
```json
{
"end": [
{
"type": "keyboard",
"show_scroll": false,
"icons": {
"caps_on": "󰪛",
"layout_map": {
"English (US)": "🇺🇸",
"Ukrainian": "🇺🇦"
}
}
}
]
}
```
</details>
<details>
<summary>TOML</summary>
```toml
[[end]]
type = "keyboard"
show_scroll = false
[end.icons]
caps_on = "󰪛"
[end.icons.layout_map]
"English (US)" = "🇺🇸"
Ukrainian = "🇺🇦"
```
</details>
<details>
<summary>YAML</summary>
```yaml
end:
- type: keyboard
show_scroll: false
icons:
caps_on: 󰪛
layout_map:
"English (US)": 🇺🇸
Ukrainian: 🇺🇦
```
</details>
<details>
<summary>Corn</summary>
```corn
{
end = [
{
type = "keyboard"
show_scroll = false
icons.caps_on = "󰪛"
icons.layout_map.'English (US)' = "🇺🇸"
icons.layout_map.Ukrainian = "🇺🇦"
}
]
}
```
</details>
## Styling
| Selector | Description |
| -------------------------- | ------------------------------------------ |
| `.keyboard` | Keys box container widget. |
| `.keyboard .key` | Individual key indicator container widget. |
| `.keyboard .key.enabled` | Key indicator where key is toggled on. |
| `.keyboard .key.caps` | Capslock key indicator. |
| `.keyboard .key.num` | Num lock key indicator. |
| `.keyboard .key.scroll` | Scroll lock key indicator. |
| `.keyboard .key.image` | Key indicator image icon. |
| `.keyboard .key.text-icon` | Key indicator textual icon. |
| `.keyboard .layout` | Keyboard layout indicator. |
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -7,9 +7,14 @@ For more advanced use-cases, use [custom](custom).
> Type: `label`
| Name | Type | Default | Description |
|---------|-------------------------------------------------|---------|------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Text to show on label. |
| Name | Type | Default | Description |
|-----------------------|------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Text to show on label. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
<details>
<summary>JSON</summary>

View file

@ -3,7 +3,8 @@
Windows-style taskbar that displays running windows, grouped by program.
Hovering over a program with multiple windows open shows a popup with each window.
Clicking an icon/popup item focuses or launches the program.
Left clicking an icon/popup item focuses the program if it has any open instances or otherwise launches a new instance of the program.
Middle clicking an icon always launches a new instance of the program.
Optionally displays a launchable set of favourites.
![Screenshot showing several open applications, including a popup showing multiple terminal windows.](https://f.jstanger.dev/github/ironbar/launcher.png)
@ -12,13 +13,24 @@ Optionally displays a launchable set of favourites.
> Type: `launcher`
| | Type | Default | Description |
|--------------|------------|---------|-----------------------------------------------------------------------------------------------------|
| `favorites` | `string[]` | `[]` | List of app IDs (or classes) to always show at the start of the launcher |
| `show_names` | `boolean` | `false` | Whether to show app names on the button label. Names will still show on tooltips when set to false. |
| `show_icons` | `boolean` | `true` | Whether to show app icons on the button. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `reversed` | `boolean` | `false` | Whether to reverse the order of favorites/items |
| | Type | Default | Description |
|-----------------------------|---------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `favorites` | `string[]` | `[]` | List of app IDs (or classes) to always show at the start of the launcher. |
| `show_names` | `boolean` | `false` | Whether to show app names on the button label. Names will still show on tooltips when set to false. |
| `show_icons` | `boolean` | `true` | Whether to show app icons on the button. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `launch_command` | `string` | `gtk-launch {app_name}` | Command used to launch applications. |
| `reversed` | `boolean` | `false` | Whether to reverse the order of favorites/items |
| `minimize_focused` | `boolean` | `true` | Whether to minimize a focused window when its icon is clicked. Only minimizes single windows. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `end` | Location of the ellipses and where to truncate text from. Applies to application names when `show_names` is enabled. |
| `truncate.length` | `integer` | `null` | Fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | Maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `truncate_popup.mode` | `'start'` or `'middle'` or `'end'` or `off` | `middle` | Location of the ellipses and where to truncate text from. Applies to window names within a group popup. |
| `truncate_popup.length` | `integer` | `null` | Fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate_popup.max_length` | `integer` | `25` | Maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `page_size` | `integer` | `1000` | Number of items to show on a page. When the number of items is reached, controls appear which can be used to move forward/back through the list of items. |
| `icons.page_back` | `string` or [image](images) | `󰅁` | Icon to show for page back button. |
| `icons.page_forward` | `string` or [image](images) | `󰅂` | Icon to show for page forward button. |
<details>
<summary>JSON</summary>
@ -94,14 +106,17 @@ start:
## Styling
| Selector | Description |
|-------------------------------|--------------------------|
| `.launcher` | Launcher widget box |
| `.launcher .item` | App button |
| `.launcher .item.open` | App button (open app) |
| `.launcher .item.focused` | App button (focused app) |
| `.launcher .item.urgent` | App button (urgent app) |
| `.popup-launcher` | Popup container |
| `.popup-launcher .popup-item` | Window button in popup |
| Selector | Description |
|--------------------------------------|---------------------------|
| `.launcher` | Launcher widget box |
| `.launcher .item` | App button |
| `.launcher .item.open` | App button (open app) |
| `.launcher .item.focused` | App button (focused app) |
| `.launcher .item.urgent` | App button (urgent app) |
| `.launcher .pagination` | Pagination controls box |
| `.launcher .pagination .btn-back` | Pagination back button |
| `.launcher .pagination .btn-forward` | Pagination forward button |
| `.popup-launcher` | Popup container |
| `.popup-launcher .popup-item` | Window button in popup |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).

158
docs/modules/Menu.md Normal file
View file

@ -0,0 +1,158 @@
Application menu that shows installed programs and optionally custom entries.
This works by reading all `.desktop` files on the system.
Clicking the menu button will open the main menu.
Clicking on any application category will open a sub-menu with any installed applications that match.
It is also possible to add custom categories and actions into the menu.
![Screenshot of open menu showing applications inside Office category](https://f.jstanger.dev/github/ironbar/menu.png)
## Configuration
| | Type | Default | Description |
|-----------------------|------------------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| `start` | `MenuEntry[]` | `[]` | Items to add to the start of the main menu. |
| `center` | `MenuEntry[]` | Default XDG menu | Items to add to the centre of the main menu. By default this shows a number of XDG entries that should cover all common applications. |
| `end` | `MenuEntry[]` | `[]` | Items to add to the end of the main menu. |
| `height` | `integer` | `null` | Height of the menu. Leave null to resize dynamically. |
| `width` | `integer` | `null` | Width of the menu. Leave null to resize dynamically. |
| `label` | `string` | `≡` | Label to show on the menu button on the bar. |
| `label_icon` | `string` | `null` | Icon to show on the menu button on the bar. |
| `label_icon_size` | `integer` | `16` | Size of the label_icon image. |
| `launch_command` | `string` | `gtk-launch {app_name}` | Command used to launch applications. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | Applies to popup. The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | Applies to popup. The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | Applies to popup. The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | Applies to popup. The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
### `MenuEntry`
Each entry can be one of three types:
- `xdg_entry` - Contains all applications matching the configured `categories`.
- `xdg_other` - Contains all applications not covered by `xdg_entry` categories.
- `custom` - Individual shell command entry.
| | Type | Default | Description |
|--------------|----------------------------------------|---------|----------------------------------------------------------------------------------------|
| `type` | `xdg_entry` or `xdg_other` or `custom` | | Type of the entry. |
| `label` | `string` | `''` | Label of the entry's button. |
| `icon` | `string` | `null` | Icon for the entry's button. |
| `categories` | `string[]` | `[]` | [`xfg_entry`] List of freedesktop.org categories to include in this entry's sub menu . |
| `on_click` | `string` | `''` | [`custom`] Shell command to execute when the entry's button is clicked |
### Default XDG Menu
Setting the `center` menu entries will override the default menu.
The default menu can be found in the `default` example files [here](https://github.com/jakestanger/ironbar/blob/examples/menu/).
<details>
<summary>JSON</summary>
```json
{
"start": [
{
"type": "menu",
"start": [
{
"type": "custom",
"label": "Terminal",
"on_click": "xterm"
}
],
"height": 440,
"width": 200,
"icon": "archlinux",
"label": null
}
]
}
```
</details>
<details>
<summary>TOML</summary>
```toml
[[start]]
type = "memu"
height = 400
width = 200
icon = "archlinux"
[[start.start]]
type = "custom"
label = "Terminal"
on_click = "xterm"
```
</details>
<details>
<summary>YAML</summary>
```yaml
start:
- type: "menu"
start:
- type: custom
label: Terminal
on_click: xterm
height: 440
width: 200
icon: archlinux
label: null
```
</details>
<details>
<summary>Corn</summary>
```corn
{
start = [
{
type = "menu"
start = [
{
type = "custom"
label = "Terminal"
on_click = "xterm"
}
]
height = 440
width = 200
icon = "archlinux"
label = null
}
]
}
```
</details>
## Styling
| Selector | Description |
|--------------------------------------|-----------------------------------|
| `.menu` | Menu button |
| `.popup-menu` | Main container of the popup |
| `.popup-menu .main` | Main menu of the menu |
| `.popup-menu .main .category` | Category button |
| `.popup-menu .main .category.open` | Open category button |
| `.popup-menu .main .main-start` | Container for `start` entries |
| `.popup-menu .main .main-center` | Container for `center` entries |
| `.popup-menu .main .main-end` | Container for `end` entries |
| `.popup-menu .sub-menu` | All sub-menus |
| `.popup-menu .sub-menu .application` | Application button within submenu |
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -11,29 +11,39 @@ in MPRIS mode, the widget will listen to all players and automatically detect/di
> Type: `music`
| | Type | Default | Description |
|-----------------------|---------------------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `player_type` | `'mpris'` or `'mpd'` | `mpris` | Whether to connect to MPRIS players or an MPD server. |
| `format` | `string` | `{title} / {artist}` | Format string for the widget. More info below. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `Map` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` | `null` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `icons.play` | `string` or [image](images) | `` | Icon to show when playing. |
| `icons.pause` | `string` or [image](images) | `` | Icon to show when paused. |
| `icons.prev` | `string` or [image](images) | `󰒮` | Icon to show on previous button. |
| `icons.next` | `string` or [image](images) | `󰒭` | Icon to show on next button. |
| `icons.volume` | `string` or [image](images) | `󰕾` | Icon to show under popup volume slider. |
| `icons.track` | `string` or [image](images) | `󰎈` | Icon to show next to track title. |
| `icons.album` | `string` or [image](images) | `󰀥` | Icon to show next to album name. |
| `icons.artist` | `string` or [image](images) | `󰠃` | Icon to show next to artist name. |
| `show_status_icon` | `boolean` | `true` | Whether to show the play/pause icon on the widget. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `cover_image_size` | `integer` | `128` | Size to render album art image at inside popup. |
| `host` | `string` | `localhost:6600` | [MPD Only] TCP or Unix socket for the MPD server. |
| `music_dir` | `string` | `$HOME/Music` | [MPD Only] Path to MPD server's music directory on disc. Required for album art. |
See [here](images) for information on images.
| | Type | Default | Description |
|------------------------------------|------------------------------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `player_type` | `'mpris'` or `'mpd'` | `mpris` | Whether to connect to MPRIS players or an MPD server. |
| `format` | `string` | `{title} / {artist}` | Format string for the widget. More info below. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `truncate_popup_artist` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate_popup_artist.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate_popup_artist.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate_popup_artist.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `truncate_popup_album` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate_popup_album.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate_popup_album.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate_popup_album.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `truncate_popup_title` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate_popup_title.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate_popup_title.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate_popup_title.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
| `icons.play` | `string` or [image](images) | `` | Icon to show when playing. |
| `icons.pause` | `string` or [image](images) | `` | Icon to show when paused. |
| `icons.prev` | `string` or [image](images) | `󰒮` | Icon to show on previous button. |
| `icons.next` | `string` or [image](images) | `󰒭` | Icon to show on next button. |
| `icons.volume` | `string` or [image](images) | `󰕾` | Icon to show under popup volume slider. |
| `icons.track` | `string` or [image](images) | `󰎈` | Icon to show next to track title. |
| `icons.album` | `string` or [image](images) | `󰀥` | Icon to show next to album name. |
| `icons.artist` | `string` or [image](images) | `󰠃` | Icon to show next to artist name. |
| `show_status_icon` | `boolean` | `true` | Whether to show the play/pause icon on the widget. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `cover_image_size` | `integer` | `128` | Size to render album art image at inside popup. |
| `host` | `string` | `localhost:6600` | [MPD Only] TCP or Unix socket for the MPD server. |
| `music_dir` | `string` | `$HOME/Music` | [MPD Only] Path to MPD server's music directory on disc. Required for album art. |
<details>
<summary>JSON</summary>
@ -170,4 +180,4 @@ and will be replaced with values from the currently playing track:
| `.popup-music .progress .slider` | Slider inside progress container |
| `.popup-music .progress .label` | Duration label inside progress container |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -17,6 +17,9 @@ Supports wired ethernet, wifi, cellular data and VPN connections among others.
|-------------|-----------|---------|-------------------------|
| `icon_size` | `integer` | `24` | Size to render icon at. |
> [!NOTE]
> This module does not support module-level [layout options](module-level-options#layout).
<details>
<summary>JSON</summary>

View file

@ -21,6 +21,8 @@ Clicking the widget opens the SwayNC panel.
| `icons.open_some` | `string` | `󱥁` | Icon to show when the panel is open, with notifications. |
| `icons.open_dnd` | `string` | `󱅮` | Icon to show when the panel is open, with DnD enabled. Takes higher priority than count-based icons. |
> [!NOTE]
> This module does not support module-level [layout options](module-level-options#layout).
<details>
<summary>JSON</summary>

View file

@ -3,6 +3,8 @@ Displays one or more labels containing system information.
Separating information across several labels allows for styling each one independently.
Pango markup is supported.
Options can be provided in a token to specify operations, units and formatting.
![Screenshot showing sys-info module with widgets for all of the types of formatting tokens](https://user-images.githubusercontent.com/5057870/196059090-4056d083-69f0-4e6f-9673-9e35dc29d9f0.png)
@ -10,17 +12,17 @@ Pango markup is supported.
> Type: `sys_info`
| Name | Type | Default | Description |
|--------------------|--------------------|---------|--------------------------------------------------------------------------------------------------------------------------------|
| `format` | `string[]` | `null` | Array of strings including formatting tokens. For available tokens see below. |
| `interval` | `integer` or `Map` | `5` | Seconds between refreshing. Can be a single value for all data or a map of individual refresh values for different data types. |
| `interval.memory` | `integer` | `5` | Seconds between refreshing memory data |
| `interval.cpu` | `integer` | `5` | Seconds between refreshing cpu data |
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data |
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data |
| `interval.network` | `integer` | `5` | Seconds between refreshing network data |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the labels. |
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | How the labels are laid out (not the rotation of an individual label). |
| Name | Type | Default | Description |
|--------------------|------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------------------------------------|
| `format` | `string[]` | `null` | Array of strings including formatting tokens. For available tokens see below. |
| `interval` | `integer` or `Map` | `5` | Seconds between refreshing. Can be a single value for all data or a map of individual refresh values for different data types. |
| `interval.memory` | `integer` | `5` | Seconds between refreshing memory data. |
| `interval.cpu` | `integer` | `5` | Seconds between refreshing cpu data. |
| `interval.temps` | `integer` | `5` | Seconds between refreshing temperature data. |
| `interval.disks` | `integer` | `5` | Seconds between refreshing disk data. |
| `interval.network` | `integer` | `5` | Seconds between refreshing network data. |
| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the labels. |
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | How the labels are laid out (not the rotation of an individual label). |
<details>
<summary>JSON</summary>
@ -30,12 +32,11 @@ Pango markup is supported.
"end": [
{
"format": [
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
" {memory_used} / {memory_total} GB ({memory_percent}%)",
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
"󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
"󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps",
"󰖡 {load_average:1} | {load_average:5} | {load_average:15}",
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps",
"󰖡 {load_average_1} | {load_average_5} | {load_average_15}",
"󰥔 {uptime}"
],
"interval": {
@ -60,13 +61,12 @@ Pango markup is supported.
[[end]]
type = 'sys_info'
format = [
' {cpu_percent}% | {temp_c:k10temp-Tccd1}°C',
' {memory_used} / {memory_total} GB ({memory_percent}%)',
'| {swap_used} / {swap_total} GB ({swap_percent}%)',
'󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)',
'󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps',
'󰖡 {load_average:1} | {load_average:5} | {load_average:15}',
'󰥔 {uptime}',
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps",
"󰖡 {load_average_1} | {load_average_5} | {load_average_15}",
"󰥔 {uptime}"
]
[end.interval]
@ -87,13 +87,12 @@ temps = 5
```yaml
end:
- format:
- ' {cpu_percent}% | {temp_c:k10temp-Tccd1}°C'
- ' {memory_used} / {memory_total} GB ({memory_percent}%)'
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
- '󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)'
- '󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps'
- '󰖡 {load_average:1} | {load_average:5} | {load_average:15}'
- '󰥔 {uptime}'
- " {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
- " {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
- "󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
- "󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps"
- "󰖡 {load_average_1} | {load_average_5} | {load_average_15}"
- "󰥔 {uptime}"
interval:
cpu: 1
disks: 300
@ -121,12 +120,11 @@ end:
interval.networks = 3
format = [
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C"
" {memory_used} / {memory_total} GB ({memory_percent}%)"
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
"󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
"󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps"
"󰖡 {load_average:1} | {load_average:5} | {load_average:15}"
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps"
"󰖡 {load_average_1} | {load_average_5} | {load_average_15}"
"󰥔 {uptime}"
]
}
@ -138,39 +136,179 @@ end:
### Formatting Tokens
The following tokens can be used in the `format` configuration option:
The below table lists the tokens which can be used in the `format` configuration option.
More information about each of these and the additional options can be found further below.
| Token | Description |
|--------------------------|------------------------------------------------------------------------------------|
| **CPU** | |
| `{cpu_percent}` | Total CPU utilisation percentage |
| **Memory** | |
| `{memory_free}` | Memory free in GB. |
| `{memory_used}` | Memory used in GB. |
| `{memory_total}` | Memory total in GB. |
| `{memory_percent}` | Memory utilisation percentage. |
| `{swap_free}` | Swap free in GB. |
| `{swap_used}` | Swap used in GB. |
| `{swap_total}` | Swap total in GB. |
| `{swap_percent}` | Swap utilisation percentage. |
| **Temperature** | |
| `{temp_c:[sensor]}` | Temperature in degrees C. Replace `[sensor]` with the sensor label. |
| `{temp_f:[sensor]}` | Temperature in degrees F. Replace `[sensor]` with the sensor label. |
| **Disk** | |
| `{disk_free:[mount]}` | Disk free space in GB. Replace `[mount]` with the disk mountpoint. |
| `{disk_used:[mount]}` | Disk used space in GB. Replace `[mount]` with the disk mountpoint. |
| `{disk_total:[mount]}` | Disk total space in GB. Replace `[mount]` with the disk mountpoint. |
| `{disk_percent:[mount]}` | Disk utilisation percentage. Replace `[mount]` with the disk mountpoint. |
| **Network** | |
| `{net_down:[adapter]}` | Average network download speed in Mbps. Replace `[adapter]` with the adapter name. |
| `{net_up:[adapter]}` | Average network upload speed in Mbps. Replace `[adapter]` with the adapter name. |
| **System** | |
| `{load_average:1}` | 1-minute load average. |
| `{load_average:5}` | 5-minute load average. |
| `{load_average:15}` | 15-minute load average. |
| `{uptime}` | System uptime formatted as `HH:mm`. |
| Token | Default Function | Default Unit | Default Formatting |
|--------------------------|------------------|--------------|--------------------|
| **CPU** | | | |
| `{cpu_frequency[#core]}` | `mean` | MHz | `.2` |
| `{cpu_percent[#core]}` | `mean` | % | `0<2` |
| **Memory** | | | |
| `{memory_free}` | N/A | GB | `0<4.1` |
| `{memory_available}` | N/A | GB | `0<4.1` |
| `{memory_used}` | N/A | GB | `0<4.1` |
| `{memory_total}` | N/A | GB | `0<4.1` |
| `{memory_percent}` | N/A | GB | `0<4.1` |
| `{swap_free}` | N/A | GB | `0<4.1` |
| `{swap_used}` | N/A | GB | `0<4.1` |
| `{swap_total}` | N/A | GB | `0<4.1` |
| `{swap_percent}` | N/A | GB | `0<4.1` |
| **Temperature** | | | |
| `{temp_c[#sensor]}` | `max` | °C | |
| `{temp_f[#sensor]}` | `max` | °F | |
| **Disk** | | | |
| `{disk_free[#mount]}` | `sum` | GB | |
| `{disk_used[#mount]}` | `sum` | GB | |
| `{disk_total[#mount]}` | `sum` | GB | |
| `{disk_percent[#mount]}` | `sum` | % | |
| `{disk_read[#mount]}` | `sum` | MB/s | |
| `{disk_write[#mount]}` | `sum` | MB/s | |
| **Network** | | | |
| `{net_down[#adapter]}` | `sum` | Mb/s | |
| `{net_up[#adapter]}` | `sum` | Mb/s | |
| **System** | | | |
| `{load_average_1}` | N/A | - | `.2` |
| `{load_average_5}` | N/A | - | `.2` |
| `{load_average_15}` | N/A | - | `.2` |
| `{uptime}` | N/A | ??? | ??? |
For Intel CPUs, you can typically use `coretemp-Package-id-0` for the temperature sensor. For AMD, you can use `k10temp-Tccd1`.
#### Functions and names
Many of the tokens operate on a value set, as opposed to an individual value:
- CPU tokens operate on each physical thread.
- Temperature tokens operate on each sensor.
- Disk tokens operate on each mount.
- Network tokens operate on each adapter.
By default, these will apply a function to the full set to reduce them down to a single value.
The list of available functions is shown below:
| Function | Description |
|----------|-----------------------------------------|
| `sum` | Adds each value in the set. |
| `min` | Gets the smallest value in the set. |
| `max` | Gets the largest value in the set. |
| `mean` | Gets the mean average value of the set. |
It is also possible to get only a single value from the set by specifying a name instead of a function.
| Token category | Valid name |
|----------------|-------------------------------------------------------------------------|
| CPU | A CPU thread, eg `cpu0`, `cpu1`, ... |
| Temperature | A sensor name, eg `CPUTIN`. These line up with the output of `sensors`. |
| Disk | A disk mountpoint, eg `/`, `/home`, ... |
| Network | An adapter name, eg `eth0` or `enp30s0`. |
To specify a name or function, use a `@`. For example, to show disk percent for `/home`:
```json
"{disk_percent@/home}%"
```
To show total CPU utilization where each core represents 100% (like `htop` etc):
```json
"{cpu_percent@sum}%"
```
#### Prefixes and units
For tokens which return an appropriate unit, you can specify the SI prefix (or unit in some special cases).
The following options can be supplied:
| Name | Value |
|---------|-------|
| Kilo | `k` |
| Mega | `M` |
| Giga | `G` |
| Tera | `T` |
| Peta | `P` |
| | |
| Kibi | `ki` |
| Mebi | `Mi` |
| Gibi | `Gi` |
| Tebi | `Ti` |
| Pebi | `Pi` |
| | |
| Kilobit | `kb` |
| Megabit | `Mb` |
| Gigabit | `Gb` |
To specify a prefix or unit, use a `#`. For example, to show free total disk space in terabytes:
```json
"{disk_free#T} TB"
```
#### Formatting
To control the formatting of the resultant number,
a subset of Rust's string formatting is implemented. This includes:
- Width
- Fill/Alignment
- Precision
Formatting is specified with a `:` and MUST be the last part of a token.
##### Width
The width controls the minimum string length of the value.
Specifying just a width will left-pad the value with `0` until the value reaches the target length.
The width can be any value from `1-9`. Larger values are not supported.
For example, to render CPU usage as `045%`:
```json
"{cpu_usage:3}%"
```
##### Fill/Alignment
These options can be used to control the `width` property.
To specify the fill and alignment, prefix the width with a character and a direction.
Fill characters can be any single UTF-8 character EXCEPT 1-9. Alignment must be one of:
- `<` - Left fill
- `^` - Center fill
- `>` - Right fill
For example, to render CPU usage as ` 45%`:
```json
"{cpu_usage: <3}%"
```
##### Precision
The number of decimal places a value is shown to can be controlled using precision.
Any value is supported.
To specify precision, include a `.` followed by the value. If other options are supplied, this MUST come after.
For example, to render used disk space to 2dp:
```json
"{disk_used:.2} GB"
```
---
#### Combining Options
Each of the token options can be combined to create more complex solutions.
Putting it all together, you could show the free disk space on your `/home` partition in terabytes,
left-padded with spaces to a min width of 5, and shown to 2dp as follows:
```json
"{disk_used@/home#T: <5.2} TB"
```
## Styling

View file

@ -6,11 +6,11 @@ Displays a fully interactive icon tray using the KDE `libappindicator` protocol.
> Type: `tray`
| Name | Type | Default | Description |
|----------------------|-----------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `direction` | `string` | `left_to_right` if bar is horizontal, `top_to_bottom` otherwise | Direction to display the tray items. Possible values: `top_to_bottom`, `bottom_to_top`, `left_to_right`, `right_to_left` |
| `icon_size` | `integer` | `16` | Size in pixels to display tray icons as. |
| `prefer_theme_icons` | `bool` | `true` | 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. |
| Name | Type | Default | Description |
|----------------------|------------------------------------------------------------|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `direction` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | Matches bar orientation | The direction in which to pack tray icons. |
| `icon_size` | `integer` | `16` | Size in pixels to display tray icons as. |
| `prefer_theme_icons` | `bool` | `true` | 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. |
<details>
<summary>JSON</summary>
@ -55,12 +55,10 @@ end:
```corn
{
end = [
{
end = [{
type = "tray"
direction = "top_to_bottom"
}
]
}]
}
```

View file

@ -1,5 +1,8 @@
Displays system power information such as the battery percentage, and estimated time to empty.
> [!NOTE]
> This module requires that `upower` is installed and its service running.
`TODO: ADD SCREENSHOT`
[//]: # (![Screenshot]&#40;https://user-images.githubusercontent.com/5057870/184540521-2278bdec-9742-46f0-9ac2-58a7b6f6ea1d.png&#41;)

View file

@ -1,6 +1,7 @@
Displays the current volume level.
Clicking on the widget opens a volume mixer, which allows you to change the device output level,
the default playback device, and control application volume levels individually.
Use `truncate` option to control the display of application titles in the volume mixer.
This requires PulseAudio to function (`pipewire-pulse` is supported).
@ -10,14 +11,18 @@ This requires PulseAudio to function (`pipewire-pulse` is supported).
> Type: `volume`
| Name | Type | Default | Description |
|-----------------------|----------|------------------------|----------------------------------------------------------------------------------------------------------------|
| `format` | `string` | `{icon} {percentage}%` | Format string to use for the widget button label. |
| `max_volume` | `float` | `100` | Maximum value to allow volume sliders to reach. Pulse supports values > 100 but this may result in distortion. |
| `icons.volume_high` | `string` | `󰕾` | Icon to show for high volume levels. |
| `icons.volume_medium` | `string` | `󰖀` | Icon to show for medium volume levels. |
| `icons.volume_low` | `string` | `󰕿` | Icon to show for low volume levels. |
| `icons.muted` | `string` | `󰝟` | Icon to show for muted outputs. |
| Name | Type | Default | Description |
|-----------------------|------------------------------------------------------|------------------------|----------------------------------------------------------------------------------------------------------------|
| `format` | `string` | `{icon} {percentage}%` | Format string to use for the widget button label. |
| `max_volume` | `float` | `100` | Maximum value to allow volume sliders to reach. Pulse supports values > 100 but this may result in distortion. |
| `icons.volume_high` | `string` | `󰕾` | Icon to show for high volume levels. |
| `icons.volume_medium` | `string` | `󰖀` | Icon to show for medium volume levels. |
| `icons.volume_low` | `string` | `󰕿` | Icon to show for low volume levels. |
| `icons.muted` | `string` | `󰝟` | Icon to show for muted outputs. |
| `truncate` | `'start'` or `'middle'` or `'end'` or `off` or `Map` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. Use the long-hand `Map` version if specifying a length. |
| `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `off` | The location of the ellipses and where to truncate text from. Leave null to avoid truncating. |
| `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. |
| `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. |
<details>
<summary>JSON</summary>
@ -29,6 +34,7 @@ This requires PulseAudio to function (`pipewire-pulse` is supported).
"type": "volume",
"format": "{icon} {percentage}%",
"max_volume": 100,
"truncate": "middle",
"icons": {
"volume_high": "󰕾",
"volume_medium": "󰖀",
@ -51,6 +57,7 @@ This requires PulseAudio to function (`pipewire-pulse` is supported).
type = "volume"
format = "{icon} {percentage}%"
max_volume = 100
truncate = "middle"
[end.icons]
volume_high = "󰕾"
@ -69,6 +76,7 @@ end:
- type: "volume"
format: "{icon} {percentage}%"
max_volume: 100
truncate: "middle"
icons:
volume_high: "󰕾"
volume_medium: "󰖀"
@ -88,6 +96,7 @@ end:
type = "volume"
format = "{icon} {percentage}%"
max_volume = 100
truncate = "end"
icons.volume_high = "󰕾"
icons.volume_medium = "󰖀"
icons.volume_low = "󰕿"
@ -125,4 +134,4 @@ The following tokens can be used in the `format` config option:
| `.popup-volume .apps-box .app-box .slider` | Application volume slider. |
| `.popup-volume .apps-box .app-box .btn-mute` | Application volume mute toggle button. |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -1,4 +1,5 @@
> ⚠ **This module is currently only supported on Sway and Hyprland**
> [!IMPORTANT]
> This module is currently only supported on Sway, Hyprland and Niri**
Shows all current workspaces. Clicking a workspace changes focus to it.
@ -8,14 +9,14 @@ Shows all current workspaces. Clicking a workspace changes focus to it.
> Type: `workspaces`
| Name | Type | Default | Description |
|----------------|---------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name_map` | `Map<string, string or image>` | `{}` | A map of actual workspace names to their display labels/images. Workspaces use their actual name if not present in the map. See [here](images) for information on images. |
| `favorites` | `Map<string, string[]>` or `string[]` | `[]` | Workspaces to always show. This can be for all monitors, or a map to set per monitor. |
| `hidden` | `string[]` | `[]` | A list of workspace names to never show |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `all_monitors` | `boolean` | `false` | Whether to display workspaces from all monitors. When `false`, only shows workspaces on the current monitor. |
| `sort` | `'added'` or `'alphanumeric'` | `alphanumeric` | The method used for sorting workspaces. `added` always appends to the end, `alphanumeric` sorts by number/name. |
| Name | Type | Default | Description |
|----------------|---------------------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name_map` | `Map<string, string or image>` | `{}` | A map of actual workspace names to their display labels/images. Workspaces use their actual name if not present in the map. See [here](images) for information on images. |
| `favorites` | `Map<string, string[]>` or `string[]` | `[]` | Workspaces to always show. This can be for all monitors, or a map to set per monitor. |
| `hidden` | `string[]` | `[]` | A list of workspace names to never show |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `all_monitors` | `boolean` | `false` | Whether to display workspaces from all monitors. When `false`, only shows workspaces on the current monitor. |
| `sort` | `'added'` or `'label'` or `'name'` | `label` | The method used for sorting workspaces. `added` always appends to the end, `label` sorts by displayed value, and `name` sorts by workspace name. |
<details>
<summary>JSON</summary>
@ -98,15 +99,16 @@ end:
## Styling
| Selector | Description |
|--------------------------------|--------------------------------------|
| `.workspaces` | Workspaces widget box |
| `.workspaces .item` | Workspace button |
| `.workspaces .item.focused` | Workspace button (workspace focused) |
| Selector | Description |
| ------------------------------ | ------------------------------------------------------- |
| `.workspaces` | Workspaces widget box |
| `.workspaces .item` | Workspace button |
| `.workspaces .item.focused` | Workspace button (workspace focused) |
| `.workspaces .item.visible` | Workspace button (workspace visible, including focused) |
| `.workspaces .item.inactive` | Workspace button (favourite, not currently open)
| `.workspaces .item .icon` | Workspace button icon (any type) |
| `.workspaces .item .text-icon` | Workspace button icon (textual only) |
| `.workspaces .item .image` | Workspace button icon (image only) |
| `.workspaces .item.urgent` | Workspace button (workspace contains urgent window) |
| `.workspaces .item.inactive` | Workspace button (favourite, not currently open) |
| `.workspaces .item .icon` | Workspace button icon (any type) |
| `.workspaces .item .text-icon` | Workspace button icon (textual only) |
| `.workspaces .item .image` | Workspace button icon (image only) |
For more information on styling, please see the [styling guide](styling-guide).

View file

@ -55,13 +55,12 @@ let {
interval.networks = 3
format = [
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C"
" {memory_used} / {memory_total} GB ({memory_percent}%)"
"| {swap_used} / {swap_total} GB ({swap_percent}%)"
"󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)"
"󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps"
"󰖡 {load_average:1} | {load_average:5} | {load_average:15}"
"󰥔 {uptime}"
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C"
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)"
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s"
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps"
"󰖡 {load_average1} | {load_average5} | {load_average15}"
"󰥔 {uptime}"
]
}

View file

@ -1,5 +1,4 @@
{
"$schema": "https://f.jstanger.dev/github/ironbar/schema.json",
"anchor_to_edges": true,
"position": "bottom",
"icon_theme": "Paper",
@ -64,12 +63,11 @@
"networks": 3
},
"format": [
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
" {memory_used} / {memory_total} GB ({memory_percent}%)",
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
"󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
"󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps",
"󰖡 {load_average:1} | {load_average:5} | {load_average:15}",
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps",
"󰖡 {load_average1} | {load_average5} | {load_average15}",
"󰥔 {uptime}"
]
},

View file

@ -53,12 +53,11 @@ interval = 500
[[end]]
type = "sys_info"
format = [
" {cpu_percent}% | {temp_c:k10temp-Tccd1}°C",
" {memory_used} / {memory_total} GB ({memory_percent}%)",
"| {swap_used} / {swap_total} GB ({swap_percent}%)",
"󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)",
"󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps",
"󰖡 {load_average:1} | {load_average:5} | {load_average:15}",
" {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C",
" {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)",
"󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s",
"󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps",
"󰖡 {load_average1} | {load_average5} | {load_average15}",
"󰥔 {uptime}",
]

View file

@ -1,4 +1,3 @@
$schema: https://f.jstanger.dev/github/ironbar/schema.json
anchor_to_edges: true
position: bottom
icon_theme: Paper
@ -44,12 +43,11 @@ end:
disks: 300
networks: 3
format:
-  {cpu_percent}% | {temp_c:k10temp-Tccd1}°C
-  {memory_used} / {memory_total} GB ({memory_percent}%)
- '| {swap_used} / {swap_total} GB ({swap_percent}%)'
- 󰋊 {disk_used:/} / {disk_total:/} GB ({disk_percent:/}%)
- 󰓢 {net_down:enp39s0} / {net_up:enp39s0} Mbps
- 󰖡 {load_average:1} | {load_average:5} | {load_average:15}
-  {cpu_percent}% | {cpu_frequency} GHz | {temp_c@CPUTIN}°C
-  {memory_used} / {memory_total} GB ({memory_available} | {memory_percent2}%) | {swap_used} / {swap_total} GB ({swap_free} | {swap_percent}%)
- 󰋊 {disk_used#T@/:.1} / {disk_total#T@/:.1} TB ({disk_percent@/}%) | {disk_read} / {disk_write} MB/s
- 󰓢 {net_down@enp39s0} / {net_up@enp39s0} Mbps
- 󰖡 {load_average1} | {load_average5} | {load_average15}
- 󰥔 {uptime}
- type: volume
format: '{icon} {percentage}%'

102
examples/menu/default.corn Normal file
View file

@ -0,0 +1,102 @@
let {
$menu = [
{
type = "xdg_entry"
label = "Accessories"
icon = "accessories"
categories = [
"Accessibility"
"Core"
"Legacy"
"Utility"
]
}
{
type = "xdg_entry"
label = "Development"
icon = "applications-development"
categories = [
"Development"
]
}
{
type = "xdg_entry"
label = "Education"
icon = "applications-education"
categories = [
"Education"
]
}
{
type = "xdg_entry"
label = "Games"
icon = "applications-games"
categories = [
"Games"
]
}
{
type = "xdg_entry"
label = "Graphics"
icon = "applications-graphics"
categories = [
"Graphics"
]
}
{
type = "xdg_entry"
label = "Multimedia"
icon = "applications-multimedia"
categories = [
"Audio"
"Video"
"AudioVideo"
]
}
{
type = "xdg_entry"
label = "Network"
icon = "applications-internet"
categories = [
"Network"
]
}
{
type = "xdg_entry"
label = "Office"
icon = "applications-office"
categories = [
"Office"
]
}
{
type = "xdg_entry"
label = "Science"
icon = "applications-science"
categories = [
"Science"
]
}
{
type = "xdg_entry"
label = "System"
icon = "applications-system"
categories = [
"Emulator"
"System"
]
}
{ type = "xdg_other" }
{
type = "xdg_entry"
label = "Settings"
icon = "preferences-system"
categories = [
"Settings"
"Screensaver"
]
}
]
} in {
start = [ { type = "menu" center = $menu } ]
}

107
examples/menu/default.json Normal file
View file

@ -0,0 +1,107 @@
{
"start": [
{
"type": "menu",
"center": [
{
"type": "xdg_entry",
"label": "Accessories",
"icon": "accessories",
"categories": [
"Accessibility",
"Core",
"Legacy",
"Utility"
]
},
{
"type": "xdg_entry",
"label": "Development",
"icon": "applications-development",
"categories": [
"Development"
]
},
{
"type": "xdg_entry",
"label": "Education",
"icon": "applications-education",
"categories": [
"Education"
]
},
{
"type": "xdg_entry",
"label": "Games",
"icon": "applications-games",
"categories": [
"Games"
]
},
{
"type": "xdg_entry",
"label": "Graphics",
"icon": "applications-graphics",
"categories": [
"Graphics"
]
},
{
"type": "xdg_entry",
"label": "Multimedia",
"icon": "applications-multimedia",
"categories": [
"Audio",
"Video",
"AudioVideo"
]
},
{
"type": "xdg_entry",
"label": "Network",
"icon": "applications-internet",
"categories": [
"Network"
]
},
{
"type": "xdg_entry",
"label": "Office",
"icon": "applications-office",
"categories": [
"Office"
]
},
{
"type": "xdg_entry",
"label": "Science",
"icon": "applications-science",
"categories": [
"Science"
]
},
{
"type": "xdg_entry",
"label": "System",
"icon": "applications-system",
"categories": [
"Emulator",
"System"
]
},
{
"type": "xdg_other"
},
{
"type": "xdg_entry",
"label": "Settings",
"icon": "preferences-system",
"categories": [
"Settings",
"Screensaver"
]
}
]
}
]
}

View file

@ -0,0 +1,87 @@
[[start]]
type = "menu"
[[start.center]]
type = "xdg_entry"
label = "Accessories"
icon = "accessories"
categories = [
"Accessibility",
"Core",
"Legacy",
"Utility",
]
[[start.center]]
type = "xdg_entry"
label = "Development"
icon = "applications-development"
categories = ["Development"]
[[start.center]]
type = "xdg_entry"
label = "Education"
icon = "applications-education"
categories = ["Education"]
[[start.center]]
type = "xdg_entry"
label = "Games"
icon = "applications-games"
categories = ["Games"]
[[start.center]]
type = "xdg_entry"
label = "Graphics"
icon = "applications-graphics"
categories = ["Graphics"]
[[start.center]]
type = "xdg_entry"
label = "Multimedia"
icon = "applications-multimedia"
categories = [
"Audio",
"Video",
"AudioVideo",
]
[[start.center]]
type = "xdg_entry"
label = "Network"
icon = "applications-internet"
categories = ["Network"]
[[start.center]]
type = "xdg_entry"
label = "Office"
icon = "applications-office"
categories = ["Office"]
[[start.center]]
type = "xdg_entry"
label = "Science"
icon = "applications-science"
categories = ["Science"]
[[start.center]]
type = "xdg_entry"
label = "System"
icon = "applications-system"
categories = [
"Emulator",
"System",
]
[[start.center]]
type = "xdg_other"
[[start.center]]
type = "xdg_entry"
label = "Settings"
icon = "preferences-system"
categories = [
"Settings",
"Screensaver",
]

68
examples/menu/default.yml Normal file
View file

@ -0,0 +1,68 @@
start:
- type: menu
center:
- type: xdg_entry
label: Accessories
icon: accessories
categories:
- Accessibility
- Core
- Legacy
- Utility
- type: xdg_entry
label: Development
icon: applications-development
categories:
- Development
- type: xdg_entry
label: Education
icon: applications-education
categories:
- Education
- type: xdg_entry
label: Games
icon: applications-games
categories:
- Games
- type: xdg_entry
label: Graphics
icon: applications-graphics
categories:
- Graphics
- type: xdg_entry
label: Multimedia
icon: applications-multimedia
categories:
- Audio
- Video
- AudioVideo
- type: xdg_entry
label: Network
icon: applications-internet
categories:
- Network
- type: xdg_entry
label: Office
icon: applications-office
categories:
- Office
- type: xdg_entry
label: Science
icon: applications-science
categories:
- Science
- type: xdg_entry
label: System
icon: applications-system
categories:
- Emulator
- System
- type: xdg_other
- type: xdg_entry
label: Settings
icon: preferences-system
categories:
- Settings
- Screensaver

View file

@ -201,6 +201,10 @@ scale trough {
background-color: @color_bg_dark;
}
.workspaces .item.urgent {
background-color: @color_urgent;
}
.workspaces .item:hover {
box-shadow: inset 0 -3px;
}

View file

@ -1,60 +0,0 @@
let {
$config_dir = "/home/jake/.config/ironbar"
$workspaces = { type = "workspaces" }
$launcher = { type = "launcher" }
$volume = {
type = "volume"
format = "{icon} {percentage}%"
max_volume = 100
icons.volume_high = "󰕾"
icons.volume_medium = "󰖀"
icons.volume_low = "󰕿"
icons.muted = "󰝟"
}
$network_manager = { type = "networkmanager" }
$clock = {
type = "clock"
// disable_popup = true
// format = "<span color='#f2777a'>%d/%m/%Y</span> <span color='#69c'>%H:%M:%S</span>"
}
$tray = { type = "tray" prefer_theme_icons = false }
// $label = { type = "label" label = "<span color='#color'>hello</span>" }
$label = { type = "label" label = "#random" }
$clipboard = { type = "clipboard" }
$notifications = {
type = "notifications"
show_count = true
icons.closed_none = "󰍥"
icons.closed_some = "󱥂"
icons.closed_dnd = "󱅯"
icons.open_none = "󰍡"
icons.open_some = "󱥁"
icons.open_dnd = "󱅮"
}
$focused = { type = "focused" }
$cairo = { type = "cairo" path = "$config_dir/clock.lua" frequency = 50 width = 300 height = 300 }
$custom = {
type = "custom"
bar = [ { type = "button" on_click = "popup:toggle" widgets = [ $focused ] } ]
popup = [ { type = "box" orientation = "v" widgets = [ $clock $cairo ] } ]
}
$mpris = { type = "music" }
} in {
// ironvar_defaults.color = "red"
position = "bottom"
icon_theme = "Paper"
start = [ $workspaces $label ]
center = [ $custom ]
end = [ $notifications $clock ]
}

86
flake.lock generated
View file

@ -1,22 +1,17 @@
{
"nodes": {
"crane": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"flake-compat": {
"locked": {
"lastModified": 1722704454,
"narHash": "sha256-lcut8uZMSa80z+aWpxg+9nM8BKWtpU59rtcpMXtHd1Q=",
"owner": "ipetkov",
"repo": "crane",
"rev": "852a59f9672c3413d75bca2b3e9cb4c661cacfc3",
"lastModified": 1747046372,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
@ -25,11 +20,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1721727458,
"narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=",
"lastModified": 1745925850,
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
"owner": "nix-community",
"repo": "naersk",
"rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11",
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
"type": "github"
},
"original": {
@ -38,27 +33,44 @@
"type": "github"
}
},
"nix-systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1722640603,
"narHash": "sha256-TcXjLVNd3VeH1qKPH335Tc4RbFDbZQX+d7rqnDUoRaY=",
"lastModified": 1751251929,
"narHash": "sha256-IJWIzZSkBsDzS7iS/iwSwur+xFkWqeLYC4kdf8ObtOM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "81610abc161d4021b29199aa464d6a1a521e0cc9",
"rev": "b95255df2360a45ddbb03817a68869d5cb01bf96",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1722630782,
"narHash": "sha256-hMyG9/WlUi0Ho9VkRrrez7SeNlDzLxalm9FwY7n/Noo=",
"lastModified": 1751011381,
"narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d04953086551086b44b6f3c6b7eeb26294f207da",
"rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7",
"type": "github"
},
"original": {
@ -70,30 +82,10 @@
},
"root": {
"inputs": {
"crane": "crane",
"flake-compat": "flake-compat",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1722738111,
"narHash": "sha256-cWD5pCs9AYb+512/yCx9D0Pl5KcmyuXHeJpsDw/D1vs=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "27ec296d93cb4b2d03e8cbd019b1b4cde8c34280",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
"nix-systems": "nix-systems",
"nixpkgs": "nixpkgs_2"
}
}
},

280
flake.nix
View file

@ -3,224 +3,88 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
crane = {
url = "github:ipetkov/crane";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-compat.url = "github:edolstra/flake-compat";
naersk.url = "github:nix-community/naersk";
nix-systems.url = "github:nix-systems/default-linux";
};
outputs = { self, nixpkgs, rust-overlay, crane, naersk, ... }:
let
inherit (nixpkgs) lib;
genSystems = lib.genAttrs [ "aarch64-linux" "x86_64-linux" ];
pkgsFor = system:
import nixpkgs {
inherit system;
overlays = [ self.overlays.default rust-overlay.overlays.default ];
outputs = {
self,
nixpkgs,
naersk,
nix-systems,
...
}: let
forAllSystems = function:
nixpkgs.lib.genAttrs (import nix-systems) (system: function nixpkgs.legacyPackages.${system});
mkDate = longDate: (nixpkgs.lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
in {
# Devshell
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
packages = builtins.attrValues {
inherit
(pkgs)
cargo
clippy
rustfmt
gtk3
gtk-layer-shell
gcc
openssl
libdbusmenu-gtk3
libpulseaudio
libinput
libevdev
luajit
;
inherit (pkgs.luajitPackages) lgi;
};
mkRustToolchain = pkgs:
pkgs.rust-bin.stable.latest.default.override {
extensions = [ "rust-src" ];
nativeBuildInputs = [
pkgs.pkg-config
];
};
});
# Packages
packages = forAllSystems (pkgs: {
ironbar = let
props = builtins.fromTOML (builtins.readFile ./Cargo.toml);
version =
props.package.version
+ "+date="
+ (mkDate (self.lastModifiedDate or "19700101"))
+ "_"
+ (self.shortRev or "dirty");
naersk' = pkgs.callPackage naersk {};
in
pkgs.callPackage ./nix/package.nix {
inherit version;
naersk = naersk';
};
in {
overlays.default = final: prev:
let
rust = mkRustToolchain final;
default = self.packages.${pkgs.hostPlatform.system}.ironbar;
});
craneLib = (crane.mkLib final).overrideToolchain rust;
# Apps
apps = forAllSystems (pkgs: {
ironbar = {
type = "app";
program = pkgs.lib.getExe self.packages.${pkgs.hostPlatform.system}.ironbar;
};
default = self.apps.ironbar;
});
naersk' = prev.callPackage naersk {
cargo = rust;
rustc = rust;
};
rustPlatform = prev.makeRustPlatform {
cargo = rust;
rustc = rust;
};
props = builtins.fromTOML (builtins.readFile ./Cargo.toml);
mkDate = longDate:
(lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
builder = "naersk";
in {
ironbar = let
version = props.package.version + "+date="
+ (mkDate (self.lastModifiedDate or "19700101")) + "_"
+ (self.shortRev or "dirty");
in if builder == "crane" then
prev.callPackage ./nix/default.nix {
inherit version;
inherit rustPlatform;
builderName = builder;
builder = craneLib;
}
else if builder == "naersk" then
prev.callPackage ./nix/default.nix {
inherit version;
inherit rustPlatform;
builderName = builder;
builder = naersk';
}
else
prev.callPackage ./nix/default.nix {
inherit version;
inherit rustPlatform;
builderName = builder;
};
};
packages = genSystems (system:
let pkgs = pkgsFor system;
in (self.overlays.default pkgs pkgs) // {
default = self.packages.${system}.ironbar;
});
apps = genSystems (system:
let pkgs = pkgsFor system;
in rec {
ironbar = {
type = "app";
program = "${pkgs.ironbar}/bin/ironbar";
};
default = ironbar;
});
devShells = genSystems (system:
let
pkgs = pkgsFor system;
rust = mkRustToolchain pkgs;
in {
default = pkgs.mkShell {
packages = with pkgs; [
rust
rust-analyzer-unwrapped
gcc
gtk3
gtk-layer-shell
pkg-config
openssl
gdk-pixbuf
glib
glib-networking
shared-mime-info
gnome.adwaita-icon-theme
hicolor-icon-theme
gsettings-desktop-schemas
libxkbcommon
libpulseaudio
luajit
luajitPackages.lgi
];
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";
};
});
homeManagerModules.default = { config, lib, pkgs, ... }:
let
cfg = config.programs.ironbar;
defaultIronbarPackage =
self.packages.${pkgs.hostPlatform.system}.default;
jsonFormat = pkgs.formats.json { };
in {
options.programs.ironbar = {
enable = lib.mkEnableOption "ironbar status bar";
package = lib.mkOption {
type = with lib.types; package;
default = defaultIronbarPackage;
description = "The package for ironbar to use.";
};
systemd = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.isLinux;
description = "Whether to enable to systemd service for ironbar.";
};
style = lib.mkOption {
type = lib.types.either (lib.types.lines) (lib.types.path);
default = "";
description = "The stylesheet to apply to ironbar.";
};
config = lib.mkOption {
type = jsonFormat.type;
default = { };
description = "The config to pass to ironbar.";
};
features = lib.mkOption {
type = lib.types.listOf lib.types.nonEmptyStr;
default = [ ];
description = "The features to be used.";
};
};
config = let pkg = cfg.package.override { features = cfg.features; };
in lib.mkIf cfg.enable {
home.packages = [ pkg ];
xdg.configFile = {
"ironbar/config.json" = lib.mkIf (cfg.config != "") {
source = jsonFormat.generate "ironbar-config" cfg.config;
};
"ironbar/style.css" = lib.mkIf (cfg.style != "") (
if builtins.isPath cfg.style || lib.isStorePath cfg.style then
{ source = cfg.style; }
else
{ text = cfg.style; }
);
};
systemd.user.services.ironbar = lib.mkIf cfg.systemd {
Unit = {
Description = "Systemd service for Ironbar";
Requires = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
ExecStart = "${pkg}/bin/ironbar";
};
Install.WantedBy = with config.wayland.windowManager; [
(lib.mkIf hyprland.systemd.enable "hyprland-session.target")
(lib.mkIf sway.systemd.enable "sway-session.target")
(lib.mkIf river.systemd.enable "river-session.target")
];
};
};
};
};
homeManagerModules.default = import ./nix/module.nix self;
};
nixConfig = {
extra-substituters = [ "https://cache.garnix.io" ];
extra-trusted-public-keys =
[ "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" ];
extra-substituters = ["https://cache.garnix.io"];
extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="];
};
}

View file

@ -1,129 +0,0 @@
{
gtk3,
gdk-pixbuf,
librsvg,
webp-pixbuf-loader,
gobject-introspection,
glib-networking,
glib,
shared-mime-info,
gsettings-desktop-schemas,
wrapGAppsHook,
gtk-layer-shell,
gnome,
libxkbcommon,
libpulseaudio,
openssl,
luajit,
luajitPackages,
pkg-config,
hicolor-icon-theme,
rustPlatform,
lib,
version ? "git",
features ? [],
builderName ? "nix",
builder ? {},
}: let
hasFeature = f: features == [ ] || builtins.elem f features;
basePkg = rec {
inherit version;
pname = "ironbar";
src = builtins.path {
name = "ironbar";
path = lib.cleanSource ../.;
};
nativeBuildInputs = [
pkg-config
wrapGAppsHook
gobject-introspection
];
buildInputs = [
gtk3
gdk-pixbuf
glib
gtk-layer-shell
glib-networking
shared-mime-info
gnome.adwaita-icon-theme
hicolor-icon-theme
gsettings-desktop-schemas
libxkbcommon ]
++ (if hasFeature "http" then [ openssl ] else [])
++ (if hasFeature "volume" then [ libpulseaudio ] else [])
++ (if hasFeature "cairo" then [ luajit ] else []);
propagatedBuildInputs = [ gtk3 ];
lgi = luajitPackages.lgi;
gappsWrapperArgs = ''
# Thumbnailers
--prefix XDG_DATA_DIRS : "${gdk-pixbuf}/share"
--prefix XDG_DATA_DIRS : "${librsvg}/share"
--prefix XDG_DATA_DIRS : "${webp-pixbuf-loader}/share"
--prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
# gtk-launch
--suffix PATH : "${lib.makeBinPath [ gtk3 ]}"
''
+ (if hasFeature "cairo" then ''
--prefix LUA_PATH : "./?.lua;${lgi}/share/lua/5.1/?.lua;${lgi}/share/lua/5.1/?/init.lua;${luajit}/share/lua/5.1/\?.lua;${luajit}/share/lua/5.1/?/init.lua"
--prefix LUA_CPATH : "./?.so;${lgi}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/loadall.so"
'' else "");
preFixup = ''
gappsWrapperArgs+=(
${gappsWrapperArgs}
)
'';
passthru = {
updateScript = gnome.updateScript {
packageName = pname;
attrPath = "gnome.${pname}";
};
};
meta = with lib; {
homepage = "https://github.com/JakeStanger/ironbar";
description =
"Customisable gtk-layer-shell wlroots/sway bar written in rust.";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "ironbar";
};
};
flags = let
noDefault = if features == [ ] then "" else "--no-default-features";
featuresStr = if features == [ ] then
""
else
''-F "${builtins.concatStringsSep "," features}"'';
in [ noDefault featuresStr ];
in if builderName == "naersk" then
builder.buildPackage (basePkg // { cargoBuildOptions = old: old ++ flags; })
else if builderName == "crane" then
builder.buildPackage (basePkg // {
cargoExtraArgs = builtins.concatStringsSep " " flags;
doCheck = false;
})
else
rustPlatform.buildRustPackage (basePkg // {
buildNoDefaultFeatures = features != [ ];
buildFeatures = features;
cargoDeps = rustPlatform.importCargoLock { lockFile = ../Cargo.lock; };
cargoLock.lockFile = ../Cargo.lock;
cargoLock.outputHashes."stray-0.1.3" =
"sha256-7mvsWZFmPWti9AiX67h6ZlWiVVRZRWIxq3pVaviOUtc=";
})

96
nix/module.nix Normal file
View file

@ -0,0 +1,96 @@
self: {
config,
lib,
pkgs,
...
}: let
cfg = config.programs.ironbar;
defaultIronbarPackage = self.packages.${pkgs.hostPlatform.system}.default;
jsonFormat = pkgs.formats.json {};
inherit
(lib)
types
mkOption
mkEnableOption
mkIf
getExe
;
in {
options.programs.ironbar = {
enable = mkEnableOption "ironbar status bar";
package = mkOption {
type = types.package;
default = defaultIronbarPackage;
apply = pkg: pkg.override {features = cfg.features;};
description = "The package for ironbar to use.";
};
systemd = mkEnableOption "systemd service for ironbar.";
style = mkOption {
type = types.either (types.lines) (types.path);
default = "";
description = "The stylesheet to apply to ironbar.";
};
config = mkOption {
type = jsonFormat.type;
default = null;
description = "The config to pass to ironbar.";
};
features = mkOption {
type = types.listOf types.nonEmptyStr;
default = [];
description = "The features to be used.";
};
};
config = mkIf cfg.enable {
home.packages = [
cfg.package
];
xdg.configFile = {
"ironbar/config.json" = mkIf (cfg.config != null) {
onChange = "${getExe cfg.package} reload";
source = jsonFormat.generate "ironbar-config" cfg.config;
};
"ironbar/style.css" = mkIf (cfg.style != "") (
if builtins.isPath cfg.style || lib.isStorePath cfg.style
then {source = cfg.style;}
else {text = cfg.style;}
);
};
systemd.user.services.ironbar = mkIf cfg.systemd {
Unit = {
Description = "Systemd service for Ironbar";
Documentation = "https://github.com/JakeStanger/ironbar";
PartOf = [
config.wayland.systemd.target
"tray.target"
];
After = [config.wayland.systemd.target];
ConditionEnvironment = "WAYLAND_DISPLAY";
};
Service = {
ExecReload = "${getExe cfg.package} reload";
ExecStart = "${getExe cfg.package}";
KillMode = "mixed";
Restart = "on-failure";
};
Install.WantedBy = [
config.wayland.systemd.target
"tray.target"
(mkIf config.wayland.windowManager.hyprland.enable "hyprland-session.target")
(mkIf config.wayland.windowManager.sway.enable "sway-session.target")
(mkIf config.wayland.windowManager.river.enable "river-session.target")
];
};
};
}

152
nix/package.nix Normal file
View file

@ -0,0 +1,152 @@
{
gtk3,
gdk-pixbuf,
librsvg,
webp-pixbuf-loader,
gobject-introspection,
glib-networking,
glib,
shared-mime-info,
gsettings-desktop-schemas,
wrapGAppsHook,
gtk-layer-shell,
gnome,
libxkbcommon,
libdbusmenu-gtk3,
libpulseaudio,
libinput,
libevdev,
openssl,
luajit,
luajitPackages,
pkg-config,
installShellFiles,
adwaita-icon-theme,
hicolor-icon-theme,
lib,
version ? "git",
features ? [],
naersk,
}: let
hasFeature = f: features == [] || builtins.elem f features;
flags = let
noDefault =
if features == []
then ""
else "--no-default-features";
featuresStr =
if features == []
then ""
else ''-F "${builtins.concatStringsSep "," features}"'';
in [
noDefault
featuresStr
];
lgi = luajitPackages.lgi;
gappsWrapperArgs =
''
# Thumbnailers
--prefix XDG_DATA_DIRS : "${gdk-pixbuf}/share"
--prefix XDG_DATA_DIRS : "${librsvg}/share"
--prefix XDG_DATA_DIRS : "${webp-pixbuf-loader}/share"
--prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
# gtk-launch
--suffix PATH : "${lib.makeBinPath [gtk3]}"
''
+ lib.optionalString (hasFeature "cairo") ''
--prefix LUA_PATH : "./?.lua;${lgi}/share/lua/5.1/?.lua;${lgi}/share/lua/5.1/?/init.lua;${luajit}/share/lua/5.1/\?.lua;${luajit}/share/lua/5.1/?/init.lua"
--prefix LUA_CPATH : "./?.so;${lgi}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/?.so;${luajit}/lib/lua/5.1/loadall.so"
'';
in
naersk.buildPackage {
inherit version;
pname = "ironbar";
src = let
fs = lib.fileset;
root = ../.;
nixRelated = fs.fileFilter (file: file.hasExt "nix" || file.name == "flake.lock") root;
cicdRelated = fs.unions [
(lib.path.append root "Dockerfile")
(lib.path.append root ".github")
];
ideRelated = fs.unions [
(lib.path.append root ".idea")
];
in
fs.toSource {
inherit root;
# NOTE: can possibly filter out more
fileset = fs.difference root (
fs.unions [
nixRelated
cicdRelated
ideRelated
]
);
};
nativeBuildInputs = [
pkg-config
wrapGAppsHook
gobject-introspection
installShellFiles
];
buildInputs =
[
gtk3
gdk-pixbuf
glib
gtk-layer-shell
glib-networking
shared-mime-info
adwaita-icon-theme
hicolor-icon-theme
gsettings-desktop-schemas
libxkbcommon
]
++ lib.optionals (hasFeature "http") [openssl]
++ lib.optionals (hasFeature "tray") [libdbusmenu-gtk3]
++ lib.optionals (hasFeature "volume") [libpulseaudio]
++ lib.optionals (hasFeature "cairo") [luajit]
++ lib.optionals (hasFeature "keyboard") [
libinput
libevdev
];
propagatedBuildInputs = [gtk3];
cargoBuildOptions = old: old ++ flags;
preFixup = ''
gappsWrapperArgs+=(
${gappsWrapperArgs}
)
'';
postInstall = ''
installShellCompletion --cmd ironbar \
--bash target/completions/ironbar.bash \
--fish target/completions/ironbar.fish \
--zsh target/completions/_ironbar
'';
passthru = {
updateScript = gnome.updateScript {
packageName = "ironbar";
attrPath = "gnome.ironbar";
};
};
meta = {
homepage = "https://github.com/JakeStanger/ironbar";
description = "Customisable gtk-layer-shell wlroots/sway bar written in rust.";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "ironbar";
};
}

View file

@ -1,20 +1,12 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
cargo
clippy
rustfmt
gtk3
gtk-layer-shell
gcc
openssl
libpulseaudio
luajit
luajitPackages.lgi
];
nativeBuildInputs = with pkgs; [
pkg-config
];
}
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) {src = ./.;}).shellNix

View file

@ -1,16 +1,16 @@
use crate::Ironbar;
use crate::config::{BarConfig, BarPosition, MarginConfig, ModuleConfig};
use crate::modules::{BarModuleFactory, ModuleInfo, ModuleLocation};
use crate::popup::Popup;
use crate::Ironbar;
use color_eyre::Result;
use glib::Propagation;
use gtk::gdk::Monitor;
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, IconTheme, Orientation, Window, WindowType};
use gtk::{Application, ApplicationWindow, Orientation, Window, WindowType};
use gtk_layer_shell::LayerShell;
use std::rc::Rc;
use std::time::Duration;
use tracing::{debug, info};
use tracing::{debug, error, info};
#[derive(Debug, Clone)]
enum Inner {
@ -22,6 +22,7 @@ enum Inner {
pub struct Bar {
name: String,
monitor_name: String,
monitor_size: (i32, i32),
position: BarPosition,
ironbar: Rc<Ironbar>,
@ -41,6 +42,7 @@ impl Bar {
pub fn new(
app: &Application,
monitor_name: String,
monitor_size: (i32, i32),
config: BarConfig,
ironbar: Rc<Ironbar>,
) -> Self {
@ -89,6 +91,7 @@ impl Bar {
Self {
name,
monitor_name,
monitor_size,
position,
ironbar,
window,
@ -146,7 +149,7 @@ impl Bar {
}
}
let load_result = self.load_modules(config, monitor)?;
let load_result = self.load_modules(config, monitor, self.monitor_size)?;
self.show(!start_hidden);
@ -243,12 +246,12 @@ impl Bar {
}
/// Loads the configured modules onto a bar.
fn load_modules(&self, config: BarConfig, monitor: &Monitor) -> Result<BarLoadResult> {
let icon_theme = IconTheme::new();
if let Some(ref theme) = config.icon_theme {
icon_theme.set_custom_theme(Some(theme));
}
fn load_modules(
&self,
config: BarConfig,
monitor: &Monitor,
output_size: (i32, i32),
) -> Result<BarLoadResult> {
let app = &self.window.application().expect("to exist");
macro_rules! info {
@ -259,13 +262,17 @@ impl Bar {
monitor,
output_name: &self.monitor_name,
location: $location,
icon_theme: &icon_theme,
}
};
}
// popup ignores module location so can bodge this for now
let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap);
let popup = Popup::new(
&self.ironbar,
&info!(ModuleLocation::Left),
output_size,
config.popup_gap,
);
let popup = Rc::new(popup);
if let Some(modules) = config.start {
@ -333,7 +340,7 @@ impl Bar {
/// Sets the window visibility status
pub fn set_visible(&self, visible: bool) {
self.window.set_visible(visible)
self.window.set_visible(visible);
}
pub fn set_exclusive(&self, exclusive: bool) {
@ -374,7 +381,10 @@ fn add_modules(
let module_factory = BarModuleFactory::new(ironbar.clone(), popup.clone()).into();
for config in modules {
config.create(&module_factory, content, info)?;
let name = config.name();
if let Err(err) = config.create(&module_factory, content, info) {
error!("failed to create module {name}: {:?}", err);
}
}
Ok(())
@ -384,9 +394,10 @@ pub fn create_bar(
app: &Application,
monitor: &Monitor,
monitor_name: String,
monitor_size: (i32, i32),
config: BarConfig,
ironbar: Rc<Ironbar>,
) -> Result<Bar> {
let bar = Bar::new(app, monitor_name, config, ironbar);
let bar = Bar::new(app, monitor_name, monitor_size, config, ironbar);
bar.init(monitor)
}

261
src/channels.rs Normal file
View file

@ -0,0 +1,261 @@
use crate::modules::ModuleUpdateEvent;
use crate::spawn;
use smithay_client_toolkit::reexports::calloop;
use std::fmt::Debug;
use tokio::sync::{broadcast, mpsc};
pub trait SyncSenderExt<T> {
/// Asynchronously sends a message on the channel,
/// panicking if it cannot be sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
fn send_expect(&self, message: T);
}
impl<T> SyncSenderExt<T> for std::sync::mpsc::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
impl<T> SyncSenderExt<T> for calloop::channel::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
impl<T: Debug> SyncSenderExt<T> for broadcast::Sender<T> {
#[inline]
fn send_expect(&self, message: T) {
self.send(message).expect(crate::error::ERR_CHANNEL_SEND);
}
}
pub trait AsyncSenderExt<T>: Sync + Send + Sized + Clone {
/// Asynchronously sends a message on the channel,
/// panicking if it cannot be sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
fn send_expect(&self, message: T) -> impl Future<Output = ()> + Send;
/// Asynchronously sends a message on the channel,
/// spawning a task to allow it to be sent in the background,
/// and panicking if it cannot be sent.
///
/// Note that this function will return *before* the message is sent.
///
/// This should be used in cases where sending should *never* fail,
/// or where failing indicates a serious bug.
#[inline]
fn send_spawn(&self, message: T)
where
Self: 'static,
T: Send + 'static,
{
let tx = self.clone();
spawn(async move { tx.send_expect(message).await });
}
/// Shorthand for [`AsyncSenderExt::send_expect`]
/// when sending a [`ModuleUpdateEvent::Update`].
#[inline]
async fn send_update<U: Clone>(&self, update: U)
where
Self: AsyncSenderExt<ModuleUpdateEvent<U>>,
{
self.send_expect(ModuleUpdateEvent::Update(update)).await;
}
/// Shorthand for [`AsyncSenderExt::send_spawn`]
/// when sending a [`ModuleUpdateEvent::Update`].
#[inline]
fn send_update_spawn<U>(&self, update: U)
where
Self: AsyncSenderExt<ModuleUpdateEvent<U>> + 'static,
U: Clone + Send + 'static,
{
self.send_spawn(ModuleUpdateEvent::Update(update));
}
}
impl<T: Send> AsyncSenderExt<T> for mpsc::Sender<T> {
#[inline]
async fn send_expect(&self, message: T) {
self.send(message)
.await
.expect(crate::error::ERR_CHANNEL_SEND);
}
}
pub trait MpscReceiverExt<T> {
/// Spawns a `GLib` future on the local thread, and calls `rx.recv()`
/// in a loop, passing the message to `f`.
///
/// This allows use of `GObjects` and futures in the same context.#
///
/// `deps` is a single reference, or tuple of references of clonable objects,
/// to be consumed inside the closure.
/// This avoids needing to `element.clone()` everywhere.
fn recv_glib<D, Fn>(self, deps: D, f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) + 'static;
}
impl<T: 'static> MpscReceiverExt<T> for mpsc::Receiver<T> {
fn recv_glib<D, Fn>(mut self, deps: D, mut f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) + 'static,
{
let deps = deps.clone_content();
glib::spawn_future_local(async move {
while let Some(val) = self.recv().await {
f(&deps, val);
}
});
}
}
pub trait BroadcastReceiverExt<T>
where
T: Debug + Clone + 'static,
{
/// Spawns a `GLib` future on the local thread, and calls `rx.recv()`
/// in a loop, passing the message to `f`.
///
/// This allows use of `GObjects` and futures in the same context.
///
/// `deps` is a single reference, or tuple of references of clonable objects,
/// to be consumed inside the closure.
/// This avoids needing to `element.clone()` everywhere.
fn recv_glib<D, Fn>(self, deps: D, f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) + 'static;
/// Like [`BroadcastReceiverExt::recv_glib`], but the closure must return a [`Future`].
fn recv_glib_async<D, Fn, F>(self, deps: D, f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) -> F + 'static,
F: Future;
}
impl<T> BroadcastReceiverExt<T> for broadcast::Receiver<T>
where
T: Debug + Clone + 'static,
{
fn recv_glib<D, Fn>(mut self, deps: D, mut f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) + 'static,
{
let deps = deps.clone_content();
glib::spawn_future_local(async move {
loop {
match self.recv().await {
Ok(val) => f(&deps, val),
Err(broadcast::error::RecvError::Lagged(count)) => {
tracing::warn!(
"Channel lagged behind by {count}, this may result in unexpected or broken behaviour"
);
}
Err(err) => {
tracing::error!("{err:?}");
break;
}
}
}
});
}
fn recv_glib_async<D, Fn, F>(mut self, deps: D, mut f: Fn)
where
D: Dependency,
D::Target: 'static,
Fn: FnMut(&D::Target, T) -> F + 'static,
F: Future,
{
let deps = deps.clone_content();
glib::spawn_future_local(async move {
loop {
match self.recv().await {
Ok(val) => {
f(&deps, val).await;
}
Err(broadcast::error::RecvError::Lagged(count)) => {
tracing::warn!(
"Channel lagged behind by {count}, this may result in unexpected or broken behaviour"
);
}
Err(err) => {
tracing::error!("{err:?}");
break;
}
}
}
});
}
}
/// `recv_glib` callback dependency
/// or dependency tuple.
pub trait Dependency: Clone {
type Target;
fn clone_content(&self) -> Self::Target;
}
impl Dependency for () {
type Target = ();
fn clone_content(&self) -> Self::Target {}
}
impl<'a, T> Dependency for &'a T
where
T: Clone + 'a,
{
type Target = T;
fn clone_content(&self) -> T {
T::clone(self)
}
}
macro_rules! impl_dependency {
($($idx:tt $t:ident),+) => {
impl<'a, $($t),+> Dependency for ($(&'a $t),+)
where
$($t: Clone + 'a),+
{
type Target = ($($t),+);
fn clone_content(&self) -> Self::Target {
($(self.$idx.clone()),+)
}
}
};
}
impl_dependency!(0 T1, 1 T2);
impl_dependency!(0 T1, 1 T2, 2 T3);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11);
impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11, 11 T12);

View file

@ -1,6 +1,5 @@
use crate::error::ExitCode;
use crate::ipc::commands::Command;
use crate::ipc::responses::Response;
use crate::ipc::{Command, Response};
use clap::{Parser, ValueEnum};
use serde::{Deserialize, Serialize};
use std::process::exit;
@ -46,6 +45,7 @@ pub fn handle_response(response: Response, format: Format) {
Format::Plain => match response {
Response::Ok => println!("ok"),
Response::OkValue { value } => println!("{value}"),
Response::Multi { values } => println!("{}", values.join("\n")),
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
},
Format::Json => println!(

View file

@ -1,7 +1,8 @@
use super::wayland::{self, ClipboardItem};
use crate::{arc_mut, lock, register_client, spawn, try_send};
use indexmap::map::Iter;
use crate::channels::AsyncSenderExt;
use crate::{arc_mut, lock, register_client, spawn};
use indexmap::IndexMap;
use indexmap::map::Iter;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use tracing::{debug, trace};
@ -46,7 +47,7 @@ impl Client {
let senders = lock!(senders);
let iter = senders.iter();
for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Add(item.clone()));
tx.send_spawn(ClipboardEvent::Add(item.clone()));
}
lock!(cache).insert(item, senders.len());
@ -74,16 +75,17 @@ impl Client {
let removed_id = lock!(cache)
.remove_ref_first()
.expect("Clipboard cache unexpectedly empty");
try_send!(tx, ClipboardEvent::Remove(removed_id));
tx.send_spawn(ClipboardEvent::Remove(removed_id));
}
try_send!(tx, ClipboardEvent::Add(item.clone()));
tx.send_spawn(ClipboardEvent::Add(item.clone()));
}
},
|existing_id| {
let senders = lock!(senders);
let iter = senders.iter();
for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Activate(existing_id));
tx.send_spawn(ClipboardEvent::Activate(existing_id));
}
},
);
@ -106,7 +108,7 @@ impl Client {
let iter = cache.iter();
for (_, (item, _)) in iter {
try_send!(tx, ClipboardEvent::Add(item.clone()));
tx.send_spawn(ClipboardEvent::Add(item.clone()));
}
}
@ -130,7 +132,7 @@ impl Client {
let senders = lock!(self.senders);
let iter = senders.iter();
for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Activate(id));
tx.send_spawn(ClipboardEvent::Activate(id));
}
}
@ -140,7 +142,7 @@ impl Client {
let senders = lock!(self.senders);
let iter = senders.iter();
for (tx, _) in iter {
try_send!(tx, ClipboardEvent::Remove(id));
tx.send_spawn(ClipboardEvent::Remove(id));
}
}
}

View file

@ -1,37 +1,73 @@
use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::{arc_mut, lock, send, spawn_blocking};
#[cfg(feature = "bindmode+hyprland")]
use super::{BindModeClient, BindModeUpdate};
#[cfg(feature = "keyboard+hyprland")]
use super::{KeyboardLayoutClient, KeyboardLayoutUpdate};
use super::{Visibility, Workspace};
use crate::channels::SyncSenderExt;
use crate::{arc_mut, lock, spawn_blocking};
use color_eyre::Result;
use hyprland::data::{Workspace as HWorkspace, Workspaces};
use hyprland::ctl::switch_xkb_layout;
use hyprland::data::{Devices, Workspace as HWorkspace, Workspaces};
use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial};
use hyprland::event_listener::EventListener;
use hyprland::prelude::*;
use hyprland::shared::{HyprDataVec, WorkspaceType};
use tokio::sync::broadcast::{channel, Receiver, Sender};
use tracing::{debug, error, info};
use tokio::sync::broadcast::{Receiver, Sender, channel};
use tracing::{debug, error, info, warn};
#[cfg(feature = "workspaces")]
use super::WorkspaceUpdate;
#[derive(Debug)]
struct TxRx<T> {
tx: Sender<T>,
_rx: Receiver<T>,
}
impl<T: Clone> TxRx<T> {
fn new() -> Self {
let (tx, rx) = channel(16);
Self { tx, _rx: rx }
}
}
#[derive(Debug)]
pub struct Client {
workspace_tx: Sender<WorkspaceUpdate>,
_workspace_rx: Receiver<WorkspaceUpdate>,
#[cfg(feature = "workspaces+hyprland")]
workspace: TxRx<WorkspaceUpdate>,
#[cfg(feature = "keyboard+hyprland")]
keyboard_layout: TxRx<KeyboardLayoutUpdate>,
#[cfg(feature = "bindmode+hyprland")]
bindmode: TxRx<BindModeUpdate>,
}
impl Client {
pub(crate) fn new() -> Self {
let (workspace_tx, workspace_rx) = channel(16);
let instance = Self {
workspace_tx,
_workspace_rx: workspace_rx,
#[cfg(feature = "workspaces+hyprland")]
workspace: TxRx::new(),
#[cfg(feature = "keyboard+hyprland")]
keyboard_layout: TxRx::new(),
#[cfg(feature = "bindmode+hyprland")]
bindmode: TxRx::new(),
};
instance.listen_workspace_events();
instance.listen_events();
instance
}
fn listen_workspace_events(&self) {
fn listen_events(&self) {
info!("Starting Hyprland event listener");
let tx = self.workspace_tx.clone();
#[cfg(feature = "workspaces+hyprland")]
let workspace_tx = self.workspace.tx.clone();
#[cfg(feature = "keyboard+hyprland")]
let keyboard_layout_tx = self.keyboard_layout.tx.clone();
#[cfg(feature = "bindmode+hyprland")]
let bindmode_tx = self.bindmode.tx.clone();
spawn_blocking(move || {
let mut event_listener = EventListener::new();
@ -40,179 +76,319 @@ impl Client {
let lock = arc_mut!(());
// cache the active workspace since Hyprland doesn't give us the prev active
let active = Self::get_active_workspace().expect("Failed to get active workspace");
let active = arc_mut!(Some(active));
#[cfg(feature = "workspaces+hyprland")]
Self::listen_workspace_events(&workspace_tx, &mut event_listener, &lock);
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
#[cfg(feature = "keyboard+hyprland")]
Self::listen_keyboard_events(&keyboard_layout_tx, &mut event_listener, &lock);
event_listener.add_workspace_added_handler(move |workspace_type| {
let _lock = lock!(lock);
debug!("Added workspace: {workspace_type:?}");
#[cfg(feature = "bindmode+hyprland")]
Self::listen_bindmode_events(&bindmode_tx, &mut event_listener, &lock);
let workspace_name = get_workspace_name(workspace_type);
let prev_workspace = lock!(active);
if let Err(err) = event_listener.start_listener() {
error!("Failed to start listener: {err:#}");
}
});
}
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
#[cfg(feature = "workspaces+hyprland")]
fn listen_workspace_events(
tx: &Sender<WorkspaceUpdate>,
event_listener: &mut EventListener,
lock: &std::sync::Arc<std::sync::Mutex<()>>,
) {
let active = Self::get_active_workspace().map_or_else(
|err| {
error!("Failed to get active workspace: {err:#?}");
None
},
Some,
);
let active = arc_mut!(active);
if let Some(workspace) = workspace {
send!(tx, WorkspaceUpdate::Add(workspace));
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
event_listener.add_workspace_added_handler(move |event| {
let _lock = lock!(lock);
debug!("Added workspace: {event:?}");
let workspace_name = get_workspace_name(event.name);
let prev_workspace = lock!(active);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
match workspace {
Ok(Some(workspace)) => {
tx.send_expect(WorkspaceUpdate::Add(workspace));
}
});
}
Err(e) => error!("Failed to get workspace: {e:#}"),
_ => {}
}
});
}
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
event_listener.add_workspace_change_handler(move |workspace_type| {
let _lock = lock!(lock);
event_listener.add_workspace_changed_handler(move |event| {
let _lock = lock!(lock);
let mut prev_workspace = lock!(active);
let mut prev_workspace = lock!(active);
debug!(
"Received workspace change: {:?} -> {workspace_type:?}",
prev_workspace.as_ref().map(|w| &w.id)
);
debug!(
"Received workspace change: {:?} -> {event:?}",
prev_workspace.as_ref().map(|w| &w.id)
);
let workspace_name = get_workspace_name(workspace_type);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
let workspace_name = get_workspace_name(event.name);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
workspace.map_or_else(
|| {
error!("Unable to locate workspace");
},
|workspace| {
// there may be another type of update so dispatch that regardless of focus change
if !workspace.visibility.is_focused() {
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
}
},
);
});
}
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
event_listener.add_active_monitor_change_handler(move |event_data| {
let _lock = lock!(lock);
let workspace_type = event_data.workspace;
let mut prev_workspace = lock!(active);
debug!(
"Received active monitor change: {:?} -> {workspace_type:?}",
prev_workspace.as_ref().map(|w| &w.name)
);
let workspace_name = get_workspace_name(workspace_type);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
if let Some((false, workspace)) =
workspace.map(|w| (w.visibility.is_focused(), w))
{
match workspace {
Ok(Some(workspace)) if !workspace.visibility.is_focused() => {
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
} else {
}
Ok(None) => {
error!("Unable to locate workspace");
}
});
}
Err(e) => error!("Failed to get workspace: {e:#}"),
_ => {}
}
});
}
{
let tx = tx.clone();
let lock = lock.clone();
{
let tx = tx.clone();
let lock = lock.clone();
let active = active.clone();
event_listener.add_workspace_moved_handler(move |event_data| {
let _lock = lock!(lock);
let workspace_type = event_data.workspace;
debug!("Received workspace move: {workspace_type:?}");
event_listener.add_active_monitor_changed_handler(move |event_data| {
let _lock = lock!(lock);
let Some(workspace_type) = event_data.workspace_name else {
warn!("Received active monitor change with no workspace name");
return;
};
let mut prev_workspace = lock!(active);
let mut prev_workspace = lock!(active);
let workspace_name = get_workspace_name(workspace_type);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
debug!(
"Received active monitor change: {:?} -> {workspace_type:?}",
prev_workspace.as_ref().map(|w| &w.name)
);
if let Some(workspace) = workspace {
send!(tx, WorkspaceUpdate::Move(workspace.clone()));
let workspace_name = get_workspace_name(workspace_type);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
if !workspace.visibility.is_focused() {
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
}
match workspace {
Ok(Some(workspace)) if !workspace.visibility.is_focused() => {
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
}
Ok(None) => {
error!("Unable to locate workspace");
}
Err(e) => error!("Failed to get workspace: {e:#}"),
_ => {}
}
});
}
{
let tx = tx.clone();
let lock = lock.clone();
event_listener.add_workspace_moved_handler(move |event_data| {
let _lock = lock!(lock);
let workspace_type = event_data.name;
debug!("Received workspace move: {workspace_type:?}");
let mut prev_workspace = lock!(active);
let workspace_name = get_workspace_name(workspace_type);
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
match workspace {
Ok(Some(workspace)) if !workspace.visibility.is_focused() => {
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
}
Ok(None) => {
error!("Unable to locate workspace");
}
Err(e) => error!("Failed to get workspace: {e:#}"),
_ => {}
}
});
}
{
let tx = tx.clone();
let lock = lock.clone();
event_listener.add_workspace_renamed_handler(move |data| {
let _lock = lock!(lock);
debug!("Received workspace rename: {data:?}");
tx.send_expect(WorkspaceUpdate::Rename {
id: data.id as i64,
name: data.name,
});
});
}
{
let tx = tx.clone();
let lock = lock.clone();
event_listener.add_workspace_deleted_handler(move |data| {
let _lock = lock!(lock);
debug!("Received workspace destroy: {data:?}");
tx.send_expect(WorkspaceUpdate::Remove(data.id as i64));
});
}
{
let tx = tx.clone();
let lock = lock.clone();
event_listener.add_urgent_state_changed_handler(move |address| {
let _lock = lock!(lock);
debug!("Received urgent state: {address:?}");
let clients = match hyprland::data::Clients::get() {
Ok(clients) => clients,
Err(err) => {
error!("Failed to get clients: {err}");
return;
}
};
clients.iter().find(|c| c.address == address).map_or_else(
|| {
error!("Unable to locate client");
},
|c| {
tx.send_expect(WorkspaceUpdate::Urgent {
id: c.workspace.id as i64,
urgent: true,
});
},
);
});
}
}
#[cfg(feature = "keyboard+hyprland")]
fn listen_keyboard_events(
keyboard_layout_tx: &Sender<KeyboardLayoutUpdate>,
event_listener: &mut EventListener,
lock: &std::sync::Arc<std::sync::Mutex<()>>,
) {
let tx = keyboard_layout_tx.clone();
let lock = lock.clone();
event_listener.add_layout_changed_handler(move |layout_event| {
let _lock = lock!(lock);
let layout = if layout_event.layout_name.is_empty() {
// FIXME: This field is empty due to bug in `hyprland-rs_0.4.0-alpha.3`. Which is already fixed in last betas
// The layout may be empty due to a bug in `hyprland-rs`, because of which the `layout_event` is incorrect.
//
// Instead of:
// ```
// LayoutEvent {
// keyboard_name: "keychron-keychron-c2",
// layout_name: "English (US)",
// }
// ```
//
// We get:
// ```
// LayoutEvent {
// keyboard_name: "keychron-keychron-c2,English (US)",
// layout_name: "",
// }
// ```
//
// Here we are trying to recover `layout_name` from `keyboard_name`
let layout = layout_event.keyboard_name.as_str().split(',').nth(1);
let Some(layout) = layout else {
error!(
"Failed to get layout from string: {}. The failed logic is a workaround for a bug in `hyprland 0.4.0-alpha.3`", layout_event.keyboard_name);
return;
};
layout.into()
}
else {
layout_event.layout_name
};
{
let tx = tx.clone();
let lock = lock.clone();
debug!("Received layout: {layout:?}");
tx.send_expect(KeyboardLayoutUpdate(layout));
});
}
event_listener.add_workspace_rename_handler(move |data| {
let _lock = lock!(lock);
#[cfg(feature = "bindmode+hyprland")]
fn listen_bindmode_events(
bindmode_tx: &Sender<BindModeUpdate>,
event_listener: &mut EventListener,
lock: &std::sync::Arc<std::sync::Mutex<()>>,
) {
let tx = bindmode_tx.clone();
let lock = lock.clone();
send!(
tx,
WorkspaceUpdate::Rename {
id: data.workspace_id as i64,
name: data.workspace_name
}
);
});
}
event_listener.add_sub_map_changed_handler(move |bind_mode| {
let _lock = lock!(lock);
debug!("Received bind mode: {bind_mode:?}");
{
event_listener.add_workspace_destroy_handler(move |data| {
let _lock = lock!(lock);
debug!("Received workspace destroy: {data:?}");
send!(tx, WorkspaceUpdate::Remove(data.workspace_id as i64));
});
}
event_listener
.start_listener()
.expect("Failed to start listener");
tx.send_expect(BindModeUpdate {
name: bind_mode,
pango_markup: false,
});
});
}
/// Sends a `WorkspaceUpdate::Focus` event
/// and updates the active workspace cache.
#[cfg(feature = "workspaces+hyprland")]
fn send_focus_change(
prev_workspace: &mut Option<Workspace>,
workspace: Workspace,
tx: &Sender<WorkspaceUpdate>,
) {
send!(
tx,
WorkspaceUpdate::Focus {
old: prev_workspace.take(),
new: workspace.clone(),
}
);
tx.send_expect(WorkspaceUpdate::Focus {
old: prev_workspace.take(),
new: workspace.clone(),
});
tx.send_expect(WorkspaceUpdate::Urgent {
id: workspace.id,
urgent: false,
});
prev_workspace.replace(workspace);
}
/// Gets a workspace by name from the server, given the active workspace if known.
fn get_workspace(name: &str, active: Option<&Workspace>) -> Option<Workspace> {
Workspaces::get()
.expect("Failed to get workspaces")
.into_iter()
.find_map(|w| {
if w.name == name {
let vis = Visibility::from((&w, active.map(|w| w.name.as_ref()), &|w| {
create_is_visible()(w)
}));
#[cfg(feature = "workspaces+hyprland")]
fn get_workspace(name: &str, active: Option<&Workspace>) -> Result<Option<Workspace>> {
let workspace = Workspaces::get()?.into_iter().find_map(|w| {
if w.name == name {
let vis = Visibility::from((&w, active.map(|w| w.name.as_ref()), &|w| {
create_is_visible()(w)
}));
Some(Workspace::from((vis, w)))
} else {
None
}
})
Some(Workspace::from((vis, w)))
} else {
None
}
});
Ok(workspace)
}
/// Gets the active workspace from the server.
@ -222,43 +398,100 @@ impl Client {
}
}
impl WorkspaceClient for Client {
fn focus(&self, id: String) -> Result<()> {
let identifier = id.parse::<i32>().map_or_else(
|_| WorkspaceIdentifierWithSpecial::Name(&id),
WorkspaceIdentifierWithSpecial::Id,
);
#[cfg(feature = "workspaces+hyprland")]
impl super::WorkspaceClient for Client {
fn focus(&self, id: i64) {
let identifier = WorkspaceIdentifierWithSpecial::Id(id as i32);
Dispatch::call(DispatchType::Workspace(identifier))?;
Ok(())
if let Err(e) = Dispatch::call(DispatchType::Workspace(identifier)) {
error!("Couldn't focus workspace '{id}': {e:#}");
}
}
fn subscribe_workspace_change(&self) -> Receiver<WorkspaceUpdate> {
let rx = self.workspace_tx.subscribe();
fn subscribe(&self) -> Receiver<WorkspaceUpdate> {
let rx = self.workspace.tx.subscribe();
{
let tx = self.workspace_tx.clone();
let active_id = HWorkspace::get_active().ok().map(|active| active.name);
let is_visible = create_is_visible();
let active_id = HWorkspace::get_active().ok().map(|active| active.name);
let is_visible = create_is_visible();
match Workspaces::get() {
Ok(workspaces) => {
let workspaces = workspaces
.into_iter()
.map(|w| {
let vis = Visibility::from((&w, active_id.as_deref(), &is_visible));
Workspace::from((vis, w))
})
.collect();
let workspaces = Workspaces::get()
.expect("Failed to get workspaces")
.into_iter()
.map(|w| {
let vis = Visibility::from((&w, active_id.as_deref(), &is_visible));
Workspace::from((vis, w))
})
.collect();
send!(tx, WorkspaceUpdate::Init(workspaces));
self.workspace
.tx
.send_expect(WorkspaceUpdate::Init(workspaces));
}
Err(e) => {
error!("Failed to get workspaces: {e:#}");
}
}
rx
}
}
#[cfg(feature = "keyboard+hyprland")]
impl KeyboardLayoutClient for Client {
fn set_next_active(&self) {
let Ok(devices) = Devices::get() else {
error!("Failed to get devices");
return;
};
let device = devices
.keyboards
.iter()
.find(|k| k.main)
.map(|k| k.name.clone());
if let Some(device) = device {
if let Err(e) =
switch_xkb_layout::call(device, switch_xkb_layout::SwitchXKBLayoutCmdTypes::Next)
{
error!("Failed to switch keyboard layout due to Hyprland error: {e}");
}
} else {
error!("Failed to get keyboard device from hyprland");
}
}
fn subscribe(&self) -> Receiver<KeyboardLayoutUpdate> {
let rx = self.keyboard_layout.tx.subscribe();
match Devices::get().map(|devices| {
devices
.keyboards
.iter()
.find(|k| k.main)
.map(|k| k.active_keymap.clone())
}) {
Ok(Some(layout)) => {
self.keyboard_layout
.tx
.send_expect(KeyboardLayoutUpdate(layout));
}
Ok(None) => error!("Failed to get current keyboard layout hyprland"),
Err(err) => error!("Failed to get devices: {err:#?}"),
}
rx
}
}
#[cfg(feature = "bindmode+hyprland")]
impl BindModeClient for Client {
fn subscribe(&self) -> Result<Receiver<BindModeUpdate>> {
Ok(self.bindmode.tx.subscribe())
}
}
fn get_workspace_name(name: WorkspaceType) -> String {
match name {
WorkspaceType::Regular(name) => name,

View file

@ -1,4 +1,5 @@
use crate::{await_sync, register_fallible_client};
use crate::clients::ClientResult;
use crate::register_fallible_client;
use cfg_if::cfg_if;
use color_eyre::{Help, Report, Result};
use std::fmt::{Debug, Display, Formatter};
@ -6,16 +7,20 @@ use std::sync::Arc;
use tokio::sync::broadcast;
use tracing::debug;
#[cfg(feature = "workspaces+hyprland")]
#[cfg(feature = "hyprland")]
pub mod hyprland;
#[cfg(feature = "workspaces+sway")]
#[cfg(feature = "niri")]
pub mod niri;
#[cfg(feature = "sway")]
pub mod sway;
pub enum Compositor {
#[cfg(feature = "workspaces+sway")]
#[cfg(feature = "sway")]
Sway,
#[cfg(feature = "workspaces+hyprland")]
#[cfg(feature = "hyprland")]
Hyprland,
#[cfg(feature = "niri")]
Niri,
Unsupported,
}
@ -25,10 +30,12 @@ impl Display for Compositor {
f,
"{}",
match self {
#[cfg(feature = "workspaces+sway")]
#[cfg(any(feature = "sway"))]
Self::Sway => "Sway",
#[cfg(feature = "workspaces+hyprland")]
#[cfg(any(feature = "hyprland"))]
Self::Hyprland => "Hyprland",
#[cfg(feature = "workspaces+niri")]
Self::Niri => "Niri",
Self::Unsupported => "Unsupported",
}
)
@ -41,32 +48,90 @@ impl Compositor {
fn get_current() -> Self {
if std::env::var("SWAYSOCK").is_ok() {
cfg_if! {
if #[cfg(feature = "workspaces+sway")] { Self::Sway }
if #[cfg(feature = "sway")] { Self::Sway }
else { tracing::error!("Not compiled with Sway support"); Self::Unsupported }
}
} else if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() {
cfg_if! {
if #[cfg(feature = "workspaces+hyprland")] { Self::Hyprland }
if #[cfg(feature = "hyprland")] { Self::Hyprland }
else { tracing::error!("Not compiled with Hyprland support"); Self::Unsupported }
}
} else if std::env::var("NIRI_SOCKET").is_ok() {
cfg_if! {
if #[cfg(feature = "niri")] { Self::Niri }
else {tracing::error!("Not compiled with Niri support"); Self::Unsupported }
}
} else {
Self::Unsupported
}
}
#[cfg(feature = "bindmode")]
pub fn create_bindmode_client(
clients: &mut super::Clients,
) -> ClientResult<dyn BindModeClient + Send + Sync> {
let current = Self::get_current();
debug!("Getting keyboard_layout client for: {current}");
match current {
#[cfg(feature = "bindmode+sway")]
Self::Sway => Ok(clients.sway()?),
#[cfg(feature = "bindmode+hyprland")]
Self::Hyprland => Ok(clients.hyprland()),
#[cfg(feature = "niri")]
Self::Niri => Err(Report::msg("Unsupported compositor")
.note("Currently bindmode is only supported by Sway and Hyprland")),
Self::Unsupported => Err(Report::msg("Unsupported compositor")
.note("Currently bindmode is only supported by Sway and Hyprland")),
#[allow(unreachable_patterns)]
_ => Err(Report::msg("Unsupported compositor")
.note("Bindmode feature is disabled for this compositor")),
}
}
#[cfg(feature = "keyboard")]
pub fn create_keyboard_layout_client(
clients: &mut super::Clients,
) -> ClientResult<dyn KeyboardLayoutClient + Send + Sync> {
let current = Self::get_current();
debug!("Getting keyboard_layout client for: {current}");
match current {
#[cfg(feature = "keyboard+sway")]
Self::Sway => Ok(clients.sway()?),
#[cfg(feature = "keyboard+hyprland")]
Self::Hyprland => Ok(clients.hyprland()),
#[cfg(feature = "niri")]
Self::Niri => Err(Report::msg("Unsupported compositor").note(
"Currently keyboard layout functionality are only supported by Sway and Hyprland",
)),
Self::Unsupported => Err(Report::msg("Unsupported compositor").note(
"Currently keyboard layout functionality are only supported by Sway and Hyprland",
)),
#[allow(unreachable_patterns)]
_ => Err(Report::msg("Unsupported compositor")
.note("Keyboard layout feature is disabled for this compositor")),
}
}
/// Creates a new instance of
/// the workspace client for the current compositor.
pub fn create_workspace_client() -> Result<Arc<dyn WorkspaceClient + Send + Sync>> {
#[cfg(feature = "workspaces")]
pub fn create_workspace_client(
clients: &mut super::Clients,
) -> Result<Arc<dyn WorkspaceClient + Send + Sync>> {
let current = Self::get_current();
debug!("Getting workspace client for: {current}");
match current {
#[cfg(feature = "workspaces+sway")]
Self::Sway => await_sync(async { sway::Client::new().await })
.map(|client| Arc::new(client) as Arc<dyn WorkspaceClient + Send + Sync>),
Self::Sway => Ok(clients.sway()?),
#[cfg(feature = "workspaces+hyprland")]
Self::Hyprland => Ok(Arc::new(hyprland::Client::new())),
Self::Hyprland => Ok(clients.hyprland()),
#[cfg(feature = "workspaces+niri")]
Self::Niri => Ok(Arc::new(niri::Client::new())),
Self::Unsupported => Err(Report::msg("Unsupported compositor")
.note("Currently workspaces are only supported by Sway and Hyprland")),
.note("Currently workspaces are only supported by Sway, Niri and Hyprland")),
#[allow(unreachable_patterns)]
_ => Err(Report::msg("Unsupported compositor")
.note("Workspaces feature is disabled for this compositor")),
}
}
}
@ -83,29 +148,29 @@ pub struct Workspace {
pub visibility: Visibility,
}
/// Indicates workspace visibility. Visible workspaces have a boolean flag to indicate if they are also focused.
/// Yes, this is the same signature as Option<bool>, but it's impl is a lot more suited for our case.
/// Indicates workspace visibility.
/// Visible workspaces have a boolean flag to indicate if they are also focused.
#[derive(Debug, Copy, Clone)]
pub enum Visibility {
Visible(bool),
Visible { focused: bool },
Hidden,
}
impl Visibility {
pub fn visible() -> Self {
Self::Visible(false)
Self::Visible { focused: false }
}
pub fn focused() -> Self {
Self::Visible(true)
Self::Visible { focused: true }
}
pub fn is_visible(self) -> bool {
matches!(self, Self::Visible(_))
matches!(self, Self::Visible { .. })
}
pub fn is_focused(self) -> bool {
if let Self::Visible(focused) = self {
if let Self::Visible { focused } = self {
focused
} else {
false
@ -114,6 +179,11 @@ impl Visibility {
}
#[derive(Debug, Clone)]
#[cfg(feature = "keyboard")]
pub struct KeyboardLayoutUpdate(pub String);
#[derive(Debug, Clone)]
#[cfg(feature = "workspaces")]
pub enum WorkspaceUpdate {
/// Provides an initial list of workspaces.
/// This is re-sent to all subscribers when a new subscription is created.
@ -132,6 +202,12 @@ pub enum WorkspaceUpdate {
name: String,
},
/// The urgent state of a node changed.
Urgent {
id: i64,
urgent: bool,
},
/// An update was triggered by the compositor but this was not mapped by Ironbar.
///
/// This is purely used for ergonomics within the compositor clients
@ -139,12 +215,44 @@ pub enum WorkspaceUpdate {
Unknown,
}
pub trait WorkspaceClient: Debug + Send + Sync {
/// Requests the workspace with this name is focused.
fn focus(&self, name: String) -> Result<()>;
/// Creates a new to workspace event receiver.
fn subscribe_workspace_change(&self) -> broadcast::Receiver<WorkspaceUpdate>;
#[derive(Clone, Debug)]
#[cfg(feature = "bindmode")]
pub struct BindModeUpdate {
/// The binding mode that became active.
pub name: String,
/// Whether the mode should be parsed as pango markup.
pub pango_markup: bool,
}
#[cfg(feature = "workspaces")]
pub trait WorkspaceClient: Debug + Send + Sync {
/// Requests the workspace with this id is focused.
fn focus(&self, id: i64);
/// Creates a new to workspace event receiver.
fn subscribe(&self) -> broadcast::Receiver<WorkspaceUpdate>;
}
#[cfg(feature = "workspaces")]
register_fallible_client!(dyn WorkspaceClient, workspaces);
#[cfg(feature = "keyboard")]
pub trait KeyboardLayoutClient: Debug + Send + Sync {
/// Switches to the next layout.
fn set_next_active(&self);
/// Creates a new to keyboard layout event receiver.
fn subscribe(&self) -> broadcast::Receiver<KeyboardLayoutUpdate>;
}
#[cfg(feature = "keyboard")]
register_fallible_client!(dyn KeyboardLayoutClient, keyboard_layout);
#[cfg(feature = "bindmode")]
pub trait BindModeClient: Debug + Send + Sync {
/// Add a callback for bindmode updates.
fn subscribe(&self) -> Result<broadcast::Receiver<BindModeUpdate>>;
}
#[cfg(feature = "bindmode")]
register_fallible_client!(dyn BindModeClient, bindmode);

View file

@ -0,0 +1,117 @@
/// Taken from the `niri_ipc` crate.
/// Only a relevant snippet has been extracted
/// to reduce compile times.
use crate::clients::compositor::Workspace as IronWorkspace;
use crate::{await_sync, clients::compositor::Visibility};
use color_eyre::eyre::{Result, eyre};
use core::str;
use serde::{Deserialize, Serialize};
use std::{env, path::Path};
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
net::UnixStream,
};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Request {
Action(Action),
EventStream,
}
pub type Reply = Result<Response, String>;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Response {
Handled,
Workspaces(Vec<Workspace>),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Action {
FocusWorkspace { reference: WorkspaceReferenceArg },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum WorkspaceReferenceArg {
Name(String),
Id(u64),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Workspace {
pub id: u64,
pub idx: u8,
pub name: Option<String>,
pub output: Option<String>,
pub is_active: bool,
pub is_focused: bool,
}
impl From<&Workspace> for IronWorkspace {
fn from(workspace: &Workspace) -> IronWorkspace {
// Workspaces in niri don't neccessarily have names.
// If the niri workspace has a name then it is assigned as is,
// but if it does not have a name, the monitor index is used.
Self {
id: workspace.id as i64,
name: workspace.name.clone().unwrap_or(workspace.idx.to_string()),
monitor: workspace.output.clone().unwrap_or_default(),
visibility: if workspace.is_active {
Visibility::Visible {
focused: workspace.is_focused,
}
} else {
Visibility::Hidden
},
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Event {
WorkspacesChanged { workspaces: Vec<Workspace> },
WorkspaceActivated { id: u64, focused: bool },
Other,
}
#[derive(Debug)]
pub struct Connection(UnixStream);
impl Connection {
pub async fn connect() -> Result<Self> {
let socket_path =
env::var_os("NIRI_SOCKET").ok_or_else(|| eyre!("NIRI_SOCKET not found!"))?;
Self::connect_to(socket_path).await
}
pub async fn connect_to(path: impl AsRef<Path>) -> Result<Self> {
let raw_stream = UnixStream::connect(path.as_ref()).await?;
let stream = raw_stream;
Ok(Self(stream))
}
pub async fn send(
&mut self,
request: Request,
) -> Result<(Reply, impl FnMut() -> Result<Event> + '_)> {
let Self(stream) = self;
let mut buf = serde_json::to_string(&request)?;
stream.write_all(buf.as_bytes()).await?;
stream.shutdown().await?;
buf.clear();
let mut reader = BufReader::new(stream);
reader.read_line(&mut buf).await?;
let reply = serde_json::from_str(&buf)?;
let events = move || {
buf.clear();
await_sync(async {
reader.read_line(&mut buf).await.unwrap_or(0);
});
let event: Event = serde_json::from_str(&buf).unwrap_or(Event::Other);
Ok(event)
};
Ok((reply, events))
}
}

View file

@ -0,0 +1,223 @@
use super::{Workspace as IronWorkspace, WorkspaceClient, WorkspaceUpdate};
use crate::channels::SyncSenderExt;
use crate::clients::compositor::Visibility;
use crate::{arc_rw, read_lock, spawn, write_lock};
use color_eyre::Report;
use connection::{Action, Connection, Event, Request, WorkspaceReferenceArg};
use std::sync::{Arc, RwLock};
use tokio::sync::broadcast;
use tracing::{debug, error, warn};
mod connection;
#[derive(Debug)]
pub struct Client {
tx: broadcast::Sender<WorkspaceUpdate>,
_rx: broadcast::Receiver<WorkspaceUpdate>,
workspaces: Arc<RwLock<Vec<IronWorkspace>>>,
}
impl Client {
pub fn new() -> Self {
let (tx, rx) = broadcast::channel(32);
let tx2 = tx.clone();
let workspace_state = arc_rw!(vec![]);
let workspace_state2 = workspace_state.clone();
spawn(async move {
let mut conn = Connection::connect().await?;
let (_, mut event_listener) = conn.send(Request::EventStream).await?;
let mut first_event = true;
loop {
let events = match event_listener() {
Ok(Event::WorkspacesChanged { workspaces }) => {
debug!("WorkspacesChanged: {:?}", workspaces);
// Niri only has a WorkspacesChanged Event and Ironbar has 4 events which have to be handled: Add, Remove, Rename and Move.
// This is handled by keeping a previous state of workspaces and comparing with the new state for changes.
let new_workspaces: Vec<IronWorkspace> = workspaces
.into_iter()
.map(|w| IronWorkspace::from(&w))
.collect();
let mut updates: Vec<WorkspaceUpdate> = vec![];
if first_event {
// Niri's WorkspacesChanged event does not initially sort workspaces by ID when first output,
// which makes sort = added meaningless. Therefore, new_workspaces are sorted by ID here to ensure a consistent addition order.
let mut new_workspaces = new_workspaces.clone();
new_workspaces.sort_by_key(|w| w.id);
updates.push(WorkspaceUpdate::Init(new_workspaces));
first_event = false;
} else {
// first pass - add/update
for workspace in &new_workspaces {
let workspace_state = read_lock!(workspace_state);
let old_workspace = workspace_state
.iter()
.find(|&w: &&IronWorkspace| w.id == workspace.id);
match old_workspace {
None => updates.push(WorkspaceUpdate::Add(workspace.clone())),
Some(old_workspace) => {
if workspace.name != old_workspace.name {
updates.push(WorkspaceUpdate::Rename {
id: workspace.id,
name: workspace.name.clone(),
});
}
if workspace.monitor != old_workspace.monitor {
updates.push(WorkspaceUpdate::Move(workspace.clone()));
}
}
}
}
// second pass - delete
for workspace in read_lock!(workspace_state).iter() {
let exists = new_workspaces.iter().any(|w| w.id == workspace.id);
if !exists {
updates.push(WorkspaceUpdate::Remove(workspace.id));
}
}
}
*write_lock!(workspace_state) = new_workspaces;
updates
}
Ok(Event::WorkspaceActivated { id, focused }) => {
debug!("WorkspaceActivated: id: {}, focused: {}", id, focused);
// workspace with id is activated, if focus is true then it is also focused
// if focused is true then focus has changed => find old focused workspace. set it to inactive and set current
//
// we use indexes here as both new/old need to be mutable
let new_index = read_lock!(workspace_state)
.iter()
.position(|w| w.id == id as i64);
if let Some(new_index) = new_index {
if focused {
let old_index = read_lock!(workspace_state)
.iter()
.position(|w| w.visibility.is_focused());
if let Some(old_index) = old_index {
write_lock!(workspace_state)[new_index].visibility =
Visibility::focused();
if read_lock!(workspace_state)[old_index].monitor
== read_lock!(workspace_state)[new_index].monitor
{
write_lock!(workspace_state)[old_index].visibility =
Visibility::Hidden;
} else {
write_lock!(workspace_state)[old_index].visibility =
Visibility::visible();
}
vec![WorkspaceUpdate::Focus {
old: Some(read_lock!(workspace_state)[old_index].clone()),
new: read_lock!(workspace_state)[new_index].clone(),
}]
} else {
write_lock!(workspace_state)[new_index].visibility =
Visibility::focused();
vec![WorkspaceUpdate::Focus {
old: None,
new: read_lock!(workspace_state)[new_index].clone(),
}]
}
} else {
// if focused is false means active workspace on a particular monitor has changed =>
// change all workspaces on monitor to inactive and change current workspace as active
write_lock!(workspace_state)[new_index].visibility =
Visibility::visible();
let old_index = read_lock!(workspace_state).iter().position(|w| {
(w.visibility.is_focused() || w.visibility.is_visible())
&& w.monitor
== read_lock!(workspace_state)[new_index].monitor
});
if let Some(old_index) = old_index {
write_lock!(workspace_state)[old_index].visibility =
Visibility::Hidden;
vec![]
} else {
vec![]
}
}
} else {
warn!("No workspace with id for new focus/visible workspace found");
vec![]
}
}
Ok(Event::Other) => {
vec![]
}
Err(err) => {
error!("{err:?}");
break;
}
};
for event in events {
tx.send_expect(event);
}
}
Ok::<(), Report>(())
});
Self {
tx: tx2,
_rx: rx,
workspaces: workspace_state2,
}
}
}
impl WorkspaceClient for Client {
fn focus(&self, id: i64) {
debug!("focusing workspace with id: {}", id);
// this does annoyingly require spawning a separate connection for every focus call
// the alternative is sticking the conn behind a mutex which could perform worse
spawn(async move {
let mut conn = Connection::connect().await?;
let command = Request::Action(Action::FocusWorkspace {
reference: WorkspaceReferenceArg::Id(id as u64),
});
if let Err(err) = conn.send(command).await {
error!("failed to send command: {err:?}");
}
Ok::<(), Report>(())
});
}
fn subscribe(&self) -> broadcast::Receiver<WorkspaceUpdate> {
let rx = self.tx.subscribe();
let workspaces = read_lock!(self.workspaces);
if !workspaces.is_empty() {
self.tx
.send_expect(WorkspaceUpdate::Init(workspaces.clone()));
}
rx
}
}

View file

@ -1,85 +1,66 @@
use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::{await_sync, send, spawn};
use color_eyre::{Report, Result};
use futures_lite::StreamExt;
use std::sync::Arc;
use swayipc_async::{Connection, Event, EventType, Node, WorkspaceChange, WorkspaceEvent};
use tokio::sync::broadcast::{channel, Receiver, Sender};
use tokio::sync::Mutex;
use tracing::{info, trace};
use super::{Visibility, Workspace};
use crate::channels::SyncSenderExt;
use crate::clients::sway::Client;
use crate::{await_sync, error, spawn};
use color_eyre::Report;
use swayipc_async::{InputChange, InputEvent, Node, WorkspaceChange, WorkspaceEvent};
use tokio::sync::broadcast::{Receiver, channel};
#[derive(Debug)]
pub struct Client {
client: Arc<Mutex<Connection>>,
workspace_tx: Sender<WorkspaceUpdate>,
_workspace_rx: Receiver<WorkspaceUpdate>,
}
#[cfg(feature = "workspaces")]
use super::WorkspaceUpdate;
impl Client {
pub(crate) async fn new() -> Result<Self> {
// Avoid using `arc_mut!` here because we need tokio Mutex.
let client = Arc::new(Mutex::new(Connection::new().await?));
info!("Sway IPC subscription client connected");
#[cfg(feature = "workspaces+sway")]
impl super::WorkspaceClient for Client {
fn focus(&self, id: i64) {
let client = self.connection().clone();
spawn(async move {
let mut client = client.lock().await;
let (workspace_tx, workspace_rx) = channel(16);
let name = client
.get_workspaces()
.await?
.into_iter()
.find(|w| w.id == id)
.map(|w| w.name);
{
// create 2nd client as subscription takes ownership
let client = Connection::new().await?;
let workspace_tx = workspace_tx.clone();
let Some(name) = name else {
return Err(Report::msg(format!("couldn't find workspace with id {id}")));
};
spawn(async move {
let event_types = [EventType::Workspace];
let mut events = client.subscribe(event_types).await?;
if let Err(e) = client.run_command(format!("workspace {name}")).await {
return Err(Report::msg(format!(
"Couldn't focus workspace '{id}': {e:#}"
)));
}
while let Some(event) = events.next().await {
trace!("event: {:?}", event);
if let Event::Workspace(event) = event? {
let event = WorkspaceUpdate::from(*event);
if !matches!(event, WorkspaceUpdate::Unknown) {
workspace_tx.send(event)?;
}
};
}
Ok::<(), Report>(())
});
}
Ok(Self {
client,
workspace_tx,
_workspace_rx: workspace_rx,
})
}
}
impl WorkspaceClient for Client {
fn focus(&self, id: String) -> Result<()> {
await_sync(async move {
let mut client = self.client.lock().await;
client.run_command(format!("workspace {id}")).await
})?;
Ok(())
Ok(())
});
}
fn subscribe_workspace_change(&self) -> Receiver<WorkspaceUpdate> {
let rx = self.workspace_tx.subscribe();
fn subscribe(&self) -> Receiver<WorkspaceUpdate> {
let (tx, rx) = channel(16);
{
let tx = self.workspace_tx.clone();
let client = self.client.clone();
let client = self.connection().clone();
await_sync(async {
let mut client = client.lock().await;
let workspaces = client.get_workspaces().await.expect("to get workspaces");
// TODO: this needs refactoring
await_sync(async {
let mut client = client.lock().await;
let workspaces = client.get_workspaces().await.expect("to get workspaces");
let event =
WorkspaceUpdate::Init(workspaces.into_iter().map(Workspace::from).collect());
let event =
WorkspaceUpdate::Init(workspaces.into_iter().map(Workspace::from).collect());
send!(tx, event);
});
}
tx.send_expect(event);
drop(client);
self.add_listener::<WorkspaceEvent>(move |event| {
let update = WorkspaceUpdate::from(event.clone());
tx.send_expect(update);
})
.await
.expect("to add listener");
});
rx
}
@ -135,6 +116,7 @@ impl From<&swayipc_async::Workspace> for Visibility {
}
}
#[cfg(feature = "workspaces")]
impl From<WorkspaceEvent> for WorkspaceUpdate {
fn from(event: WorkspaceEvent) -> Self {
match event.change {
@ -151,7 +133,136 @@ impl From<WorkspaceEvent> for WorkspaceUpdate {
WorkspaceChange::Move => {
Self::Move(event.current.expect("Missing current workspace").into())
}
WorkspaceChange::Rename => {
if let Some(node) = event.current {
Self::Rename {
id: node.id,
name: node.name.unwrap_or_default(),
}
} else {
Self::Unknown
}
}
WorkspaceChange::Urgent => {
if let Some(node) = event.current {
Self::Urgent {
id: node.id,
urgent: node.urgent,
}
} else {
Self::Unknown
}
}
_ => Self::Unknown,
}
}
}
#[cfg(feature = "keyboard+sway")]
use super::{KeyboardLayoutClient, KeyboardLayoutUpdate};
#[cfg(feature = "keyboard+sway")]
impl KeyboardLayoutClient for Client {
fn set_next_active(&self) {
let client = self.connection().clone();
spawn(async move {
let mut client = client.lock().await;
let inputs = client.get_inputs().await.expect("to get inputs");
if let Some(keyboard) = inputs
.into_iter()
.find(|i| i.xkb_active_layout_name.is_some())
{
if let Err(e) = client
.run_command(format!(
"input {} xkb_switch_layout next",
keyboard.identifier
))
.await
{
error!("Failed to switch keyboard layout due to Sway error: {e}");
}
} else {
error!("Failed to get keyboard identifier from Sway");
}
});
}
fn subscribe(&self) -> Receiver<KeyboardLayoutUpdate> {
let (tx, rx) = channel(16);
let client = self.connection().clone();
await_sync(async {
let mut client = client.lock().await;
let inputs = client.get_inputs().await.expect("to get inputs");
if let Some(layout) = inputs.into_iter().find_map(|i| i.xkb_active_layout_name) {
tx.send_expect(KeyboardLayoutUpdate(layout));
} else {
error!("Failed to get keyboard layout from Sway!");
}
drop(client);
self.add_listener::<InputEvent>(move |event| {
if let Ok(layout) = KeyboardLayoutUpdate::try_from(event.clone()) {
tx.send_expect(layout);
}
})
.await
.expect("to add listener");
});
rx
}
}
#[cfg(feature = "keyboard+sway")]
impl TryFrom<InputEvent> for KeyboardLayoutUpdate {
type Error = ();
fn try_from(value: InputEvent) -> Result<Self, Self::Error> {
match value.change {
InputChange::XkbLayout => {
if let Some(layout) = value.input.xkb_active_layout_name {
Ok(KeyboardLayoutUpdate(layout))
} else {
Err(())
}
}
_ => Err(()),
}
}
}
#[cfg(feature = "bindmode+sway")]
use super::{BindModeClient, BindModeUpdate};
#[cfg(feature = "bindmode+sway")]
impl BindModeClient for Client {
fn subscribe(&self) -> Result<Receiver<BindModeUpdate>, Report> {
let (tx, rx) = channel(16);
await_sync(async {
self.add_listener::<swayipc_async::ModeEvent>(move |mode| {
tracing::trace!("mode: {:?}", mode);
// when no binding is active the bindmode is named "default", but we must display
// nothing in this case.
let name = if mode.change == "default" {
String::new()
} else {
mode.change.clone()
};
tx.send_expect(BindModeUpdate {
name,
pango_markup: mode.pango_markup,
});
})
.await
})?;
Ok(rx)
}
}

236
src/clients/libinput.rs Normal file
View file

@ -0,0 +1,236 @@
use crate::channels::SyncSenderExt;
use crate::{Ironbar, arc_rw, read_lock, spawn, write_lock};
use color_eyre::{Report, Result};
use colpetto::event::{AsRawEvent, DeviceEvent, KeyState, KeyboardEvent};
use colpetto::{DeviceCapability, Libinput};
use evdev_rs::DeviceWrapper;
use evdev_rs::enums::{EV_KEY, EV_LED, EventCode, int_to_ev_key};
use futures_lite::StreamExt;
use rustix::fs::{Mode, OFlags, open};
use rustix::io::Errno;
use std::ffi::{CStr, CString, c_int};
use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
use std::os::unix::io::OwnedFd;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::Duration;
use tokio::sync::broadcast;
use tokio::task::LocalSet;
use tokio::time::sleep;
use tracing::{debug, error};
#[derive(Debug, Copy, Clone)]
pub enum Key {
Caps,
Num,
Scroll,
}
impl From<Key> for EV_KEY {
fn from(value: Key) -> Self {
match value {
Key::Caps => Self::KEY_CAPSLOCK,
Key::Num => Self::KEY_NUMLOCK,
Key::Scroll => Self::KEY_SCROLLLOCK,
}
}
}
impl TryFrom<EV_KEY> for Key {
type Error = Report;
fn try_from(value: EV_KEY) -> std::result::Result<Self, Self::Error> {
match value {
EV_KEY::KEY_CAPSLOCK => Ok(Key::Caps),
EV_KEY::KEY_NUMLOCK => Ok(Key::Num),
EV_KEY::KEY_SCROLLLOCK => Ok(Key::Scroll),
_ => Err(Report::msg("provided key is not supported toggle key")),
}
}
}
impl Key {
fn get_state<P: AsRef<Path>>(self, device_path: P) -> Result<bool> {
let device = evdev_rs::Device::new_from_path(device_path)?;
match self {
Self::Caps => device.event_value(&EventCode::EV_LED(EV_LED::LED_CAPSL)),
Self::Num => device.event_value(&EventCode::EV_LED(EV_LED::LED_NUML)),
Self::Scroll => device.event_value(&EventCode::EV_LED(EV_LED::LED_SCROLLL)),
}
.map(|v| v > 0)
.ok_or_else(|| Report::msg("failed to get key status"))
}
}
#[derive(Debug, Copy, Clone)]
pub struct KeyEvent {
pub key: Key,
pub state: bool,
}
#[derive(Debug, Copy, Clone)]
pub enum Event {
Device,
Key(KeyEvent),
}
struct KeyData<P: AsRef<Path>> {
device_path: P,
key: EV_KEY,
}
impl<P: AsRef<Path>> TryFrom<KeyData<P>> for Event {
type Error = Report;
fn try_from(data: KeyData<P>) -> Result<Self> {
let key = Key::try_from(data.key)?;
key.get_state(data.device_path)
.map(|state| KeyEvent { key, state })
.map(Event::Key)
}
}
#[derive(Debug)]
pub struct Client {
tx: broadcast::Sender<Event>,
_rx: broadcast::Receiver<Event>,
seat: String,
known_devices: Arc<RwLock<Vec<PathBuf>>>,
}
impl Client {
pub fn init(seat: String) -> Arc<Self> {
let client = Arc::new(Self::new(seat));
{
let client = client.clone();
std::thread::spawn(move || {
let local = LocalSet::new();
local.spawn_local(async move {
if let Err(err) = client.run().await {
error!("{err:?}");
}
});
Ironbar::runtime().block_on(local);
});
}
client
}
fn new(seat: String) -> Self {
let (tx, rx) = broadcast::channel(4);
Self {
tx,
_rx: rx,
seat,
known_devices: arc_rw!(vec![]),
}
}
fn open_restricted(path: &CStr, flags: c_int) -> std::result::Result<RawFd, i32> {
open(path, OFlags::from_bits_retain(flags as u32), Mode::empty())
.map(IntoRawFd::into_raw_fd)
.map_err(Errno::raw_os_error)
}
fn close_restricted(fd: c_int) {
drop(unsafe { OwnedFd::from_raw_fd(fd) });
}
async fn run(&self) -> Result<()> {
let mut libinput = Libinput::with_tracing(Self::open_restricted, Self::close_restricted)?;
libinput.udev_assign_seat(CString::new(&*self.seat)?.as_c_str())?;
let mut stream = libinput.event_stream()?;
while let Some(event) = stream.try_next().await? {
match event {
colpetto::Event::Device(DeviceEvent::Added(event)) => {
let device = event.device();
if !device.has_capability(DeviceCapability::Keyboard) {
continue;
}
let name = device.name();
let Some(device) = event.device().udev_device() else {
continue;
};
if let Some(device_path) = device.devnode() {
// not all devices which report as keyboards actually are one -
// fire test event so we can figure out if it is
let caps_event: Result<Event> = KeyData {
device_path,
key: EV_KEY::KEY_CAPSLOCK,
}
.try_into();
if caps_event.is_ok() {
debug!(
"new keyboard device: {} | {}",
name.to_string_lossy(),
device_path.display()
);
write_lock!(self.known_devices).push(device_path.to_path_buf());
self.tx.send_expect(Event::Device);
}
}
}
colpetto::Event::Keyboard(KeyboardEvent::Key(event))
if event.key_state() == KeyState::Released =>
{
let Some(device) = event.device().udev_device() else {
continue;
};
let Some(
key @ (EV_KEY::KEY_CAPSLOCK | EV_KEY::KEY_NUMLOCK | EV_KEY::KEY_SCROLLLOCK),
) = int_to_ev_key(event.key())
else {
continue;
};
if let Some(device_path) = device.devnode().map(PathBuf::from) {
let tx = self.tx.clone();
// need to spawn a task to avoid blocking
spawn(async move {
// wait for kb to change
sleep(Duration::from_millis(50)).await;
let data = KeyData { device_path, key };
if let Ok(event) = data.try_into() {
tx.send_expect(event);
}
});
}
}
_ => {}
}
}
Err(Report::msg("unexpected end of stream"))
}
pub fn get_state(&self, key: Key) -> bool {
read_lock!(self.known_devices)
.iter()
.map(|device_path| key.get_state(device_path))
.filter_map(Result::ok)
.reduce(|state, curr| state || curr)
.unwrap_or_default()
}
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
self.tx.subscribe()
}
}

View file

@ -1,21 +1,33 @@
use crate::{await_sync, Ironbar};
use crate::{Ironbar, await_sync};
use color_eyre::Result;
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
#[cfg(feature = "clipboard")]
pub mod clipboard;
#[cfg(feature = "workspaces")]
#[cfg(any(
feature = "bindmode",
feature = "hyprland",
feature = "keyboard",
feature = "workspaces",
))]
pub mod compositor;
#[cfg(feature = "keyboard")]
pub mod libinput;
#[cfg(feature = "cairo")]
pub mod lua;
#[cfg(feature = "music")]
pub mod music;
#[cfg(feature = "network_manager")]
pub mod networkmanager;
#[cfg(feature = "sway")]
pub mod sway;
#[cfg(feature = "notifications")]
pub mod swaync;
#[cfg(feature = "sys_info")]
pub mod sysinfo;
#[cfg(feature = "tray")]
pub mod tray;
#[cfg(feature = "upower")]
@ -31,16 +43,28 @@ pub struct Clients {
wayland: Option<Arc<wayland::Client>>,
#[cfg(feature = "workspaces")]
workspaces: Option<Arc<dyn compositor::WorkspaceClient>>,
#[cfg(feature = "sway")]
sway: Option<Arc<sway::Client>>,
#[cfg(feature = "hyprland")]
hyprland: Option<Arc<compositor::hyprland::Client>>,
#[cfg(feature = "bindmode")]
bindmode: Option<Arc<dyn compositor::BindModeClient>>,
#[cfg(feature = "clipboard")]
clipboard: Option<Arc<clipboard::Client>>,
#[cfg(feature = "keyboard")]
libinput: HashMap<Box<str>, Arc<libinput::Client>>,
#[cfg(feature = "keyboard")]
keyboard_layout: Option<Arc<dyn compositor::KeyboardLayoutClient>>,
#[cfg(feature = "cairo")]
lua: Option<Rc<lua::LuaEngine>>,
#[cfg(feature = "music")]
music: std::collections::HashMap<music::ClientType, Arc<dyn music::MusicClient>>,
music: HashMap<music::ClientType, Arc<dyn music::MusicClient>>,
#[cfg(feature = "network_manager")]
network_manager: Option<Arc<networkmanager::Client>>,
#[cfg(feature = "notifications")]
notifications: Option<Arc<swaync::Client>>,
#[cfg(feature = "sys_info")]
sys_info: Option<Arc<sysinfo::Client>>,
#[cfg(feature = "tray")]
tray: Option<Arc<tray::Client>>,
#[cfg(feature = "upower")]
@ -73,18 +97,68 @@ impl Clients {
#[cfg(feature = "workspaces")]
pub fn workspaces(&mut self) -> ClientResult<dyn compositor::WorkspaceClient> {
let client = match &self.workspaces {
Some(workspaces) => workspaces.clone(),
None => {
let client = compositor::Compositor::create_workspace_client()?;
self.workspaces.replace(client.clone());
client
}
let client = if let Some(workspaces) = &self.workspaces {
workspaces.clone()
} else {
let client = compositor::Compositor::create_workspace_client(self)?;
self.workspaces.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "keyboard")]
pub fn keyboard_layout(&mut self) -> ClientResult<dyn compositor::KeyboardLayoutClient> {
let client = if let Some(keyboard_layout) = &self.keyboard_layout {
keyboard_layout.clone()
} else {
let client = compositor::Compositor::create_keyboard_layout_client(self)?;
self.keyboard_layout.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "bindmode")]
pub fn bindmode(&mut self) -> ClientResult<dyn compositor::BindModeClient> {
let client = if let Some(client) = &self.bindmode {
client.clone()
} else {
let client = compositor::Compositor::create_bindmode_client(self)?;
self.bindmode.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "sway")]
pub fn sway(&mut self) -> ClientResult<sway::Client> {
let client = if let Some(client) = &self.sway {
client.clone()
} else {
let client = await_sync(async { sway::Client::new().await })?;
let client = Arc::new(client);
self.sway.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "hyprland")]
pub fn hyprland(&mut self) -> Arc<compositor::hyprland::Client> {
if let Some(client) = &self.hyprland {
client.clone()
} else {
let client = Arc::new(compositor::hyprland::Client::new());
self.hyprland.replace(client.clone());
client
}
}
#[cfg(feature = "cairo")]
pub fn lua(&mut self, config_dir: &Path) -> Rc<lua::LuaEngine> {
self.lua
@ -92,6 +166,17 @@ impl Clients {
.clone()
}
#[cfg(feature = "keyboard")]
pub fn libinput(&mut self, seat: &str) -> Arc<libinput::Client> {
if let Some(client) = self.libinput.get(seat) {
client.clone()
} else {
let client = libinput::Client::init(seat.to_string());
self.libinput.insert(seat.into(), client.clone());
client
}
}
#[cfg(feature = "music")]
pub fn music(&mut self, client_type: music::ClientType) -> Arc<dyn music::MusicClient> {
self.music
@ -102,55 +187,68 @@ impl Clients {
#[cfg(feature = "network_manager")]
pub fn network_manager(&mut self) -> ClientResult<networkmanager::Client> {
match &self.network_manager {
Some(client) => Ok(client.clone()),
None => {
let client = networkmanager::create_client()?;
self.network_manager = Some(client.clone());
Ok(client)
}
if let Some(client) = &self.network_manager {
Ok(client.clone())
} else {
let client = await_sync(async move { networkmanager::create_client().await })?;
self.network_manager = Some(client.clone());
Ok(client)
}
}
#[cfg(feature = "notifications")]
pub fn notifications(&mut self) -> ClientResult<swaync::Client> {
let client = match &self.notifications {
Some(client) => client.clone(),
None => {
let client = await_sync(async { swaync::Client::new().await })?;
let client = Arc::new(client);
self.notifications.replace(client.clone());
client
}
let client = if let Some(client) = &self.notifications {
client.clone()
} else {
let client = await_sync(async { swaync::Client::new().await })?;
let client = Arc::new(client);
self.notifications.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "sys_info")]
pub fn sys_info(&mut self) -> Arc<sysinfo::Client> {
self.sys_info
.get_or_insert_with(|| {
let client = Arc::new(sysinfo::Client::new());
#[cfg(feature = "ipc")]
Ironbar::variable_manager().register_namespace("sysinfo", client.clone());
client
})
.clone()
}
#[cfg(feature = "tray")]
pub fn tray(&mut self) -> ClientResult<tray::Client> {
let client = match &self.tray {
Some(client) => client.clone(),
None => {
let service_name = format!("{}-{}", env!("CARGO_CRATE_NAME"), Ironbar::unique_id());
let client = await_sync(async { tray::Client::new(&service_name).await })?;
let client = Arc::new(client);
self.tray.replace(client.clone());
client
}
let client = if let Some(client) = &self.tray {
client.clone()
} else {
let client = await_sync(async { tray::Client::new().await })?;
let client = Arc::new(client);
self.tray.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "upower")]
pub fn upower(&mut self) -> Arc<zbus::fdo::PropertiesProxy<'static>> {
self.upower
.get_or_insert_with(|| {
crate::await_sync(async { upower::create_display_proxy().await })
})
.clone()
pub fn upower(&mut self) -> ClientResult<zbus::fdo::PropertiesProxy<'static>> {
let client = if let Some(client) = &self.upower {
client.clone()
} else {
let client = await_sync(async { upower::create_display_proxy().await })?;
self.upower.replace(client.clone());
client
};
Ok(client)
}
#[cfg(feature = "volume")]

View file

@ -70,13 +70,17 @@ pub trait MusicClient: Debug + Send + Sync {
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum ClientType {
#[cfg(feature = "music+mpd")]
Mpd { host: String, music_dir: PathBuf },
#[cfg(feature = "music+mpris")]
Mpris,
}
pub fn create_client(client_type: ClientType) -> Arc<dyn MusicClient> {
match client_type {
#[cfg(feature = "music+mpd")]
ClientType::Mpd { host, music_dir } => Arc::new(mpd::Client::new(host, music_dir)),
#[cfg(feature = "music+mpris")]
ClientType::Mpris => Arc::new(mpris::Client::new()),
}
}

View file

@ -1,14 +1,15 @@
use super::{
MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track, TICK_INTERVAL_MS,
MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, TICK_INTERVAL_MS, Track,
};
use crate::{await_sync, send, spawn, Ironbar};
use crate::channels::SyncSenderExt;
use crate::{Ironbar, await_sync, spawn};
use color_eyre::Report;
use color_eyre::Result;
use mpd_client::client::{ConnectionEvent, Subsystem};
use mpd_client::commands::{self, SeekMode};
use mpd_client::responses::{PlayState, Song};
use mpd_client::tag::Tag;
use mpd_utils::{mpd_client, PersistentClient};
use mpd_utils::{PersistentClient, mpd_client};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
@ -97,7 +98,7 @@ impl Client {
let status = Status::from(status);
let update = PlayerUpdate::Update(Box::new(track), status);
send!(tx, update);
tx.send_expect(update);
}
Ok(())
@ -113,7 +114,7 @@ impl Client {
elapsed: status.elapsed,
});
send!(tx, update);
tx.send_expect(update);
}
}
}

View file

@ -1,6 +1,7 @@
use super::{MusicClient, PlayerState, PlayerUpdate, Status, Track, TICK_INTERVAL_MS};
use super::{MusicClient, PlayerState, PlayerUpdate, Status, TICK_INTERVAL_MS, Track};
use crate::channels::SyncSenderExt;
use crate::clients::music::ProgressTick;
use crate::{arc_mut, lock, send, spawn_blocking};
use crate::{arc_mut, lock, spawn_blocking};
use color_eyre::Result;
use mpris::{DBusError, Event, Metadata, PlaybackStatus, Player, PlayerFinder};
use std::cmp;
@ -47,10 +48,14 @@ impl Client {
)) if transport_error.name() == Some(NO_ACTIVE_PLAYER)
|| transport_error.name() == Some(NO_REPLY) =>
{
Vec::new()
vec![]
}
_ => {
error!("D-Bus error getting MPRIS players: {e:?}");
vec![]
}
_ => panic!("Failed to connect to D-Bus"),
});
// Acquire the lock of current_player before players to avoid deadlock.
// There are places where we lock on current_player and players, but we always lock on current_player first.
// This is because we almost never need to lock on players without locking on current_player.
@ -133,7 +138,7 @@ impl Client {
let mut players_locked = lock!(players);
players_locked.remove(identity);
if players_locked.is_empty() {
send!(tx, PlayerUpdate::Update(Box::new(None), Status::default()));
tx.send_expect(PlayerUpdate::Update(Box::new(None), Status::default()));
}
};
@ -208,7 +213,7 @@ impl Client {
let track = Track::from(metadata);
let player_update = PlayerUpdate::Update(Box::new(Some(track)), status);
send!(tx, player_update);
tx.send_expect(player_update);
Ok(())
}
@ -238,7 +243,7 @@ impl Client {
duration: metadata.length(),
});
send!(tx, update);
tx.send_expect(update);
}
}
}
@ -286,14 +291,22 @@ impl MusicClient for Client {
fn seek(&self, duration: Duration) -> Result<()> {
if let Some(player) = Self::get_player(self) {
let pos = player.get_position().unwrap_or_default();
// if possible, use `set_position` instead of `seek` because some players have issues with seeking
// see https://github.com/JakeStanger/ironbar/issues/970
if let Ok(metadata) = player.get_metadata() {
if let Some(track_id) = metadata.track_id() {
player.set_position(track_id, &duration)?;
} else {
let pos = player.get_position().unwrap_or_default();
let duration = duration.as_micros() as i64;
let position = pos.as_micros() as i64;
let duration = duration.as_micros() as i64;
let position = pos.as_micros() as i64;
let seek = cmp::max(duration, 0) - position;
let seek = cmp::max(duration, 0) - position;
player.seek(seek)?;
player.seek(seek)?;
}
}
} else {
error!("Could not find player");
}
@ -315,7 +328,9 @@ impl MusicClient for Client {
state: PlayerState::Stopped,
volume_percent: None,
};
send!(self.tx, PlayerUpdate::Update(Box::new(None), status));
self.tx
.send_expect(PlayerUpdate::Update(Box::new(None), status));
}
rx

View file

@ -1,71 +1,71 @@
use color_eyre::Result;
use zbus::dbus_proxy;
use zbus::proxy;
use zbus::zvariant::{ObjectPath, OwnedValue, Str};
#[dbus_proxy(
#[proxy(
default_service = "org.freedesktop.NetworkManager",
interface = "org.freedesktop.NetworkManager",
default_path = "/org/freedesktop/NetworkManager"
)]
trait Dbus {
#[dbus_proxy(property)]
pub(super) trait Dbus {
#[zbus(property)]
fn active_connections(&self) -> Result<Vec<ObjectPath>>;
#[dbus_proxy(property)]
#[zbus(property)]
fn devices(&self) -> Result<Vec<ObjectPath>>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn networking_enabled(&self) -> Result<bool>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn primary_connection(&self) -> Result<ObjectPath>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn primary_connection_type(&self) -> Result<Str>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn wireless_enabled(&self) -> Result<bool>;
}
#[dbus_proxy(
#[proxy(
default_service = "org.freedesktop.NetworkManager",
interface = "org.freedesktop.NetworkManager.Connection.Active"
)]
trait ActiveConnectionDbus {
// #[dbus_proxy(property)]
pub(super) trait ActiveConnectionDbus {
// #[zbus(property)]
// fn connection(&self) -> Result<ObjectPath>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn default(&self) -> Result<bool>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn default6(&self) -> Result<bool>;
#[dbus_proxy(property)]
#[zbus(property)]
fn devices(&self) -> Result<Vec<ObjectPath>>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn id(&self) -> Result<Str>;
#[dbus_proxy(property)]
#[zbus(property)]
fn type_(&self) -> Result<Str>;
// #[dbus_proxy(property)]
// #[zbus(property)]
// fn uuid(&self) -> Result<Str>;
}
#[dbus_proxy(
#[proxy(
default_service = "org.freedesktop.NetworkManager",
interface = "org.freedesktop.NetworkManager.Device"
)]
trait DeviceDbus {
// #[dbus_proxy(property)]
pub(super) trait DeviceDbus {
// #[zbus(property)]
// fn active_connection(&self) -> Result<ObjectPath>;
#[dbus_proxy(property)]
#[zbus(property)]
fn device_type(&self) -> Result<DeviceType>;
#[dbus_proxy(property)]
#[zbus(property)]
fn state(&self) -> Result<DeviceState>;
}

View file

@ -2,17 +2,16 @@ use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use color_eyre::Result;
use futures_lite::StreamExt;
use futures_signals::signal::{Mutable, MutableSignalCloned};
use tracing::error;
use zbus::blocking::Connection;
use zbus::Connection;
use zbus::zvariant::ObjectPath;
use crate::clients::networkmanager::dbus::{
ActiveConnectionDbusProxyBlocking, DbusProxyBlocking, DeviceDbusProxyBlocking,
};
use crate::clients::networkmanager::dbus::{ActiveConnectionDbusProxy, DbusProxy, DeviceDbusProxy};
use crate::clients::networkmanager::state::{
determine_cellular_state, determine_vpn_state, determine_wifi_state, determine_wired_state,
CellularState, State, VpnState, WifiState, WiredState,
CellularState, State, VpnState, WifiState, WiredState, determine_cellular_state,
determine_vpn_state, determine_wifi_state, determine_wired_state,
};
use crate::{
read_lock, register_fallible_client, spawn_blocking, spawn_blocking_result, write_lock,
@ -29,23 +28,23 @@ pub struct Client(Arc<ClientInner<'static>>);
#[derive(Debug)]
struct ClientInner<'l> {
state: Mutable<State>,
root_object: &'l DbusProxyBlocking<'l>,
active_connections: RwLock<PathMap<'l, ActiveConnectionDbusProxyBlocking<'l>>>,
devices: RwLock<PathMap<'l, DeviceDbusProxyBlocking<'l>>>,
root_object: &'l DbusProxy<'l>,
active_connections: RwLock<PathMap<'l, ActiveConnectionDbusProxy<'l>>>,
devices: RwLock<PathMap<'l, DeviceDbusProxy<'l>>>,
dbus_connection: Connection,
}
impl Client {
fn new() -> Result<Client> {
async fn new() -> Result<Client> {
let state = Mutable::new(State {
wired: WiredState::Unknown,
wifi: WifiState::Unknown,
cellular: CellularState::Unknown,
vpn: VpnState::Unknown,
});
let dbus_connection = Connection::system()?;
let dbus_connection = Connection::system().await?;
let root_object = {
let root_object = DbusProxyBlocking::new(&dbus_connection)?;
let root_object = DbusProxy::new(&dbus_connection).await?;
// Workaround for the fact that zbus (unnecessarily) requires a static lifetime here
Box::leak(Box::new(root_object))
};
@ -59,158 +58,159 @@ impl Client {
})))
}
fn run(&self) -> Result<()> {
macro_rules! update_state_for_device_change {
($client:ident) => {
$client.state.set(State {
wired: determine_wired_state(&read_lock!($client.devices))?,
wifi: determine_wifi_state(&read_lock!($client.devices))?,
cellular: determine_cellular_state(&read_lock!($client.devices))?,
vpn: $client.state.get_cloned().vpn,
});
};
async fn run(&self) -> Result<()> {
// TODO: Reimplement DBus watching without these write-only macros
let mut active_connections_stream = self.0.root_object.receive_active_connections_changed().await;
while let Some(change) = active_connections_stream.next().await {
}
macro_rules! initialise_path_map {
(
$client:expr,
$path_map:ident,
$proxy_type:ident
$(, |$new_path:ident| $property_watcher:expr)*
) => {
let new_paths = $client.root_object.$path_map()?;
let mut path_map = HashMap::new();
for new_path in new_paths {
let new_proxy = $proxy_type::builder(&$client.dbus_connection)
.path(new_path.clone())?
.build()?;
path_map.insert(new_path.clone(), new_proxy);
$({
let $new_path = &new_path;
$property_watcher;
})*
}
*write_lock!($client.$path_map) = path_map;
};
}
// ActiveConnectionDbusProxy::builder(&self.0.dbus_connection)
macro_rules! spawn_path_list_watcher {
(
$client:expr,
$property:ident,
$property_changes:ident,
$proxy_type:ident,
|$state_client:ident| $state_update:expr
$(, |$property_client:ident, $new_path:ident| $property_watcher:expr)*
) => {
let client = $client.clone();
spawn_blocking_result!({
let changes = client.root_object.$property_changes();
for _ in changes {
let mut new_path_map = HashMap::new();
{
let new_paths = client.root_object.$property()?;
let path_map = read_lock!(client.$property);
for new_path in new_paths {
if path_map.contains_key(&new_path) {
let proxy = path_map
.get(&new_path)
.expect("Should contain the key, guarded by runtime check");
new_path_map.insert(new_path, proxy.to_owned());
} else {
let new_proxy = $proxy_type::builder(&client.dbus_connection)
.path(new_path.clone())?
.build()?;
new_path_map.insert(new_path.clone(), new_proxy);
$({
let $property_client = &client;
let $new_path = &new_path;
$property_watcher;
})*
}
}
}
*write_lock!(client.$property) = new_path_map;
let $state_client = &client;
$state_update;
}
Ok(())
});
}
}
macro_rules! spawn_property_watcher {
(
$client:expr,
$path:expr,
$property_changes:ident,
$containing_list:ident,
|$inner_client:ident| $state_update:expr
) => {
let client = $client.clone();
let path = $path.clone();
spawn_blocking_result!({
let changes = read_lock!(client.$containing_list)
.get(&path)
.expect("Should contain the key upon watcher start")
.$property_changes();
for _ in changes {
if !read_lock!(client.$containing_list).contains_key(&path) {
break;
}
let $inner_client = &client;
$state_update;
}
Ok(())
});
};
}
initialise_path_map!(
self.0,
active_connections,
ActiveConnectionDbusProxyBlocking
);
initialise_path_map!(self.0, devices, DeviceDbusProxyBlocking, |path| {
spawn_property_watcher!(self.0, path, receive_state_changed, devices, |client| {
update_state_for_device_change!(client);
});
});
self.0.state.set(State {
wired: determine_wired_state(&read_lock!(self.0.devices))?,
wifi: determine_wifi_state(&read_lock!(self.0.devices))?,
cellular: determine_cellular_state(&read_lock!(self.0.devices))?,
vpn: determine_vpn_state(&read_lock!(self.0.active_connections))?,
});
spawn_path_list_watcher!(
self.0,
active_connections,
receive_active_connections_changed,
ActiveConnectionDbusProxyBlocking,
|client| {
client.state.set(State {
wired: client.state.get_cloned().wired,
wifi: client.state.get_cloned().wifi,
cellular: client.state.get_cloned().cellular,
vpn: determine_vpn_state(&read_lock!(client.active_connections))?,
});
}
);
spawn_path_list_watcher!(
self.0,
devices,
receive_devices_changed,
DeviceDbusProxyBlocking,
|client| {
update_state_for_device_change!(client);
},
|client, path| {
spawn_property_watcher!(client, path, receive_state_changed, devices, |client| {
update_state_for_device_change!(client);
});
}
);
// macro_rules! update_state_for_device_change {
// ($client:ident) => {
// $client.state.set(State {
// wired: determine_wired_state(&read_lock!($client.devices)).await?,
// wifi: determine_wifi_state(&read_lock!($client.devices)).await?,
// cellular: determine_cellular_state(&read_lock!($client.devices)).await?,
// vpn: $client.state.get_cloned().vpn,
// });
// };
// }
//
// macro_rules! initialise_path_map {
// (
// $client:expr,
// $path_map:ident,
// $proxy_type:ident
// $(, |$new_path:ident| $property_watcher:expr)*
// ) => {
// let new_paths = $client.root_object.$path_map().await?;
// let mut path_map = HashMap::new();
// for new_path in new_paths {
// let new_proxy = $proxy_type::builder(&$client.dbus_connection)
// .path(new_path.clone())?
// .build().await?;
// path_map.insert(new_path.clone(), new_proxy);
// $({
// let $new_path = &new_path;
// $property_watcher;
// })*
// }
// *write_lock!($client.$path_map) = path_map;
// };
// }
//
// macro_rules! spawn_path_list_watcher {
// (
// $client:expr,
// $property:ident,
// $property_changes:ident,
// $proxy_type:ident,
// |$state_client:ident| $state_update:expr
// $(, |$property_client:ident, $new_path:ident| $property_watcher:expr)*
// ) => {
// let client = $client.clone();
//
// let changes = client.root_object.$property_changes();
// for _ in changes {
// let mut new_path_map = HashMap::new();
// {
// let new_paths = client.root_object.$property()?;
// let path_map = read_lock!(client.$property);
// for new_path in new_paths {
// if path_map.contains_key(&new_path) {
// let proxy = path_map
// .get(&new_path)
// .expect("Should contain the key, guarded by runtime check");
// new_path_map.insert(new_path, proxy.to_owned());
// } else {
// let new_proxy = $proxy_type::builder(&client.dbus_connection)
// .path(new_path.clone())?
// .build()?;
// new_path_map.insert(new_path.clone(), new_proxy);
// $({
// let $property_client = &client;
// let $new_path = &new_path;
// $property_watcher;
// })*
// }
// }
// }
// *write_lock!(client.$property) = new_path_map;
// let $state_client = &client;
// $state_update;
// }
// }
// }
//
// macro_rules! spawn_property_watcher {
// (
// $client:expr,
// $path:expr,
// $property_changes:ident,
// $containing_list:ident,
// |$inner_client:ident| $state_update:expr
// ) => {
// let client = $client.clone();
// let path = $path.clone();
//
// let changes = read_lock!(client.$containing_list)
// .get(&path)
// .expect("Should contain the key upon watcher start")
// .$property_changes().await;
// for _ in changes {
// if !read_lock!(client.$containing_list).contains_key(&path) {
// break;
// }
// let $inner_client = &client;
// $state_update;
// }
// };
// }
//
// initialise_path_map!(self.0, active_connections, ActiveConnectionDbusProxy);
// initialise_path_map!(self.0, devices, DeviceDbusProxy, |path| {
// spawn_property_watcher!(self.0, path, receive_state_changed, devices, |client| {
// update_state_for_device_change!(client);
// });
// });
// self.0.state.set(State {
// wired: determine_wired_state(&read_lock!(self.0.devices))?,
// wifi: determine_wifi_state(&read_lock!(self.0.devices))?,
// cellular: determine_cellular_state(&read_lock!(self.0.devices))?,
// vpn: determine_vpn_state(&read_lock!(self.0.active_connections))?,
// });
//
// spawn_path_list_watcher!(
// self.0,
// active_connections,
// receive_active_connections_changed,
// ActiveConnectionDbusProxy,
// |client| {
// client.state.set(State {
// wired: client.state.get_cloned().wired,
// wifi: client.state.get_cloned().wifi,
// cellular: client.state.get_cloned().cellular,
// vpn: determine_vpn_state(&read_lock!(client.active_connections))?,
// });
// }
// );
// spawn_path_list_watcher!(
// self.0,
// devices,
// receive_devices_changed,
// DeviceDbusProxy,
// |client| {
// update_state_for_device_change!(client);
// },
// |client, path| {
// spawn_property_watcher!(client, path, receive_state_changed, devices, |client| {
// update_state_for_device_change!(client);
// });
// }
// );
Ok(())
}
@ -220,15 +220,9 @@ impl Client {
}
}
pub fn create_client() -> Result<Arc<Client>> {
let client = Arc::new(Client::new()?);
{
let client = client.clone();
spawn_blocking_result!({
client.run()?;
Ok(())
});
}
pub async fn create_client() -> Result<Arc<Client>> {
let client = Arc::new(Client::new().await?);
client.run().await?;
Ok(client)
}

View file

@ -1,9 +1,9 @@
use color_eyre::Result;
use crate::clients::networkmanager::dbus::{
ActiveConnectionDbusProxyBlocking, DeviceDbusProxyBlocking, DeviceState, DeviceType,
};
use crate::clients::networkmanager::PathMap;
use crate::clients::networkmanager::dbus::{
ActiveConnectionDbusProxy, DeviceDbusProxy, DeviceState, DeviceType,
};
#[derive(Clone, Debug)]
pub struct State {
@ -56,16 +56,16 @@ pub struct VpnConnectedState {
pub name: String,
}
pub(super) fn determine_wired_state(
devices: &PathMap<DeviceDbusProxyBlocking>,
pub(super) async fn determine_wired_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<WiredState> {
let mut present = false;
let mut connected = false;
for device in devices.values() {
if device.device_type()? == DeviceType::Ethernet {
if device.device_type().await? == DeviceType::Ethernet {
present = true;
if device.state()?.is_enabled() {
if device.state().await?.is_enabled() {
connected = true;
break;
}
@ -81,19 +81,19 @@ pub(super) fn determine_wired_state(
}
}
pub(super) fn determine_wifi_state(
devices: &PathMap<DeviceDbusProxyBlocking>,
pub(super) async fn determine_wifi_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<WifiState> {
let mut present = false;
let mut enabled = false;
let mut connected = false;
for device in devices.values() {
if device.device_type()? == DeviceType::Wifi {
if device.device_type().await? == DeviceType::Wifi {
present = true;
if device.state()?.is_enabled() {
if device.state().await?.is_enabled() {
enabled = true;
if device.state()? == DeviceState::Activated {
if device.state().await? == DeviceState::Activated {
connected = true;
break;
}
@ -115,19 +115,19 @@ pub(super) fn determine_wifi_state(
}
}
pub(super) fn determine_cellular_state(
devices: &PathMap<DeviceDbusProxyBlocking>,
pub(super) async fn determine_cellular_state(
devices: &PathMap<'_, DeviceDbusProxy<'_>>,
) -> Result<CellularState> {
let mut present = false;
let mut enabled = false;
let mut connected = false;
for device in devices.values() {
if device.device_type()? == DeviceType::Modem {
if device.device_type().await? == DeviceType::Modem {
present = true;
if device.state()?.is_enabled() {
if device.state().await?.is_enabled() {
enabled = true;
if device.state()? == DeviceState::Activated {
if device.state().await? == DeviceState::Activated {
connected = true;
break;
}
@ -146,11 +146,11 @@ pub(super) fn determine_cellular_state(
}
}
pub(super) fn determine_vpn_state(
active_connections: &PathMap<ActiveConnectionDbusProxyBlocking>,
pub(super) async fn determine_vpn_state(
active_connections: &PathMap<'_, ActiveConnectionDbusProxy<'_>>,
) -> Result<VpnState> {
for connection in active_connections.values() {
match connection.type_()?.as_str() {
match connection.type_().await?.as_str() {
"vpn" | "wireguard" => {
return Ok(VpnState::Connected(VpnConnectedState {
name: "unknown".into(),

181
src/clients/sway.rs Normal file
View file

@ -0,0 +1,181 @@
use crate::spawn;
use color_eyre::{Report, Result};
use futures_lite::StreamExt;
use std::sync::Arc;
use swayipc_async::{Connection, Event, EventType};
use tokio::sync::Mutex;
use tracing::{info, trace};
type SyncFn<T> = dyn Fn(&T) + Sync + Send;
struct TaskState {
join_handle: Option<tokio::task::JoinHandle<Result<()>>>,
// could have been a `HashMap<EventType, Vec<Box<dyn Fn(&Event) + Sync + Send>>>`, but we don't
// expect enough listeners to justify the constant overhead of a hashmap.
listeners: Arc<Vec<(EventType, Box<SyncFn<Event>>)>>,
}
pub struct Client {
connection: Arc<Mutex<Connection>>,
task_state: Mutex<TaskState>,
}
impl std::fmt::Debug for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Client")
.field("client", &"Connection")
.field("task_state", &format_args!("<...>"))
.finish()
}
}
impl Client {
pub(crate) async fn new() -> Result<Self> {
// Avoid using `arc_mut!` here because we need tokio Mutex.
let client = Arc::new(Mutex::new(Connection::new().await?));
info!("Sway IPC subscription client connected");
Ok(Self {
connection: client,
task_state: Mutex::new(TaskState {
listeners: Arc::new(Vec::new()),
join_handle: None,
}),
})
}
pub fn connection(&self) -> &Arc<Mutex<Connection>> {
&self.connection
}
pub async fn add_listener<T: SwayIpcEvent>(
&self,
f: impl Fn(&T) + Sync + Send + 'static,
) -> Result<()> {
self.add_listener_type(
T::EVENT_TYPE,
Box::new(move |event| {
let event = T::from_event(event).expect("event type mismatch");
f(event);
}),
)
.await
}
pub async fn add_listener_type(
&self,
event_type: EventType,
f: Box<SyncFn<Event>>,
) -> Result<()> {
// abort current running task
let TaskState {
join_handle,
listeners,
} = &mut *self.task_state.lock().await;
if let Some(handle) = join_handle.take() {
handle.abort();
let _ = handle.await;
}
// Only the task and self have a reference to listeners, and we just abort the task. This
// is the only reference to listeners, so we can safely get a mutable reference.
let listeners_mut = Arc::get_mut(listeners)
.ok_or_else(|| Report::msg("Failed to get mutable reference to listeners"))?;
listeners_mut.push((event_type, f));
// create new client as subscription takes ownership
let client = Connection::new().await?;
let event_types = listeners.iter().map(|(t, _)| *t).collect::<Vec<_>>();
let listeners = listeners.clone();
let handle = spawn(async move {
let mut events = client.subscribe(&event_types).await?;
while let Some(event) = events.next().await {
trace!("event: {:?}", event);
let event = event?;
let ty = sway_event_to_event_type(&event);
for (t, f) in listeners.iter() {
if *t == ty {
f(&event);
}
}
}
Ok::<(), Report>(())
});
*join_handle = Some(handle);
Ok(())
}
}
fn sway_event_to_event_type(event: &Event) -> EventType {
match event {
Event::Workspace(_) => EventType::Workspace,
Event::Mode(_) => EventType::Mode,
Event::Window(_) => EventType::Window,
Event::BarConfigUpdate(_) => EventType::BarConfigUpdate,
Event::Binding(_) => EventType::Binding,
Event::Shutdown(_) => EventType::Shutdown,
Event::Tick(_) => EventType::Tick,
Event::BarStateUpdate(_) => EventType::BarStateUpdate,
Event::Input(_) => EventType::Input,
_ => todo!(),
}
}
pub trait SwayIpcEvent {
const EVENT_TYPE: EventType;
fn from_event(e: &Event) -> Option<&Self>;
}
macro_rules! sway_ipc_event_impl {
(@ $($t:tt)*) => { $($t)* };
($t:ty, $v:expr, $($m:tt)*) => {
sway_ipc_event_impl! {@
impl SwayIpcEvent for $t {
const EVENT_TYPE: EventType = $v;
fn from_event(e: &Event) -> Option<&Self> {
match e {
$($m)* (x) => Some(x),
_ => None,
}
}
}
}
};
}
sway_ipc_event_impl!(
swayipc_async::WorkspaceEvent,
EventType::Workspace,
Event::Workspace
);
sway_ipc_event_impl!(swayipc_async::ModeEvent, EventType::Mode, Event::Mode);
sway_ipc_event_impl!(swayipc_async::WindowEvent, EventType::Window, Event::Window);
sway_ipc_event_impl!(
swayipc_async::BarConfig,
EventType::BarConfigUpdate,
Event::BarConfigUpdate
);
sway_ipc_event_impl!(
swayipc_async::BindingEvent,
EventType::Binding,
Event::Binding
);
sway_ipc_event_impl!(
swayipc_async::ShutdownEvent,
EventType::Shutdown,
Event::Shutdown
);
sway_ipc_event_impl!(swayipc_async::TickEvent, EventType::Tick, Event::Tick);
sway_ipc_event_impl!(
swayipc_async::BarStateUpdateEvent,
EventType::BarStateUpdate,
Event::BarStateUpdate
);
sway_ipc_event_impl!(swayipc_async::InputEvent, EventType::Input, Event::Input);

View file

@ -20,12 +20,14 @@
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
#[zbus::dbus_proxy(
use zbus::proxy;
#[proxy(
interface = "org.erikreider.swaync.cc",
default_service = "org.erikreider.swaync.cc",
default_path = "/org/erikreider/swaync/cc"
)]
trait SwayNc {
pub trait SwayNc {
/// AddInhibitor method
fn add_inhibitor(&self, application_id: &str) -> zbus::Result<bool>;
@ -90,11 +92,11 @@ trait SwayNc {
fn toggle_visibility(&self) -> zbus::Result<()>;
/// Subscribe signal
#[dbus_proxy(signal)]
#[zbus(signal)]
fn subscribe(&self, count: u32, dnd: bool, cc_open: bool) -> zbus::Result<()>;
/// SubscribeV2 signal
#[dbus_proxy(signal)]
#[zbus(signal)]
fn subscribe_v2(
&self,
count: u32,
@ -104,8 +106,8 @@ trait SwayNc {
) -> zbus::Result<()>;
/// Inhibited property
#[dbus_proxy(property)]
#[zbus(property)]
fn inhibited(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
#[zbus(property)]
fn set_inhibited(&self, value: bool) -> zbus::Result<()>;
}

View file

@ -1,6 +1,7 @@
mod dbus;
use crate::{register_fallible_client, send, spawn};
use crate::channels::SyncSenderExt;
use crate::{register_fallible_client, spawn};
use color_eyre::{Report, Result};
use dbus::SwayNcProxy;
use serde::Deserialize;
@ -54,9 +55,13 @@ impl Client {
spawn(async move {
while let Some(ev) = stream.next().await {
let ev = ev.body::<Event>().expect("to deserialize");
let ev = ev
.message()
.body()
.deserialize::<Event>()
.expect("to deserialize");
debug!("Received event: {ev:?}");
send!(tx, ev);
tx.send_expect(ev);
}
});
}

621
src/clients/sysinfo.rs Normal file
View file

@ -0,0 +1,621 @@
use crate::modules::sysinfo::Interval;
use crate::{lock, register_client};
use color_eyre::{Report, Result};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use sysinfo::{Components, Disks, LoadAvg, Networks, RefreshKind, System};
#[repr(u64)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum Prefix {
#[default]
None = 1,
Kilo = 1000,
Mega = Prefix::Kilo as u64 * 1000,
Giga = Prefix::Mega as u64 * 1000,
Tera = Prefix::Giga as u64 * 1000,
Peta = Prefix::Tera as u64 * 1000,
Kibi = 1024,
Mebi = Prefix::Kibi as u64 * 1024,
Gibi = Prefix::Mebi as u64 * 1024,
Tebi = Prefix::Gibi as u64 * 1024,
Pebi = Prefix::Tebi as u64 * 1024,
// # Units
// These are special cases
// where you'd actually want to do slightly more than a prefix alone.
// Included as part of the prefix system for simplicity.
KiloBit = 128,
MegaBit = Prefix::KiloBit as u64 * 1024,
GigaBit = Prefix::MegaBit as u64 * 1024,
}
#[derive(Debug, Clone)]
pub enum Function {
None,
Sum,
Min,
Max,
Mean,
Name(String),
}
impl FromStr for Function {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"sum" => Ok(Self::Sum),
"min" => Ok(Self::Min),
"max" => Ok(Self::Max),
"mean" => Ok(Self::Mean),
"" => Err(()),
_ => Ok(Self::Name(s.to_string())),
}
}
}
#[derive(Debug)]
pub struct ValueSet {
values: HashMap<Box<str>, Value>,
}
impl FromIterator<(Box<str>, Value)> for ValueSet {
fn from_iter<T: IntoIterator<Item = (Box<str>, Value)>>(iter: T) -> Self {
Self {
values: iter.into_iter().collect(),
}
}
}
impl ValueSet {
fn values(&self, prefix: Prefix) -> impl Iterator<Item = f64> + use<'_> {
self.values
.values()
.map(move |v| v.get(prefix))
.filter(|v| !v.is_nan())
}
pub fn apply(&self, function: &Function, prefix: Prefix) -> f64 {
match function {
Function::None => 0.0,
Function::Sum => self.sum(prefix),
Function::Min => self.min(prefix),
Function::Max => self.max(prefix),
Function::Mean => self.mean(prefix),
Function::Name(name) => self
.values
.get(&Box::from(name.as_str()))
.map(|v| v.get(prefix))
.unwrap_or_default(),
}
}
fn sum(&self, prefix: Prefix) -> f64 {
self.values(prefix).sum()
}
fn min(&self, prefix: Prefix) -> f64 {
self.values(prefix)
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
.unwrap_or_default()
}
fn max(&self, prefix: Prefix) -> f64 {
self.values(prefix)
.max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
.unwrap_or_default()
}
fn mean(&self, prefix: Prefix) -> f64 {
self.sum(prefix) / self.values.len() as f64
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Value {
value: f64,
prefix: Prefix,
}
impl Value {
pub fn new(value: f64) -> Self {
Self::new_with_prefix(value, Prefix::None)
}
pub fn new_with_prefix(value: f64, prefix: Prefix) -> Self {
Self { value, prefix }
}
pub fn get(self, prefix: Prefix) -> f64 {
if prefix == self.prefix {
self.value
} else {
let scale = self.prefix as u64 as f64 / prefix as u64 as f64;
self.value * scale
}
}
}
#[derive(Debug)]
pub struct Client {
system: Mutex<System>,
disks: Mutex<Disks>,
components: Mutex<Components>,
networks: Mutex<Networks>,
load_average: Mutex<LoadAvg>,
}
impl Client {
pub fn new() -> Self {
let refresh_kind = RefreshKind::everything().without_processes();
let system = System::new_with_specifics(refresh_kind);
let disks = Disks::new_with_refreshed_list();
let components = Components::new_with_refreshed_list();
let networks = Networks::new_with_refreshed_list();
let load_average = System::load_average();
Self {
system: Mutex::new(system),
disks: Mutex::new(disks),
components: Mutex::new(components),
networks: Mutex::new(networks),
load_average: Mutex::new(load_average),
}
}
pub fn refresh_cpu(&self) {
lock!(self.system).refresh_cpu_all();
}
pub fn refresh_memory(&self) {
lock!(self.system).refresh_memory();
}
pub fn refresh_network(&self) {
lock!(self.networks).refresh(true);
}
pub fn refresh_temps(&self) {
lock!(self.components).refresh(true);
}
pub fn refresh_disks(&self) {
lock!(self.disks).refresh(true);
}
pub fn refresh_load_average(&self) {
*lock!(self.load_average) = System::load_average();
}
pub fn cpu_frequency(&self) -> ValueSet {
lock!(self.system)
.cpus()
.iter()
.map(|cpu| {
(
cpu.name().into(),
Value::new_with_prefix(cpu.frequency() as f64, Prefix::Mega),
)
})
.collect()
}
pub fn cpu_percent(&self) -> ValueSet {
lock!(self.system)
.cpus()
.iter()
.map(|cpu| (cpu.name().into(), Value::new(cpu.cpu_usage() as f64)))
.collect()
}
pub fn memory_free(&self) -> Value {
Value::new(lock!(self.system).free_memory() as f64)
}
pub fn memory_available(&self) -> Value {
Value::new(lock!(self.system).available_memory() as f64)
}
pub fn memory_total(&self) -> Value {
Value::new(lock!(self.system).total_memory() as f64)
}
pub fn memory_used(&self) -> Value {
Value::new(lock!(self.system).used_memory() as f64)
}
pub fn memory_percent(&self) -> Value {
let total = lock!(self.system).total_memory() as f64;
let used = lock!(self.system).used_memory() as f64;
Value::new(used / total * 100.0)
}
pub fn swap_free(&self) -> Value {
Value::new(lock!(self.system).free_swap() as f64)
}
pub fn swap_total(&self) -> Value {
Value::new(lock!(self.system).total_swap() as f64)
}
pub fn swap_used(&self) -> Value {
Value::new(lock!(self.system).used_swap() as f64)
}
pub fn swap_percent(&self) -> Value {
let total = lock!(self.system).total_swap() as f64;
let used = lock!(self.system).used_swap() as f64;
Value::new(used / total * 100.0)
}
pub fn temp_c(&self) -> ValueSet {
lock!(self.components)
.iter()
.map(|comp| {
(
comp.label().into(),
Value::new(comp.temperature().unwrap_or_default() as f64),
)
})
.collect()
}
pub fn temp_f(&self) -> ValueSet {
lock!(self.components)
.iter()
.map(|comp| {
(
comp.label().into(),
Value::new(c_to_f(comp.temperature().unwrap_or_default() as f64)),
)
})
.collect()
}
pub fn disk_free(&self) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new(disk.available_space() as f64),
)
})
.collect()
}
pub fn disk_total(&self) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new(disk.total_space() as f64),
)
})
.collect()
}
pub fn disk_used(&self) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new((disk.total_space() - disk.available_space()) as f64),
)
})
.collect()
}
pub fn disk_percent(&self) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new(
(disk.total_space() - disk.available_space()) as f64
/ disk.total_space() as f64
* 100.0,
),
)
})
.collect()
}
pub fn disk_read(&self, interval: Interval) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new(disk.usage().read_bytes as f64 / interval.disks() as f64),
)
})
.collect()
}
pub fn disk_write(&self, interval: Interval) -> ValueSet {
lock!(self.disks)
.iter()
.map(|disk| {
(
disk.mount_point().to_string_lossy().into(),
Value::new(disk.usage().written_bytes as f64 / interval.disks() as f64),
)
})
.collect()
}
pub fn net_down(&self, interval: Interval) -> ValueSet {
lock!(self.networks)
.iter()
.map(|(name, net)| {
(
name.as_str().into(),
Value::new(net.received() as f64 / interval.networks() as f64),
)
})
.collect()
}
pub fn net_up(&self, interval: Interval) -> ValueSet {
lock!(self.networks)
.iter()
.map(|(name, net)| {
(
name.as_str().into(),
Value::new(net.transmitted() as f64 / interval.networks() as f64),
)
})
.collect()
}
pub fn load_average_1(&self) -> Value {
Value::new(lock!(self.load_average).one)
}
pub fn load_average_5(&self) -> Value {
Value::new(lock!(self.load_average).five)
}
pub fn load_average_15(&self) -> Value {
Value::new(lock!(self.load_average).fifteen)
}
/// Gets system uptime formatted as `HH:mm`.
pub fn uptime() -> String {
let uptime = System::uptime();
let hours = uptime / 3600;
format!("{:0>2}:{:0>2}", hours, (uptime % 3600) / 60)
}
}
register_client!(Client, sys_info);
const fn c_to_f(c: f64) -> f64 {
c / 5.0 * 9.0 + 32.0
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TokenType {
CpuFrequency,
CpuPercent,
MemoryFree,
MemoryAvailable,
MemoryTotal,
MemoryUsed,
MemoryPercent,
SwapFree,
SwapTotal,
SwapUsed,
SwapPercent,
TempC,
TempF,
DiskFree,
DiskTotal,
DiskUsed,
DiskPercent,
DiskRead,
DiskWrite,
NetDown,
NetUp,
LoadAverage1,
LoadAverage5,
LoadAverage15,
Uptime,
}
impl FromStr for TokenType {
type Err = Report;
fn from_str(s: &str) -> Result<Self> {
match s {
"cpu_frequency" => Ok(Self::CpuFrequency),
"cpu_percent" => Ok(Self::CpuPercent),
"memory_free" => Ok(Self::MemoryFree),
"memory_available" => Ok(Self::MemoryAvailable),
"memory_total" => Ok(Self::MemoryTotal),
"memory_used" => Ok(Self::MemoryUsed),
"memory_percent" => Ok(Self::MemoryPercent),
"swap_free" => Ok(Self::SwapFree),
"swap_total" => Ok(Self::SwapTotal),
"swap_used" => Ok(Self::SwapUsed),
"swap_percent" => Ok(Self::SwapPercent),
"temp_c" => Ok(Self::TempC),
"temp_f" => Ok(Self::TempF),
"disk_free" => Ok(Self::DiskFree),
"disk_total" => Ok(Self::DiskTotal),
"disk_used" => Ok(Self::DiskUsed),
"disk_percent" => Ok(Self::DiskPercent),
"disk_read" => Ok(Self::DiskRead),
"disk_write" => Ok(Self::DiskWrite),
"net_down" => Ok(Self::NetDown),
"net_up" => Ok(Self::NetUp),
"load_average_1" => Ok(Self::LoadAverage1),
"load_average_5" => Ok(Self::LoadAverage5),
"load_average_15" => Ok(Self::LoadAverage15),
"uptime" => Ok(Self::Uptime),
_ => Err(Report::msg(format!("invalid token type: '{s}'"))),
}
}
}
#[cfg(feature = "ipc")]
use crate::ironvar::Namespace;
#[cfg(feature = "ipc")]
impl Namespace for Client {
fn get(&self, key: &str) -> Option<String> {
let get = |value: Value| Some(value.get(Prefix::None).to_string());
let token = TokenType::from_str(key).ok()?;
match token {
TokenType::CpuFrequency => None,
TokenType::CpuPercent => None,
TokenType::MemoryFree => get(self.memory_free()),
TokenType::MemoryAvailable => get(self.memory_available()),
TokenType::MemoryTotal => get(self.memory_total()),
TokenType::MemoryUsed => get(self.memory_used()),
TokenType::MemoryPercent => get(self.memory_percent()),
TokenType::SwapFree => get(self.swap_free()),
TokenType::SwapTotal => get(self.swap_total()),
TokenType::SwapUsed => get(self.swap_used()),
TokenType::SwapPercent => get(self.swap_percent()),
TokenType::TempC => None,
TokenType::TempF => None,
TokenType::DiskFree => None,
TokenType::DiskTotal => None,
TokenType::DiskUsed => None,
TokenType::DiskPercent => None,
TokenType::DiskRead => None,
TokenType::DiskWrite => None,
TokenType::NetDown => None,
TokenType::NetUp => None,
TokenType::LoadAverage1 => get(self.load_average_1()),
TokenType::LoadAverage5 => get(self.load_average_5()),
TokenType::LoadAverage15 => get(self.load_average_15()),
TokenType::Uptime => Some(Client::uptime()),
}
}
fn list(&self) -> Vec<String> {
vec![
"memory_free",
"memory_available",
"memory_total",
"memory_used",
"memory_percent",
"swap_free",
"swap_total",
"swap_used",
"swap_percent",
"load_average_1",
"load_average_5",
"load_average_15",
"uptime",
]
.into_iter()
.map(ToString::to_string)
.collect()
}
fn namespaces(&self) -> Vec<String> {
vec![
"cpu_frequency",
"cpu_percent",
"temp_c",
"temp_f",
"disk_free",
"disk_total",
"disk_used",
"disk_percent",
"disk_read",
"disk_write",
"net_down",
"net_up",
]
.into_iter()
.map(ToString::to_string)
.collect()
}
fn get_namespace(&self, key: &str) -> Option<Arc<dyn Namespace + Sync + Send>> {
let token = TokenType::from_str(key).ok()?;
match token {
TokenType::CpuFrequency => Some(Arc::new(self.cpu_frequency())),
TokenType::CpuPercent => Some(Arc::new(self.cpu_percent())),
TokenType::MemoryFree => None,
TokenType::MemoryAvailable => None,
TokenType::MemoryTotal => None,
TokenType::MemoryUsed => None,
TokenType::MemoryPercent => None,
TokenType::SwapFree => None,
TokenType::SwapTotal => None,
TokenType::SwapUsed => None,
TokenType::SwapPercent => None,
TokenType::TempC => Some(Arc::new(self.temp_c())),
TokenType::TempF => Some(Arc::new(self.temp_f())),
TokenType::DiskFree => Some(Arc::new(self.disk_free())),
TokenType::DiskTotal => Some(Arc::new(self.disk_total())),
TokenType::DiskUsed => Some(Arc::new(self.disk_used())),
TokenType::DiskPercent => Some(Arc::new(self.disk_percent())),
TokenType::DiskRead => Some(Arc::new(self.disk_read(Interval::All(1)))),
TokenType::DiskWrite => Some(Arc::new(self.disk_write(Interval::All(1)))),
TokenType::NetDown => Some(Arc::new(self.net_down(Interval::All(1)))),
TokenType::NetUp => Some(Arc::new(self.net_up(Interval::All(1)))),
TokenType::LoadAverage1 => None,
TokenType::LoadAverage5 => None,
TokenType::LoadAverage15 => None,
TokenType::Uptime => None,
}
}
}
#[cfg(feature = "ipc")]
impl Namespace for ValueSet {
fn get(&self, key: &str) -> Option<String> {
let function = Function::from_str(key).ok()?;
Some(self.apply(&function, Prefix::None).to_string())
}
fn list(&self) -> Vec<String> {
let mut vec = vec!["sum", "min", "max", "mean"]
.into_iter()
.map(ToString::to_string)
.collect::<Vec<_>>();
vec.extend(self.values.keys().map(ToString::to_string));
vec
}
fn namespaces(&self) -> Vec<String> {
vec![]
}
fn get_namespace(&self, _key: &str) -> Option<Arc<dyn Namespace + Sync + Send>> {
None
}
}

View file

@ -1,35 +0,0 @@
use crate::register_client;
use std::sync::Arc;
use upower_dbus::UPowerProxy;
use zbus::fdo::PropertiesProxy;
pub async fn create_display_proxy() -> Arc<PropertiesProxy<'static>> {
let dbus = Box::pin(zbus::Connection::system())
.await
.expect("failed to create connection to system bus");
let device_proxy = UPowerProxy::new(&dbus)
.await
.expect("failed to create upower proxy");
let display_device = device_proxy
.get_display_device()
.await
.unwrap_or_else(|_| panic!("failed to get display device for {device_proxy:?}"));
let path = display_device.path().to_owned();
let proxy = PropertiesProxy::builder(&dbus)
.destination("org.freedesktop.UPower")
.expect("failed to set proxy destination address")
.path(path)
.expect("failed to set proxy path")
.cache_properties(zbus::CacheProperties::No)
.build()
.await
.expect("failed to build proxy");
Arc::new(proxy)
}
register_client!(PropertiesProxy<'static>, upower);

159
src/clients/upower/dbus.rs Normal file
View file

@ -0,0 +1,159 @@
/// Originally taken from `upower-dbus` crate
/// <https://github.com/pop-os/upower-dbus/blob/main/LICENSE>
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use zbus::proxy;
use zbus::zvariant::OwnedValue;
#[derive(Debug, Copy, Clone, PartialEq, Eq, OwnedValue)]
#[repr(u32)]
pub enum BatteryState {
Unknown = 0,
Charging = 1,
Discharging = 2,
Empty = 3,
FullyCharged = 4,
PendingCharge = 5,
PendingDischarge = 6,
}
#[derive(Debug, Copy, Clone, OwnedValue)]
#[repr(u32)]
pub enum BatteryType {
Unknown = 0,
LinePower = 1,
Battery = 2,
Ups = 3,
Monitor = 4,
Mouse = 5,
Keyboard = 6,
Pda = 7,
Phone = 8,
}
#[derive(Debug, Copy, Clone, OwnedValue)]
#[repr(u32)]
pub enum BatteryLevel {
Unknown = 0,
None = 1,
Low = 3,
Critical = 4,
Normal = 6,
High = 7,
Full = 8,
}
#[proxy(
interface = "org.freedesktop.UPower.Device",
default_service = "org.freedesktop.UPower",
assume_defaults = false
)]
pub trait Device {
#[zbus(property)]
fn battery_level(&self) -> zbus::Result<BatteryLevel>;
#[zbus(property)]
fn capacity(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn energy(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn energy_empty(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn energy_full(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn energy_full_design(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn has_history(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn has_statistics(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn icon_name(&self) -> zbus::Result<String>;
#[zbus(property)]
fn is_present(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn is_rechargeable(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn luminosity(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn model(&self) -> zbus::Result<String>;
#[zbus(property)]
fn native_path(&self) -> zbus::Result<String>;
#[zbus(property)]
fn online(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn percentage(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn power_supply(&self) -> zbus::Result<bool>;
fn refresh(&self) -> zbus::Result<()>;
#[zbus(property)]
fn serial(&self) -> zbus::Result<String>;
#[zbus(property)]
fn state(&self) -> zbus::Result<BatteryState>;
#[zbus(property)]
fn temperature(&self) -> zbus::Result<f64>;
#[zbus(property, name = "Type")]
fn type_(&self) -> zbus::Result<BatteryType>;
#[zbus(property)]
fn vendor(&self) -> zbus::Result<String>;
#[zbus(property)]
fn voltage(&self) -> zbus::Result<f64>;
}
#[proxy(interface = "org.freedesktop.UPower", assume_defaults = true)]
pub trait UPower {
/// EnumerateDevices method
fn enumerate_devices(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// GetCriticalAction method
fn get_critical_action(&self) -> zbus::Result<String>;
/// GetDisplayDevice method
#[zbus(object = "Device")]
fn get_display_device(&self);
/// DeviceAdded signal
#[zbus(signal)]
fn device_added(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// DeviceRemoved signal
#[zbus(signal)]
fn device_removed(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// DaemonVersion property
#[zbus(property)]
fn daemon_version(&self) -> zbus::Result<String>;
/// LidIsClosed property
#[zbus(property)]
fn lid_is_closed(&self) -> zbus::Result<bool>;
/// LidIsPresent property
#[zbus(property)]
fn lid_is_present(&self) -> zbus::Result<bool>;
/// OnBattery property
#[zbus(property)]
fn on_battery(&self) -> zbus::Result<bool>;
}

33
src/clients/upower/mod.rs Normal file
View file

@ -0,0 +1,33 @@
mod dbus;
use crate::clients::ClientResult;
use crate::register_fallible_client;
use dbus::UPowerProxy;
use std::sync::Arc;
use zbus::fdo::PropertiesProxy;
use zbus::proxy::CacheProperties;
pub use dbus::BatteryState;
pub async fn create_display_proxy() -> ClientResult<PropertiesProxy<'static>> {
let dbus = Box::pin(zbus::Connection::system()).await?;
let device_proxy = UPowerProxy::new(&dbus).await?;
let display_device = device_proxy.get_display_device().await?;
let path = display_device.inner().path();
let proxy = PropertiesProxy::builder(&dbus)
.destination("org.freedesktop.UPower")
.expect("failed to set proxy destination address")
.path(path)
.expect("failed to set proxy path")
.cache_properties(CacheProperties::No)
.build()
.await?;
Ok(Arc::new(proxy))
}
register_fallible_client!(PropertiesProxy<'static>, upower);

View file

@ -1,7 +1,7 @@
mod sink;
mod sink_input;
use crate::{arc_mut, lock, register_client, send, spawn_blocking, APP_ID};
use crate::{APP_ID, arc_mut, lock, register_client, spawn_blocking};
use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::introspect::{Introspector, ServerInfo};
use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation};
@ -12,8 +12,9 @@ use libpulse_binding::volume::{ChannelVolumes, Volume};
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};
use tokio::sync::broadcast;
use tracing::{debug, error, info, warn};
use tracing::{debug, error, info, trace, warn};
use crate::channels::SyncSenderExt;
pub use sink::Sink;
pub use sink_input::SinkInput;
@ -230,6 +231,8 @@ fn on_event(
return;
};
trace!("server event: {facility:?}, op: {op:?}, i: {i}");
match facility {
Facility::Server => on_server_event(context, &data.sinks, &data.default_sink_name, tx),
Facility::Sink => sink::on_event(context, &data.sinks, &data.default_sink_name, tx, op, i),
@ -269,7 +272,7 @@ fn set_default_sink(
{
sink.active = true;
debug!("Set sink active: {}", sink.name);
send!(tx, Event::UpdateSink(sink.clone()));
tx.send_expect(Event::UpdateSink(sink.clone()));
} else {
warn!("Couldn't find sink: {}", default_sink_name);
}

View file

@ -1,13 +1,14 @@
use super::{percent_to_volume, volume_to_percent, ArcMutVec, Client, ConnectionState, Event};
use crate::{lock, send};
use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::channels::SyncSenderExt;
use crate::lock;
use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInfo;
use libpulse_binding::context::subscribe::Operation;
use libpulse_binding::context::Context;
use libpulse_binding::def::SinkState;
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{Arc, Mutex, mpsc};
use tokio::sync::broadcast;
use tracing::{debug, error};
use tracing::{debug, error, instrument, trace};
#[derive(Debug, Clone)]
pub struct Sink {
@ -41,16 +42,19 @@ impl From<&SinkInfo<'_>> for Sink {
}
impl Client {
#[instrument(level = "trace")]
pub fn sinks(&self) -> Arc<Mutex<Vec<Sink>>> {
self.data.sinks.clone()
}
#[instrument(level = "trace")]
pub fn set_default_sink(&self, name: &str) {
if let ConnectionState::Connected { context, .. } = &*lock!(self.connection) {
lock!(context).set_default_sink(name, |_| {});
}
}
#[instrument(level = "trace")]
pub fn set_sink_volume(&self, name: &str, volume_percent: f64) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
let (tx, rx) = mpsc::channel();
@ -59,7 +63,7 @@ impl Client {
let ListResult::Item(info) = info else {
return;
};
send!(tx, info.volume);
tx.send_expect(info.volume);
});
let new_volume = percent_to_volume(volume_percent);
@ -73,6 +77,7 @@ impl Client {
}
}
#[instrument(level = "trace")]
pub fn set_sink_muted(&self, name: &str, muted: bool) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
introspector.set_sink_mute_by_name(name, muted, None);
@ -122,8 +127,10 @@ pub fn add(info: ListResult<&SinkInfo>, sinks: &ArcMutVec<Sink>, tx: &broadcast:
return;
};
trace!("adding {info:?}");
lock!(sinks).push(info.into());
send!(tx, Event::AddSink(info.into()));
tx.send_expect(Event::AddSink(info.into()));
}
fn update(
@ -136,6 +143,8 @@ fn update(
return;
};
trace!("updating {info:?}");
{
let mut sinks = lock!(sinks);
let Some(pos) = sinks.iter().position(|sink| sink.index == info.index) else {
@ -162,14 +171,16 @@ fn update(
}
}
send!(tx, Event::UpdateSink(sink));
tx.send_expect(Event::UpdateSink(sink));
}
fn remove(index: u32, sinks: &ArcMutVec<Sink>, tx: &broadcast::Sender<Event>) {
trace!("removing {index}");
let mut sinks = lock!(sinks);
if let Some(pos) = sinks.iter().position(|s| s.index == index) {
let info = sinks.remove(pos);
send!(tx, Event::RemoveSink(info.name));
tx.send_expect(Event::RemoveSink(info.name));
}
}

View file

@ -1,12 +1,13 @@
use super::{percent_to_volume, volume_to_percent, ArcMutVec, Client, ConnectionState, Event};
use crate::{lock, send};
use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
use crate::channels::SyncSenderExt;
use crate::lock;
use libpulse_binding::callbacks::ListResult;
use libpulse_binding::context::Context;
use libpulse_binding::context::introspect::SinkInputInfo;
use libpulse_binding::context::subscribe::Operation;
use libpulse_binding::context::Context;
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{Arc, Mutex, mpsc};
use tokio::sync::broadcast;
use tracing::{debug, error};
use tracing::{debug, error, instrument, trace};
#[derive(Debug, Clone)]
pub struct SinkInput {
@ -35,10 +36,12 @@ impl From<&SinkInputInfo<'_>> for SinkInput {
}
impl Client {
#[instrument(level = "trace")]
pub fn sink_inputs(&self) -> Arc<Mutex<Vec<SinkInput>>> {
self.data.sink_inputs.clone()
}
#[instrument(level = "trace")]
pub fn set_input_volume(&self, index: u32, volume_percent: f64) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
let (tx, rx) = mpsc::channel();
@ -47,7 +50,7 @@ impl Client {
let ListResult::Item(info) = info else {
return;
};
send!(tx, info.volume);
tx.send_expect(info.volume);
});
let new_volume = percent_to_volume(volume_percent);
@ -61,6 +64,7 @@ impl Client {
}
}
#[instrument(level = "trace")]
pub fn set_input_muted(&self, index: u32, muted: bool) {
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
introspector.set_sink_input_mute(index, muted, None);
@ -112,8 +116,10 @@ pub fn add(
return;
};
trace!("adding {info:?}");
lock!(inputs).push(info.into());
send!(tx, Event::AddInput(info.into()));
tx.send_expect(Event::AddInput(info.into()));
}
fn update(
@ -125,6 +131,8 @@ fn update(
return;
};
trace!("updating {info:?}");
{
let mut inputs = lock!(inputs);
let Some(pos) = inputs.iter().position(|input| input.index == info.index) else {
@ -135,14 +143,16 @@ fn update(
inputs[pos] = info.into();
}
send!(tx, Event::UpdateInput(info.into()));
tx.send_expect(Event::UpdateInput(info.into()));
}
fn remove(index: u32, inputs: &ArcMutVec<SinkInput>, tx: &broadcast::Sender<Event>) {
let mut inputs = lock!(inputs);
trace!("removing {index}");
if let Some(pos) = inputs.iter().position(|s| s.index == index) {
let info = inputs.remove(pos);
send!(tx, Event::RemoveInput(info.index));
tx.send_expect(Event::RemoveInput(info.index));
}
}

View file

@ -2,17 +2,18 @@ mod macros;
mod wl_output;
mod wl_seat;
use crate::error::{ExitCode, ERR_CHANNEL_RECV};
use crate::{arc_mut, lock, register_client, send, spawn, spawn_blocking};
use crate::error::{ERR_CHANNEL_RECV, ExitCode};
use crate::{arc_mut, lock, register_client, spawn, spawn_blocking};
use std::process::exit;
use std::sync::{Arc, Mutex};
use crate::channels::SyncSenderExt;
use calloop_channel::Event::Msg;
use cfg_if::cfg_if;
use color_eyre::Report;
use color_eyre::{Help, Report};
use smithay_client_toolkit::output::OutputState;
use smithay_client_toolkit::reexports::calloop::EventLoop;
use smithay_client_toolkit::reexports::calloop::channel as calloop_channel;
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle};
use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
use smithay_client_toolkit::registry::{ProvidesRegistryState, RegistryState};
use smithay_client_toolkit::seat::SeatState;
@ -43,7 +44,6 @@ cfg_if! {
use self::wlr_data_control::device::DataControlDevice;
use self::wlr_data_control::manager::DataControlDeviceManagerState;
use self::wlr_data_control::source::CopyPasteSource;
use self::wlr_data_control::SelectionOfferItem;
use wayland_client::protocol::wl_seat::WlSeat;
pub use wlr_data_control::{ClipboardItem, ClipboardValue};
@ -76,6 +76,8 @@ pub enum Request {
ToplevelInfoAll,
#[cfg(feature = "launcher")]
ToplevelFocus(usize),
#[cfg(feature = "launcher")]
ToplevelMinimize(usize),
#[cfg(feature = "clipboard")]
CopyToClipboard(ClipboardItem),
@ -150,12 +152,12 @@ impl Client {
spawn(async move {
while let Some(event) = event_rx.recv().await {
match event {
Event::Output(event) => send!(output_tx, event),
Event::Output(event) => output_tx.send_expect(event),
#[cfg(any(feature = "focused", feature = "launcher"))]
Event::Toplevel(event) => send!(toplevel_tx, event),
Event::Toplevel(event) => toplevel_tx.send_expect(event),
#[cfg(feature = "clipboard")]
Event::Clipboard(item) => send!(clipboard_tx, item),
};
Event::Clipboard(item) => clipboard_tx.send_expect(item),
}
}
});
}
@ -175,7 +177,7 @@ impl Client {
/// Sends a request to the environment event loop,
/// and returns the response.
fn send_request(&self, request: Request) -> Response {
send!(self.tx, request);
self.tx.send_expect(request);
lock!(self.rx).recv().expect(ERR_CHANNEL_RECV)
}
@ -193,7 +195,6 @@ pub struct Environment {
seat_state: SeatState,
queue_handle: QueueHandle<Self>,
loop_handle: LoopHandle<'static, Self>,
event_tx: mpsc::Sender<Event>,
response_tx: std::sync::mpsc::Sender<Response>,
@ -204,14 +205,12 @@ pub struct Environment {
// -- clipboard --
#[cfg(feature = "clipboard")]
data_control_device_manager_state: DataControlDeviceManagerState,
data_control_device_manager_state: Option<DataControlDeviceManagerState>,
#[cfg(feature = "clipboard")]
data_control_devices: Vec<DataControlDeviceEntry>,
#[cfg(feature = "clipboard")]
copy_paste_sources: Vec<CopyPasteSource>,
#[cfg(feature = "clipboard")]
selection_offers: Vec<SelectionOfferItem>,
// local state
#[cfg(feature = "clipboard")]
@ -265,12 +264,30 @@ impl Environment {
let output_state = OutputState::new(&globals, &qh);
let seat_state = SeatState::new(&globals, &qh);
#[cfg(any(feature = "focused", feature = "launcher"))]
ToplevelManagerState::bind(&globals, &qh)
.expect("to bind to wlr_foreign_toplevel_manager global");
if let Err(err) = ToplevelManagerState::bind(&globals, &qh) {
error!("{:?}",
Report::new(err)
.wrap_err("Failed to bind to wlr_foreign_toplevel_manager global")
.note("This is likely a due to the current compositor not supporting the required protocol")
.note("launcher and focused modules will not work")
);
}
#[cfg(feature = "clipboard")]
let data_control_device_manager_state = DataControlDeviceManagerState::bind(&globals, &qh)
.expect("to bind to wlr_data_control_device_manager global");
let data_control_device_manager_state = match DataControlDeviceManagerState::bind(
&globals, &qh,
) {
Ok(state) => Some(state),
Err(err) => {
error!("{:?}",
Report::new(err)
.wrap_err("Failed to bind to wlr_data_control_device global")
.note("This is likely a due to the current compositor not supporting the required protocol")
.note("clipboard module will not work")
);
None
}
};
let mut env = Self {
registry_state,
@ -279,7 +296,6 @@ impl Environment {
#[cfg(feature = "clipboard")]
data_control_device_manager_state,
queue_handle: qh,
loop_handle: loop_handle.clone(),
event_tx,
response_tx,
#[cfg(any(feature = "focused", feature = "launcher"))]
@ -290,8 +306,6 @@ impl Environment {
#[cfg(feature = "clipboard")]
copy_paste_sources: vec![],
#[cfg(feature = "clipboard")]
selection_offers: vec![],
#[cfg(feature = "clipboard")]
clipboard: arc_mut!(None),
};
@ -320,12 +334,12 @@ impl Environment {
match event {
Msg(Request::Roundtrip) => {
debug!("received roundtrip request");
send!(env.response_tx, Response::Ok);
env.response_tx.send_expect(Response::Ok);
}
#[cfg(feature = "ipc")]
Msg(Request::OutputInfoAll) => {
let infos = env.output_info_all();
send!(env.response_tx, Response::OutputInfoAll(infos));
env.response_tx.send_expect(Response::OutputInfoAll(infos));
}
#[cfg(any(feature = "focused", feature = "launcher"))]
Msg(Request::ToplevelInfoAll) => {
@ -334,31 +348,46 @@ impl Environment {
.iter()
.filter_map(ToplevelHandle::info)
.collect();
send!(env.response_tx, Response::ToplevelInfoAll(infos));
env.response_tx
.send_expect(Response::ToplevelInfoAll(infos));
}
#[cfg(feature = "launcher")]
Msg(Request::ToplevelFocus(id)) => {
let handle = env
.handles
.iter()
.find(|handle| handle.info().map_or(false, |info| info.id == id));
.find(|handle| handle.info().is_some_and(|info| info.id == id));
if let Some(handle) = handle {
let seat = env.default_seat();
handle.focus(&seat);
}
send!(env.response_tx, Response::Ok);
env.response_tx.send_expect(Response::Ok);
}
#[cfg(feature = "launcher")]
Msg(Request::ToplevelMinimize(id)) => {
let handle = env
.handles
.iter()
.find(|handle| handle.info().is_some_and(|info| info.id == id));
if let Some(handle) = handle {
handle.minimize();
}
env.response_tx.send_expect(Response::Ok);
}
#[cfg(feature = "clipboard")]
Msg(Request::CopyToClipboard(item)) => {
env.copy_to_clipboard(item);
send!(env.response_tx, Response::Ok);
env.response_tx.send_expect(Response::Ok);
}
#[cfg(feature = "clipboard")]
Msg(Request::ClipboardItem) => {
let item = lock!(env.clipboard).clone();
send!(env.response_tx, Response::ClipboardItem(item));
env.response_tx.send_expect(Response::ClipboardItem(item));
}
calloop_channel::Event::Closed => error!("request channel unexpectedly closed"),
}

View file

@ -1,5 +1,5 @@
use super::{Client, Environment, Event};
use crate::try_send;
use crate::channels::AsyncSenderExt;
use smithay_client_toolkit::output::{OutputHandler, OutputInfo, OutputState};
use tokio::sync::broadcast;
use tracing::{debug, error};
@ -12,7 +12,7 @@ pub struct OutputEvent {
pub event_type: OutputEventType,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputEventType {
New,
Update,
@ -63,13 +63,10 @@ impl OutputHandler for Environment {
fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handler received new output");
if let Some(info) = self.output_state.info(&output) {
try_send!(
self.event_tx,
Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::New
})
);
self.event_tx.send_spawn(Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::New,
}));
} else {
error!("Output is missing information!");
}
@ -78,13 +75,10 @@ impl OutputHandler for Environment {
fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handle received output update");
if let Some(info) = self.output_state.info(&output) {
try_send!(
self.event_tx,
Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::Update
})
);
self.event_tx.send_spawn(Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::Update,
}));
} else {
error!("Output is missing information!");
}
@ -93,13 +87,10 @@ impl OutputHandler for Environment {
fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
debug!("Handle received output destruction");
if let Some(info) = self.output_state.info(&output) {
try_send!(
self.event_tx,
Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::Destroyed
})
);
self.event_tx.send_spawn(Event::Output(OutputEvent {
output: info,
event_type: OutputEventType::Destroyed,
}));
} else {
error!("Output is missing information!");
}

View file

@ -1,6 +1,6 @@
use super::Environment;
use smithay_client_toolkit::seat::{Capability, SeatHandler, SeatState};
use tracing::debug;
use tracing::{debug, error};
use wayland_client::protocol::wl_seat::WlSeat;
use wayland_client::{Connection, QueueHandle};
@ -37,7 +37,11 @@ impl SeatHandler for Environment {
{
debug!("Adding new data control device");
// create the data device here for this seat
let data_control_device_manager = &self.data_control_device_manager_state;
let Some(data_control_device_manager) = &self.data_control_device_manager_state else {
error!("data_control_device_manager not available, cannot copy");
return;
};
let data_control_device = data_control_device_manager.get_data_device(qh, &seat);
self.data_control_devices
.push(super::DataControlDeviceEntry {

View file

@ -6,7 +6,7 @@ use crate::error::ERR_WAYLAND_DATA;
use crate::lock;
use std::sync::{Arc, Mutex};
use tracing::warn;
use wayland_client::{event_created_child, Connection, Dispatch, Proxy, QueueHandle};
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle, event_created_child};
use wayland_protocols_wlr::data_control::v1::client::{
zwlr_data_control_device_v1::{Event, ZwlrDataControlDeviceV1},
zwlr_data_control_offer_v1::ZwlrDataControlOfferV1,
@ -37,7 +37,9 @@ pub trait DataControlDeviceDataExt: Send + Sync {
fn selection_mime_types(&self) -> Vec<String> {
let inner = self.data_control_device_data();
lock!(lock!(inner.inner).selection_offer)
let offer = &lock!(inner.inner).selection_offer;
lock!(offer)
.as_ref()
.map(|offer| {
let data = offer
@ -51,14 +53,14 @@ pub trait DataControlDeviceDataExt: Send + Sync {
/// Get the active selection offer if it exists.
fn selection_offer(&self) -> Option<SelectionOffer> {
let inner = self.data_control_device_data();
lock!(lock!(inner.inner).selection_offer)
.as_ref()
.and_then(|offer| {
let data = offer
.data::<Self::DataControlOfferInner>()
.expect(ERR_WAYLAND_DATA);
data.as_selection_offer()
})
let offer = &lock!(inner.inner).selection_offer;
lock!(offer).as_ref().and_then(|offer| {
let data = offer
.data::<Self::DataControlOfferInner>()
.expect(ERR_WAYLAND_DATA);
data.as_selection_offer()
})
}
}
@ -159,7 +161,9 @@ where
}
}
Event::Finished => {
warn!("Data control offer is no longer valid, but has not been dropped by client. This could cause clipboard issues.");
warn!(
"Data control offer is no longer valid, but has not been dropped by client. This could cause clipboard issues."
);
}
_ => {}
}

View file

@ -4,23 +4,29 @@ pub mod offer;
pub mod source;
use self::device::{DataControlDeviceDataExt, DataControlDeviceHandler};
use self::offer::{DataControlDeviceOffer, DataControlOfferHandler, SelectionOffer};
use self::offer::{DataControlDeviceOffer, DataControlOfferHandler};
use self::source::DataControlSourceHandler;
use super::{Client, Environment, Event, Request, Response};
use crate::{lock, try_send, Ironbar};
use crate::channels::AsyncSenderExt;
use crate::{Ironbar, lock, spawn};
use color_eyre::Result;
use device::DataControlDevice;
use glib::Bytes;
use nix::fcntl::{fcntl, F_GETPIPE_SZ, F_SETPIPE_SZ};
use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout};
use rustix::buffer::spare_capacity;
use rustix::event::epoll;
use rustix::event::epoll::CreateFlags;
use rustix::fs::Timespec;
use rustix::pipe::{fcntl_getpipe_size, fcntl_setpipe_size};
use smithay_client_toolkit::data_device_manager::WritePipe;
use smithay_client_toolkit::reexports::calloop::{PostAction, RegistrationToken};
use std::cmp::min;
use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use std::io::Write;
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
use std::sync::Arc;
use std::time::Duration;
use std::{fs, io};
use tokio::io::AsyncReadExt;
use tokio::sync::broadcast;
use tracing::{debug, error, trace};
use wayland_client::{Connection, QueueHandle};
@ -28,12 +34,6 @@ use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_source_v1
const INTERNAL_MIME_TYPE: &str = "x-ironbar-internal";
#[derive(Debug)]
pub struct SelectionOfferItem {
offer: SelectionOffer,
token: Option<RegistrationToken>,
}
/// Represents a value which can be read/written
/// to/from the system clipboard and surrounding metadata.
///
@ -148,6 +148,11 @@ impl Environment {
pub fn copy_to_clipboard(&mut self, item: ClipboardItem) {
debug!("Copying item to clipboard: {item:?}");
let Some(data_control_device_manager) = &self.data_control_device_manager_state else {
error!("data_control_device_manager not available, cannot copy");
return;
};
let seat = self.default_seat();
let Some(device) = self
.data_control_devices
@ -157,9 +162,8 @@ impl Environment {
return;
};
let source = self
.data_control_device_manager_state
.create_copy_paste_source(&self.queue_handle, [INTERNAL_MIME_TYPE, &item.mime_type]);
let source = data_control_device_manager
.create_copy_paste_source(&self.queue_handle, [&item.mime_type, INTERNAL_MIME_TYPE]);
source.set_selection(&device.device);
self.copy_paste_sources.push(source);
@ -168,22 +172,20 @@ impl Environment {
}
/// Reads an offer file handle into a new `ClipboardItem`.
fn read_file(mime_type: &MimeType, file: &mut File) -> io::Result<ClipboardItem> {
async fn read_file(
mime_type: &MimeType,
file: &mut tokio::net::unix::pipe::Receiver,
) -> io::Result<ClipboardItem> {
let mut buf = vec![];
file.read_to_end(&mut buf).await?;
let value = match mime_type.category {
MimeTypeCategory::Text => {
let mut txt = String::new();
file.read_to_string(&mut txt)?;
let txt = String::from_utf8_lossy(&buf).to_string();
ClipboardValue::Text(txt)
}
MimeTypeCategory::Image => {
let mut bytes = vec![];
file.read_to_end(&mut bytes)?;
debug!("Read bytes: {}", bytes.len());
let bytes = Bytes::from(&bytes);
let bytes = Bytes::from(&buf);
ClipboardValue::Image(bytes)
}
};
@ -214,68 +216,33 @@ impl DataControlDeviceHandler for Environment {
}
if let Some(offer) = data_device.selection_offer() {
self.selection_offers
.push(SelectionOfferItem { offer, token: None });
let cur_offer = self
.selection_offers
.last_mut()
.expect("Failed to get current offer");
// clear prev
let Some(mime_type) = MimeType::parse_multiple(&mime_types) else {
lock!(self.clipboard).take();
// send an event so the clipboard module is aware it's changed
try_send!(
self.event_tx,
Event::Clipboard(ClipboardItem {
id: usize::MAX,
mime_type: String::new().into(),
value: Arc::new(ClipboardValue::Other)
})
);
self.event_tx.send_spawn(Event::Clipboard(ClipboardItem {
id: usize::MAX,
mime_type: String::new().into(),
value: Arc::new(ClipboardValue::Other),
}));
return;
};
debug!("Receiving mime type: {}", mime_type.value);
if let Ok(read_pipe) = cur_offer.offer.receive(mime_type.value.clone()) {
let offer_clone = cur_offer.offer.clone();
if let Ok(mut read_pipe) = offer.receive(mime_type.value.clone()) {
let tx = self.event_tx.clone();
let clipboard = self.clipboard.clone();
let token =
self.loop_handle
.insert_source(read_pipe, move |(), file, state| unsafe {
let item = state
.selection_offers
.iter()
.position(|o| o.offer == offer_clone)
.map(|p| state.selection_offers.remove(p))
.expect("Failed to find selection offer item");
match Self::read_file(&mime_type, file.get_mut()) {
Ok(item) => {
lock!(clipboard).replace(item.clone());
try_send!(tx, Event::Clipboard(item));
}
Err(err) => error!("{err:?}"),
}
state
.loop_handle
.remove(item.token.expect("Missing item token"));
PostAction::Remove
});
match token {
Ok(token) => {
cur_offer.token.replace(token);
spawn(async move {
match Self::read_file(&mime_type, &mut read_pipe).await {
Ok(item) => {
lock!(clipboard).replace(item.clone());
tx.send_spawn(Event::Clipboard(item));
}
Err(err) => error!("{err:?}"),
}
Err(err) => error!("Failed to insert read pipe event: {err:?}"),
}
});
}
}
}
@ -313,7 +280,7 @@ impl DataControlSourceHandler for Environment {
source: &ZwlrDataControlSourceV1,
mime: String,
write_pipe: WritePipe,
) {
) -> Result<()> {
debug!("Handler received source send request event ({mime})");
if let Some(item) = lock!(self.clipboard).clone() {
@ -330,32 +297,34 @@ impl DataControlSourceHandler for Environment {
ClipboardValue::Image(bytes) => bytes.as_ref(),
ClipboardValue::Other => panic!(
"{:?}",
io::Error::new(ErrorKind::Other, "Attempted to copy unsupported mime type")
io::Error::other("Attempted to copy unsupported mime type")
),
};
let pipe_size = set_pipe_size(fd.as_raw_fd(), bytes.len())
.expect("Failed to increase pipe size");
let pipe_size =
set_pipe_size(fd.as_fd(), bytes.len()).expect("Failed to increase pipe size");
let mut file = File::from(fd.try_clone().expect("to be able to clone"));
debug!("Writing {} bytes", bytes.len());
let mut events = (0..16).map(|_| EpollEvent::empty()).collect::<Vec<_>>();
let epoll_event = EpollEvent::new(EpollFlags::EPOLLOUT, 0);
let epoll = epoll::create(CreateFlags::CLOEXEC)?;
epoll::add(
&epoll,
fd,
epoll::EventData::new_u64(0),
epoll::EventFlags::OUT,
)?;
let epoll_fd =
Epoll::new(EpollCreateFlags::empty()).expect("to get valid file descriptor");
epoll_fd
.add(fd, epoll_event)
.expect("to send valid epoll operation");
let mut events = Vec::with_capacity(16);
let timeout = EpollTimeout::from(100u16);
while !bytes.is_empty() {
let chunk = &bytes[..min(pipe_size as usize, bytes.len())];
let chunk = &bytes[..min(pipe_size, bytes.len())];
epoll_fd
.wait(&mut events, timeout)
.expect("Failed to wait to epoll");
epoll::wait(
&epoll,
spare_capacity(&mut events),
Some(&Timespec::try_from(Duration::from_millis(100))?),
)?;
match file.write(chunk) {
Ok(written) => {
@ -371,9 +340,11 @@ impl DataControlSourceHandler for Environment {
debug!("Done writing");
} else {
error!("Failed to find source");
error!("Failed to find source (mime: '{mime}')");
}
}
Ok(())
}
fn cancelled(
@ -398,7 +369,7 @@ impl DataControlSourceHandler for Environment {
/// it will be clamped at this.
///
/// Returns the new size if succeeded.
fn set_pipe_size(fd: RawFd, size: usize) -> io::Result<i32> {
fn set_pipe_size(fd: BorrowedFd, size: usize) -> io::Result<usize> {
// clamp size at kernel max
let max_pipe_size = fs::read_to_string("/proc/sys/fs/pipe-max-size")
.expect("Failed to find pipe-max-size virtual kernel file")
@ -408,23 +379,24 @@ fn set_pipe_size(fd: RawFd, size: usize) -> io::Result<i32> {
let size = min(size, max_pipe_size);
let curr_size = fcntl(fd, F_GETPIPE_SZ)? as usize;
let curr_size = fcntl_getpipe_size(fd)?;
trace!("Current pipe size: {curr_size}");
let new_size = if size > curr_size {
trace!("Requesting pipe size increase to (at least): {size}");
let res = fcntl(fd, F_SETPIPE_SZ(size as i32))?;
fcntl_setpipe_size(fd, size)?;
let res = fcntl_getpipe_size(fd)?;
trace!("New pipe size: {res}");
if res < size as i32 {
if res < size {
return Err(io::Error::last_os_error());
}
res
} else {
size as i32
size
};
Ok(new_size)

View file

@ -1,12 +1,11 @@
use super::manager::DataControlDeviceManagerState;
use crate::lock;
use nix::fcntl::OFlag;
use nix::unistd::pipe2;
use rustix::pipe::{PipeFlags, pipe_with};
use smithay_client_toolkit::data_device_manager::data_offer::DataOfferError;
use smithay_client_toolkit::data_device_manager::ReadPipe;
use std::ops::DerefMut;
use std::os::fd::AsFd;
use std::sync::{Arc, Mutex};
use tokio::net::unix::pipe::Receiver;
use tracing::trace;
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_offer_v1::{
@ -36,8 +35,8 @@ impl PartialEq for SelectionOffer {
}
impl SelectionOffer {
pub fn receive(&self, mime_type: String) -> Result<ReadPipe, DataOfferError> {
unsafe { receive(&self.data_offer, mime_type) }.map_err(DataOfferError::Io)
pub fn receive(&self, mime_type: String) -> Result<Receiver, DataOfferError> {
receive(&self.data_offer, mime_type).map_err(DataOfferError::Io)
}
}
@ -169,14 +168,11 @@ where
///
/// Fails if too many file descriptors were already open and a pipe
/// could not be created.
pub unsafe fn receive(
offer: &ZwlrDataControlOfferV1,
mime_type: String,
) -> std::io::Result<ReadPipe> {
pub fn receive(offer: &ZwlrDataControlOfferV1, mime_type: String) -> std::io::Result<Receiver> {
// create a pipe
let (readfd, writefd) = pipe2(OFlag::O_CLOEXEC)?;
let (readfd, writefd) = pipe_with(PipeFlags::CLOEXEC)?;
offer.receive(mime_type, writefd.as_fd());
Ok(ReadPipe::from(readfd))
Receiver::from_owned_fd(readfd)
}

View file

@ -1,6 +1,8 @@
use super::device::DataControlDevice;
use super::manager::DataControlDeviceManagerState;
use color_eyre::Result;
use smithay_client_toolkit::data_device_manager::WritePipe;
use tracing::error;
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle};
use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_source_v1::{
Event, ZwlrDataControlSourceV1,
@ -23,7 +25,7 @@ impl DataControlSourceDataExt for DataControlSourceData {
///
/// The functions defined in this trait are called as `DataSource` events are received from the compositor.
pub trait DataControlSourceHandler: Sized {
/// This may be called multiple times, once for each accepted mime type from the destination, if any.
// /// This may be called multiple times, once for each accepted mime type from the destination, if any.
// fn accept_mime(
// &mut self,
// conn: &Connection,
@ -41,7 +43,7 @@ pub trait DataControlSourceHandler: Sized {
source: &ZwlrDataControlSourceV1,
mime: String,
fd: WritePipe,
);
) -> Result<()>;
/// The data source is no longer valid
/// Cleanup & destroy this resource
@ -68,7 +70,9 @@ where
) {
match event {
Event::Send { mime_type, fd } => {
state.send_request(conn, qh, source, mime_type, fd.into());
if let Err(err) = state.send_request(conn, qh, source, mime_type, fd.into()) {
error!("{err:#}");
}
}
Event::Cancelled => {
state.cancelled(conn, qh, source);

View file

@ -1,5 +1,5 @@
use super::manager::ToplevelManagerState;
use crate::{lock, Ironbar};
use crate::{Ironbar, lock};
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use tracing::trace;
@ -33,6 +33,11 @@ impl ToplevelHandle {
trace!("Activating handle");
self.handle.activate(seat);
}
pub fn minimize(&self) {
trace!("Minimizing handle");
self.handle.set_minimized();
}
}
#[derive(Debug, Default)]
@ -146,7 +151,7 @@ where
ToplevelHandle {
handle: handle.clone(),
},
)
);
}
Event::Done if !lock!(data.inner).closed => {
{

View file

@ -4,7 +4,7 @@ use smithay_client_toolkit::globals::{GlobalData, ProvidesBoundGlobal};
use std::marker::PhantomData;
use tracing::{debug, warn};
use wayland_client::globals::{BindError, GlobalList};
use wayland_client::{event_created_child, Connection, Dispatch, QueueHandle};
use wayland_client::{Connection, Dispatch, QueueHandle, event_created_child};
use wayland_protocols_wlr::foreign_toplevel::v1::client::{
zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1,
zwlr_foreign_toplevel_manager_v1::{Event, ZwlrForeignToplevelManagerV1},
@ -67,7 +67,9 @@ where
state.toplevel(conn, qhandle);
}
Event::Finished => {
warn!("Foreign toplevel manager is no longer valid, but has not been dropped by client. This could cause window tracking issues.");
warn!(
"Foreign toplevel manager is no longer valid, but has not been dropped by client. This could cause window tracking issues."
);
}
_ => {}
}

View file

@ -4,11 +4,11 @@ pub mod manager;
use self::handle::ToplevelHandleHandler;
use self::manager::ToplevelManagerHandler;
use super::{Client, Environment, Event, Request, Response};
use crate::try_send;
use tokio::sync::broadcast;
use tracing::{debug, error, trace};
use wayland_client::{Connection, QueueHandle};
use crate::channels::AsyncSenderExt;
pub use handle::{ToplevelHandle, ToplevelInfo};
#[derive(Debug, Clone)]
@ -36,6 +36,15 @@ impl Client {
}
}
/// Minimizes the toplevel with the provided ID.
#[cfg(feature = "launcher")]
pub fn toplevel_minimize(&self, handle_id: usize) {
match self.send_request(Request::ToplevelMinimize(handle_id)) {
Response::Ok => (),
_ => unreachable!(),
}
}
/// Subscribes to events from toplevels.
pub fn subscribe_toplevels(&self) -> broadcast::Receiver<ToplevelEvent> {
self.toplevel_channel.0.subscribe()
@ -54,10 +63,16 @@ impl ToplevelHandleHandler for Environment {
match handle.info() {
Some(info) => {
if info.app_id.is_empty() {
trace!("ignoring xwayland dialog");
return;
}
trace!("Adding new handle: {info:?}");
self.handles.push(handle.clone());
if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::New(info)));
self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::New(info)));
}
}
None => {
@ -78,7 +93,8 @@ impl ToplevelHandleHandler for Environment {
Some(info) => {
trace!("Updating handle: {info:?}");
if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::Update(info)));
self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::Update(info)));
}
}
None => {
@ -97,7 +113,8 @@ impl ToplevelHandleHandler for Environment {
self.handles.retain(|h| h != &handle);
if let Some(info) = handle.info() {
try_send!(self.event_tx, Event::Toplevel(ToplevelEvent::Remove(info)));
self.event_tx
.send_spawn(Event::Toplevel(ToplevelEvent::Remove(info)));
}
}
}

View file

@ -1,9 +1,9 @@
use crate::dynamic_value::{dynamic_string, DynamicBool};
use crate::dynamic_value::{DynamicBool, dynamic_string};
use crate::script::{Script, ScriptInput};
use glib::Propagation;
use gtk::gdk::ScrollDirection;
use gtk::prelude::*;
use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType};
use gtk::{EventBox, Justification, Orientation, Revealer, RevealerTransitionType};
use serde::Deserialize;
use tracing::trace;
@ -198,6 +198,28 @@ impl From<ModuleOrientation> for Orientation {
}
}
#[derive(Debug, Default, Deserialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum ModuleJustification {
#[default]
Left,
Right,
Center,
Fill,
}
impl From<ModuleJustification> for Justification {
fn from(o: ModuleJustification) -> Self {
match o {
ModuleJustification::Left => Self::Left,
ModuleJustification::Right => Self::Right,
ModuleJustification::Center => Self::Center,
ModuleJustification::Fill => Self::Fill,
}
}
}
impl TransitionType {
pub const fn to_revealer_transition_type(
&self,
@ -246,6 +268,13 @@ impl CommonConfig {
let script = match event.direction() {
ScrollDirection::Up => scroll_up_script.as_ref(),
ScrollDirection::Down => scroll_down_script.as_ref(),
ScrollDirection::Smooth => {
if event.scroll_deltas().unwrap_or_default().1 > 0.0 {
scroll_down_script.as_ref()
} else {
scroll_up_script.as_ref()
}
}
_ => None,
};
@ -272,8 +301,7 @@ impl CommonConfig {
install_oneshot!(self.on_mouse_exit, connect_leave_notify_event);
if let Some(tooltip) = self.tooltip {
let container = container.clone();
dynamic_string(&tooltip, move |string| {
dynamic_string(&tooltip, container, move |container, string| {
container.set_tooltip_text(Some(&string));
});
}
@ -285,19 +313,15 @@ impl CommonConfig {
container.show_all();
},
|show_if| {
// need to keep clone here for the notify callback
let container = container.clone();
{
let revealer = revealer.clone();
let container = container.clone();
show_if.subscribe(move |success| {
if success {
container.show_all();
}
revealer.set_reveal_child(success);
});
}
show_if.subscribe((revealer, &container), |(revealer, container), success| {
if success {
container.show_all();
}
revealer.set_reveal_child(success);
});
revealer.connect_child_revealed_notify(move |revealer| {
if !revealer.reveals_child() {

View file

@ -37,26 +37,24 @@ 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>,
D: 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))
value.map_or(Ok(Layer::Top), |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")),
})
}
#[cfg(feature = "schema")]
pub fn schema_layer(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
pub fn schema_layer(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
use schemars::JsonSchema;
let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
let mut schema: schemars::schema::SchemaObject = <String>::json_schema(generator).into();
schema.enum_values = Some(vec![
"background".into(),
"bottom".into(),
@ -79,7 +77,7 @@ impl BarPosition {
/// Gets the angle that label text should be displayed at
/// based on this position.
pub const fn get_angle(self) -> f64 {
pub const fn angle(self) -> f64 {
match self {
Self::Top | Self::Bottom => 0.0,
Self::Left => 90.0,

35
src/config/layout.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::config::{ModuleJustification, ModuleOrientation};
use crate::modules::ModuleInfo;
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize, Default)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct LayoutConfig {
/// The orientation to display the widget contents.
/// Setting to vertical will rotate text 90 degrees.
///
/// **Valid options**: `horizontal`, `vertical`
/// <br>
/// **Default**: `horizontal`
orientation: Option<ModuleOrientation>,
/// The justification (alignment) of the widget text shown on the bar.
///
/// **Valid options**: `left`, `right`, `center`, `fill`
/// <br>
/// **Default**: `left`
#[serde(default)]
pub justify: ModuleJustification,
}
impl LayoutConfig {
pub fn orientation(&self, info: &ModuleInfo) -> gtk::Orientation {
self.orientation
.map_or(info.bar_position.orientation(), ModuleOrientation::into)
}
pub fn angle(&self, info: &ModuleInfo) -> f64 {
self.orientation
.map_or(info.bar_position.angle(), ModuleOrientation::to_angle)
}
}

View file

@ -1,25 +1,35 @@
mod common;
mod r#impl;
mod layout;
mod truncate;
#[cfg(feature = "bindmode")]
use crate::modules::bindmode::Bindmode;
#[cfg(feature = "cairo")]
use crate::modules::cairo::CairoModule;
#[cfg(feature = "clipboard")]
use crate::modules::clipboard::ClipboardModule;
#[cfg(feature = "clock")]
use crate::modules::clock::ClockModule;
#[cfg(feature = "custom")]
use crate::modules::custom::CustomModule;
#[cfg(feature = "focused")]
use crate::modules::focused::FocusedModule;
#[cfg(feature = "keyboard")]
use crate::modules::keyboard::KeyboardModule;
#[cfg(feature = "label")]
use crate::modules::label::LabelModule;
#[cfg(feature = "launcher")]
use crate::modules::launcher::LauncherModule;
#[cfg(feature = "menu")]
use crate::modules::menu::MenuModule;
#[cfg(feature = "music")]
use crate::modules::music::MusicModule;
#[cfg(feature = "network_manager")]
use crate::modules::networkmanager::NetworkManagerModule;
#[cfg(feature = "notifications")]
use crate::modules::notifications::NotificationsModule;
#[cfg(feature = "script")]
use crate::modules::script::ScriptModule;
#[cfg(feature = "sys_info")]
use crate::modules::sysinfo::SysInfoModule;
@ -35,37 +45,46 @@ use crate::modules::workspaces::WorkspacesModule;
use crate::modules::{AnyModuleFactory, ModuleFactory, ModuleInfo};
use cfg_if::cfg_if;
use color_eyre::Result;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::Deserialize;
use std::collections::HashMap;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
pub use self::common::{CommonConfig, ModuleOrientation, TransitionType};
pub use self::truncate::TruncateMode;
pub use self::common::{CommonConfig, ModuleJustification, ModuleOrientation, TransitionType};
pub use self::layout::LayoutConfig;
pub use self::truncate::{EllipsizeMode, TruncateMode};
#[derive(Debug, Deserialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum ModuleConfig {
#[cfg(feature = "bindmode")]
Bindmode(Box<Bindmode>),
#[cfg(feature = "cairo")]
Cairo(Box<CairoModule>),
#[cfg(feature = "clipboard")]
Clipboard(Box<ClipboardModule>),
#[cfg(feature = "clock")]
Clock(Box<ClockModule>),
#[cfg(feature = "custom")]
Custom(Box<CustomModule>),
#[cfg(feature = "focused")]
Focused(Box<FocusedModule>),
#[cfg(feature = "keyboard")]
Keyboard(Box<KeyboardModule>),
#[cfg(feature = "label")]
Label(Box<LabelModule>),
#[cfg(feature = "launcher")]
Launcher(Box<LauncherModule>),
#[cfg(feature = "menu")]
Menu(Box<MenuModule>),
#[cfg(feature = "music")]
Music(Box<MusicModule>),
#[cfg(feature = "network_manager")]
NetworkManager(Box<NetworkManagerModule>),
#[cfg(feature = "notifications")]
Notifications(Box<NotificationsModule>),
#[cfg(feature = "script")]
Script(Box<ScriptModule>),
#[cfg(feature = "sys_info")]
SysInfo(Box<SysInfoModule>),
@ -93,24 +112,33 @@ impl ModuleConfig {
}
match self {
#[cfg(feature = "bindmode")]
Self::Bindmode(module) => create!(module),
#[cfg(feature = "cairo")]
Self::Cairo(module) => create!(module),
#[cfg(feature = "clipboard")]
Self::Clipboard(module) => create!(module),
#[cfg(feature = "clock")]
Self::Clock(module) => create!(module),
#[cfg(feature = "custom")]
Self::Custom(module) => create!(module),
#[cfg(feature = "focused")]
Self::Focused(module) => create!(module),
#[cfg(feature = "keyboard")]
Self::Keyboard(module) => create!(module),
#[cfg(feature = "label")]
Self::Label(module) => create!(module),
#[cfg(feature = "launcher")]
Self::Launcher(module) => create!(module),
#[cfg(feature = "menu")]
Self::Menu(module) => create!(module),
#[cfg(feature = "music")]
Self::Music(module) => create!(module),
#[cfg(feature = "network_manager")]
Self::NetworkManager(module) => create!(module),
#[cfg(feature = "notifications")]
Self::Notifications(module) => create!(module),
#[cfg(feature = "script")]
Self::Script(module) => create!(module),
#[cfg(feature = "sys_info")]
Self::SysInfo(module) => create!(module),
@ -124,6 +152,53 @@ impl ModuleConfig {
Self::Workspaces(module) => create!(module),
}
}
pub fn name(&self) -> String {
match self {
#[cfg(feature = "bindmode")]
ModuleConfig::Bindmode(_) => "Bindmode",
#[cfg(feature = "cairo")]
ModuleConfig::Cairo(_) => "Cario",
#[cfg(feature = "clipboard")]
ModuleConfig::Clipboard(_) => "Clipboard",
#[cfg(feature = "clock")]
ModuleConfig::Clock(_) => "Clock",
#[cfg(feature = "custom")]
ModuleConfig::Custom(_) => "Custom",
#[cfg(feature = "focused")]
ModuleConfig::Focused(_) => "Focused",
#[cfg(feature = "keyboard")]
ModuleConfig::Keyboard(_) => "Keyboard",
#[cfg(feature = "label")]
ModuleConfig::Label(_) => "Label",
#[cfg(feature = "launcher")]
ModuleConfig::Launcher(_) => "Launcher",
#[cfg(feature = "menu")]
ModuleConfig::Menu(_) => "Menu",
#[cfg(feature = "music")]
ModuleConfig::Music(_) => "Music",
#[cfg(feature = "network_manager")]
ModuleConfig::NetworkManager(_) => "NetworkManager",
#[cfg(feature = "notifications")]
ModuleConfig::Notifications(_) => "Notifications",
#[cfg(feature = "script")]
ModuleConfig::Script(_) => "Script",
#[cfg(feature = "sys_info")]
ModuleConfig::SysInfo(_) => "SysInfo",
#[cfg(feature = "tray")]
ModuleConfig::Tray(_) => "Tray",
#[cfg(feature = "upower")]
ModuleConfig::Upower(_) => "UPower",
#[cfg(feature = "volume")]
ModuleConfig::Volume(_) => "Volume",
#[cfg(feature = "workspaces")]
ModuleConfig::Workspaces(_) => "Workspaces",
// in case no modules are compiled
#[allow(unreachable_patterns)]
_ => "",
}
.to_string()
}
}
#[derive(Debug, Clone)]
@ -272,12 +347,6 @@ pub struct BarConfig {
#[serde(default)]
pub autohide: Option<u64>,
/// The name of the GTK icon theme to use.
/// Leave unset to use the default Adwaita theme.
///
/// **Default**: `null`
pub icon_theme: Option<String>,
/// An array of modules to append to the start of the bar.
/// Depending on the orientation, this is either the top of the left edge.
///
@ -325,10 +394,12 @@ impl Default for BarConfig {
height: default_bar_height(),
start_hidden: None,
autohide: None,
icon_theme: None,
#[cfg(feature = "label")]
start: Some(vec![ModuleConfig::Label(
LabelModule::new(" Using default config".to_string()).into(),
)]),
#[cfg(not(feature = "label"))]
start: None,
center,
end,
anchor_to_edges: default_true(),
@ -376,6 +447,19 @@ pub struct Config {
///
/// Providing this option overrides the single, global `bar` option.
pub monitors: Option<HashMap<String, MonitorConfig>>,
/// The name of the GTK icon theme to use.
/// Leave unset to use the default Adwaita theme.
///
/// **Default**: `null`
pub icon_theme: Option<String>,
/// Map of app IDs (or classes) to icon names,
/// overriding the app's default icon.
///
/// **Default**: `{}`
#[serde(default)]
pub icon_overrides: HashMap<String, String>,
}
const fn default_layer() -> gtk_layer_shell::Layer {
@ -397,3 +481,7 @@ pub const fn default_false() -> bool {
pub const fn default_true() -> bool {
true
}
pub fn default_launch_command() -> String {
String::from("gtk-launch {app_name}")
}

View file

@ -1,19 +1,21 @@
use gtk::pango::EllipsizeMode as GtkEllipsizeMode;
use gtk::prelude::*;
use serde::Deserialize;
#[derive(Debug, Deserialize, Clone, Copy)]
#[derive(Debug, Deserialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum EllipsizeMode {
None,
Start,
Middle,
#[default]
End,
}
impl From<EllipsizeMode> for GtkEllipsizeMode {
fn from(value: EllipsizeMode) -> Self {
match value {
EllipsizeMode::None => Self::None,
EllipsizeMode::Start => Self::Start,
EllipsizeMode::Middle => Self::Middle,
EllipsizeMode::End => Self::End,
@ -27,10 +29,23 @@ impl From<EllipsizeMode> for GtkEllipsizeMode {
///
/// The option can be configured in one of two modes.
///
/// **Default**: `Auto (end)`
///
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(untagged)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum TruncateMode {
/// Do not truncate content.
///
/// Setting this option may cause excessively long content to overflow other widgets,
/// shifting them off-screen.
///
/// # Example
///
/// ```corn
/// { truncate = "off" }
Off,
/// Auto mode lets GTK decide when to ellipsize.
///
/// To use this mode, set the truncate option to a string
@ -44,7 +59,7 @@ pub enum TruncateMode {
///
/// **Valid options**: `start`, `middle`, `end`
/// <br>
/// **Default**: `null`
/// **Default**: `end`
Auto(EllipsizeMode),
/// Length mode defines a fixed point at which to ellipsize.
@ -88,36 +103,34 @@ pub enum TruncateMode {
},
}
impl TruncateMode {
const fn mode(&self) -> EllipsizeMode {
match self {
Self::Length { mode, .. } | Self::Auto(mode) => *mode,
}
impl Default for TruncateMode {
fn default() -> Self {
Self::Auto(EllipsizeMode::default())
}
}
const fn length(&self) -> Option<i32> {
impl TruncateMode {
pub const fn length(&self) -> Option<i32> {
match self {
Self::Auto(_) => None,
Self::Auto(_) | Self::Off => None,
Self::Length { length, .. } => *length,
}
}
const fn max_length(&self) -> Option<i32> {
pub const fn max_length(&self) -> Option<i32> {
match self {
Self::Auto(_) => None,
Self::Auto(_) | Self::Off => None,
Self::Length { max_length, .. } => *max_length,
}
}
}
pub fn truncate_label(&self, label: &gtk::Label) {
label.set_ellipsize(self.mode().into());
if let Some(length) = self.length() {
label.set_width_chars(length);
}
if let Some(length) = self.max_length() {
label.set_max_width_chars(length);
}
impl From<TruncateMode> for GtkEllipsizeMode {
fn from(value: TruncateMode) -> Self {
let mode = match value {
TruncateMode::Off => EllipsizeMode::None,
TruncateMode::Length { mode, .. } | TruncateMode::Auto(mode) => mode,
};
mode.into()
}
}

View file

@ -1,37 +1,302 @@
use std::collections::{HashMap, HashSet};
use crate::spawn;
use color_eyre::{Help, Report, Result};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};
use tracing::warn;
use std::process::{Command, Stdio};
use std::sync::Arc;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::sync::Mutex;
use tracing::{debug, error};
use walkdir::{DirEntry, WalkDir};
use crate::lock;
type DesktopFile = HashMap<String, Vec<String>>;
fn desktop_files() -> &'static Mutex<HashMap<PathBuf, DesktopFile>> {
static DESKTOP_FILES: OnceLock<Mutex<HashMap<PathBuf, DesktopFile>>> = OnceLock::new();
DESKTOP_FILES.get_or_init(|| Mutex::new(HashMap::new()))
#[derive(Debug, Clone)]
enum DesktopFileRef {
Unloaded(PathBuf),
Loaded(DesktopFile),
}
fn desktop_files_look_out_keys() -> &'static HashSet<&'static str> {
static DESKTOP_FILES_LOOK_OUT_KEYS: OnceLock<HashSet<&'static str>> = OnceLock::new();
DESKTOP_FILES_LOOK_OUT_KEYS
.get_or_init(|| HashSet::from(["Name", "StartupWMClass", "Exec", "Icon"]))
impl DesktopFileRef {
async fn get(&mut self) -> Result<DesktopFile> {
match self {
DesktopFileRef::Unloaded(path) => {
let (tx, rx) = tokio::sync::oneshot::channel();
let path = path.clone();
spawn(async move { tx.send(Self::load(&path).await) });
let file = rx.await??;
*self = DesktopFileRef::Loaded(file.clone());
Ok(file)
}
DesktopFileRef::Loaded(file) => Ok(file.clone()),
}
}
async fn load(file_path: &Path) -> Result<DesktopFile> {
debug!("loading applications file: {}", file_path.display());
let file = tokio::fs::File::open(file_path).await?;
let mut desktop_file = DesktopFile::new(
file_path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
);
let mut lines = BufReader::new(file).lines();
let mut has_name = false;
let mut has_type = false;
let mut has_wm_class = false;
let mut has_exec = false;
let mut has_icon = false;
let mut has_categories = false;
let mut has_no_display = false;
while let Ok(Some(line)) = lines.next_line().await {
let Some((key, value)) = line.split_once('=') else {
continue;
};
match key {
"Name" if !has_name => {
desktop_file.name = Some(value.to_string());
has_name = true;
}
"Type" if !has_type => {
desktop_file.app_type = Some(value.to_string());
has_type = true;
}
"StartupWMClass" if !has_wm_class => {
desktop_file.startup_wm_class = Some(value.to_string());
has_wm_class = true;
}
"Exec" if !has_exec => {
desktop_file.exec = Some(value.to_string());
has_exec = true;
}
"Icon" if !has_icon => {
desktop_file.icon = Some(value.to_string());
has_icon = true;
}
"Categories" if !has_categories => {
desktop_file.categories = value.split(';').map(|s| s.to_string()).collect();
has_categories = true;
}
"NoDisplay" if !has_no_display => {
desktop_file.no_display = Some(value.parse()?);
has_no_display = true;
}
_ => {}
}
// parsing complete - don't bother with the rest of the lines
if has_name
&& has_type
&& has_wm_class
&& has_exec
&& has_icon
&& has_categories
&& has_no_display
{
break;
}
}
Ok(desktop_file)
}
}
/// Finds directories that should contain `.desktop` files
/// and exist on the filesystem.
fn find_application_dirs() -> Vec<PathBuf> {
#[derive(Debug, Clone)]
pub struct DesktopFile {
pub file_name: String,
pub name: Option<String>,
pub app_type: Option<String>,
pub startup_wm_class: Option<String>,
pub exec: Option<String>,
pub icon: Option<String>,
pub categories: Vec<String>,
pub no_display: Option<bool>,
}
impl DesktopFile {
fn new(file_name: String) -> Self {
Self {
file_name,
name: None,
app_type: None,
startup_wm_class: None,
exec: None,
icon: None,
categories: vec![],
no_display: None,
}
}
}
type FileMap = HashMap<Box<str>, DesktopFileRef>;
/// Desktop file cache and resolver.
///
/// Files are lazy-loaded as required on resolution.
#[derive(Debug, Clone)]
pub struct DesktopFiles {
files: Arc<Mutex<FileMap>>,
}
impl Default for DesktopFiles {
fn default() -> Self {
Self::new()
}
}
impl DesktopFiles {
/// Creates a new instance,
/// scanning disk to generate a list of (unloaded) file refs in the process.
pub fn new() -> Self {
let desktop_files: FileMap = dirs()
.iter()
.flat_map(|path| files(path))
.map(|file| {
(
file.file_stem()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
.to_string()
.into(),
DesktopFileRef::Unloaded(file),
)
})
.collect();
debug!("resolved {} files", desktop_files.len());
Self {
files: Arc::new(Mutex::new(desktop_files)),
}
}
pub async fn get_all(&self) -> Result<Vec<DesktopFile>> {
let mut files = self.files.lock().await;
let mut res = Vec::with_capacity(files.len());
for file in files.values_mut() {
let file = file.get().await?;
res.push(file);
}
Ok(res)
}
/// Attempts to locate a applications file by file name or contents.
///
/// Input should typically be the app id, app name or icon.
pub async fn find(&self, input: &str) -> Result<Option<DesktopFile>> {
let mut res = self.find_by_file_name(input).await?;
if res.is_none() {
res = self.find_by_file_contents(input).await?;
}
debug!("found match for app_id {input}: {}", res.is_some());
Ok(res)
}
/// Checks file names for an exact or partial match of the provided input.
async fn find_by_file_name(&self, input: &str) -> Result<Option<DesktopFile>> {
let mut files = self.files.lock().await;
let mut file_ref = files
.iter_mut()
.find(|&(name, _)| name.eq_ignore_ascii_case(input));
if file_ref.is_none() {
file_ref = files.iter_mut().find(
|&(name, _)| // this will attempt to find flatpak apps that are in the format
// `com.company.app` or `com.app.something`
input
.split(&[' ', ':', '@', '.', '_'][..])
.any(|part| name.eq_ignore_ascii_case(part)),
);
}
let file_ref = file_ref.map(|(_, file)| file);
if let Some(file_ref) = file_ref {
let file = file_ref.get().await?;
Ok(Some(file))
} else {
Ok(None)
}
}
/// Checks file contents for an exact or partial match of the provided input.
async fn find_by_file_contents(&self, app_id: &str) -> Result<Option<DesktopFile>> {
let mut files = self.files.lock().await;
// first pass - check name for exact match
for (_, file_ref) in files.iter_mut() {
let file = file_ref.get().await?;
if let Some(name) = &file.name {
if name.eq_ignore_ascii_case(app_id) {
return Ok(Some(file));
}
}
}
// second pass - check name for partial match
for (_, file_ref) in files.iter_mut() {
let file = file_ref.get().await?;
if let Some(name) = &file.name {
if name.to_lowercase().contains(app_id) {
return Ok(Some(file));
}
}
}
// third pass - check remaining fields for partial match
for (_, file_ref) in files.iter_mut() {
let file = file_ref.get().await?;
if let Some(name) = &file.exec {
if name.to_lowercase().contains(app_id) {
return Ok(Some(file));
}
}
if let Some(name) = &file.startup_wm_class {
if name.to_lowercase().contains(app_id) {
return Ok(Some(file));
}
}
if let Some(name) = &file.icon {
if name.to_lowercase().contains(app_id) {
return Ok(Some(file));
}
}
}
Ok(None)
}
}
/// Gets a list of paths to all directories
/// containing `.applications` files.
fn dirs() -> Vec<PathBuf> {
let mut dirs = vec![
PathBuf::from("/usr/share/applications"), // system installed apps
PathBuf::from("/var/lib/flatpak/exports/share/applications"), // flatpak apps
];
let xdg_dirs = env::var_os("XDG_DATA_DIRS");
if let Some(xdg_dirs) = xdg_dirs {
for mut xdg_dir in env::split_paths(&xdg_dirs).map(PathBuf::from) {
let xdg_dirs = env::var("XDG_DATA_DIRS");
if let Ok(xdg_dirs) = xdg_dirs {
for mut xdg_dir in env::split_paths(&xdg_dirs) {
xdg_dir.push("applications");
dirs.push(xdg_dir);
}
@ -43,157 +308,85 @@ fn find_application_dirs() -> Vec<PathBuf> {
dirs.push(user_dir);
}
dirs.into_iter().filter(|dir| dir.exists()).collect()
dirs.into_iter().filter(|dir| dir.exists()).rev().collect()
}
/// Finds all the desktop files
fn find_desktop_files() -> Vec<PathBuf> {
let dirs = find_application_dirs();
dirs.into_iter()
.flat_map(|dir| {
WalkDir::new(dir)
.max_depth(5)
.into_iter()
.filter_map(Result::ok)
.map(DirEntry::into_path)
.filter(|file| file.is_file() && file.extension().unwrap_or_default() == "desktop")
})
/// Gets a list of all `.applications` files in the provided directory.
///
/// The directory is recursed to a maximum depth of 5.
fn files(dir: &Path) -> Vec<PathBuf> {
WalkDir::new(dir)
.max_depth(5)
.into_iter()
.filter_map(Result::ok)
.map(DirEntry::into_path)
.filter(|file| file.is_file() && file.extension().unwrap_or_default() == "desktop")
.collect()
}
/// Attempts to locate a `.desktop` file for an app id
pub fn find_desktop_file(app_id: &str) -> Option<PathBuf> {
// this is necessary to invalidate the cache
let files = find_desktop_files();
find_desktop_file_by_filename(app_id, &files)
.or_else(|| find_desktop_file_by_filedata(app_id, &files))
/// Starts a `.desktop` file with the provided formatted command.
pub fn open_program(file_name: &str, str: &str) {
let expanded = str.replace("{app_name}", file_name);
let launch_command_parts: Vec<&str> = expanded.split_whitespace().collect();
if let Err(err) = Command::new(&launch_command_parts[0])
.args(&launch_command_parts[1..])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
{
error!(
"{:?}",
Report::new(err)
.wrap_err("Failed to run launch command.")
.suggestion("Perhaps the applications file is invalid?")
);
}
}
/// Finds the correct desktop file using a simple condition check
fn find_desktop_file_by_filename(app_id: &str, files: &[PathBuf]) -> Option<PathBuf> {
let with_names = files
.iter()
.map(|f| {
(
f,
f.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase(),
)
})
.collect::<Vec<_>>();
#[cfg(test)]
mod tests {
use super::*;
with_names
.iter()
// first pass - check for exact match
.find(|(_, name)| name.eq_ignore_ascii_case(app_id))
// second pass - check for substring
.or_else(|| {
with_names.iter().find(|(_, name)| {
// this will attempt to find flatpak apps that are in the format
// `com.company.app` or `com.app.something`
app_id
.split(&[' ', ':', '@', '.', '_'][..])
.any(|part| name.eq_ignore_ascii_case(part))
})
})
.map(|(file, _)| file.into())
}
/// Finds the correct desktop file using the keys in `DESKTOP_FILES_LOOK_OUT_KEYS`
fn find_desktop_file_by_filedata(app_id: &str, files: &[PathBuf]) -> Option<PathBuf> {
let app_id = &app_id.to_lowercase();
let mut desktop_files_cache = lock!(desktop_files());
let files = files
.iter()
.filter_map(|file| {
let parsed_desktop_file = parse_desktop_file(file)?;
desktop_files_cache.insert(file.clone(), parsed_desktop_file.clone());
Some((file.clone(), parsed_desktop_file))
})
.collect::<Vec<_>>();
let file = files
.iter()
// first pass - check name key for exact match
.find(|(_, desktop_file)| {
desktop_file
.get("Name")
.is_some_and(|names| names.iter().any(|name| name.eq_ignore_ascii_case(app_id)))
})
// second pass - check name key for substring
.or_else(|| {
files.iter().find(|(_, desktop_file)| {
desktop_file.get("Name").is_some_and(|names| {
names
.iter()
.any(|name| name.to_lowercase().contains(app_id))
})
})
})
// third pass - check all keys for substring
.or_else(|| {
files.iter().find(|(_, desktop_file)| {
desktop_file
.values()
.flatten()
.any(|value| value.to_lowercase().contains(app_id))
})
});
file.map(|(path, _)| path).cloned()
}
/// Parses a desktop file into a hashmap of keys/vector(values).
fn parse_desktop_file(path: &Path) -> Option<DesktopFile> {
let Ok(file) = fs::read_to_string(path) else {
warn!("Couldn't Open File: {}", path.display());
return None;
};
let mut desktop_file: DesktopFile = DesktopFile::new();
file.lines()
.filter_map(|line| {
let (key, value) = line.split_once('=')?;
let key = key.trim();
let value = value.trim();
if desktop_files_look_out_keys().contains(key) {
Some((key, value))
} else {
None
}
})
.for_each(|(key, value)| {
desktop_file
.entry(key.to_string())
.or_default()
.push(value.to_string());
});
Some(desktop_file)
}
/// Attempts to get the icon name from the app's `.desktop` file.
pub fn get_desktop_icon_name(app_id: &str) -> Option<String> {
let path = find_desktop_file(app_id)?;
let mut desktop_files_cache = lock!(desktop_files());
let desktop_file = match desktop_files_cache.get(&path) {
Some(desktop_file) => desktop_file,
_ => desktop_files_cache
.entry(path.clone())
.or_insert_with(|| parse_desktop_file(&path).expect("desktop_file")),
};
let mut icons = desktop_file.get("Icon").into_iter().flatten();
icons.next().map(std::string::ToString::to_string)
fn setup() {
unsafe {
let pwd = env::current_dir().unwrap();
env::set_var("XDG_DATA_DIRS", format!("{}/test-configs", pwd.display()));
}
}
#[tokio::test]
async fn find_by_filename() {
setup();
let desktop_files = DesktopFiles::new();
let file = desktop_files.find_by_file_name("firefox").await.unwrap();
assert!(file.is_some());
assert_eq!(file.unwrap().file_name, "firefox.desktop");
}
#[tokio::test]
async fn find_by_file_contents() {
setup();
let desktop_files = DesktopFiles::new();
let file = desktop_files.find_by_file_contents("427520").await.unwrap();
assert!(file.is_some());
assert_eq!(file.unwrap().file_name, "Factorio.desktop");
}
#[tokio::test]
async fn parser() {
let mut file_ref =
DesktopFileRef::Unloaded(PathBuf::from("test-configs/applications/firefox.desktop"));
let file = file_ref.get().await.unwrap();
assert_eq!(file.name, Some("Firefox".to_string()));
assert_eq!(file.icon, Some("firefox".to_string()));
assert_eq!(file.exec, Some("/usr/lib/firefox/firefox %u".to_string()));
assert_eq!(file.startup_wm_class, Some("firefox".to_string()));
assert_eq!(file.app_type, Some("Application".to_string()));
}
}

Some files were not shown because too many files have changed in this diff Show more