Compare commits

..

No commits in common. 'master' and 'v0.8.11' have entirely different histories.

@ -22,7 +22,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
arch: [x86_64, aarch64] arch: [x86_64, aarch64]
feature: [x11, gnome, kde, wlroots] feature: [x11, gnome, kde, sway, hypr, wlroots]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
@ -48,6 +48,18 @@ jobs:
name: xremap-${{ matrix.arch }}-${{ matrix.feature }} name: xremap-${{ matrix.arch }}-${{ matrix.feature }}
path: target/${{ matrix.arch }}-unknown-linux-musl/release/xremap-linux-${{ matrix.arch }}-${{ matrix.feature }}.zip path: target/${{ matrix.arch }}-unknown-linux-musl/release/xremap-linux-${{ matrix.arch }}-${{ matrix.feature }}.zip
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: cargo fmt
run: cargo fmt -- --check
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -62,8 +74,7 @@ jobs:
key: ubuntu-latest key: ubuntu-latest
- run: cargo test - run: cargo test
# Release xremap binaries on GitHub publish:
release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- build - build
@ -76,17 +87,31 @@ jobs:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
override: true override: true
- uses: Swatinem/rust-cache@v1
with:
key: ubuntu-latest
# Release crate
- name: cargo login
run: cargo login "$CARGO_TOKEN"
env:
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
- run: cargo publish
# Fetch x86_64 binary # Fetch x86_64 binary
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-x11, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-x11, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-gnome, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-gnome, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-kde, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-kde, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-sway, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-hypr, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-wlroots, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-x86_64-wlroots, path: package/ } }
# Fetch aarch64 binary # Fetch aarch64 binary
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-x11, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-x11, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-gnome, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-gnome, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-kde, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-kde, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-sway, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-hypr, path: package/ } }
- { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-wlroots, path: package/ } } - { uses: actions/download-artifact@v3, with: { name: xremap-aarch64-wlroots, path: package/ } }
# Release binary # Release binary
@ -94,32 +119,7 @@ jobs:
run: | run: |
export VERSION=$(echo "$GITHUB_REF" | sed -e 's!refs/tags/!!') export VERSION=$(echo "$GITHUB_REF" | sed -e 's!refs/tags/!!')
curl -L "https://github.com/tcnksm/ghr/releases/download/${GHR_VERSION}/ghr_${GHR_VERSION}_linux_amd64.tar.gz" | tar xvz curl -L "https://github.com/tcnksm/ghr/releases/download/${GHR_VERSION}/ghr_${GHR_VERSION}_linux_amd64.tar.gz" | tar xvz
"ghr_${GHR_VERSION}_linux_amd64/ghr" -u xremap -r xremap -replace -n "$VERSION" "$VERSION" package/ "ghr_${GHR_VERSION}_linux_amd64/ghr" -u k0kubun -r xremap -replace -n "$VERSION" "$VERSION" package/
env: env:
GHR_VERSION: v0.16.2 GHR_VERSION: v0.14.0
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Publish xremap to crates.io
publish:
runs-on: ubuntu-latest
needs:
- build
- test
if: ${{ startsWith(github.ref, 'refs/tags/') }}
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
with:
key: ubuntu-latest
# Release crate
- name: cargo login
run: cargo login "$CARGO_TOKEN"
env:
CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
- run: cargo publish

