├── .github └── workflows │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── PACKAGING.md ├── README.md ├── assets └── io.github.zefr0x.ianny.svg ├── build.rs ├── io.github.zefr0x.ianny.desktop ├── justfile ├── meson.build ├── meson_options.txt ├── po ├── LINGUAS ├── ar.po ├── io.github.zefr0x.ianny.pot └── meson.build └── src ├── config.rs ├── main.rs ├── meson.build └── wayland.rs /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This Github workflow will create a new release when a tag is pushed, 2 | # then it will build the binary and it will add it to the release assets 3 | # in a tar.gz archive along with other components. 4 | # 5 | # Reference: 6 | # https://github.com/BurntSushi/ripgrep/blob/master/.github/workflows/release.yml 7 | # https://github.com/rhino-linux/rhino-setup/blob/master/.github/workflows/package.yml 8 | 9 | name: release 10 | permissions: 11 | "contents": "write" 12 | on: 13 | push: 14 | tags: 15 | - "v[0-9]+.[0-9]+.[0-9]+*" 16 | 17 | jobs: 18 | create-release: 19 | name: create-release 20 | runs-on: ubuntu-latest 21 | outputs: 22 | upload_url: ${{ steps.release.outputs.upload_url }} 23 | version: ${{ github.ref_name }} 24 | steps: 25 | - name: Create Github Release 26 | id: release 27 | uses: actions/create-release@v1 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | with: 31 | tag_name: ${{ github.ref_name }} 32 | release_name: ${{ github.ref_name }} 33 | 34 | build-release: 35 | name: build-release 36 | needs: ["create-release"] 37 | runs-on: ${{ matrix.os }} 38 | env: 39 | TARGET_DIR: ./target 40 | # Emit backtraces on panics. 41 | RUST_BACKTRACE: 1 42 | strategy: 43 | matrix: 44 | build: [x86_64-linux-gnu] 45 | # FIX: Building for arm and i686. 46 | include: 47 | - build: x86_64-linux-gnu 48 | os: ubuntu-latest 49 | rust: stable 50 | target: x86_64-unknown-linux-gnu 51 | # - build: x86_64-linux-musl 52 | # os: ubuntu-latest 53 | # rust: stable 54 | # target: x86_64-unknown-linux-musl 55 | # - build: i686-linux-gnu 56 | # os: ubuntu-latest 57 | # rust: stable 58 | # target: i686-unknown-linux-gnu 59 | # - build: i686-linux-musl 60 | # os: ubuntu-latest 61 | # rust: stable 62 | # target: i686-unknown-linux-musl 63 | # - build: arm-linux-gnueabihf 64 | # os: ubuntu-latest 65 | # rust: stable 66 | # target: arm-unknown-linux-gnueabihf 67 | # - build: arm-linux-musleabihf 68 | # os: ubuntu-latest 69 | # rust: stable 70 | # target: arm-unknown-linux-musleabihf 71 | steps: 72 | - name: Checkout Repository 73 | uses: actions/checkout@v3 74 | 75 | - name: Install Rust Toolchain 76 | uses: dtolnay/rust-toolchain@v1 77 | with: 78 | toolchain: ${{ matrix.rust }} 79 | targets: ${{ matrix.target }} 80 | 81 | - name: Install System Deps 82 | shell: bash 83 | # FIX: Install deps for arm and i686 84 | run: sudo apt install libdbus-1-dev desktop-file-utils gettext meson -y 85 | 86 | # FIX: Building for arm and i686 with meson 87 | - name: Setup 88 | run: meson builddir -Drustc_target=${{ matrix.target }} 89 | 90 | - name: Build 91 | run: meson install -C builddir --destdir "builds/${{ matrix.target }}" 92 | 93 | - name: Create Archive 94 | shell: bash 95 | run: | 96 | archive="ianny-${{ needs.create-release.outputs.version }}-${{ matrix.target }}.tar.gz" 97 | 98 | tar -C "builddir/builds/" --create "${{ matrix.target }}/" --gzip --file "$archive" 99 | echo "ASSET=$archive" >> $GITHUB_ENV 100 | 101 | - name: Upload Release Archive 102 | uses: actions/upload-release-asset@v1 103 | env: 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 105 | with: 106 | upload_url: ${{ needs.create-release.outputs.upload_url }} 107 | asset_path: ${{ env.ASSET }} 108 | asset_name: ${{ env.ASSET }} 109 | asset_content_type: application/octet-stream 110 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /build 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | skip: [fmt, clippy, cargo-check] 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v5.0.0 7 | hooks: 8 | - id: trailing-whitespace 9 | - id: end-of-file-fixer 10 | - id: check-yaml 11 | - id: check-added-large-files 12 | - repo: https://github.com/doublify/pre-commit-rust 13 | rev: v1.0 14 | hooks: 15 | - id: fmt 16 | - id: clippy 17 | - id: cargo-check 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | # 2.1.1 4 | 5 | ## Fixed 6 | 7 | - Panic when two consecutive idle signals are sent. 8 | - Old `ext_idle_notification_v1` object wasn't destroyed in new seat dispatch event. 9 | - Faulty comparison of compositor's `ext_idle_notifier_v1` version caused fault fallback from `timer.ignore_idle_inhibitors` config option. 10 | 11 | ## Dependencies 12 | 13 | - All dependencies are up to date. 14 | 15 | # 2.1.0 16 | 17 | ## Added 18 | 19 | - Add option to ignore idle inhibitors 20 | 21 | ## Improved 22 | 23 | - Prettier and classified log messages. 24 | 25 | # 2.0.0 26 | 27 | ## Breaking Changes 28 | 29 | - Remove support for deprecated [`org_kde_kwin_idle`](https://wayland.app/protocols/kde-idle) wayland protocol. 30 | 31 | ## Fixed 32 | 33 | - Relay on system time, not just thread sleeping. Now the timer will make progress even while the device is suspended. 34 | 35 | # 1.0.0 36 | 37 | ## Fixed 38 | 39 | - Don't specify an icon, since there is none yet. 40 | 41 | ## Dependencies 42 | 43 | - All dependencies are up to date. 44 | 45 | # 1.0.0rc 46 | 47 | ## Improved 48 | 49 | - Better quality and handle more errors. 50 | 51 | ## Changed 52 | 53 | - Use system's gettext instead of compiling it. 54 | 55 | ## Dependencies 56 | 57 | - All dependencies are up to date. 58 | 59 | # 1.0.0beta.1 60 | 61 | ## Added 62 | 63 | - `notification:show_progress_bar` and `notification:minimum_update_delay` config options. 64 | - Error message when no Wayland compositor is detected. 65 | 66 | ## Fixed 67 | 68 | - No ensured delay between notification updates. 69 | - No progress bar handling for durations less than 100 seconds. 70 | 71 | # 1.0.0beta.0 72 | 73 | ## Improved 74 | 75 | - Reduce release builds binary size. 76 | - Just use one idel interface if both are implemented in the wayland compositor. 77 | 78 | # 0.1.0alpha.2 79 | 80 | ## Added 81 | 82 | - i18n support. 83 | 84 | ## Changed 85 | 86 | - Use seconds rather then minutes as a time unit. `Breaking Change` 87 | - Fix typo in a config option, replace `long_break_tiemout` with `long_break_timeout`. `Breaking Change` 88 | 89 | ## Localization 90 | 91 | - Add the Arabic language. 92 | 93 | ## Dependencies 94 | 95 | - All dependencies are up to date. 96 | 97 | # 0.1.0alpha.1 98 | 99 | ## Added 100 | 101 | - Config file support. 102 | 103 | ## Dependencies 104 | 105 | - All dependencies are up to date. 106 | 107 | # 0.1.0alpha.0 108 | 109 | First release. 110 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Bug report 4 | 5 | For now [GitHub Bug Traker](https://github.com/zefr0x/ianny/issues) is used for this project. 6 | 7 | ## Development 8 | 9 | - The `stable` [Rust](https://www.rust-lang.org/) programming language is used mainly in this project. 10 | - Dependencies management handled using [Cargo](https://doc.rust-lang.org/stable/cargo/). 11 | - Building handled using [Meson](https://mesonbuild.com/) 12 | 13 | ### Style 14 | 15 | - You should document every thing to keep the code easy for reading. Every file, every function, and any line that need a comment. 16 | 17 | #### Setup `pre-commit` 18 | 19 | To make every thing easy [**`pre-commit`**](https://pre-commit.com/) is used in this project, it should run in every commit, so you shouldn't commit any thing without checking it. 20 | 21 | First install it: 22 | 23 | ```shell 24 | pip install pre-commit 25 | ``` 26 | 27 | > It's better to use your OS's package manager to install it. 28 | 29 | Then add it as a git hook while you are inside the repository: 30 | 31 | ```shell 32 | pre-commit install 33 | ``` 34 | 35 | # Translation 36 | 37 | We use `gettext` in this project, you have `.po` files to translate and a `.desktop` file. 38 | 39 | ## Files to Translate 40 | 41 | - `po/.po` 42 | - `io.github.zefr0x.ianny.desktop` 43 | 44 | ## Adding New Language 45 | 46 | 1. Copy `po/io.github.zefr0x.ianny.pot` to `po/.po`. 47 | 2. Add the language ID to the `po/LINGUAS` file (IDs are separated by space). 48 | 3. Start editing the `.po` file using any editor you want to use (e.g. [`Gtranslator`](https://wiki.gnome.org/Apps/Gtranslator)). 49 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 19 | 20 | [[package]] 21 | name = "bitflags" 22 | version = "1.3.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "2.9.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 31 | 32 | [[package]] 33 | name = "block" 34 | version = "0.1.6" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 37 | 38 | [[package]] 39 | name = "block2" 40 | version = "0.6.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" 43 | dependencies = [ 44 | "objc2", 45 | ] 46 | 47 | [[package]] 48 | name = "cc" 49 | version = "1.2.23" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" 52 | dependencies = [ 53 | "shlex", 54 | ] 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "colored" 64 | version = "2.2.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" 67 | dependencies = [ 68 | "lazy_static", 69 | "windows-sys 0.48.0", 70 | ] 71 | 72 | [[package]] 73 | name = "dbus" 74 | version = "0.9.7" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" 77 | dependencies = [ 78 | "libc", 79 | "libdbus-sys", 80 | "winapi", 81 | ] 82 | 83 | [[package]] 84 | name = "deranged" 85 | version = "0.4.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 88 | dependencies = [ 89 | "powerfmt", 90 | ] 91 | 92 | [[package]] 93 | name = "dispatch2" 94 | version = "0.3.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" 97 | dependencies = [ 98 | "bitflags 2.9.1", 99 | "objc2", 100 | ] 101 | 102 | [[package]] 103 | name = "downcast-rs" 104 | version = "1.2.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" 107 | 108 | [[package]] 109 | name = "equivalent" 110 | version = "1.0.2" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 113 | 114 | [[package]] 115 | name = "errno" 116 | version = "0.3.12" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 119 | dependencies = [ 120 | "libc", 121 | "windows-sys 0.59.0", 122 | ] 123 | 124 | [[package]] 125 | name = "fastrand" 126 | version = "2.3.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 129 | 130 | [[package]] 131 | name = "futures-core" 132 | version = "0.3.31" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 135 | 136 | [[package]] 137 | name = "futures-io" 138 | version = "0.3.31" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 141 | 142 | [[package]] 143 | name = "futures-lite" 144 | version = "2.6.0" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" 147 | dependencies = [ 148 | "fastrand", 149 | "futures-core", 150 | "futures-io", 151 | "parking", 152 | "pin-project-lite", 153 | ] 154 | 155 | [[package]] 156 | name = "gcd" 157 | version = "2.3.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 160 | 161 | [[package]] 162 | name = "gettext-rs" 163 | version = "0.7.2" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "a44e92f7dc08430aca7ed55de161253a22276dfd69c5526e5c5e95d1f7cf338a" 166 | dependencies = [ 167 | "gettext-sys", 168 | "locale_config", 169 | ] 170 | 171 | [[package]] 172 | name = "gettext-sys" 173 | version = "0.22.5" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "bb45773f5b8945f12aecd04558f545964f943dacda1b1155b3d738f5469ef661" 176 | dependencies = [ 177 | "cc", 178 | "temp-dir", 179 | ] 180 | 181 | [[package]] 182 | name = "hashbrown" 183 | version = "0.15.3" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 186 | 187 | [[package]] 188 | name = "ianny" 189 | version = "2.1.1" 190 | dependencies = [ 191 | "gcd", 192 | "gettext-rs", 193 | "log", 194 | "notify-rust", 195 | "serde", 196 | "simple_logger", 197 | "single-instance", 198 | "toml", 199 | "wayland-client", 200 | "wayland-protocols", 201 | "xdg", 202 | ] 203 | 204 | [[package]] 205 | name = "indexmap" 206 | version = "2.9.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 209 | dependencies = [ 210 | "equivalent", 211 | "hashbrown", 212 | ] 213 | 214 | [[package]] 215 | name = "lazy_static" 216 | version = "1.5.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 219 | 220 | [[package]] 221 | name = "libc" 222 | version = "0.2.172" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 225 | 226 | [[package]] 227 | name = "libdbus-sys" 228 | version = "0.2.5" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" 231 | dependencies = [ 232 | "pkg-config", 233 | ] 234 | 235 | [[package]] 236 | name = "linux-raw-sys" 237 | version = "0.4.15" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 240 | 241 | [[package]] 242 | name = "locale_config" 243 | version = "0.3.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" 246 | dependencies = [ 247 | "lazy_static", 248 | "objc", 249 | "objc-foundation", 250 | "regex", 251 | "winapi", 252 | ] 253 | 254 | [[package]] 255 | name = "log" 256 | version = "0.4.27" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 259 | 260 | [[package]] 261 | name = "mac-notification-sys" 262 | version = "0.6.4" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "0b95dfb34071d1592b45622bf93e315e3a72d414b6782aca9a015c12bec367ef" 265 | dependencies = [ 266 | "cc", 267 | "objc2", 268 | "objc2-foundation", 269 | "time", 270 | ] 271 | 272 | [[package]] 273 | name = "malloc_buf" 274 | version = "0.0.6" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 277 | dependencies = [ 278 | "libc", 279 | ] 280 | 281 | [[package]] 282 | name = "memchr" 283 | version = "2.7.4" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 286 | 287 | [[package]] 288 | name = "memoffset" 289 | version = "0.6.5" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 292 | dependencies = [ 293 | "autocfg", 294 | ] 295 | 296 | [[package]] 297 | name = "nix" 298 | version = "0.23.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" 301 | dependencies = [ 302 | "bitflags 1.3.2", 303 | "cc", 304 | "cfg-if", 305 | "libc", 306 | "memoffset", 307 | ] 308 | 309 | [[package]] 310 | name = "notify-rust" 311 | version = "4.11.7" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" 314 | dependencies = [ 315 | "dbus", 316 | "futures-lite", 317 | "log", 318 | "mac-notification-sys", 319 | "tauri-winrt-notification", 320 | ] 321 | 322 | [[package]] 323 | name = "num-conv" 324 | version = "0.1.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 327 | 328 | [[package]] 329 | name = "objc" 330 | version = "0.2.7" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 333 | dependencies = [ 334 | "malloc_buf", 335 | ] 336 | 337 | [[package]] 338 | name = "objc-foundation" 339 | version = "0.1.1" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" 342 | dependencies = [ 343 | "block", 344 | "objc", 345 | "objc_id", 346 | ] 347 | 348 | [[package]] 349 | name = "objc2" 350 | version = "0.6.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" 353 | dependencies = [ 354 | "objc2-encode", 355 | ] 356 | 357 | [[package]] 358 | name = "objc2-core-foundation" 359 | version = "0.3.1" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" 362 | dependencies = [ 363 | "bitflags 2.9.1", 364 | "dispatch2", 365 | "objc2", 366 | ] 367 | 368 | [[package]] 369 | name = "objc2-encode" 370 | version = "4.1.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" 373 | 374 | [[package]] 375 | name = "objc2-foundation" 376 | version = "0.3.1" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" 379 | dependencies = [ 380 | "bitflags 2.9.1", 381 | "block2", 382 | "libc", 383 | "objc2", 384 | "objc2-core-foundation", 385 | ] 386 | 387 | [[package]] 388 | name = "objc_id" 389 | version = "0.1.1" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" 392 | dependencies = [ 393 | "objc", 394 | ] 395 | 396 | [[package]] 397 | name = "parking" 398 | version = "2.2.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 401 | 402 | [[package]] 403 | name = "pin-project-lite" 404 | version = "0.2.16" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 407 | 408 | [[package]] 409 | name = "pkg-config" 410 | version = "0.3.32" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 413 | 414 | [[package]] 415 | name = "powerfmt" 416 | version = "0.2.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 419 | 420 | [[package]] 421 | name = "proc-macro2" 422 | version = "1.0.95" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 425 | dependencies = [ 426 | "unicode-ident", 427 | ] 428 | 429 | [[package]] 430 | name = "quick-xml" 431 | version = "0.37.5" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" 434 | dependencies = [ 435 | "memchr", 436 | ] 437 | 438 | [[package]] 439 | name = "quote" 440 | version = "1.0.40" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 443 | dependencies = [ 444 | "proc-macro2", 445 | ] 446 | 447 | [[package]] 448 | name = "regex" 449 | version = "1.11.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 452 | dependencies = [ 453 | "aho-corasick", 454 | "memchr", 455 | "regex-automata", 456 | "regex-syntax", 457 | ] 458 | 459 | [[package]] 460 | name = "regex-automata" 461 | version = "0.4.9" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 464 | dependencies = [ 465 | "aho-corasick", 466 | "memchr", 467 | "regex-syntax", 468 | ] 469 | 470 | [[package]] 471 | name = "regex-syntax" 472 | version = "0.8.5" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 475 | 476 | [[package]] 477 | name = "rustix" 478 | version = "0.38.44" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 481 | dependencies = [ 482 | "bitflags 2.9.1", 483 | "errno", 484 | "libc", 485 | "linux-raw-sys", 486 | "windows-sys 0.59.0", 487 | ] 488 | 489 | [[package]] 490 | name = "serde" 491 | version = "1.0.219" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 494 | dependencies = [ 495 | "serde_derive", 496 | ] 497 | 498 | [[package]] 499 | name = "serde_derive" 500 | version = "1.0.219" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 503 | dependencies = [ 504 | "proc-macro2", 505 | "quote", 506 | "syn", 507 | ] 508 | 509 | [[package]] 510 | name = "serde_spanned" 511 | version = "0.6.8" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 514 | dependencies = [ 515 | "serde", 516 | ] 517 | 518 | [[package]] 519 | name = "shlex" 520 | version = "1.3.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 523 | 524 | [[package]] 525 | name = "simple_logger" 526 | version = "5.0.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" 529 | dependencies = [ 530 | "colored", 531 | "log", 532 | "windows-sys 0.48.0", 533 | ] 534 | 535 | [[package]] 536 | name = "single-instance" 537 | version = "0.3.3" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "4637485391f8545c9d3dbf60f9d9aab27a90c789a700999677583bcb17c8795d" 540 | dependencies = [ 541 | "libc", 542 | "nix", 543 | "thiserror 1.0.69", 544 | "widestring", 545 | "winapi", 546 | ] 547 | 548 | [[package]] 549 | name = "smallvec" 550 | version = "1.15.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 553 | 554 | [[package]] 555 | name = "syn" 556 | version = "2.0.101" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 559 | dependencies = [ 560 | "proc-macro2", 561 | "quote", 562 | "unicode-ident", 563 | ] 564 | 565 | [[package]] 566 | name = "tauri-winrt-notification" 567 | version = "0.7.2" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" 570 | dependencies = [ 571 | "quick-xml", 572 | "thiserror 2.0.12", 573 | "windows", 574 | "windows-version", 575 | ] 576 | 577 | [[package]] 578 | name = "temp-dir" 579 | version = "0.1.14" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" 582 | 583 | [[package]] 584 | name = "thiserror" 585 | version = "1.0.69" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 588 | dependencies = [ 589 | "thiserror-impl 1.0.69", 590 | ] 591 | 592 | [[package]] 593 | name = "thiserror" 594 | version = "2.0.12" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 597 | dependencies = [ 598 | "thiserror-impl 2.0.12", 599 | ] 600 | 601 | [[package]] 602 | name = "thiserror-impl" 603 | version = "1.0.69" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 606 | dependencies = [ 607 | "proc-macro2", 608 | "quote", 609 | "syn", 610 | ] 611 | 612 | [[package]] 613 | name = "thiserror-impl" 614 | version = "2.0.12" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 617 | dependencies = [ 618 | "proc-macro2", 619 | "quote", 620 | "syn", 621 | ] 622 | 623 | [[package]] 624 | name = "time" 625 | version = "0.3.41" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 628 | dependencies = [ 629 | "deranged", 630 | "num-conv", 631 | "powerfmt", 632 | "serde", 633 | "time-core", 634 | ] 635 | 636 | [[package]] 637 | name = "time-core" 638 | version = "0.1.4" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 641 | 642 | [[package]] 643 | name = "toml" 644 | version = "0.8.22" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 647 | dependencies = [ 648 | "serde", 649 | "serde_spanned", 650 | "toml_datetime", 651 | "toml_edit", 652 | ] 653 | 654 | [[package]] 655 | name = "toml_datetime" 656 | version = "0.6.9" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 659 | dependencies = [ 660 | "serde", 661 | ] 662 | 663 | [[package]] 664 | name = "toml_edit" 665 | version = "0.22.26" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 668 | dependencies = [ 669 | "indexmap", 670 | "serde", 671 | "serde_spanned", 672 | "toml_datetime", 673 | "toml_write", 674 | "winnow", 675 | ] 676 | 677 | [[package]] 678 | name = "toml_write" 679 | version = "0.1.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 682 | 683 | [[package]] 684 | name = "unicode-ident" 685 | version = "1.0.18" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 688 | 689 | [[package]] 690 | name = "wayland-backend" 691 | version = "0.3.10" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" 694 | dependencies = [ 695 | "cc", 696 | "downcast-rs", 697 | "rustix", 698 | "smallvec", 699 | "wayland-sys", 700 | ] 701 | 702 | [[package]] 703 | name = "wayland-client" 704 | version = "0.31.10" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" 707 | dependencies = [ 708 | "bitflags 2.9.1", 709 | "rustix", 710 | "wayland-backend", 711 | "wayland-scanner", 712 | ] 713 | 714 | [[package]] 715 | name = "wayland-protocols" 716 | version = "0.32.8" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" 719 | dependencies = [ 720 | "bitflags 2.9.1", 721 | "wayland-backend", 722 | "wayland-client", 723 | "wayland-scanner", 724 | ] 725 | 726 | [[package]] 727 | name = "wayland-scanner" 728 | version = "0.31.6" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" 731 | dependencies = [ 732 | "proc-macro2", 733 | "quick-xml", 734 | "quote", 735 | ] 736 | 737 | [[package]] 738 | name = "wayland-sys" 739 | version = "0.31.6" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" 742 | dependencies = [ 743 | "pkg-config", 744 | ] 745 | 746 | [[package]] 747 | name = "widestring" 748 | version = "0.4.3" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" 751 | 752 | [[package]] 753 | name = "winapi" 754 | version = "0.3.9" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 757 | dependencies = [ 758 | "winapi-i686-pc-windows-gnu", 759 | "winapi-x86_64-pc-windows-gnu", 760 | ] 761 | 762 | [[package]] 763 | name = "winapi-i686-pc-windows-gnu" 764 | version = "0.4.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 767 | 768 | [[package]] 769 | name = "winapi-x86_64-pc-windows-gnu" 770 | version = "0.4.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 773 | 774 | [[package]] 775 | name = "windows" 776 | version = "0.61.1" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" 779 | dependencies = [ 780 | "windows-collections", 781 | "windows-core", 782 | "windows-future", 783 | "windows-link", 784 | "windows-numerics", 785 | ] 786 | 787 | [[package]] 788 | name = "windows-collections" 789 | version = "0.2.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 792 | dependencies = [ 793 | "windows-core", 794 | ] 795 | 796 | [[package]] 797 | name = "windows-core" 798 | version = "0.61.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" 801 | dependencies = [ 802 | "windows-implement", 803 | "windows-interface", 804 | "windows-link", 805 | "windows-result", 806 | "windows-strings", 807 | ] 808 | 809 | [[package]] 810 | name = "windows-future" 811 | version = "0.2.0" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" 814 | dependencies = [ 815 | "windows-core", 816 | "windows-link", 817 | ] 818 | 819 | [[package]] 820 | name = "windows-implement" 821 | version = "0.60.0" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 824 | dependencies = [ 825 | "proc-macro2", 826 | "quote", 827 | "syn", 828 | ] 829 | 830 | [[package]] 831 | name = "windows-interface" 832 | version = "0.59.1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 835 | dependencies = [ 836 | "proc-macro2", 837 | "quote", 838 | "syn", 839 | ] 840 | 841 | [[package]] 842 | name = "windows-link" 843 | version = "0.1.1" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 846 | 847 | [[package]] 848 | name = "windows-numerics" 849 | version = "0.2.0" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 852 | dependencies = [ 853 | "windows-core", 854 | "windows-link", 855 | ] 856 | 857 | [[package]] 858 | name = "windows-result" 859 | version = "0.3.2" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" 862 | dependencies = [ 863 | "windows-link", 864 | ] 865 | 866 | [[package]] 867 | name = "windows-strings" 868 | version = "0.4.0" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" 871 | dependencies = [ 872 | "windows-link", 873 | ] 874 | 875 | [[package]] 876 | name = "windows-sys" 877 | version = "0.48.0" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 880 | dependencies = [ 881 | "windows-targets 0.48.5", 882 | ] 883 | 884 | [[package]] 885 | name = "windows-sys" 886 | version = "0.59.0" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 889 | dependencies = [ 890 | "windows-targets 0.52.6", 891 | ] 892 | 893 | [[package]] 894 | name = "windows-targets" 895 | version = "0.48.5" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 898 | dependencies = [ 899 | "windows_aarch64_gnullvm 0.48.5", 900 | "windows_aarch64_msvc 0.48.5", 901 | "windows_i686_gnu 0.48.5", 902 | "windows_i686_msvc 0.48.5", 903 | "windows_x86_64_gnu 0.48.5", 904 | "windows_x86_64_gnullvm 0.48.5", 905 | "windows_x86_64_msvc 0.48.5", 906 | ] 907 | 908 | [[package]] 909 | name = "windows-targets" 910 | version = "0.52.6" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 913 | dependencies = [ 914 | "windows_aarch64_gnullvm 0.52.6", 915 | "windows_aarch64_msvc 0.52.6", 916 | "windows_i686_gnu 0.52.6", 917 | "windows_i686_gnullvm", 918 | "windows_i686_msvc 0.52.6", 919 | "windows_x86_64_gnu 0.52.6", 920 | "windows_x86_64_gnullvm 0.52.6", 921 | "windows_x86_64_msvc 0.52.6", 922 | ] 923 | 924 | [[package]] 925 | name = "windows-version" 926 | version = "0.1.4" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" 929 | dependencies = [ 930 | "windows-link", 931 | ] 932 | 933 | [[package]] 934 | name = "windows_aarch64_gnullvm" 935 | version = "0.48.5" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 938 | 939 | [[package]] 940 | name = "windows_aarch64_gnullvm" 941 | version = "0.52.6" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 944 | 945 | [[package]] 946 | name = "windows_aarch64_msvc" 947 | version = "0.48.5" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 950 | 951 | [[package]] 952 | name = "windows_aarch64_msvc" 953 | version = "0.52.6" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 956 | 957 | [[package]] 958 | name = "windows_i686_gnu" 959 | version = "0.48.5" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 962 | 963 | [[package]] 964 | name = "windows_i686_gnu" 965 | version = "0.52.6" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 968 | 969 | [[package]] 970 | name = "windows_i686_gnullvm" 971 | version = "0.52.6" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 974 | 975 | [[package]] 976 | name = "windows_i686_msvc" 977 | version = "0.48.5" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 980 | 981 | [[package]] 982 | name = "windows_i686_msvc" 983 | version = "0.52.6" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 986 | 987 | [[package]] 988 | name = "windows_x86_64_gnu" 989 | version = "0.48.5" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 992 | 993 | [[package]] 994 | name = "windows_x86_64_gnu" 995 | version = "0.52.6" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 998 | 999 | [[package]] 1000 | name = "windows_x86_64_gnullvm" 1001 | version = "0.48.5" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1004 | 1005 | [[package]] 1006 | name = "windows_x86_64_gnullvm" 1007 | version = "0.52.6" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1010 | 1011 | [[package]] 1012 | name = "windows_x86_64_msvc" 1013 | version = "0.48.5" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1016 | 1017 | [[package]] 1018 | name = "windows_x86_64_msvc" 1019 | version = "0.52.6" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1022 | 1023 | [[package]] 1024 | name = "winnow" 1025 | version = "0.7.10" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" 1028 | dependencies = [ 1029 | "memchr", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "xdg" 1034 | version = "3.0.0" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" 1037 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ianny" 3 | version = "2.1.1" 4 | authors = ["zefr0x"] 5 | edition = "2024" 6 | description = "Desktop utility that helps preventing repetitive strain injuries by keeping track of usage patterns and periodically informing user to take breaks" 7 | readme = "README.md" 8 | homepage = "https://github.com/zefr0x/ianny" 9 | repository = "https://github.com/zefr0x/ianny" 10 | license = "GPL-3.0" 11 | build = "build.rs" 12 | 13 | [profile.release] 14 | lto = true 15 | codegen-units = 1 16 | 17 | [dependencies] 18 | gcd = "2.3.0" 19 | gettext-rs = { version = "0.7.2", features = ["gettext-system"] } 20 | log = "0.4.27" 21 | notify-rust = { version = "4.11.7", default-features = false, features = ["d"] } 22 | serde = { version = "1.0.219", features = ["derive"] } 23 | simple_logger = { version = "5.0.0", default-features = false, features = [ 24 | "colors", 25 | "stderr", 26 | ] } 27 | single-instance = "0.3.3" 28 | toml = "0.8.22" 29 | wayland-client = "0.31.10" 30 | wayland-protocols = { version = "0.32.8", features = ["client", "staging"] } 31 | xdg = "3.0.0" 32 | 33 | # TODO: Consider commented lints when they are stable. 34 | [lints.rust] 35 | absolute_paths_not_starting_with_crate = "deny" 36 | explicit_outlives_requirements = "warn" 37 | # fuzzy_provenance_casts = "warn" 38 | # lossy_provenance_casts = "warn" 39 | # multiple_supertrait_upcastable = "warn" 40 | # must_not_suspend = "warn" 41 | non_ascii_idents = "forbid" 42 | # non_exhaustive_omitted_patterns = "warn" 43 | unit_bindings = "warn" 44 | unnameable_types = "warn" 45 | unsafe_code = "forbid" 46 | unused_crate_dependencies = "warn" 47 | unused_lifetimes = "warn" 48 | unused_macro_rules = "warn" 49 | unused_qualifications = "warn" 50 | variant_size_differences = "warn" 51 | 52 | [lints.clippy] 53 | pedantic = { level = "warn", priority = -1 } 54 | nursery = { level = "warn", priority = -1 } 55 | alloc_instead_of_core = "warn" 56 | allow_attributes = "deny" 57 | allow_attributes_without_reason = "warn" 58 | assertions_on_result_states = "warn" 59 | clone_on_ref_ptr = "deny" 60 | create_dir = "warn" 61 | dbg_macro = "warn" 62 | decimal_literal_representation = "warn" 63 | default_numeric_fallback = "warn" 64 | deref_by_slicing = "warn" 65 | empty_drop = "warn" 66 | empty_enum_variants_with_brackets = "deny" 67 | empty_structs_with_brackets = "deny" 68 | error_impl_error = "deny" 69 | filetype_is_file = "warn" 70 | float_cmp_const = "deny" 71 | format_push_string = "warn" 72 | get_unwrap = "warn" 73 | infinite_loop = "warn" 74 | let_underscore_untyped = "warn" 75 | lossy_float_literal = "warn" 76 | map_err_ignore = "warn" 77 | mem_forget = "warn" 78 | multiple_inherent_impl = "warn" 79 | multiple_unsafe_ops_per_block = "deny" 80 | mutex_atomic = "warn" 81 | # TODO: Enable after false-positive is solved (https://github.com/rust-lang/rust-clippy/issues/9378) 82 | # pattern_type_mismatch = "warn" 83 | print_stderr = "warn" 84 | print_stdout = "warn" 85 | pub_with_shorthand = "warn" 86 | rc_buffer = "warn" 87 | rc_mutex = "deny" 88 | redundant_type_annotations = "warn" 89 | rest_pat_in_fully_bound_structs = "deny" 90 | same_name_method = "warn" 91 | self_named_module_files = "deny" 92 | semicolon_inside_block = "warn" 93 | single_char_lifetime_names = "warn" 94 | std_instead_of_core = "warn" 95 | str_to_string = "warn" 96 | string_lit_chars_any = "warn" 97 | string_slice = "warn" 98 | string_to_string = "warn" 99 | tests_outside_test_module = "warn" 100 | todo = "warn" 101 | try_err = "warn" 102 | undocumented_unsafe_blocks = "warn" 103 | unnecessary_safety_comment = "warn" 104 | unnecessary_safety_doc = "warn" 105 | unneeded_field_pattern = "warn" 106 | unseparated_literal_suffix = "warn" 107 | verbose_file_reads = "warn" 108 | wildcard_dependencies = "forbid" 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /PACKAGING.md: -------------------------------------------------------------------------------- 1 | # Packaging 2 | 3 | [![Please do not ship work in progress to users](https://dont-ship.it/dontshipwip.svg)](https://dont-ship.it/) 4 | 5 | ## Releases 6 | 7 | ### What is a release? 8 | 9 | The [Semantic Versioning](https://semver.org/) is used for version numbering in this project. 10 | 11 | A release is a [git tag](https://git-scm.com/docs/git-tag) in the `main` branch of the project's git repository that starts with the letter `v` followed by the Semantic Versioning based version number. 12 | 13 | ### Creating a new release 14 | 15 | 1. Update the version number in the [`Cargo.toml`](Cargo.toml) file. 16 | 2. Write what happened from fixes, changes, and updates and every thing in this release in the [CHANGELOG.md](CHANGELOG.md) file. 17 | 3. Add a new `` tag under the `` tag in the [`io.github.zefr0x.ianny.metainfo.xml`](io.github.zefr0x.ianny.metainfo.xml) file with information about the release and a link to the CHANGELOG file. 18 | 4. Create a git commit with all of those changes. 19 | 5. Create a signed git tag with a `v` letter followed by the version number e.g. for `v1.5.3` you should do `git tag -s v1.5.3`. 20 | 6. Push changes to the remote using `git push origin main --tags` 21 | 22 | ## Official Packages 23 | 24 | - [Meson](https://mesonbuild.com/) is used as a build system and [Cargo](https://doc.rust-lang.org/cargo/) as dependencies manager. 25 | 26 | ### AUR 27 | 28 | - [ianny-git](https://aur.archlinux.org/packages/ianny-git) 29 | 30 | ## What should be packaged? 31 | 32 | Only stable [releases](#releases) should be packaged. Neither an `alpha` nor a `beta` release should be. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Ianny | عَيْنِي

4 | 5 | [![release](https://github.com/zefr0x/ianny/actions/workflows/release.yml/badge.svg)](https://github.com/zefr0x/ianny/actions/workflows/release.yml) 6 | 7 | Simple, light-weight, easy to use, and effective [Linux](https://en.wikipedia.org/wiki/Linux) [Wayland]() desktop utility that helps with preventing [repetitive strain injuries](https://en.wikipedia.org/wiki/Repetitive_strain_injury) by keeping track of usage patterns and periodically informing the user to take breaks. 8 | 9 | --- 10 | 11 | [
Install

](#installation) 12 | [
Contribute

](CONTRIBUTING.md) 13 | [
Packaging

](PACKAGING.md) 14 | 15 | --- 16 | 17 |
18 | 19 | ## Features 20 | 21 | - ⚙ Simple config to tweak its behavior. 22 | - 🚀 Auto start it with your desktop environment. 23 | - 🚫 [X11](https://en.wikipedia.org/wiki/X_Window_System) is not supported. 24 | - 🚫 Microsoft Windows is definitely not supported. 25 | 26 | ## Requirements 27 | 28 | - [Wayland Compositor]() that optionally implements [`ext_idle_notifier_v1`](https://wayland.app/protocols/ext-idle-notify-v1) 29 | - [Notification Daemon](https://wiki.archlinux.org/title/Desktop_notifications#Notification_servers) that implements [`org.freedesktop.Notifications`](https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html) 30 | - [libdbus-1.so](https://www.freedesktop.org/wiki/Software/dbus/) installed in your system 31 | - [Linux libc](https://en.wikipedia.org/wiki/C_standard_library) via either [glibc](https://www.gnu.org/software/libc/) or [musl libc](https://musl.libc.org/) 32 | 33 | ## Installation 34 | 35 | [![Packaging status](https://repology.org/badge/vertical-allrepos/ianny.svg?columns=3)](https://repology.org/project/ianny/versions) 36 | 37 | ### Arch Linux 38 | 39 | All packages are available on AUR, you can: 40 | 41 | - Build locally from latest stable release: [ianny](https://aur.archlinux.org/packages/ianny) 42 | - Build locally from latest Git commit: [ianny-git](https://aur.archlinux.org/packages/ianny-git) 43 | - Use the binary built by GitHub: [ianny-bin](https://aur.archlinux.org/packages/ianny-bin) 44 | 45 | ### Download Binary From GitHub 46 | 47 | For every new release a GitHub workflow will build a binary in GitHub servers and will upload it as a release asset in GitHub releases. 48 | 49 | You can find the latest GitHub release [here](https://github.com/zefr0x/ianny/releases/latest) or the releases page [here](https://github.com/zefr0x/ianny/releases). 50 | 51 | ## Build 52 | 53 | > [!Note] 54 | > You need to have [`cargo`](https://doc.rust-lang.org/cargo/), [`meson`](https://mesonbuild.com/) and [`libdbus-1-dev`](https://www.freedesktop.org/wiki/Software/dbus/) installed in your system. 55 | 56 | > [!NOTE] 57 | > For cross compilation you will need to set the `rustc_target` meson option, and create [`.cargo/config.toml`](https://doc.rust-lang.org/cargo/reference/config.html) file to set a `linker` to be used for your target. 58 | 59 | ```shell 60 | git clone https://github.com/zefr0x/ianny.git 61 | 62 | cd ianny 63 | 64 | # Checkout to a release tag e.g. v1.0.1 65 | git checkout vx.x.x 66 | 67 | meson setup builddir -Dbuildtype=release 68 | meson compile -C builddir 69 | ``` 70 | 71 | You will find the binary in `./builddir/src/ianny` 72 | 73 | To install: 74 | 75 | ```shell 76 | meson install -C builddir 77 | ``` 78 | 79 | # Usage 80 | 81 | You just need to execute the binary either directly or by enabling it to auto start with your desktop environment's settings, since it provides a `.desktop` file for auto-start. 82 | 83 | # Config 84 | 85 | The defaults might not fit your needs, so you can change them via a config file. 86 | 87 | The config file is `$XDG_CONFIG_HOME/io.github.zefr0x.ianny/config.toml` or by default `~/.config/io.github.zefr0x.ianny/config.toml`. Just create it and specify the options you need with the [toml format](https://toml.io/): 88 | 89 | ```toml 90 | [timer] 91 | # Enabling this will only consider user input alone for idle state, e.g. you will not have breaks when watching videos or playing music without any user input. 92 | ignore_idle_inhibitors = false 93 | # Timer will stop and reset when you are idle for this amount of seconds. 94 | idle_timeout = 240 95 | # Active duration that activates a break. 96 | short_break_timeout = 1200 97 | long_break_timeout = 3840 98 | # Breaks duration. 99 | short_break_duration = 120 100 | long_break_duration = 240 101 | 102 | [notification] 103 | show_progress_bar = true 104 | # Minimum delay of updating the progress bar (lower than 1s may return an error). 105 | minimum_update_delay = 1 106 | ``` 107 | 108 | > [!Note] 109 | > Time specified in seconds 110 | 111 | ## Q&A 112 | 113 | Q: What does `Ianny` mean? 114 | 115 | - It is an Arabic word `عَيْنِي` that could be translated to `My Eye` in English. 116 | 117 | ## Inspired by 118 | 119 | - [KDE's RSIBreak](https://userbase.kde.org/RSIBreak) 120 | -------------------------------------------------------------------------------- /assets/io.github.zefr0x.ianny.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zefr0x/ianny/441853b5994edfab247df3523b9f29a1da973a9c/assets/io.github.zefr0x.ianny.svg -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /io.github.zefr0x.ianny.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.5 3 | Type=Application 4 | Name=Ianny 5 | Name[ar]=عَيْنِي 6 | GenericName=Break Notifier 7 | GenericName[ar]=مُنَبِّهُ اِستِرَاحَة 8 | Comment=Help preventing repetitive strain injuries by keeping track of usage patterns and periodically informing user to take breaks. 9 | Comment[ar]=يُساعِدُ فِي تَجَنُّبِ إصَابَاتِ الإجهَادِ المُتَكَرِّرِ مِن خِلَالِ تَتَبُّعِ أنمَاطِ الاِستِخدَامِ وَتَذكِيِرِ المُستَخدِمِ بِشَكلٍ دَورِيٍّ لِأخذِ اِستِرَاحَات 10 | Exec=ianny 11 | NoDisplay=true 12 | Terminal=false 13 | SingleMainWindow=true 14 | Categories=Utility 15 | Keywords=RSI;eye;break; 16 | Keywords[ar]=إجهاد;عين;استراحة; 17 | X-GNOME-Autostart-enabled=true 18 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | _default: 2 | @just --list 3 | 4 | lint_all: 5 | pre-commit run --all-files 6 | 7 | pot: 8 | # Generate .pot file from sorce code using `xtr` (https://github.com/woboq/tr). 9 | xtr --output po/io.github.zefr0x.ianny.pot --package-name Ianny src/main.rs 10 | sed -i 1,2d po/io.github.zefr0x.ianny.pot 11 | 12 | update_po: 13 | for lang in `cat ./po/LINGUAS`; do \ 14 | msgmerge --update ./po/${lang}.po ./po/io.github.zefr0x.ianny.pot; \ 15 | done 16 | 17 | todo: 18 | rg "(.(TODO|FIXME|FIX|HACK|WARN|PREF|NOTE): )|(todo!)" --glob !{{ file_name(justfile()) }} 19 | 20 | # vim: set ft=make : 21 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('ianny', 'rust', 2 | version : '2.1.1', 3 | license : 'GPL3', 4 | default_options : ['warning_level=2']) 5 | 6 | application_id = 'io.github.zefr0x.ianny' 7 | 8 | # Get options 9 | buildtype = get_option('buildtype') 10 | 11 | prefix = get_option('prefix') 12 | bindir = prefix / get_option('bindir') 13 | 14 | rustc_target = get_option('rustc_target') 15 | 16 | # Check for deps 17 | dbus_dep = dependency('dbus-1', version : '>=1.6.0') 18 | 19 | desktop_utils = find_program('desktop-file-validate', required: false) 20 | cargo = find_program('cargo', required: true) 21 | 22 | # Desktop file 23 | desktop_file ='@0@.desktop'.format(application_id) 24 | 25 | if desktop_utils.found() 26 | test('Validate desktop file', desktop_utils, 27 | args: [desktop_file] 28 | ) 29 | endif 30 | 31 | # Build summaries 32 | summary( 33 | { 34 | 'Build Type': buildtype, 35 | }, 36 | section: 'Build Summary', 37 | ) 38 | 39 | # Other meson.build files 40 | subdir('src/') 41 | subdir('po/') 42 | 43 | 44 | # Install .desktop files 45 | install_data( 46 | desktop_file, 47 | install_dir: 'share/applications' 48 | ) 49 | 50 | install_data( 51 | desktop_file, 52 | install_dir: '/etc/xdg/autostart/' 53 | ) 54 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option ( 2 | 'rustc_target', 3 | description: 'Rust target triple', 4 | type: 'string', 5 | value: '' 6 | ) 7 | 8 | # By default it will use a special cargo-home just for the build. 9 | option( 10 | 'cargo-home', 11 | type: 'string', 12 | value: '' 13 | ) 14 | 15 | # By default it will connect to crates.io to check and downlaod missing rust deps. 16 | option( 17 | 'offline-build', 18 | type: 'boolean', 19 | value: false 20 | ) 21 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | ar 2 | -------------------------------------------------------------------------------- /po/ar.po: -------------------------------------------------------------------------------- 1 | # This file is distributed under the same license as the Ianny package. 2 | # zefr0x <>, 2023-2024. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: Ianny VERSION\n" 7 | "Report-Msgid-Bugs-To: \n" 8 | "POT-Creation-Date: 2024-02-16 08:11+0000\n" 9 | "PO-Revision-Date: 2024-02-16 11:15+0300\n" 10 | "Last-Translator: zefr0x <>\n" 11 | "Language-Team: Arabic <>\n" 12 | "Language: ar\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Plural-Forms: nplurals=6; plural= n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 17 | "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" 18 | "X-Generator: Gtranslator 45.3\n" 19 | 20 | #: src/main.rs:27 21 | msgid "Take a break for" 22 | msgstr "خُذ اِستِراحَةً لِمُدَّةِ" 23 | 24 | #: src/main.rs:35 25 | msgid "minute" 26 | msgid_plural "minutes" 27 | msgstr[0] "لَحظَة" 28 | msgstr[1] "دَقِيقَةٍ وَاحِدَة" 29 | msgstr[2] "دَقِيقَتانِ اِثنَتان" 30 | msgstr[3] "دَقِائِق" 31 | msgstr[4] "دَقِيقَة" 32 | msgstr[5] "دَقِيقَة" 33 | 34 | #: src/main.rs:40 35 | msgid " and" 36 | msgstr " وَ" 37 | 38 | #: src/main.rs:48 39 | msgid "second" 40 | msgid_plural "seconds" 41 | msgstr[0] "لَحظَة" 42 | msgstr[1] "ثَانِيَةٌ وَاحِدَة" 43 | msgstr[2] "ثَانِيَتَانِ اِثنَتان" 44 | msgstr[3] "ثَوانِي" 45 | msgstr[4] "ثَانِيَة" 46 | msgstr[5] "ثَانِيَة" 47 | 48 | #: src/main.rs:54 49 | msgid "Break Time!" 50 | msgstr "وَقتُ اِستِراحَة!" 51 | 52 | #: src/main.rs:57 53 | msgid "Ianny" 54 | msgstr "عَيْنِي" 55 | -------------------------------------------------------------------------------- /po/io.github.zefr0x.ianny.pot: -------------------------------------------------------------------------------- 1 | # This file is distributed under the same license as the Ianny package. 2 | # FIRST AUTHOR , YEAR. 3 | # 4 | #, fuzzy 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Ianny VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2024-02-16 08:11+0000\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: FULL NAME \n" 12 | "Language-Team: LANGUAGE \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: src/main.rs:27 19 | msgid "Take a break for" 20 | msgstr "" 21 | 22 | #: src/main.rs:35 23 | msgid "minute" 24 | msgid_plural "minutes" 25 | msgstr[0] "" 26 | msgstr[1] "" 27 | 28 | #: src/main.rs:40 29 | msgid " and" 30 | msgstr "" 31 | 32 | #: src/main.rs:48 33 | msgid "second" 34 | msgid_plural "seconds" 35 | msgstr[0] "" 36 | msgstr[1] "" 37 | 38 | #: src/main.rs:54 39 | msgid "Break Time!" 40 | msgstr "" 41 | 42 | #: src/main.rs:57 43 | msgid "Ianny" 44 | msgstr "" 45 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n = import('i18n') 2 | 3 | i18n.gettext(application_id) 4 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use log::info; 2 | 3 | #[derive(Default, Debug, serde::Deserialize)] 4 | #[serde(default)] 5 | pub struct Config { 6 | pub notification: Notification, 7 | pub timer: Timer, 8 | } 9 | 10 | #[derive(Debug, serde::Deserialize)] 11 | #[serde(default)] 12 | pub struct Notification { 13 | pub show_progress_bar: bool, 14 | pub minimum_update_delay: u64, 15 | } 16 | 17 | #[derive(Debug, serde::Deserialize)] 18 | #[serde(default)] 19 | pub struct Timer { 20 | pub ignore_idle_inhibitors: bool, 21 | pub idle_timeout: u32, // Seconds 22 | pub short_break_timeout: u64, // Seconds 23 | pub long_break_timeout: u64, // Seconds 24 | pub short_break_duration: u64, // Seconds 25 | pub long_break_duration: u64, // Seconds 26 | } 27 | 28 | impl Default for Notification { 29 | fn default() -> Self { 30 | Self { 31 | show_progress_bar: true, 32 | minimum_update_delay: 1, 33 | } 34 | } 35 | } 36 | 37 | impl Default for Timer { 38 | fn default() -> Self { 39 | Self { 40 | ignore_idle_inhibitors: false, 41 | idle_timeout: 240, // Seconds (7 minutes) 42 | short_break_timeout: 1200, // Seconds (20 minutes) 43 | long_break_timeout: 3840, // Seconds (64 minutes) 44 | short_break_duration: 120, // Seconds (2 minutes) 45 | long_break_duration: 240, // Seconds (7 minutes) 46 | } 47 | } 48 | } 49 | 50 | impl Config { 51 | pub fn load() -> Self { 52 | let config_file = Self::get_config_file(); 53 | 54 | toml::from_str(&std::fs::read_to_string(&config_file).map_or_else( 55 | |_| String::new(), 56 | |content| { 57 | info!("Read config from: {}", &config_file.to_string_lossy()); 58 | content 59 | }, 60 | )) 61 | .expect("Failed to parse conifg file") 62 | } 63 | 64 | fn get_config_file() -> std::path::PathBuf { 65 | xdg::BaseDirectories::with_prefix(crate::APP_ID) 66 | .get_config_file("config.toml") 67 | .expect("Can't find XDG base config directory") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod wayland; 3 | 4 | use core::{fmt::Write, ops::AddAssign, time::Duration}; 5 | use std::{ 6 | env, 7 | sync::{LazyLock, mpsc}, 8 | time::Instant, 9 | }; 10 | 11 | use gettextrs::{gettext, ngettext}; 12 | use log::{error, info}; 13 | use single_instance::SingleInstance; 14 | 15 | const APP_ID: &str = "io.github.zefr0x.ianny"; 16 | 17 | static CONFIG: LazyLock = LazyLock::new(|| { 18 | let config = config::Config::load(); 19 | 20 | info!("{:?}", &config); 21 | 22 | config 23 | }); 24 | 25 | /// Display a break notification for specific duration than return the real system time it toke 26 | /// while displaying this notification. 27 | fn show_break_notification( 28 | break_time: Duration, 29 | notification_sound_hint: notify_rust::Hint, 30 | ) -> Duration { 31 | use notify_rust::{Hint, Notification, Timeout, Urgency}; 32 | 33 | let minutes = break_time.as_secs() / 60; 34 | let seconds = break_time.as_secs() % 60; 35 | 36 | let mut message = gettext("Take a break for"); 37 | 38 | if minutes != 0 { 39 | // FIX: Languages where number should be after the word. 40 | write!( 41 | message, 42 | " {} {}", 43 | minutes, 44 | &ngettext("minute", "minutes", u32::try_from(minutes).unwrap()) 45 | ) 46 | .unwrap(); 47 | } 48 | if minutes != 0 && seconds != 0 { 49 | message += &gettext(" and"); 50 | } 51 | if seconds != 0 { 52 | // FIX: Languages where number should be after the word. 53 | write!( 54 | message, 55 | " {} {}", 56 | seconds, 57 | &ngettext("second", "seconds", u32::try_from(seconds).unwrap()) 58 | ) 59 | .unwrap(); 60 | } 61 | 62 | let mut handle = Notification::new() 63 | .summary(&gettext("Break Time!")) 64 | .body(&message) 65 | .appname(&gettext("Ianny")) 66 | .hint(notification_sound_hint) 67 | .hint(Hint::Urgency(Urgency::Critical)) 68 | .hint(Hint::Resident(true)) 69 | .timeout(Timeout::Never) 70 | .show() 71 | .expect("Failed to send notification"); 72 | 73 | let mut last_time = Instant::now(); 74 | let mut accumulative_time = Duration::from_secs(0); 75 | #[expect(clippy::cast_precision_loss, reason = "Working with small numbers")] 76 | let step = 77 | CONFIG.notification.minimum_update_delay as f64 / break_time.as_secs_f64() * 100.0_f64; 78 | let step_duration = Duration::from_secs(CONFIG.notification.minimum_update_delay); 79 | 80 | let mut i: f64 = 0.0; 81 | 82 | #[expect(clippy::while_float, reason = "Precision is not an issue")] 83 | while i < 100.0_f64 { 84 | std::thread::sleep(step_duration); 85 | let last_time_copy = last_time; 86 | last_time = Instant::now(); 87 | let time_diff = Instant::now().duration_since(last_time_copy); 88 | 89 | accumulative_time += time_diff; 90 | 91 | i += step * time_diff.div_duration_f64(step_duration); 92 | 93 | if CONFIG.notification.show_progress_bar { 94 | // FIX: Floating point problems leads to update when not needed. 95 | // HACK: The f64 data type is used to minimize the impact. 96 | #[expect(clippy::cast_possible_truncation, reason = "Truncation is intentional")] 97 | if (i as i32) != ((i - step) as i32) { 98 | // Progress bar update 99 | handle.hint(Hint::CustomInt("value".to_owned(), i as i32)); 100 | } 101 | } 102 | 103 | handle.update(); 104 | } 105 | 106 | handle.close(); 107 | 108 | accumulative_time 109 | } 110 | 111 | fn main() -> ! { 112 | simple_logger::SimpleLogger::new().init().unwrap(); 113 | 114 | // Check if the app is already running 115 | let app_instance = SingleInstance::new(APP_ID).unwrap(); 116 | if !app_instance.is_single() { 117 | error!("{APP_ID} is already running."); 118 | std::process::exit(1); 119 | } 120 | 121 | // Find and load locale 122 | let app_lang = gettextrs::setlocale( 123 | gettextrs::LocaleCategory::LcAll, 124 | env::var("LC_ALL").unwrap_or_else(|_| { 125 | env::var("LC_CTYPE").unwrap_or_else(|_| env::var("LANG").unwrap_or_default()) 126 | }), 127 | ) 128 | .expect("Failed to set locale, please use a valid system locale and make sure it's enabled"); 129 | gettextrs::textdomain(APP_ID).unwrap(); 130 | // FIX: Also support /usr/local/share/locale/ 131 | gettextrs::bindtextdomain(APP_ID, "/usr/share/locale").unwrap(); 132 | gettextrs::bind_textdomain_codeset(APP_ID, "UTF-8").unwrap(); 133 | 134 | info!("Application locale: {}", String::from_utf8_lossy(&app_lang)); 135 | 136 | // Sync channel to share the idle/active state with the timer 137 | // 138 | // NOTE: Both idle and resume can happen during a break or timer pause, 139 | // so we need to buffer two messages in order to catch both. 140 | // Also we should guarantee that the main thread is not blocked 141 | // (only buffer two messages, and drop any new ones till processed), 142 | // and we must handle both messages sequentially before catching new pair 143 | // (one idle signal must be followed by at least one resume signal). 144 | // By limiting the buffer to two messages we also avoid repeating the 145 | // timer loop cycle for an already resumed idle state. 146 | let (signal_sender, signal_receiver) = mpsc::sync_channel(2); 147 | 148 | // Timer thread 149 | std::thread::spawn(move || -> ! { 150 | let pause_duration = core::cmp::min( 151 | gcd::binary_u64( 152 | CONFIG.timer.short_break_timeout, 153 | CONFIG.timer.long_break_timeout, 154 | ), // Calculate GCD 155 | u64::from(CONFIG.timer.idle_timeout) + 1, // NOTE: Extra one second to make sure 156 | ); // seconds 157 | 158 | let mut short_time_pased = 0; // seconds 159 | let mut long_time_pased = 0; // seconds 160 | let mut last_time = Instant::now(); 161 | 162 | // TODO: Handle separate idle timeout for both long and short timers. 163 | 164 | // Timer loop. 165 | loop { 166 | std::thread::sleep(Duration::from_secs(pause_duration)); 167 | // NOTE: Get around freezing after calculating time_diff and 168 | // before resetting last_time. Since the time between will 169 | // be dropped without having it in the next calculations. 170 | let last_time_copy = last_time; 171 | last_time = Instant::now(); 172 | 173 | let time_diff = Instant::now().duration_since(last_time_copy).as_secs(); 174 | 175 | if time_diff - pause_duration >= u64::from(CONFIG.timer.idle_timeout) { 176 | long_time_pased = 0; 177 | short_time_pased = 0; 178 | last_time = Instant::now(); 179 | 180 | info!("Timer resetted since idle happend while process was suspended"); 181 | } else { 182 | short_time_pased.add_assign(time_diff); 183 | long_time_pased.add_assign(time_diff); 184 | } 185 | 186 | if signal_receiver.try_recv() == Ok(wayland::Signal::Idled) { 187 | // Wait for change, tell user resume from idle. 188 | loop { 189 | if signal_receiver.recv() == Ok(wayland::Signal::Resumed) { 190 | // Clean the channel from any other event. 191 | while signal_receiver.try_recv().is_ok() {} 192 | 193 | // Reset timers. 194 | long_time_pased = 0; 195 | short_time_pased = 0; 196 | last_time = Instant::now(); 197 | 198 | info!("Timer resetted"); 199 | break; 200 | } 201 | } 202 | } else if long_time_pased >= CONFIG.timer.long_break_timeout { 203 | info!("Long break starts"); 204 | 205 | show_break_notification( 206 | Duration::from_secs(CONFIG.timer.long_break_duration), 207 | notify_rust::Hint::SoundName("suspend-error".to_owned()), // Name or file 208 | ); 209 | 210 | info!("Long break ends"); 211 | 212 | // Reset timers. 213 | long_time_pased = 0; 214 | short_time_pased = 0; 215 | last_time = Instant::now(); 216 | } else if short_time_pased >= CONFIG.timer.short_break_timeout { 217 | info!("Short break starts"); 218 | 219 | if show_break_notification( 220 | Duration::from_secs(CONFIG.timer.short_break_duration), 221 | notify_rust::Hint::SoundName("suspend-error".to_owned()), // Name or file 222 | ) 223 | .as_secs() 224 | - CONFIG.timer.short_break_duration 225 | >= u64::from(CONFIG.timer.idle_timeout) 226 | { 227 | long_time_pased = 0; 228 | 229 | info!("Long break timer resetted since idle happend during short break"); 230 | } 231 | 232 | info!("Short break ends"); 233 | 234 | // Reset timer. 235 | short_time_pased = 0; 236 | last_time = Instant::now(); 237 | } 238 | } 239 | }); 240 | 241 | // Connect to Wayland server 242 | let conn = wayland_client::Connection::connect_to_env() 243 | .expect("Not able to detect a wayland compositor"); 244 | 245 | let mut event_queue = conn.new_event_queue::(); 246 | let queue_handle = event_queue.handle(); 247 | 248 | let display = conn.display(); 249 | 250 | let _registry = display.get_registry(&queue_handle, ()); 251 | 252 | // Create main state for the app to store shared things. 253 | let mut state = wayland::State::new(signal_sender); 254 | 255 | event_queue 256 | .roundtrip(&mut state) 257 | .expect("Failed to cause a synchronous round trip with the wayland server"); 258 | 259 | // TODO: Make it a single threaded application. 260 | 261 | // Main loop. 262 | loop { 263 | event_queue 264 | .blocking_dispatch(&mut state) 265 | .expect("Failed to block waiting for events and dispatch them"); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | if get_option('cargo-home') == '' 2 | cargo_env = [ 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home' ] 3 | else 4 | cargo_env = [ 'CARGO_HOME=' + get_option('cargo-home') ] 5 | endif 6 | 7 | cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ] 8 | cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ] 9 | if rustc_target != '' 10 | cargo_options += [ '--target', rustc_target ] 11 | endif 12 | 13 | # When cargo-home already contain needed deps and we want to build offline. 14 | if get_option('offline-build') 15 | cargo_options += [ '--offline' ] 16 | endif 17 | 18 | if buildtype == 'plain' or buildtype == 'release' or buildtype == 'minsize' 19 | cargo_profile = 'release' 20 | cargo_options += [ '--release' ] 21 | else 22 | cargo_profile = 'debug' 23 | message('Building in debug mode') 24 | endif 25 | 26 | cargo_build = custom_target( 27 | 'cargo-build', 28 | build_by_default: true, 29 | build_always_stale: true, 30 | output: meson.project_name(), 31 | console: true, 32 | install: true, 33 | install_dir: bindir, 34 | command: [ 35 | 'env', 36 | cargo_env, 37 | cargo, 'build', 38 | cargo_options, 39 | '&&', 40 | 'cp', 'src' / rustc_target / cargo_profile / meson.project_name(), '@OUTPUT@', 41 | ] 42 | ) 43 | -------------------------------------------------------------------------------- /src/wayland.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | 3 | use log::{error, info, trace}; 4 | use wayland_client::{ 5 | Proxy, 6 | protocol::{wl_registry, wl_seat}, 7 | }; 8 | use wayland_protocols::ext::idle_notify::v1::client::{ 9 | ext_idle_notification_v1, ext_idle_notifier_v1, 10 | }; 11 | 12 | use crate::CONFIG; 13 | 14 | #[derive(Debug, Eq, PartialEq)] 15 | pub enum Signal { 16 | Idled, 17 | Resumed, 18 | } 19 | 20 | type GlobalName = u32; 21 | 22 | pub struct State { 23 | idle_notifier: Option<(GlobalName, ext_idle_notifier_v1::ExtIdleNotifierV1)>, 24 | idle_notification: Option, 25 | signal_sender: mpsc::SyncSender, 26 | } 27 | 28 | impl State { 29 | pub const fn new(signal_sender: mpsc::SyncSender) -> Self { 30 | Self { 31 | idle_notifier: None, 32 | idle_notification: None, 33 | signal_sender, 34 | } 35 | } 36 | } 37 | 38 | impl wayland_client::Dispatch for State { 39 | fn event( 40 | state: &mut Self, 41 | registry: &wl_registry::WlRegistry, 42 | event: wl_registry::Event, 43 | _data: &(), 44 | _conn: &wayland_client::Connection, 45 | queue_handle: &wayland_client::QueueHandle, 46 | ) { 47 | match event { 48 | wl_registry::Event::Global { 49 | name, 50 | interface, 51 | version, 52 | } => { 53 | match interface.as_str() { 54 | "wl_seat" => { 55 | // TODO: Support newest version of wl_seat. 56 | let wl_seat = 57 | registry.bind::(name, 1, queue_handle, ()); 58 | 59 | trace!("Binded to {}", wl_seat.id()); 60 | } 61 | "ext_idle_notifier_v1" => { 62 | let idle_notifier = registry 63 | .bind::( 64 | name, 65 | version, 66 | queue_handle, 67 | (), 68 | ); 69 | 70 | trace!("Binded to {}", idle_notifier.id()); 71 | 72 | state.idle_notifier = Some((name, idle_notifier)); 73 | } 74 | _ => {} 75 | } 76 | } 77 | wl_registry::Event::GlobalRemove { name } => { 78 | if let Some((idle_notifier_name, idle_notifier)) = &state.idle_notifier { 79 | if name == *idle_notifier_name { 80 | idle_notifier.destroy(); 81 | state.idle_notifier = None; 82 | 83 | trace!("Destroyed ext_idle_notifier_v1"); 84 | 85 | if let Some(idle_notification) = &state.idle_notification { 86 | idle_notification.destroy(); 87 | state.idle_notification = None; 88 | 89 | trace!("Destroyed ext_idle_notification_v1"); 90 | } 91 | } 92 | } 93 | } 94 | _ => {} 95 | } 96 | } 97 | } 98 | 99 | impl wayland_client::Dispatch for State { 100 | fn event( 101 | state: &mut Self, 102 | seat: &wl_seat::WlSeat, 103 | _event: wl_seat::Event, 104 | _data: &(), 105 | _conn: &wayland_client::Connection, 106 | queue_handle: &wayland_client::QueueHandle, 107 | ) { 108 | // FIX: Support multiseat configuration. 109 | if let Some((_, idle_notifier)) = &state.idle_notifier { 110 | let idle_timeout = CONFIG.timer.idle_timeout * 1000; // milliseconds 111 | 112 | if let Some(idle_notification) = &state.idle_notification { 113 | idle_notification.destroy(); 114 | state.idle_notification = None; 115 | 116 | trace!("Destroyed ext_idle_notification_v1"); 117 | } 118 | 119 | let idle_notification = if CONFIG.timer.ignore_idle_inhibitors 120 | && idle_notifier.version() 121 | >= ext_idle_notifier_v1::REQ_GET_INPUT_IDLE_NOTIFICATION_SINCE 122 | { 123 | idle_notifier.get_input_idle_notification(idle_timeout, seat, queue_handle, ()) 124 | } else { 125 | if CONFIG.timer.ignore_idle_inhibitors { 126 | error!( 127 | "Failed to ignore idle inhibitors, your wayland compositor's idle notifier does not support this feature." 128 | ); 129 | } 130 | 131 | idle_notifier.get_idle_notification(idle_timeout, seat, queue_handle, ()) 132 | }; 133 | 134 | trace!("Created {}", idle_notification.id()); 135 | 136 | state.idle_notification = Some(idle_notification); 137 | } 138 | } 139 | } 140 | 141 | impl wayland_client::Dispatch for State { 142 | fn event( 143 | _state: &mut Self, 144 | _idle_notifier: &ext_idle_notifier_v1::ExtIdleNotifierV1, 145 | _event: ext_idle_notifier_v1::Event, 146 | &(): &(), 147 | _conn: &wayland_client::Connection, 148 | _queue_handle: &wayland_client::QueueHandle, 149 | ) { 150 | // No events 151 | } 152 | } 153 | 154 | impl wayland_client::Dispatch for State { 155 | fn event( 156 | state: &mut Self, 157 | _idle_notification: &ext_idle_notification_v1::ExtIdleNotificationV1, 158 | event: ext_idle_notification_v1::Event, 159 | _data: &(), 160 | _conn: &wayland_client::Connection, 161 | _queue_handle: &wayland_client::QueueHandle, 162 | ) { 163 | match event { 164 | ext_idle_notification_v1::Event::Idled => { 165 | info!("Idled"); 166 | 167 | match state.signal_sender.try_send(Signal::Idled) { 168 | Ok(()) | Err(mpsc::TrySendError::Full(_)) => (), 169 | Err(mpsc::TrySendError::Disconnected(_)) => { 170 | panic!("Timer disconnected, `Idled` signal could not be sent") 171 | } 172 | } 173 | } 174 | ext_idle_notification_v1::Event::Resumed => { 175 | info!("Resumed"); 176 | 177 | match state.signal_sender.try_send(Signal::Resumed) { 178 | Ok(()) | Err(mpsc::TrySendError::Full(_)) => (), 179 | Err(mpsc::TrySendError::Disconnected(_)) => { 180 | panic!("Timer disconnected, `Resumed` signal could not be sent") 181 | } 182 | } 183 | } 184 | _ => {} 185 | } 186 | } 187 | } 188 | --------------------------------------------------------------------------------