@ -1,51 +1,3 @@
## v0.10.1
- X11: Handle WM\_CLASS reply without terminating null-byte [#525](https://github.com/xremap/xremap/pull/525)
## v0.10.0
- Drop `sway` and `hypr` clients [#479](https://github.com/xremap/xremap/pull/479)
- As recommended since v0.8.9, please use `wlroots` client instead.
## v0.9.0
- Add `enable_wheel` option in the config [#478](https://github.com/xremap/xremap/pull/478)
- Enable `REL_WHEEL` and `REL_HWHEEL` by default regardless of `--mouse` option.
[#478](https://github.com/xremap/xremap/pull/478)
- This reverts v0.8.2 and v0.8.10.
- Arch Linux users on systemd-253-1 can continue to disable them by top-level `enable_wheel: false`.
See [#260](https://github.com/xremap/xremap/pull/260) for details.
- Emit no key event instead of `KEY_UNKNOWN` on `skip_key_event` [#462](https://github.com/xremap/xremap/pull/462)
## v0.8.18
- Fix issues in the release pipeline
## v0.8.17
- Add `window` matcher for the `kde` client [#448](https://github.com/xremap/xremap/pull/448)
## v0.8.16
- Add `window` matcher for the `wlroots` client [#447](https://github.com/k0kubun/xremap/issues/447)
- Handle new KWin API for the `kde` client [#437](https://github.com/k0kubun/xremap/issues/437)
## v0.8.15
- Add `skip_key_event` option to `press`/`release` modmap action [#420](https://github.com/k0kubun/xremap/issues/420)
## v0.8.14
- Support TOML as a config file format [#404](https://github.com/k0kubun/xremap/issues/404)
## v0.8.13
- Add `shared` field for anchors and aliases [#402](https://github.com/k0kubun/xremap/issues/402)
## v0.8.12
- Add `device` filter to `modmap` and `keymap` entries [#380](https://github.com/k0kubun/xremap/issues/380)
## v0.8.11 ## v0.8.11
- Use `xremap` instead of `xremap pid=$pid` as the device name if it doesn't conflict - Use `xremap` instead of `xremap pid=$pid` as the device name if it doesn't conflict

532
Cargo.lock generated

@ -2,6 +2,21 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.0.2" version = "1.0.2"
@ -77,9 +92,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.86" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "async-io" name = "async-io"
@ -110,17 +125,43 @@ dependencies = [
"event-listener", "event-listener",
] ]
[[package]]
name = "async-trait"
version = "0.1.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -158,6 +199,12 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.80" version = "1.0.80"
@ -227,7 +274,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -251,6 +298,12 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.4" version = "0.8.4"
@ -287,7 +340,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -298,7 +351,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -323,13 +376,26 @@ dependencies = [
[[package]] [[package]]
name = "derive-where" name = "derive-where"
version = "1.2.7" version = "1.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" checksum = "146398d62142a0f35248a608f17edf0dde57338354966d6e41d0eb2d16980ccb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
]
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 1.0.109",
] ]
[[package]] [[package]]
@ -341,6 +407,12 @@ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "downcast-rs" name = "downcast-rs"
version = "1.2.0" version = "1.2.0"
@ -370,9 +442,9 @@ dependencies = [
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.2" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [ dependencies = [
"humantime", "humantime",
"is-terminal", "is-terminal",
@ -410,9 +482,9 @@ dependencies = [
[[package]] [[package]]
name = "evdev" name = "evdev"
version = "0.12.2" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab6055a93a963297befb0f4f6e18f314aec9767a4bbe88b151126df2433610a7" checksum = "2bed59fcc8cfd6b190814a509018388462d3b203cf6dd10db5c00087e72a83f3"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"cfg-if", "cfg-if",
@ -444,9 +516,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "fork" name = "fork"
version = "0.2.0" version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05dc8b302e04a1c27f4fe694439ef0f29779ca4edc205b7b58f00db04e29656d" checksum = "bf2ca97a59201425e7ee4d197c9c4fea282fe87a97d666a580bda889b95b8e88"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -528,7 +600,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -563,14 +635,20 @@ dependencies = [
[[package]] [[package]]
name = "gethostname" name = "gethostname"
version = "0.4.3" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177"
dependencies = [ dependencies = [
"libc", "libc",
"windows-targets", "winapi",
] ]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -579,9 +657,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.3" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]] [[package]]
name = "heck" name = "heck"
@ -607,6 +685,39 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyprland"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa94ae7899f3f1bdcad21dcd50366a60f323375a25610889246f276d7f9fb06"
dependencies = [
"async-trait",
"derive_more",
"doc-comment",
"futures",
"hex",
"hyprland-macros",
"lazy_static",
"num-traits",
"paste",
"regex",
"serde",
"serde_json",
"serde_repr",
"strum",
"tokio",
]
[[package]]
name = "hyprland-macros"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c941d3d52e979612af8cb94e8de49000c7fada2014a7791d173ab41339f4e4eb"
dependencies = [
"quote",
"syn 2.0.28",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.57" version = "0.1.57"
@ -649,20 +760,20 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.2.2" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.3", "hashbrown 0.14.0",
"serde", "serde",
] ]
[[package]] [[package]]
name = "indoc" name = "indoc"
version = "2.0.5" version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" checksum = "2c785eefb63ebd0e33416dfcb8d6da0bf27ce752843a45632a67bf10d4d4b5c4"
[[package]] [[package]]
name = "instant" name = "instant"
@ -712,9 +823,9 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
@ -744,11 +855,21 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -774,6 +895,26 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]] [[package]]
name = "nb-connect" name = "nb-connect"
version = "1.2.0" version = "1.2.0"
@ -833,6 +974,25 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.18.0"
@ -845,6 +1005,35 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.10" version = "0.2.10"
@ -885,7 +1074,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [ dependencies = [
"toml 0.5.11", "toml",
] ]
[[package]] [[package]]
@ -895,14 +1084,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"toml_edit 0.19.14", "toml_edit",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.78" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -918,9 +1107,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.35" version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -931,11 +1120,20 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.5" version = "1.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -945,9 +1143,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.5" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -956,9 +1154,24 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.2" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
@ -987,6 +1200,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.15" version = "1.0.15"
@ -999,34 +1218,45 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.121" version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
@ -1039,31 +1269,21 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
]
[[package]]
name = "serde_spanned"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
dependencies = [
"serde",
] ]
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.8.1" version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237"
dependencies = [ dependencies = [
"base64", "base64",
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.2.2", "indexmap 2.0.0",
"serde", "serde",
"serde_derive",
"serde_json", "serde_json",
"serde_with_macros", "serde_with_macros",
"time", "time",
@ -1071,29 +1291,38 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.8.1" version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.9.34+deprecated" version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [ dependencies = [
"indexmap 2.2.2", "indexmap 2.0.0",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
"unsafe-libyaml", "unsafe-libyaml",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.8" version = "0.4.8"
@ -1131,6 +1360,50 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.28",
]
[[package]]
name = "swayipc"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab1dcff328b223d85d7ca767b2e4aadbc13dad550d36b4c6b929b9ad4d26ee9a"
dependencies = [
"serde",
"serde_json",
"swayipc-types",
]
[[package]]
name = "swayipc-types"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44b43b4059d825ccc04adf9726f944d0e3aa20938f4cff3b5c6b53198afcd6b3"
dependencies = [
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -1144,9 +1417,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.48" version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1185,7 +1458,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
] ]
[[package]] [[package]]
@ -1217,57 +1490,60 @@ dependencies = [
] ]
[[package]] [[package]]
name = "toml" name = "tokio"
version = "0.5.11" version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [ dependencies = [
"serde", "autocfg",
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
] ]
[[package]] [[package]]
name = "toml" name = "tokio-macros"
version = "0.8.15" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [ dependencies = [
"serde", "proc-macro2",
"serde_spanned", "quote",
"toml_datetime", "syn 2.0.28",
"toml_edit 0.22.16",
] ]
[[package]] [[package]]
name = "toml_datetime" name = "toml"
version = "0.6.6" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_datetime"
version = "0.19.14" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
dependencies = [
"indexmap 2.2.2",
"toml_datetime",
"winnow 0.5.3",
]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.16" version = "0.19.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
dependencies = [ dependencies = [
"indexmap 2.2.2", "indexmap 2.0.0",
"serde",
"serde_spanned",
"toml_datetime", "toml_datetime",
"winnow 0.6.5", "winnow",
] ]
[[package]] [[package]]
@ -1278,9 +1554,9 @@ checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
version = "0.2.11" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@ -1294,6 +1570,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.87" version = "0.2.87"
@ -1315,7 +1597,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1337,7 +1619,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.28",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1447,6 +1729,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "winapi-wsapoll"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -1537,15 +1828,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.5.1" version = "0.5.1"
@ -1557,24 +1839,29 @@ dependencies = [
[[package]] [[package]]
name = "x11rb" name = "x11rb"
version = "0.13.1" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
dependencies = [ dependencies = [
"gethostname", "gethostname",
"rustix 0.38.4", "nix 0.26.2",
"winapi",
"winapi-wsapoll",
"x11rb-protocol", "x11rb-protocol",
] ]
[[package]] [[package]]
name = "x11rb-protocol" name = "x11rb-protocol"
version = "0.13.1" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc"
dependencies = [
"nix 0.26.2",
]
[[package]] [[package]]
name = "xremap" name = "xremap"
version = "0.10.1" version = "0.8.11"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1583,6 +1870,7 @@ dependencies = [
"env_logger", "env_logger",
"evdev", "evdev",
"fork", "fork",
"hyprland",
"indoc", "indoc",
"lazy_static", "lazy_static",
"log", "log",
@ -1592,7 +1880,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_with", "serde_with",
"serde_yaml", "serde_yaml",
"toml 0.8.15", "swayipc",
"wayland-client", "wayland-client",
"wayland-protocols-wlr", "wayland-protocols-wlr",
"x11rb", "x11rb",

@ -1,6 +1,6 @@
[package] [package]
name = "xremap" name = "xremap"
version = "0.10.1" version = "0.8.11"
edition = "2021" edition = "2021"
description = "Dynamic key remapp for X and Wayland" description = "Dynamic key remapp for X and Wayland"
license = "MIT" license = "MIT"
@ -8,30 +8,33 @@ repository = "https://github.com/k0kubun/xremap"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.75"
clap = { version = "4.3.19", features = ["derive"] } clap = { version = "4.3.19", features = ["derive"] }
clap_complete = "4.3.2" clap_complete = "4.3.2"
derive-where = "1.2.7" derive-where = "1.2.5"
env_logger = "0.10.2" env_logger = "0.10.0"
evdev = "0.12.2" evdev = "0.12.1"
fork = "0.2" fork = "0.1"
indoc = "2.0" indoc = "2.0"
lazy_static = "1.5.0" lazy_static = "1.4.0"
log = "0.4.22" log = "0.4.19"
nix = "0.26.2" nix = "0.26.2"
regex = "1.10.5" regex = "1.9.6"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
serde_with = { version = "3.8", features = ["chrono"] } serde_with = { version = "3.3", features = ["chrono"] }
serde_yaml = "0.9" serde_yaml = "0.9"
swayipc = { version = "3.0.1", optional = true }
wayland-client = { version = "0.30", optional = true } wayland-client = { version = "0.30", optional = true }
wayland-protocols-wlr = { version = "0.1", features = ["client"], optional = true } wayland-protocols-wlr = { version = "0.1", features = ["client"], optional = true }
x11rb = { version = "0.13.1", optional = true } x11rb = { version = "0.12.0", optional = true }
zbus = { version = "1.9.2", optional = true } zbus = { version = "1.9.2", optional = true }
toml = "0.8.15" hyprland = { version = "0.3.12", optional = true }
[features] [features]
gnome = ["zbus"] gnome = ["zbus"]
sway = ["swayipc"]
x11 = ["x11rb"] x11 = ["x11rb"]
hypr = ["hyprland"]
kde = ["zbus"] kde = ["zbus"]
wlroots = ["wayland-client", "wayland-protocols-wlr"] wlroots = ["wayland-client", "wayland-protocols-wlr"]

@ -20,7 +20,6 @@
* Remap a key sequence as well. You could do something like Emacs's `C-x C-c`. * Remap a key sequence as well. You could do something like Emacs's `C-x C-c`.
* Remap a key to two different keys depending on whether it's pressed alone or held. * Remap a key to two different keys depending on whether it's pressed alone or held.
* Application-specific remapping. Even if it's not supported by your application, xremap can. * Application-specific remapping. Even if it's not supported by your application, xremap can.
* Device-specific remapping.
* Automatically remap newly connected devices by starting xremap with `--watch`. * Automatically remap newly connected devices by starting xremap with `--watch`.
* Support [Emacs-like key remapping](example/emacs.yml), including the mark mode. * Support [Emacs-like key remapping](example/emacs.yml), including the mark mode.
* Trigger commands on key press/release events. * Trigger commands on key press/release events.
@ -55,15 +54,12 @@ If you are using NixOS, xremap can be installed and configured through a [flake]
Write [a config file](#Configuration) directly, or generate it with Write [a config file](#Configuration) directly, or generate it with
[xremap-ruby](https://github.com/xremap/xremap-ruby) or [xremap-python](https://github.com/xremap/xremap-python). [xremap-ruby](https://github.com/xremap/xremap-ruby) or [xremap-python](https://github.com/xremap/xremap-python).
Then run:
Then start the `xremap` daemon by running:
``` ```
sudo xremap config.yml sudo xremap config.yml
``` ```
(You will need to leave it running for your mappings to take effect.)
<details> <details>
<summary>If you want to run xremap without sudo, click here.</summary> <summary>If you want to run xremap without sudo, click here.</summary>
@ -196,40 +192,23 @@ modmap:
KEY_XXX: KEY_YYY # Required KEY_XXX: KEY_YYY # Required
# Dispatch different keys depending on whether you hold it or press it alone # Dispatch different keys depending on whether you hold it or press it alone
KEY_XXX: KEY_XXX:
held: KEY_YYY # Required, also accepts arrays held: [KEY_YYY] # Required
alone: KEY_ZZZ # Required, also accepts arrays alone: [KEY_ZZZ] # Required
alone_timeout_millis: 1000 # Optional alone_timeout_millis: 1000 # Optional
# Hook `keymap` action on key press/release events. # Hook `keymap` action on key press/release events.
KEY_XXX: KEY_XXX:
skip_key_event: false # Optional, skip original key event ,defaults to false
press: { launch: ["xdotool", "mousemove", "0", "7200"] } # Required press: { launch: ["xdotool", "mousemove", "0", "7200"] } # Required
release: { launch: ["xdotool", "mousemove", "0", "0"] } # Required release: { launch: ["xdotool", "mousemove", "0", "0"] } # Required
application: # Optional application: # Optional
not: [Application, ...] not: [Application, ...]
# or # or
only: [Application, ...] only: [Application, ...]
window: # Optional (only wlroots/kde clients supported)
not: [/regex of window title/, ...]
# or
only: [/regex of window title/, ...]
device: # Optional
not: [Device, ...]
# or
only: [Device, ...]
``` ```
For `KEY_XXX` and `KEY_YYY`, use [these names](https://github.com/emberian/evdev/blob/1d020f11b283b0648427a2844b6b980f1a268221/src/scancodes.rs#L26-L572). For `KEY_XXX` and `KEY_YYY`, use [these names](https://github.com/emberian/evdev/blob/1d020f11b283b0648427a2844b6b980f1a268221/src/scancodes.rs#L26-L572).
You can skip `KEY_` and the name is case-insensitive. So `KEY_CAPSLOCK`, `CAPSLOCK`, and `CapsLock` are the same thing. You can skip `KEY_` and the name is case-insensitive. So `KEY_CAPSLOCK`, `CAPSLOCK`, and `CapsLock` are the same thing.
Some [custom aliases](src/config/key.rs) like `SHIFT_R`, `CONTROL_L`, etc. are provided. Some [custom aliases](src/config/key.rs) like `SHIFT_R`, `CONTROL_L`, etc. are provided.
In case you don't know the name of a key, you can find out by enabling the xremap debug output:
```bash
RUST_LOG=debug xremap config.yml
# or
sudo RUST_LOG=debug xremap config.yml
```
Then press the key you want to know the name of.
If you specify a map containing `held` and `alone`, you can use the key for two purposes. If you specify a map containing `held` and `alone`, you can use the key for two purposes.
The key is considered `alone` if it's pressed and released within `alone_timeout_millis` (default: 1000) The key is considered `alone` if it's pressed and released within `alone_timeout_millis` (default: 1000)
before any other key is pressed. Otherwise it's considered `held`. before any other key is pressed. Otherwise it's considered `held`.
@ -266,14 +245,6 @@ keymap:
not: [Application, ...] not: [Application, ...]
# or # or
only: [Application, ...] only: [Application, ...]
window: # Optional (only wlroots/kde clients supported)
not: [/regex of window title/, ...]
# or
only: [/regex of window title/, ...]
device: # Optional
not: [Device, ...]
# or
only: [Device, ...]
mode: default # Optional mode: default # Optional
default_mode: default # Optional default_mode: default # Optional
``` ```
@ -344,7 +315,7 @@ However, it will only start printing, once a mapping has been triggered that use
So you have to create a mapping with a filter using a dummy application name and trigger it. So you have to create a mapping with a filter using a dummy application name and trigger it.
Then each time you switch to a new window xremap will print its caption, class, and name in the following style: Then each time you switch to a new window xremap will print its caption, class, and name in the following style:
`active window: caption: '<caption>', class: '<class>', name: '<name>'` `active window: caption: '<caption>', class: '<class>', name: '<name>'`
The `class` property should be used for application matching, while the `caption` property should be used for window matching. You want to use the class for the filter.
If you use a systemd-daemon to manage xremap, the prints will be visible in the system-logs (Can be opened with `journalctl -f`) If you use a systemd-daemon to manage xremap, the prints will be visible in the system-logs (Can be opened with `journalctl -f`)
@ -376,31 +347,6 @@ keymap:
Note how Alt-f and Alt-b work in all apps, but the definition of Alt-f is slightly different in LibreOffice Writer. When that app is active, the first definition overrides the second definition; but for any other app, only the second definition is found. This is because xremap uses the first matching definition that it finds. Note how Alt-f and Alt-b work in all apps, but the definition of Alt-f is slightly different in LibreOffice Writer. When that app is active, the first definition overrides the second definition; but for any other app, only the second definition is found. This is because xremap uses the first matching definition that it finds.
### device
Much like [`application`](#application), you may specify `{keymap,modmap}.device.{not,only}` in your configuration for device-specific remapping. Consistent with the global `--device` flag, device-matching strings may be any of:
- the full path of the device
- the filename of the device
- the device name
- a substring of the device name
To determine the names and paths of your devices, examine `xremap`'s log output at startup.
```yml
device:
not: '/dev/input/event0'
# or
not: ['event0', ...]
# or
only: 'Some Cool Device Name'
# or
only: ['Cool Device', ...]
# etc...
```
Unlike for `application`, regexs are not supported for `device`.
### virtual\_modifiers ### virtual\_modifiers
You can declare keys that should act like a modifier. You can declare keys that should act like a modifier.
@ -422,35 +368,6 @@ Some applications have trouble understanding synthesized key events, especially
Wayland. `keypress_delay_ms` can be used to workaround the issue. Wayland. `keypress_delay_ms` can be used to workaround the issue.
See [#179](https://github.com/k0kubun/xremap/issues/179) for the detail. See [#179](https://github.com/k0kubun/xremap/issues/179) for the detail.
### Shared data field
You can declare data that does not directly go into the config under the `shared` field.
This can be usefull when using Anchors and Aliases.
For more information about the use of Yaml anchors see the [Yaml specification](https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases).
#### example:
```yaml
shared:
terminals: &terminals # The & Symbol marks this entry as a Anchor
- Gnome-terminal
- Kitty
some_remaps: &some_remaps
Ctrl-f: C-right
Alt-b: C-up
keymap:
- application:
only: *terminals # we can reuse the list here
remap: *some_remaps # and we can reuse a map here.
```
## Maintainers
- @k0kubun
- @N4tus (KDE client)
- @jixiuf (wlroots client)
## License ## License
`xremap` is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). `xremap` is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

@ -1,8 +1,6 @@
# Credit: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/example/config.py#L62-L125 # Credit: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/example/config.py#L62-L125
keymap: keymap:
- name: Emacs - name: Emacs
application:
not: [Emacs]
remap: remap:
# Cursor # Cursor
C-b: { with_mark: left } C-b: { with_mark: left }
@ -34,17 +32,13 @@ keymap:
M-d: [C-delete, { set_mark: false }] M-d: [C-delete, { set_mark: false }]
# Kill line # Kill line
C-k: [Shift-end, C-x, { set_mark: false }] C-k: [Shift-end, C-x, { set_mark: false }]
# Kill word backward
Alt-backspace: [C-backspace, {set_mark: false}]
# set mark next word continuously.
C-M-space: [C-Shift-right, {set_mark: true}]
# Undo # Undo
C-slash: [C-z, { set_mark: false }] C-slash: [C-z, { set_mark: false }]
C-Shift-ro: C-z C-Shift-ro: C-z
# Mark # Mark
C-space: { set_mark: true } C-space: { set_mark: true }
# Search # Search
C-s: C-f C-s: F3
C-r: Shift-F3 C-r: Shift-F3
M-Shift-5: C-h M-Shift-5: C-h
# Cancel # Cancel

@ -24,10 +24,6 @@ impl Client for GnomeClient {
self.connect(); self.connect();
self.current_application().is_some() self.current_application().is_some()
} }
fn current_window(&mut self) -> Option<String> {
// TODO: not implemented
None
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
self.connect(); self.connect();

@ -0,0 +1,24 @@
use crate::client::Client;
use hyprland::{data::Client as HyprClient, prelude::*};
pub struct HyprlandClient;
impl HyprlandClient {
pub fn new() -> HyprlandClient {
HyprlandClient {}
}
}
impl Client for HyprlandClient {
fn supported(&mut self) -> bool {
true
}
fn current_application(&mut self) -> Option<String> {
if let Ok(win_opt) = HyprClient::get_active() {
if let Some(win) = win_opt {
return Some(win.class);
}
}
None
}
}

@ -29,14 +29,14 @@ impl Drop for KwinScriptTempFile {
} }
trait KWinScripting { trait KWinScripting {
fn load_script(&self, path: &Path) -> Result<i32, ConnectionError>; fn load_script(&self, path: &Path) -> Result<String, ConnectionError>;
fn unload_script(&self) -> Result<bool, ConnectionError>; fn unload_script(&self) -> Result<bool, ConnectionError>;
fn start_script(&self, script_obj_id: i32) -> Result<(), ConnectionError>; fn start_script(&self, script_obj_path: &str) -> Result<(), ConnectionError>;
fn is_script_loaded(&self) -> Result<bool, ConnectionError>; fn is_script_loaded(&self) -> Result<bool, ConnectionError>;
} }
impl KWinScripting for Connection { impl KWinScripting for Connection {
fn load_script(&self, path: &Path) -> Result<i32, ConnectionError> { fn load_script(&self, path: &Path) -> Result<String, ConnectionError> {
self.call_method( self.call_method(
Some("org.kde.KWin"), Some("org.kde.KWin"),
"/Scripting", "/Scripting",
@ -48,6 +48,7 @@ impl KWinScripting for Connection {
.map_err(|_| ConnectionError::LoadScriptCall)? .map_err(|_| ConnectionError::LoadScriptCall)?
.body::<i32>() .body::<i32>()
.map_err(|_| ConnectionError::InvalidLoadScriptResult) .map_err(|_| ConnectionError::InvalidLoadScriptResult)
.map(|obj_path| format!("/{obj_path}"))
} }
fn unload_script(&self) -> Result<bool, ConnectionError> { fn unload_script(&self) -> Result<bool, ConnectionError> {
@ -64,22 +65,10 @@ impl KWinScripting for Connection {
.map_err(|_| ConnectionError::InvalidUnloadScriptResult) .map_err(|_| ConnectionError::InvalidUnloadScriptResult)
} }
fn start_script(&self, script_obj_id: i32) -> Result<(), ConnectionError> { fn start_script(&self, script_obj_path: &str) -> Result<(), ConnectionError> {
for script_obj_path_fn in [|id| format!("/{id}"), |id| format!("/Scripting/Script{id}")] { self.call_method(Some("org.kde.KWin"), script_obj_path, Some("org.kde.kwin.Script"), "run", &())
if self .map_err(|_| ConnectionError::StartScriptCall)
.call_method( .map(|_| ())
Some("org.kde.KWin"),
script_obj_path_fn(script_obj_id).as_str(),
Some("org.kde.kwin.Script"),
"run",
&(),
)
.is_ok()
{
return Ok(());
}
}
Err(ConnectionError::StartScriptCall)
} }
fn is_script_loaded(&self) -> Result<bool, ConnectionError> { fn is_script_loaded(&self) -> Result<bool, ConnectionError> {
@ -102,8 +91,8 @@ fn load_kwin_script() -> Result<(), ConnectionError> {
let init_script = || { let init_script = || {
let temp_file_path = KwinScriptTempFile::new(); let temp_file_path = KwinScriptTempFile::new();
std::fs::write(&temp_file_path.0, KWIN_SCRIPT).map_err(|_| ConnectionError::WriteScriptToTempFile)?; std::fs::write(&temp_file_path.0, KWIN_SCRIPT).map_err(|_| ConnectionError::WriteScriptToTempFile)?;
let script_obj_id = dbus.load_script(&temp_file_path.0)?; let script_obj_path = dbus.load_script(&temp_file_path.0)?;
dbus.start_script(script_obj_id)?; dbus.start_script(&script_obj_path)?;
Ok(()) Ok(())
}; };
if let Err(err) = init_script() { if let Err(err) = init_script() {
@ -173,13 +162,9 @@ impl Client for KdeClient {
} }
conn_res.is_ok() conn_res.is_ok()
} }
fn current_window(&mut self) -> Option<String> {
let aw = self.active_window.lock().ok()?;
Some(aw.title.clone())
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
let aw = self.active_window.lock().ok()?; let aw = self.active_window.lock().unwrap();
Some(aw.res_class.clone()) Some(aw.res_class.clone())
} }
} }

@ -1,4 +1,4 @@
function notifyActiveWindow(client) { workspace.clientActivated.connect(function(client){
callDBus( callDBus(
"com.k0kubun.Xremap", "com.k0kubun.Xremap",
"/com/k0kubun/Xremap", "/com/k0kubun/Xremap",
@ -8,12 +8,4 @@ function notifyActiveWindow(client) {
"resourceClass" in client ? client.resourceClass : "", "resourceClass" in client ? client.resourceClass : "",
"resourceName" in client ? client.resourceName : "" "resourceName" in client ? client.resourceName : ""
); );
} });
if (workspace.windowList) {
// kde 6
workspace.windowActivated.connect(notifyActiveWindow);
} else {
// kde 5
workspace.clientActivated.connect(notifyActiveWindow);
}

@ -1,7 +1,6 @@
pub trait Client { pub trait Client {
fn supported(&mut self) -> bool; fn supported(&mut self) -> bool;
fn current_application(&mut self) -> Option<String>; fn current_application(&mut self) -> Option<String>;
fn current_window(&mut self) -> Option<String>;
} }
pub struct WMClient { pub struct WMClient {
@ -9,7 +8,6 @@ pub struct WMClient {
client: Box<dyn Client>, client: Box<dyn Client>,
supported: Option<bool>, supported: Option<bool>,
last_application: String, last_application: String,
last_window: String,
} }
impl WMClient { impl WMClient {
@ -19,28 +17,8 @@ impl WMClient {
client, client,
supported: None, supported: None,
last_application: String::new(), last_application: String::new(),
last_window: String::new(),
} }
} }
pub fn current_window(&mut self) -> Option<String> {
if self.supported.is_none() {
let supported = self.client.supported();
self.supported = Some(supported);
println!("application-client: {} (supported: {})", self.name, supported);
}
if !self.supported.unwrap() {
return None;
}
let result = self.client.current_window();
if let Some(window) = &result {
if &self.last_window != window {
self.last_window = window.clone();
println!("window: {}", window);
}
}
result
}
pub fn current_application(&mut self) -> Option<String> { pub fn current_application(&mut self) -> Option<String> {
if self.supported.is_none() { if self.supported.is_none() {
@ -77,6 +55,20 @@ pub fn build_client() -> WMClient {
WMClient::new("KDE", Box::new(kde_client::KdeClient::new())) WMClient::new("KDE", Box::new(kde_client::KdeClient::new()))
} }
#[cfg(feature = "sway")]
mod sway_client;
#[cfg(feature = "sway")]
pub fn build_client() -> WMClient {
WMClient::new("Sway", Box::new(sway_client::SwayClient::new()))
}
#[cfg(feature = "hypr")]
mod hypr_client;
#[cfg(feature = "hypr")]
pub fn build_client() -> WMClient {
WMClient::new("Hypr", Box::new(hypr_client::HyprlandClient::new()))
}
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
mod x11_client; mod x11_client;
#[cfg(feature = "x11")] #[cfg(feature = "x11")]

@ -6,9 +6,6 @@ impl Client for NullClient {
fn supported(&mut self) -> bool { fn supported(&mut self) -> bool {
false false
} }
fn current_window(&mut self) -> Option<String> {
None
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
None None

@ -0,0 +1,80 @@
use crate::client::Client;
use std::env;
use std::fs::read_dir;
use std::os::unix::ffi::OsStrExt;
use swayipc::Connection;
pub struct SwayClient {
connection: Option<Connection>,
}
impl SwayClient {
pub fn new() -> SwayClient {
SwayClient { connection: None }
}
fn connect(&mut self) {
if let None = self.connection {
if let Err(env::VarError::NotPresent) = env::var("SWAYSOCK") {
let path = match find_socket() {
Some(path) => path,
None => {
println!("Failed to locate a SWAYSOCK from /run/user/1000/sway-ipc.*");
return;
}
};
println!("$SWAYSOCK is not set. Defaulting to \"{}\"", path);
env::set_var("SWAYSOCK", path);
}
match Connection::new() {
Ok(connection) => self.connection = Some(connection),
Err(e) => println!("SwayClient#connect() failed: {}", e),
}
}
}
}
impl Client for SwayClient {
fn supported(&mut self) -> bool {
self.connect();
self.connection.is_some()
}
fn current_application(&mut self) -> Option<String> {
self.connect();
let connection = match &mut self.connection {
Some(connection) => connection,
None => return None,
};
if let Ok(node) = connection.get_tree() {
if let Some(node) = node.find_focused(|n| n.focused) {
if node.app_id.is_some() {
return node.app_id;
} else if let Some(wp) = node.window_properties {
return wp.class;
}
}
}
None
}
}
// e.g. "/run/user/1000/sway-ipc.1000.2575.sock"
fn find_socket() -> Option<String> {
let uid = 1000; // Assume a first nornal Linux user. TODO: Make it configurable
if let Some(run_user) = read_dir(format!("/run/user/{}", uid)).as_mut().ok() {
while let Some(entry) = run_user.next() {
let path = entry.ok()?.path();
if let Some(fname) = path.file_name() {
if fname.as_bytes().starts_with(b"sway-ipc.") {
if let Ok(path) = path.into_os_string().into_string() {
return Some(path);
}
}
}
}
}
None
}

@ -20,7 +20,6 @@ use crate::client::Client;
struct State { struct State {
active_window: Option<ObjectId>, active_window: Option<ObjectId>,
windows: HashMap<ObjectId, String>, windows: HashMap<ObjectId, String>,
titles: HashMap<ObjectId, String>,
} }
#[derive(Default)] #[derive(Default)]
@ -60,22 +59,6 @@ impl Client for WlRootsClient {
} }
} }
} }
fn current_window(&mut self) -> Option<String> {
let queue = self.queue.as_mut()?;
if let Err(_) = queue.roundtrip(&mut self.state) {
// try to reconnect
if let Err(err) = self.connect() {
log::error!("{err}");
return None;
}
log::debug!("Reconnected to wayland");
}
let id = self.state.active_window.as_ref()?;
self.state.titles.get(id).cloned()
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
let queue = self.queue.as_mut()?; let queue = self.queue.as_mut()?;
@ -119,7 +102,6 @@ impl Dispatch<ZwlrForeignToplevelManagerV1, ()> for State {
) { ) {
if let ManagerEvent::Toplevel { toplevel } = event { if let ManagerEvent::Toplevel { toplevel } = event {
state.windows.insert(toplevel.id(), "<unknown>".into()); state.windows.insert(toplevel.id(), "<unknown>".into());
state.titles.insert(toplevel.id(), "<unknown>".into());
} }
} }
@ -141,12 +123,8 @@ impl Dispatch<ZwlrForeignToplevelHandleV1, ()> for State {
HandleEvent::AppId { app_id } => { HandleEvent::AppId { app_id } => {
state.windows.insert(handle.id(), app_id); state.windows.insert(handle.id(), app_id);
} }
HandleEvent::Title { title } => {
state.titles.insert(handle.id(), title);
}
HandleEvent::Closed => { HandleEvent::Closed => {
state.windows.remove(&handle.id()); state.windows.remove(&handle.id());
state.titles.remove(&handle.id());
} }
HandleEvent::State { state: handle_state } => { HandleEvent::State { state: handle_state } => {
let activated = HandleState::Activated as u8; let activated = HandleState::Activated as u8;

@ -48,10 +48,6 @@ impl Client for X11Client {
return self.connection.is_some(); return self.connection.is_some();
// TODO: Test XGetInputFocus and focused_window > 0? // TODO: Test XGetInputFocus and focused_window > 0?
} }
fn current_window(&mut self) -> Option<String> {
// TODO: not implemented
None
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
self.connect(); self.connect();
@ -94,8 +90,8 @@ fn get_wm_class(client: &mut X11Client, window: Window) -> Option<String> {
if let Some(delimiter) = reply.value.iter().position(|byte| *byte == '\0' as u8) { if let Some(delimiter) = reply.value.iter().position(|byte| *byte == '\0' as u8) {
if let Ok(prefix) = String::from_utf8(reply.value[..delimiter].to_vec()) { if let Ok(prefix) = String::from_utf8(reply.value[..delimiter].to_vec()) {
let name = reply.value[(delimiter + 1)..].to_vec(); let name = reply.value[(delimiter + 1)..].to_vec();
if let Some(end) = name.iter().position(|byte| *byte == '\0' as u8).or(Some(name.len())) { if let Some(end) = name.iter().position(|byte| *byte == '\0' as u8) {
if end == name.len() - 1 || end == name.len() { if end == name.len() - 1 {
if let Ok(name) = String::from_utf8(name[..end].to_vec()) { if let Ok(name) = String::from_utf8(name[..end].to_vec()) {
return Some(format!("{prefix}.{name}")); return Some(format!("{prefix}.{name}"));
} }

@ -7,7 +7,7 @@ use serde::{Deserialize, Deserializer};
// TODO: Use trait to allow only either `only` or `not` // TODO: Use trait to allow only either `only` or `not`
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct OnlyOrNot { pub struct Application {
#[serde(default, deserialize_with = "deserialize_matchers")] #[serde(default, deserialize_with = "deserialize_matchers")]
pub only: Option<Vec<ApplicationMatcher>>, pub only: Option<Vec<ApplicationMatcher>>,
#[serde(default, deserialize_with = "deserialize_matchers")] #[serde(default, deserialize_with = "deserialize_matchers")]

@ -1,12 +0,0 @@
use crate::config::application::deserialize_string_or_vec;
use serde::Deserialize;
// TODO: Use trait to allow only either `only` or `not`
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Device {
#[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub only: Option<Vec<String>>,
#[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub not: Option<Vec<String>>,
}

@ -1,12 +1,11 @@
use crate::config::application::deserialize_string_or_vec; use crate::config::application::deserialize_string_or_vec;
use crate::config::application::OnlyOrNot; use crate::config::application::Application;
use crate::config::key_press::KeyPress; use crate::config::key_press::KeyPress;
use crate::config::keymap_action::{Actions, KeymapAction}; use crate::config::keymap_action::{Actions, KeymapAction};
use evdev::Key; use evdev::Key;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
use super::device::Device;
use super::key_press::Modifier; use super::key_press::Modifier;
// Config interface // Config interface
@ -17,9 +16,7 @@ pub struct Keymap {
pub name: String, pub name: String,
#[serde(deserialize_with = "deserialize_remap")] #[serde(deserialize_with = "deserialize_remap")]
pub remap: HashMap<KeyPress, Vec<KeymapAction>>, pub remap: HashMap<KeyPress, Vec<KeymapAction>>,
pub application: Option<OnlyOrNot>, pub application: Option<Application>,
pub window: Option<OnlyOrNot>,
pub device: Option<Device>,
#[serde(default, deserialize_with = "deserialize_string_or_vec")] #[serde(default, deserialize_with = "deserialize_string_or_vec")]
pub mode: Option<Vec<String>>, pub mode: Option<Vec<String>>,
#[serde(default)] #[serde(default)]
@ -42,9 +39,7 @@ where
pub struct KeymapEntry { pub struct KeymapEntry {
pub actions: Vec<KeymapAction>, pub actions: Vec<KeymapAction>,
pub modifiers: Vec<Modifier>, pub modifiers: Vec<Modifier>,
pub application: Option<OnlyOrNot>, pub application: Option<Application>,
pub title: Option<OnlyOrNot>,
pub device: Option<Device>,
pub mode: Option<Vec<String>>, pub mode: Option<Vec<String>>,
pub exact_match: bool, pub exact_match: bool,
} }
@ -67,8 +62,6 @@ pub fn build_keymap_table(keymaps: &Vec<Keymap>) -> HashMap<Key, Vec<KeymapEntry
actions: actions.to_vec(), actions: actions.to_vec(),
modifiers: key_press.modifiers.clone(), modifiers: key_press.modifiers.clone(),
application: keymap.application.clone(), application: keymap.application.clone(),
title: keymap.window.clone(),
device: keymap.device.clone(),
mode: keymap.mode.clone(), mode: keymap.mode.clone(),
exact_match: keymap.exact_match, exact_match: keymap.exact_match,
}); });

@ -1,5 +1,4 @@
pub mod application; pub mod application;
pub mod device;
mod key; mod key;
pub mod key_press; pub mod key_press;
pub mod keymap; pub mod keymap;
@ -12,13 +11,12 @@ pub mod remap;
mod tests; mod tests;
extern crate serde_yaml; extern crate serde_yaml;
extern crate toml;
use evdev::Key; use evdev::Key;
use keymap::Keymap; use keymap::Keymap;
use modmap::Modmap; use modmap::Modmap;
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify}; use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use serde::{de::IgnoredAny, Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::{collections::HashMap, error, fs, path::PathBuf, time::SystemTime}; use std::{collections::HashMap, error, fs, path::PathBuf, time::SystemTime};
use self::{ use self::{
@ -41,54 +39,21 @@ pub struct Config {
#[serde(default)] #[serde(default)]
pub keypress_delay_ms: u64, pub keypress_delay_ms: u64,
// Data is not used by any part of the application.
// but can be used with Anchors and Aliases
#[serde(default)]
pub shared: IgnoredAny,
// Internals // Internals
#[serde(skip)] #[serde(skip)]
pub modify_time: Option<SystemTime>, pub modify_time: Option<SystemTime>,
#[serde(skip)] #[serde(skip)]
pub keymap_table: HashMap<Key, Vec<KeymapEntry>>, pub keymap_table: HashMap<Key, Vec<KeymapEntry>>,
#[serde(default = "const_true")]
pub enable_wheel: bool,
}
enum ConfigFiletype {
Yaml,
Toml,
}
fn get_file_ext(filename: &PathBuf) -> ConfigFiletype {
match filename.extension() {
Some(f) => {
if f.to_str().unwrap_or("").to_lowercase() == "toml" {
ConfigFiletype::Toml
} else {
ConfigFiletype::Yaml
}
}
_ => ConfigFiletype::Yaml,
}
} }
pub fn load_configs(filenames: &Vec<PathBuf>) -> Result<Config, Box<dyn error::Error>> { pub fn load_configs(filenames: &Vec<PathBuf>) -> Result<Config, Box<dyn error::Error>> {
// Assumes filenames is non-empty // Assumes filenames is non-empty
let config_contents = fs::read_to_string(&filenames[0])?; let yaml = fs::read_to_string(&filenames[0])?;
let mut config: Config = serde_yaml::from_str(&yaml)?;
let mut config: Config = match get_file_ext(&filenames[0]) {
ConfigFiletype::Yaml => serde_yaml::from_str(&config_contents)?,
ConfigFiletype::Toml => toml::from_str(&config_contents)?,
};
for filename in &filenames[1..] { for filename in &filenames[1..] {
let config_contents = fs::read_to_string(&filename)?; let yaml = fs::read_to_string(&filename)?;
let c: Config = match get_file_ext(&filename) { let c: Config = serde_yaml::from_str(&yaml)?;
ConfigFiletype::Yaml => serde_yaml::from_str(&config_contents)?,
ConfigFiletype::Toml => serde_yaml::from_str(&config_contents)?,
};
config.modmap.extend(c.modmap); config.modmap.extend(c.modmap);
config.keymap.extend(c.keymap); config.keymap.extend(c.keymap);
config.virtual_modifiers.extend(c.virtual_modifiers); config.virtual_modifiers.extend(c.virtual_modifiers);
@ -134,7 +99,3 @@ where
} }
return Ok(keys); return Ok(keys);
} }
fn const_true() -> bool {
true
}

@ -1,12 +1,10 @@
use crate::config::application::OnlyOrNot; use crate::config::application::Application;
use crate::config::key::deserialize_key; use crate::config::key::deserialize_key;
use crate::config::modmap_action::ModmapAction; use crate::config::modmap_action::ModmapAction;
use evdev::Key; use evdev::Key;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
use super::device::Device;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Modmap { pub struct Modmap {
@ -14,9 +12,7 @@ pub struct Modmap {
pub name: String, pub name: String,
#[serde(deserialize_with = "deserialize_remap")] #[serde(deserialize_with = "deserialize_remap")]
pub remap: HashMap<Key, ModmapAction>, pub remap: HashMap<Key, ModmapAction>,
pub application: Option<OnlyOrNot>, pub application: Option<Application>,
pub window: Option<OnlyOrNot>,
pub device: Option<Device>,
} }
fn deserialize_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, ModmapAction>, D::Error> fn deserialize_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, ModmapAction>, D::Error>

@ -31,8 +31,6 @@ pub struct MultiPurposeKey {
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub struct PressReleaseKey { pub struct PressReleaseKey {
#[serde(default)]
pub skip_key_event: bool ,
#[serde(deserialize_with = "deserialize_actions")] #[serde(deserialize_with = "deserialize_actions")]
pub press: Vec<KeymapAction>, pub press: Vec<KeymapAction>,
#[serde(deserialize_with = "deserialize_actions")] #[serde(deserialize_with = "deserialize_actions")]

@ -1,12 +1,10 @@
use crate::Config; use crate::Config;
use indoc::indoc; use indoc::indoc;
use serde_yaml::Error;
extern crate serde_yaml;
extern crate toml;
#[test] #[test]
fn test_yaml_modmap_basic() { fn test_modmap_basic() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
modmap: modmap:
- name: Global - name: Global
remap: remap:
@ -19,8 +17,8 @@ fn test_yaml_modmap_basic() {
} }
#[test] #[test]
fn test_yaml_modmap_application() { fn test_modmap_application() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
modmap: modmap:
- remap: - remap:
Alt_L: Ctrl_L Alt_L: Ctrl_L
@ -35,8 +33,8 @@ fn test_yaml_modmap_application() {
} }
#[test] #[test]
fn test_yaml_modmap_application_regex() { fn test_modmap_application_regex() {
yaml_assert_parse(indoc! {r" assert_parse(indoc! {r"
modmap: modmap:
- remap: - remap:
Alt_L: Ctrl_L Alt_L: Ctrl_L
@ -53,8 +51,8 @@ fn test_yaml_modmap_application_regex() {
} }
#[test] #[test]
fn test_yaml_modmap_multi_purpose_key() { fn test_modmap_multi_purpose_key() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
modmap: modmap:
- remap: - remap:
Space: Space:
@ -68,8 +66,8 @@ fn test_yaml_modmap_multi_purpose_key() {
"}) "})
} }
#[test] #[test]
fn test_yaml_modmap_multi_purpose_key_multi_key() { fn test_modmap_multi_purpose_key_multi_key() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
modmap: modmap:
- remap: - remap:
Space: Space:
@ -83,16 +81,16 @@ fn test_yaml_modmap_multi_purpose_key_multi_key() {
"}) "})
} }
#[test] #[test]
fn test_yaml_virtual_modifiers() { fn test_virtual_modifiers() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
virtual_modifiers: virtual_modifiers:
- CapsLock - CapsLock
"}) "})
} }
#[test] #[test]
fn test_yaml_modmap_press_release_key() { fn test_modmap_press_release_key() {
yaml_assert_parse(indoc! {r#" assert_parse(indoc! {r#"
modmap: modmap:
- remap: - remap:
Space: Space:
@ -102,8 +100,8 @@ fn test_yaml_modmap_press_release_key() {
} }
#[test] #[test]
fn test_yaml_keymap_basic() { fn test_keymap_basic() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- name: Global - name: Global
remap: remap:
@ -114,8 +112,8 @@ fn test_yaml_keymap_basic() {
} }
#[test] #[test]
fn test_yaml_keymap_lr_modifiers() { fn test_keymap_lr_modifiers() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- name: Global - name: Global
remap: remap:
@ -126,8 +124,8 @@ fn test_yaml_keymap_lr_modifiers() {
} }
#[test] #[test]
fn test_yaml_keymap_application() { fn test_keymap_application() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- remap: - remap:
Alt-Enter: Ctrl-Enter Alt-Enter: Ctrl-Enter
@ -142,8 +140,8 @@ fn test_yaml_keymap_application() {
} }
#[test] #[test]
fn test_yaml_keymap_array() { fn test_keymap_array() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- remap: - remap:
C-w: C-w:
@ -153,8 +151,8 @@ fn test_yaml_keymap_array() {
} }
#[test] #[test]
fn test_yaml_keymap_remap() { fn test_keymap_remap() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- remap: - remap:
C-x: C-x:
@ -169,8 +167,8 @@ fn test_yaml_keymap_remap() {
} }
#[test] #[test]
fn test_yaml_keymap_launch() { fn test_keymap_launch() {
yaml_assert_parse(indoc! {r#" assert_parse(indoc! {r#"
keymap: keymap:
- remap: - remap:
KEY_GRAVE: KEY_GRAVE:
@ -182,8 +180,8 @@ fn test_yaml_keymap_launch() {
} }
#[test] #[test]
fn test_yaml_keymap_mode() { fn test_keymap_mode() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
default_mode: insert default_mode: insert
keymap: keymap:
- mode: insert - mode: insert
@ -200,8 +198,8 @@ fn test_yaml_keymap_mode() {
} }
#[test] #[test]
fn test_yaml_keymap_mark() { fn test_keymap_mark() {
yaml_assert_parse(indoc! {" assert_parse(indoc! {"
keymap: keymap:
- remap: - remap:
C-space: { set_mark: true } C-space: { set_mark: true }
@ -211,327 +209,8 @@ fn test_yaml_keymap_mark() {
"}) "})
} }
#[test] fn assert_parse(yaml: &str) {
fn test_yaml_shared_data_anchor() { let result: Result<Config, Error> = serde_yaml::from_str(yaml);
yaml_assert_parse(indoc! {"
shared:
terminals: &terminals
- Gnome-terminal
- Kitty
modmap:
- remap:
Alt_L: Ctrl_L
application:
not: *terminals
- remap:
Shift_R: Win_R
application:
only: Google-chrome
"})
}
#[test]
#[should_panic]
fn test_yaml_fail_on_data_outside_of_config_model() {
yaml_assert_parse(indoc! {"
terminals: &terminals
- Gnome-terminal
- Kitty
modmap:
- remap:
Alt_L: Ctrl_L
application:
not: *terminals
- remap:
Shift_R: Win_R
application:
only: Google-chrome
"})
}
#[test]
fn test_toml_modmap_basic() {
toml_assert_parse(indoc! {"
[[modmap]]
name = \"Global\"
[modmap.remap]
Alt_L = \"Ctrl_L\"
[[modmap]]
[modmap.remap]
Shift_R = \"Win_R\"
[modmap.application]
only = \"Google-chrome\"
"})
}
#[test]
fn test_toml_modmap_application() {
toml_assert_parse(indoc! {"
[[modmap]]
[modmap.remap]
Alt_L = \"Ctrl_L\"
[modmap.application]
not = [ \"Gnome-terminal\" ]
[[modmap]]
[modmap.remap]
Shift_R = \"Win_R\"
[modmap.application]
only = \"Google-chrome\"
"})
}
#[test]
fn test_toml_modmap_application_regex() {
toml_assert_parse(indoc! {r#"
[[modmap]]
[modmap.remap]
Alt_L = "Ctrl_L"
[modmap.application]
not = [ "/^Minecraft/", "/^Minecraft\\//", "/^Minecraft\\d/" ]
[[modmap]]
[modmap.remap]
Shift_R = "Win_R"
[modmap.application]
only = "/^Miencraft\\\\/"
"#})
}
#[test]
fn test_toml_modmap_multi_purpose_key() {
toml_assert_parse(indoc! {"
[[modmap]]
[modmap.remap.Space]
held = [ \"Shift_L\" ]
alone = \"Space\"
[[modmap]]
[modmap.remap.Muhenkan]
held = [ \"Alt_L\", \"Shift_L\" ]
alone = [ \"Muhenkan\" ]
alone_timeout_millis = 500
"})
}
#[test]
fn test_toml_modmap_multi_purpose_key_multi_key() {
toml_assert_parse(indoc! {"
[[modmap]]
[modmap.remap.Space]
held = [ \"Shift_L\" ]
alone = [ \"Shift_L\", \"A\" ]
[[modmap]]
[modmap.remap.Muhenkan]
held = [ \"Alt_L\", \"Shift_L\" ]
alone = [ \"Muhenkan\" ]
alone_timeout_millis = 500
"})
}
#[test]
fn test_toml_virtual_modifiers() {
toml_assert_parse(indoc! {"
virtual_modifiers = [ \"CapsLock\" ]
"})
}
#[test]
fn test_toml_modmap_press_release_key() {
toml_assert_parse(indoc! {r#"
[[modmap]]
[modmap.remap.Space.press]
launch = [ "wmctrl", "-x", "-a", "code.Code" ]
[modmap.remap.Space.release]
launch = ["wmctrl", "-x", "-a", "nocturn.Nocturn"]
"#})
}
#[test]
fn test_toml_keymap_basic() {
toml_assert_parse(indoc! {"
[[keymap]]
name = \"Global\"
[keymap.remap]
Alt-Enter = \"Ctrl-Enter\"
[[keymap]]
[keymap.remap]
M-S = \"C-S\"
"})
}
#[test]
fn test_toml_keymap_lr_modifiers() {
toml_assert_parse(indoc! {"
[[keymap]]
name = \"Global\"
[keymap.remap]
Alt_L-Enter = \"Ctrl_L-Enter\"
[[keymap]]
[keymap.remap]
M_R-S = \"C_L-S\"
"})
}
#[test]
fn test_toml_keymap_application() {
toml_assert_parse(indoc! {"
[[keymap]]
[keymap.remap]
Alt-Enter = \"Ctrl-Enter\"
[keymap.application]
not = \"Gnome-terminal\"
[[keymap]]
[keymap.remap]
Alt-S = \"Ctrl-S\"
[keymap.application]
only = \"Gnome-terminal\"
"})
}
#[test]
fn test_toml_keymap_array() {
toml_assert_parse(indoc! {"
[[keymap]]
[keymap.remap]
C-w = [\"Shift-C-w\", \"C-x\"]
"})
}
#[test]
fn test_toml_keymap_remap() {
toml_assert_parse(indoc! {"
[[keymap]]
[keymap.remap.C-x]
timeout_key = \"Down\"
timeout_millis = 1_000
[keymap.remap.C-x.remap]
s = \"C-w\"
[keymap.remap.C-x.remap.C-s.remap]
x = \"C-z\"
"})
}
#[test]
fn test_toml_keymap_launch() {
toml_assert_parse(indoc! {r#"
[[keymap]]
[keymap.remap.KEY_GRAVE]
launch = [ "/bin/sh", "-c", "date > /tmp/hotkey_test" ]
"#})
}
#[test]
fn test_toml_keymap_mode() {
toml_assert_parse(indoc! {"
default_mode = \"insert\"
[[keymap]]
mode = \"instert\"
[keymap.remap.Esc]
set_mode = \"normal\"
[[keymap]]
mode = \"normal\"
[keymap.remap]
h = \"Left\"
j = \"Down\"
k = \"Up\"
l = \"Right\"
[keymap.remap.i]
set_mode = \"insert\"
"})
}
#[test]
fn test_toml_keymap_mark() {
toml_assert_parse(indoc! {"
[[keymap]]
[keymap.remap]
C-g = [ \"esc\", { set_mark = false } ]
[keymap.remap.C-space]
set_mark = true
[keymap.remap.C-b]
with_mark = \"left\"
[keymap.remap.M-b]
with_mark = \"C-left\"
"})
}
#[test]
fn test_toml_shared_data_anchor() {
toml_assert_parse(indoc! {"
[shared]
terminals = [ \"Gnome-terminal\", \"Kitty\" ]
[[shared.modmap]]
[shared.modmap.remap]
Alt_L = \"Ctrl_L\"
[shared.modmap.application]
not = \"terminals\"
[[shared.modmap]]
[shared.modmap.remap]
Shift_R = \"Win_R\"
[shared.modmap.application]
only = \"Google-chrome\"
"})
}
#[test]
#[should_panic]
fn test_toml_fail_on_data_outside_of_config_model() {
toml_assert_parse(indoc! {"
terminals = [ \"Gnome-terminal\", \"Kitty\" ]
[[modmap]]
[modmap.remap]
Alt_L = \"Ctrl_L\"
[modmap.application]
not = [ \"Gnome-terminal\", \"Kitty\" ]
[[modmap]]
[modmap.remap]
Shift_R = \"Win_R\"
[modmap.application]
only = \"Google-chrome\"
"})
}
fn toml_assert_parse(toml: &str) {
let result: Result<Config, toml::de::Error> = toml::from_str(toml);
if let Err(e) = result {
panic!("{}", e)
}
}
fn yaml_assert_parse(yaml: &str) {
let result: Result<Config, serde_yaml::Error> = serde_yaml::from_str(yaml);
if let Err(e) = result { if let Err(e) = result {
panic!("{}", e) panic!("{}", e)
} }

@ -11,7 +11,7 @@ use std::error::Error;
use std::fs::read_dir; use std::fs::read_dir;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::os::unix::prelude::AsRawFd; use std::os::unix::prelude::AsRawFd;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::{io, process}; use std::{io, process};
static MOUSE_BTNS: [&str; 20] = [ static MOUSE_BTNS: [&str; 20] = [
@ -40,7 +40,7 @@ static MOUSE_BTNS: [&str; 20] = [
static mut DEVICE_NAME: Option<String> = None; static mut DEVICE_NAME: Option<String> = None;
// Credit: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/xkeysnail/output.py#L10-L32 // Credit: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/xkeysnail/output.py#L10-L32
pub fn output_device(bus_type: Option<BusType>, enable_wheel: bool) -> Result<VirtualDevice, Box<dyn Error>> { pub fn output_device(bus_type: Option<BusType>, mouse: bool) -> Result<VirtualDevice, Box<dyn Error>> {
let mut keys: AttributeSet<Key> = AttributeSet::new(); let mut keys: AttributeSet<Key> = AttributeSet::new();
for code in Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code() { for code in Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code() {
let key = Key::new(code); let key = Key::new(code);
@ -53,7 +53,7 @@ pub fn output_device(bus_type: Option<BusType>, enable_wheel: bool) -> Result<Vi
let mut relative_axes: AttributeSet<RelativeAxisType> = AttributeSet::new(); let mut relative_axes: AttributeSet<RelativeAxisType> = AttributeSet::new();
relative_axes.insert(RelativeAxisType::REL_X); relative_axes.insert(RelativeAxisType::REL_X);
relative_axes.insert(RelativeAxisType::REL_Y); relative_axes.insert(RelativeAxisType::REL_Y);
if enable_wheel { if mouse {
relative_axes.insert(RelativeAxisType::REL_HWHEEL); relative_axes.insert(RelativeAxisType::REL_HWHEEL);
relative_axes.insert(RelativeAxisType::REL_WHEEL); relative_axes.insert(RelativeAxisType::REL_WHEEL);
} }
@ -133,31 +133,6 @@ pub fn get_input_devices(
Ok(devices.into_iter().map(From::from).collect()) Ok(devices.into_iter().map(From::from).collect())
} }
#[derive(Debug)]
pub struct InputDeviceInfo<'a> {
pub name: &'a str,
pub path: &'a Path,
}
impl<'a> InputDeviceInfo<'a> {
pub fn matches(&self, filter: &String) -> bool {
let filter = filter.as_str();
// Check exact matches for explicit selection
if self.path.as_os_str() == filter || self.name == filter {
return true;
}
// eventXX shorthand for /dev/input/eventXX
if filter.starts_with("event") && self.path.file_name().expect("every device path has a file name") == filter {
return true;
}
// Allow partial matches for device names
if self.name.contains(filter) {
return true;
}
return false;
}
}
#[derive_where(PartialEq, PartialOrd, Ord)] #[derive_where(PartialEq, PartialOrd, Ord)]
pub struct InputDevice { pub struct InputDevice {
path: PathBuf, path: PathBuf,
@ -225,13 +200,6 @@ impl InputDevice {
pub fn bus_type(&self) -> BusType { pub fn bus_type(&self) -> BusType {
self.device.input_id().bus_type() self.device.input_id().bus_type()
} }
pub fn to_info(&self) -> InputDeviceInfo {
InputDeviceInfo {
name: self.device_name(),
path: &self.path,
}
}
} }
impl InputDevice { impl InputDevice {
@ -242,8 +210,8 @@ impl InputDevice {
(if device_filter.is_empty() { (if device_filter.is_empty() {
self.is_keyboard() || (mouse && self.is_mouse()) self.is_keyboard() || (mouse && self.is_mouse())
} else { } else {
self.matches_any(device_filter) self.matches(device_filter)
}) && (ignore_filter.is_empty() || !self.matches_any(ignore_filter)) }) && (ignore_filter.is_empty() || !self.matches(ignore_filter))
} }
// We can't know the device path from evdev::enumerate(). So we re-implement it. // We can't know the device path from evdev::enumerate(). So we re-implement it.
@ -278,12 +246,31 @@ impl InputDevice {
.any(|device| return device.device_name().contains(device_name)) .any(|device| return device.device_name().contains(device_name))
} }
fn matches_any(&self, filter: &[String]) -> bool { fn matches(&self, filter: &[String]) -> bool {
// Force unmatch its own device // Force unmatch its own device
if self.device_name() == Self::current_name() { if self.device_name() == Self::current_name() {
return false; return false;
} }
return filter.iter().any(|f| self.to_info().matches(f));
for device_opt in filter {
let device_opt = device_opt.as_str();
// Check exact matches for explicit selection
if self.path.as_os_str() == device_opt || self.device_name() == device_opt {
return true;
}
// eventXX shorthand for /dev/input/eventXX
if device_opt.starts_with("event")
&& self.path.file_name().expect("every device path has a file name") == device_opt
{
return true;
}
// Allow partial matches for device names
if self.device_name().contains(device_opt) {
return true;
}
}
false
} }
fn is_keyboard(&self) -> bool { fn is_keyboard(&self) -> bool {

@ -1,14 +1,12 @@
use evdev::{EventType, InputEvent, Key}; use evdev::{EventType, InputEvent, Key};
use crate::device::InputDeviceInfo;
// Input to EventHandler. This should only contain things that are easily testable. // Input to EventHandler. This should only contain things that are easily testable.
#[derive(Debug)] #[derive(Debug)]
pub enum Event<'a> { pub enum Event {
// InputEvent (EventType::KEY) sent from evdev // InputEvent (EventType::KEY) sent from evdev
KeyEvent(InputDeviceInfo<'a>, KeyEvent), KeyEvent(KeyEvent),
// InputEvent (EventType::Relative) sent from evdev // InputEvent (EventType::Relative) sent from evdev
RelativeEvent(InputDeviceInfo<'a>, RelativeEvent), RelativeEvent(RelativeEvent),
// Any other InputEvent type sent from evdev // Any other InputEvent type sent from evdev
OtherEvents(InputEvent), OtherEvents(InputEvent),
// Timer for nested override reached its timeout // Timer for nested override reached its timeout
@ -17,7 +15,7 @@ pub enum Event<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct KeyEvent { pub struct KeyEvent {
pub key: Key, key: Key,
value: KeyValue, value: KeyValue,
} }
@ -33,12 +31,12 @@ pub enum KeyValue {
Release, Release,
Repeat, Repeat,
} }
impl<'a> Event<'a> { impl Event {
// Convert evdev's raw InputEvent to xremap's internal Event // Convert evdev's raw InputEvent to xremap's internal Event
pub fn new(device: InputDeviceInfo, event: InputEvent) -> Event { pub fn new(event: InputEvent) -> Event {
let event = match event.event_type() { let event = match event.event_type() {
EventType::KEY => Event::KeyEvent(device, KeyEvent::new_with(event.code(), event.value())), EventType::KEY => Event::KeyEvent(KeyEvent::new_with(event.code(), event.value())),
EventType::RELATIVE => Event::RelativeEvent(device, RelativeEvent::new_with(event.code(), event.value())), EventType::RELATIVE => Event::RelativeEvent(RelativeEvent::new_with(event.code(), event.value())),
_ => Event::OtherEvents(event), _ => Event::OtherEvents(event),
}; };
event event

@ -1,14 +1,13 @@
use crate::action::Action; use crate::action::Action;
use crate::client::WMClient; use crate::client::WMClient;
use crate::config::application::OnlyOrNot; use crate::config::application::Application;
use crate::config::key_press::{KeyPress, Modifier}; use crate::config::key_press::{KeyPress, Modifier};
use crate::config::keymap::{build_override_table, OverrideEntry}; use crate::config::keymap::{build_override_table, OverrideEntry};
use crate::config::keymap_action::KeymapAction; use crate::config::keymap_action::KeymapAction;
use crate::config::modmap_action::{Keys, ModmapAction, MultiPurposeKey, PressReleaseKey}; use crate::config::modmap_action::{Keys, ModmapAction, MultiPurposeKey, PressReleaseKey};
use crate::config::remap::Remap; use crate::config::remap::Remap;
use crate::device::InputDeviceInfo;
use crate::event::{Event, KeyEvent, RelativeEvent}; use crate::event::{Event, KeyEvent, RelativeEvent};
use crate::{config, Config}; use crate::Config;
use evdev::Key; use evdev::Key;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::debug; use log::debug;
@ -35,7 +34,6 @@ pub struct EventHandler {
// Check the currently active application // Check the currently active application
application_client: WMClient, application_client: WMClient,
application_cache: Option<String>, application_cache: Option<String>,
title_cache: Option<String>,
// State machine for multi-purpose keys // State machine for multi-purpose keys
multi_purpose_keys: HashMap<Key, MultiPurposeKeyState>, multi_purpose_keys: HashMap<Key, MultiPurposeKeyState>,
// Current nested remaps // Current nested remaps
@ -69,7 +67,6 @@ impl EventHandler {
pressed_keys: HashMap::new(), pressed_keys: HashMap::new(),
application_client, application_client,
application_cache: None, application_cache: None,
title_cache: None,
multi_purpose_keys: HashMap::new(), multi_purpose_keys: HashMap::new(),
override_remaps: vec![], override_remaps: vec![],
override_timeout_key: None, override_timeout_key: None,
@ -88,12 +85,12 @@ impl EventHandler {
let mut mouse_movement_collection: Vec<RelativeEvent> = Vec::new(); let mut mouse_movement_collection: Vec<RelativeEvent> = Vec::new();
for event in events { for event in events {
match event { match event {
Event::KeyEvent(device, key_event) => { Event::KeyEvent(key_event) => {
self.on_key_event(key_event, config, device)?; self.on_key_event(key_event, config)?;
() ()
} }
Event::RelativeEvent(device, relative_event) => { Event::RelativeEvent(relative_event) => {
self.on_relative_event(relative_event, &mut mouse_movement_collection, config, device)? self.on_relative_event(relative_event, &mut mouse_movement_collection, config)?
} }
Event::OtherEvents(event) => self.send_action(Action::InputEvent(*event)), Event::OtherEvents(event) => self.send_action(Action::InputEvent(*event)),
@ -108,19 +105,13 @@ impl EventHandler {
} }
// Handle EventType::KEY // Handle EventType::KEY
fn on_key_event( fn on_key_event(&mut self, event: &KeyEvent, config: &Config) -> Result<bool, Box<dyn Error>> {
&mut self,
event: &KeyEvent,
config: &Config,
device: &InputDeviceInfo,
) -> Result<bool, Box<dyn Error>> {
self.application_cache = None; // expire cache self.application_cache = None; // expire cache
self.title_cache = None; // expire cache
let key = Key::new(event.code()); let key = Key::new(event.code());
debug!("=> {}: {:?}", event.value(), &key); debug!("=> {}: {:?}", event.value(), &key);
// Apply modmap // Apply modmap
let mut key_values = if let Some(key_action) = self.find_modmap(config, &key, device) { let mut key_values = if let Some(key_action) = self.find_modmap(config, &key) {
self.dispatch_keys(key_action, key, event.value())? self.dispatch_keys(key_action, key, event.value())?
} else { } else {
vec![(key, event.value())] vec![(key, event.value())]
@ -141,7 +132,7 @@ impl EventHandler {
} else if is_pressed(value) { } else if is_pressed(value) {
if self.escape_next_key { if self.escape_next_key {
self.escape_next_key = false self.escape_next_key = false
} else if let Some(actions) = self.find_keymap(config, &key, device)? { } else if let Some(actions) = self.find_keymap(config, &key)? {
self.dispatch_actions(&actions, &key)?; self.dispatch_actions(&actions, &key)?;
continue; continue;
} }
@ -168,7 +159,6 @@ impl EventHandler {
event: &RelativeEvent, event: &RelativeEvent,
mouse_movement_collection: &mut Vec<RelativeEvent>, mouse_movement_collection: &mut Vec<RelativeEvent>,
config: &Config, config: &Config,
device: &InputDeviceInfo,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// Because a "full" RELATIVE event is only one event, // Because a "full" RELATIVE event is only one event,
// it doesn't translate very well into a KEY event (because those have a "press" event and an "unpress" event). // it doesn't translate very well into a KEY event (because those have a "press" event and an "unpress" event).
@ -210,7 +200,7 @@ impl EventHandler {
}; };
// Sending a RELATIVE event "disguised" as a "fake" KEY event press to on_key_event. // Sending a RELATIVE event "disguised" as a "fake" KEY event press to on_key_event.
match self.on_key_event(&KeyEvent::new_with(key, PRESS), config, &device)? { match self.on_key_event(&KeyEvent::new_with(key, PRESS), config)? {
// the boolean value is from a variable at the end of on_key_event from event_handler, // the boolean value is from a variable at the end of on_key_event from event_handler,
// used to indicate whether the event got through unchanged. // used to indicate whether the event got through unchanged.
true => { true => {
@ -239,7 +229,7 @@ impl EventHandler {
} }
// Sending the "unpressed" version of the "fake" KEY event. // Sending the "unpressed" version of the "fake" KEY event.
self.on_key_event(&KeyEvent::new_with(key, RELEASE), config, &device)?; self.on_key_event(&KeyEvent::new_with(key, RELEASE), config)?;
Ok(()) Ok(())
} }
@ -332,11 +322,7 @@ impl EventHandler {
// fallthrough on state discrepancy // fallthrough on state discrepancy
vec![(key, value)] vec![(key, value)]
} }
ModmapAction::PressReleaseKey(PressReleaseKey { ModmapAction::PressReleaseKey(PressReleaseKey { press, release }) => {
skip_key_event,
press,
release,
}) => {
// Just hook actions, and then emit the original event. We might want to // Just hook actions, and then emit the original event. We might want to
// support reordering the key event and dispatched actions later. // support reordering the key event and dispatched actions later.
if value == PRESS || value == RELEASE { if value == PRESS || value == RELEASE {
@ -351,14 +337,8 @@ impl EventHandler {
&key, &key,
)?; )?;
} }
// Dispatch the original key as well
if skip_key_event { vec![(key, value)]
// Do not dispatch the original key
vec![]
} else {
// dispatch the original key
vec![(key, value)]
}
} }
}; };
Ok(keys) Ok(keys)
@ -385,36 +365,21 @@ impl EventHandler {
} }
} }
fn find_modmap(&mut self, config: &Config, key: &Key, device: &InputDeviceInfo) -> Option<ModmapAction> { fn find_modmap(&mut self, config: &Config, key: &Key) -> Option<ModmapAction> {
for modmap in &config.modmap { for modmap in &config.modmap {
if let Some(key_action) = modmap.remap.get(key) { if let Some(key_action) = modmap.remap.get(key) {
if let Some(window_matcher) = &modmap.window {
if !self.match_window(window_matcher) {
continue;
}
}
if let Some(application_matcher) = &modmap.application { if let Some(application_matcher) = &modmap.application {
if !self.match_application(application_matcher) { if !self.match_application(application_matcher) {
continue; continue;
} }
} }
if let Some(device_matcher) = &modmap.device {
if !self.match_device(device_matcher, device) {
continue;
}
}
return Some(key_action.clone()); return Some(key_action.clone());
} }
} }
None None
} }
fn find_keymap( fn find_keymap(&mut self, config: &Config, key: &Key) -> Result<Option<Vec<TaggedAction>>, Box<dyn Error>> {
&mut self,
config: &Config,
key: &Key,
device: &InputDeviceInfo,
) -> Result<Option<Vec<TaggedAction>>, Box<dyn Error>> {
if !self.override_remaps.is_empty() { if !self.override_remaps.is_empty() {
let entries: Vec<OverrideEntry> = self let entries: Vec<OverrideEntry> = self
.override_remaps .override_remaps
@ -466,22 +431,11 @@ impl EventHandler {
if (exact_match && extra_modifiers.len() > 0) || missing_modifiers.len() > 0 { if (exact_match && extra_modifiers.len() > 0) || missing_modifiers.len() > 0 {
continue; continue;
} }
if let Some(window_matcher) = &entry.title {
if !self.match_window(window_matcher) {
continue;
}
}
if let Some(application_matcher) = &entry.application { if let Some(application_matcher) = &entry.application {
if !self.match_application(application_matcher) { if !self.match_application(application_matcher) {
continue; continue;
} }
} }
if let Some(device_matcher) = &entry.device {
if !self.match_device(device_matcher, device) {
continue;
}
}
if let Some(modes) = &entry.mode { if let Some(modes) = &entry.mode {
if !modes.contains(&self.mode) { if !modes.contains(&self.mode) {
continue; continue;
@ -637,27 +591,8 @@ impl EventHandler {
Modifier::Key(key) => self.modifiers.contains(key), Modifier::Key(key) => self.modifiers.contains(key),
} }
} }
fn match_window(&mut self, window_matcher: &OnlyOrNot) -> bool {
// Lazily fill the wm_class cache
if self.title_cache.is_none() {
match self.application_client.current_window() {
Some(title) => self.title_cache = Some(title),
None => self.title_cache = Some(String::new()),
}
}
if let Some(title) = &self.title_cache {
if let Some(title_only) = &window_matcher.only {
return title_only.iter().any(|m| m.matches(title));
}
if let Some(title_not) = &window_matcher.not {
return title_not.iter().all(|m| !m.matches(title));
}
}
false
}
fn match_application(&mut self, application_matcher: &OnlyOrNot) -> bool { fn match_application(&mut self, application_matcher: &Application) -> bool {
// Lazily fill the wm_class cache // Lazily fill the wm_class cache
if self.application_cache.is_none() { if self.application_cache.is_none() {
match self.application_client.current_application() { match self.application_client.current_application() {
@ -677,16 +612,6 @@ impl EventHandler {
false false
} }
fn match_device(&self, device_matcher: &config::device::Device, device: &InputDeviceInfo) -> bool {
if let Some(device_only) = &device_matcher.only {
return device_only.iter().any(|m| device.matches(m));
}
if let Some(device_not) = &device_matcher.not {
return device_not.iter().all(|m| !device.matches(m));
}
false
}
fn update_modifier(&mut self, key: Key, value: i32) { fn update_modifier(&mut self, key: Key, value: i32) {
if value == PRESS { if value == PRESS {
self.modifiers.insert(key); self.modifiers.insert(key);

@ -31,19 +31,19 @@ mod event_handler;
mod tests; mod tests;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version)] #[clap(version)]
struct Args { struct Args {
/// Include a device name or path /// Include a device name or path
#[arg(long, value_delimiter = ',')] #[clap(long, use_value_delimiter = true)]
device: Vec<String>, device: Vec<String>,
/// Ignore a device name or path /// Ignore a device name or path
#[arg(long, value_delimiter = ',')] #[clap(long, use_value_delimiter = true)]
ignore: Vec<String>, ignore: Vec<String>,
/// Match mice by default /// Match mice by default
#[arg(long)] #[clap(long)]
mouse: bool, mouse: bool,
/// Targets to watch /// Targets to watch
#[arg(long, value_enum, num_args = 0.., value_delimiter = ',', require_equals = true, #[clap(long, value_enum, num_args = 0.., use_value_delimiter = true, require_equals = true,
default_missing_value = "device", verbatim_doc_comment)] default_missing_value = "device", verbatim_doc_comment)]
watch: Vec<WatchTargets>, watch: Vec<WatchTargets>,
/// Generate shell completions /// Generate shell completions
@ -51,10 +51,10 @@ struct Args {
/// You can use them by storing in your shells completion file or by running /// You can use them by storing in your shells completion file or by running
/// - in bash: eval "$(xremap --completions bash)" /// - in bash: eval "$(xremap --completions bash)"
/// - in fish: xremap --completions fish | source /// - in fish: xremap --completions fish | source
#[arg(long, value_enum, display_order = 100, value_name = "SHELL", verbatim_doc_comment)] #[clap(long, value_enum, display_order = 100, value_name = "SHELL", verbatim_doc_comment)]
completions: Option<Shell>, completions: Option<Shell>,
/// Config file(s) /// Config file(s)
#[arg(required_unless_present = "completions", num_args = 1..)] #[clap(required_unless_present = "completions", num_args = 1..)]
configs: Vec<PathBuf>, configs: Vec<PathBuf>,
} }
@ -122,11 +122,10 @@ fn main() -> anyhow::Result<()> {
let config_watcher = config_watcher(watch_config, &config_paths).context("Setting up config watcher")?; let config_watcher = config_watcher(watch_config, &config_paths).context("Setting up config watcher")?;
let watchers: Vec<_> = device_watcher.iter().chain(config_watcher.iter()).collect(); let watchers: Vec<_> = device_watcher.iter().chain(config_watcher.iter()).collect();
let mut handler = EventHandler::new(timer, &config.default_mode, delay, build_client()); let mut handler = EventHandler::new(timer, &config.default_mode, delay, build_client());
let output_device = let output_device = match output_device(input_devices.values().next().map(InputDevice::bus_type), mouse) {
match output_device(input_devices.values().next().map(InputDevice::bus_type), config.enable_wheel) { Ok(output_device) => output_device,
Ok(output_device) => output_device, Err(e) => bail!("Failed to prepare an output device: {}", e),
Err(e) => bail!("Failed to prepare an output device: {}", e), };
};
let mut dispatcher = ActionDispatcher::new(output_device); let mut dispatcher = ActionDispatcher::new(output_device);
// Main loop // Main loop
@ -227,18 +226,20 @@ fn handle_input_events(
dispatcher: &mut ActionDispatcher, dispatcher: &mut ActionDispatcher,
config: &mut Config, config: &mut Config,
) -> anyhow::Result<bool> { ) -> anyhow::Result<bool> {
let mut device_exists = true; match input_device.fetch_events().map_err(|e| (e.raw_os_error(), e)) {
let events = match input_device.fetch_events().map_err(|e| (e.raw_os_error(), e)) { Err((Some(ENODEV), _)) => Ok(false),
Err((Some(ENODEV), _)) => {
device_exists = false;
Ok(Vec::new())
}
Err((_, error)) => Err(error).context("Error fetching input events"), Err((_, error)) => Err(error).context("Error fetching input events"),
Ok(events) => Ok(events.collect()), Ok(events) => {
}?; let mut input_events: Vec<Event> = Vec::new();
let input_events = events.iter().map(|e| Event::new(input_device.to_info(), *e)).collect(); for event in events {
handle_events(handler, dispatcher, config, input_events)?; let event = Event::new(event);
Ok(device_exists) input_events.push(event);
}
handle_events(handler, dispatcher, config, input_events)?;
Ok(true)
}
}
} }
// Handle an Event with EventHandler, and dispatch Actions with ActionDispatcher // Handle an Event with EventHandler, and dispatch Actions with ActionDispatcher

@ -3,11 +3,9 @@ use evdev::InputEvent;
use evdev::Key; use evdev::Key;
use indoc::indoc; use indoc::indoc;
use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags}; use nix::sys::timerfd::{ClockId, TimerFd, TimerFlags};
use std::path::Path;
use std::time::Duration; use std::time::Duration;
use crate::client::{Client, WMClient}; use crate::client::{Client, WMClient};
use crate::device::InputDeviceInfo;
use crate::{ use crate::{
action::Action, action::Action,
config::{keymap::build_keymap_table, Config}, config::{keymap::build_keymap_table, Config},
@ -23,22 +21,12 @@ impl Client for StaticClient {
fn supported(&mut self) -> bool { fn supported(&mut self) -> bool {
true true
} }
fn current_window(&mut self) -> Option<String> {
None
}
fn current_application(&mut self) -> Option<String> { fn current_application(&mut self) -> Option<String> {
self.current_application.clone() self.current_application.clone()
} }
} }
fn get_input_device_info<'a>() -> InputDeviceInfo<'a> {
InputDeviceInfo {
name: "Some Device",
path: &Path::new("/dev/input/event0"),
}
}
#[test] #[test]
fn test_basic_modmap() { fn test_basic_modmap() {
assert_actions( assert_actions(
@ -48,10 +36,10 @@ fn test_basic_modmap() {
a: b a: b
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_A, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_B, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_B, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
@ -105,10 +93,7 @@ fn test_relative_events() {
- remap: - remap:
XRIGHTCURSOR: b XRIGHTCURSOR: b
"}, "},
vec![Event::RelativeEvent( vec![Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE))],
get_input_device_info(),
RelativeEvent::new_with(_REL_X, _POSITIVE),
)],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)),
@ -137,8 +122,8 @@ fn test_mouse_movement_event_accumulation() {
assert_actions( assert_actions(
indoc! {""}, indoc! {""},
vec![ vec![
Event::RelativeEvent(get_input_device_info(), RelativeEvent::new_with(_REL_X, _POSITIVE)), Event::RelativeEvent(RelativeEvent::new_with(_REL_X, _POSITIVE)),
Event::RelativeEvent(get_input_device_info(), RelativeEvent::new_with(_REL_Y, _POSITIVE)), Event::RelativeEvent(RelativeEvent::new_with(_REL_Y, _POSITIVE)),
], ],
vec![Action::MouseMovementEventCollection(vec![ vec![Action::MouseMovementEventCollection(vec![
RelativeEvent::new_with(_REL_X, _POSITIVE), RelativeEvent::new_with(_REL_X, _POSITIVE),
@ -266,8 +251,8 @@ fn test_interleave_modifiers() {
M-f: C-right M-f: C-right
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
@ -293,9 +278,9 @@ fn test_exact_match_true() {
M-f: C-right M-f: C-right
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
@ -315,9 +300,9 @@ fn test_exact_match_false() {
M-f: C-right M-f: C-right
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
@ -343,9 +328,9 @@ fn test_exact_match_default() {
M-f: C-right M-f: C-right
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_F, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_F, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTALT, KeyValue::Press)),
@ -374,12 +359,12 @@ fn test_exact_match_true_nested() {
h: C-a h: C-a
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
@ -403,12 +388,12 @@ fn test_exact_match_false_nested() {
h: C-a h: C-a
"}, "},
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTSHIFT, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
@ -443,10 +428,7 @@ fn test_application_override() {
assert_actions( assert_actions(
config, config,
vec![Event::KeyEvent( vec![Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press))],
get_input_device_info(),
KeyEvent::new(Key::KEY_A, KeyValue::Press),
)],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
@ -460,65 +442,7 @@ fn test_application_override() {
assert_actions_with_current_application( assert_actions_with_current_application(
config, config,
Some(String::from("firefox")), Some(String::from("firefox")),
vec![Event::KeyEvent( vec![Event::KeyEvent(KeyEvent::new(Key::KEY_A, KeyValue::Press))],
get_input_device_info(),
KeyEvent::new(Key::KEY_A, KeyValue::Press),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
);
}
#[test]
fn test_device_override() {
let config = indoc! {"
keymap:
- name: event1
device:
only: [event1]
remap:
a: C-c
- name: event0
remap:
a: C-b
"};
assert_actions(
config,
vec![Event::KeyEvent(
InputDeviceInfo {
name: "Some Device",
path: &Path::new("/dev/input/event0"),
},
KeyEvent::new(Key::KEY_A, KeyValue::Press),
)],
vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_B, KeyValue::Release)),
Action::Delay(Duration::from_nanos(0)),
Action::Delay(Duration::from_nanos(0)),
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
],
);
assert_actions(
config,
vec![Event::KeyEvent(
InputDeviceInfo {
name: "Other Device",
path: &Path::new("/dev/input/event1"),
},
KeyEvent::new(Key::KEY_A, KeyValue::Press),
)],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)),
@ -547,11 +471,11 @@ fn test_merge_remaps() {
assert_actions( assert_actions(
config, config,
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
@ -569,11 +493,11 @@ fn test_merge_remaps() {
assert_actions( assert_actions(
config, config,
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_K, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_K, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
@ -607,11 +531,11 @@ fn test_merge_remaps_with_override() {
assert_actions( assert_actions(
config, config,
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_H, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_H, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
@ -629,11 +553,11 @@ fn test_merge_remaps_with_override() {
assert_actions( assert_actions(
config, config,
vec![ vec![
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Press)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_X, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_X, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)), Event::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Release)),
Event::KeyEvent(get_input_device_info(), KeyEvent::new(Key::KEY_C, KeyValue::Press)), Event::KeyEvent(KeyEvent::new(Key::KEY_C, KeyValue::Press)),
], ],
vec![ vec![
Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)), Action::KeyEvent(KeyEvent::new(Key::KEY_LEFTCTRL, KeyValue::Press)),

Loading…
Cancel
Save