Compare commits

...

63 Commits

Author SHA1 Message Date
Travis Burtrum 270cdca14f Add TOUCHPAD_TOGGLE for linux
moparisthebest/rusty-keys/pipeline/head There was a failure building this commit Details
2023-09-05 23:36:12 -04:00
Travis Burtrum e9705db5ba Update systemd unit to restart always
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2022-04-29 08:08:50 -04:00
Travis Burtrum 10096b2709 Exclude devices with a LEFT mouse button on linux
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-12-28 00:21:00 -05:00
Travis Burtrum e6f4653570 Exclude Yubico devices on Linux
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-12-12 22:29:45 -05:00
Travis Burtrum 552cd50266 macOS support
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-10-09 01:39:10 -04:00
Travis Burtrum b0f9dfddf2 release all currently held down keys when any revert_default_key is released
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 02:27:39 -04:00
Travis Burtrum b0543d3f93 noop other identical keys
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 02:04:34 -04:00
Travis Burtrum 527ab97a04 noop identical keys
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 01:50:24 -04:00
Travis Burtrum d0e38d0e55 Avoid index out of bounds on strange keys (Apple)
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 01:36:50 -04:00
Travis Burtrum 8ffe640c4b Clean up unused warnings for all features
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 01:00:09 -04:00
Travis Burtrum bcea441630 Test compilation with each feature individually
moparisthebest/rusty-keys/pipeline/head Build queued... Details
2021-09-25 00:51:00 -04:00
Travis Burtrum 17a8f0c995 make toml and serde optional, if not included, can hardcode config
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-25 00:45:29 -04:00
Travis Burtrum 60a3f24c86 Make epoll and inotify optional dependencies enabled by default with the epoll_inotify feature
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-24 01:03:27 -04:00
Travis Burtrum c420323cdf light editorial code cleanup
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-24 00:27:10 -04:00
Travis Burtrum a454b2a2a1 fix comments and order of operations in input_device
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-24 00:13:14 -04:00
Travis Burtrum 877a35861a Updated all package versions 2021-09-24 00:10:02 -04:00
Travis Burtrum 9317d31153 update readme
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-23 23:29:56 -04:00
Travis Burtrum d785d341cb Rewrite linux mapper to use epoll instead of threads and blocking IO
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-23 23:18:33 -04:00
Travis Burtrum fa202be7da Determine keyboard devices by whether they support KEY_A or not
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-09-21 23:56:24 -04:00
Travis Burtrum 95833e3a33 Support multiple revert_default_keys
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-04-03 17:15:38 -04:00
Travis Burtrum b1bb55766d Workaround for (buggy?) Monoprice Dark Matter Aether keyboard
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2021-03-02 23:57:26 -05:00
Travis Burtrum bd87beb211 Abandon travis-ci for jenkins
moparisthebest/rusty-keys/pipeline/head This commit looks good Details
2020-11-26 18:24:53 -05:00
Travis Burtrum 965c446b51 Remove powerpc64-unknown-linux-gnu travis build as it no longer exists 2020-07-11 13:21:10 -04:00
Travis Burtrum 4a816737c7 Upgrade to all latest dependencies 2020-07-11 02:20:20 -04:00
Travis Burtrum 95f9c731e9 Get rid of ancient and broken uinput-sys, proper refactoring to come 2020-07-11 02:05:25 -04:00
Travis Burtrum 6c4e4392b4 Fix old todo thanks to new rust version 2020-07-11 00:59:35 -04:00
Travis Burtrum 432e35cb6a Add travis-ci and appveyor builds 2020-07-11 00:49:03 -04:00
Travis Burtrum 24786356c4 Cargo update and fix newly deprecated code usage 2020-07-11 00:09:43 -04:00
Travis Burtrum 62501febb9 Fix compilation of main method when not on supported platform, works as library cross-platform now 2019-10-07 19:50:17 -04:00
Travis Burtrum 4554e19f28 rusty-keys is now well supported on windows 2019-10-07 01:59:43 -04:00
Travis Burtrum f7afe198c7 Windows support 2019-10-07 01:24:29 -04:00
Travis Burtrum f47d9e4be7 keymapper.rs is now fully platform agnostic, successfully passed through generic hell 2019-10-06 13:51:01 -04:00
Travis Burtrum 50b84dea0a First part of re-factoring towards platform-agnostic keymapper.rs 2019-10-05 19:39:54 -04:00
Travis Burtrum e02b59f55d Pull out rest of udev code 2019-09-30 23:36:28 -04:00
Travis Burtrum 9281922c72 Replace serde_derive with serde with derive feature 2019-09-30 22:55:16 -04:00
Travis Burtrum c2efbb6fd1 More edition 2018 cleanup 2019-09-29 23:58:51 -04:00
Travis Burtrum f4083343da Rust 2018 edition changes 2019-09-29 23:49:23 -04:00
Travis Burtrum 95cfd637b9 Upgrade all deps and do minimal code changes to compile, untested 2019-09-29 23:41:58 -04:00
Travis Burtrum d3aeededda packaging updates in preparation for 0.0.3 2019-01-10 01:48:38 -05:00
Travis Burtrum 0ffc1695aa Add Cargo.lock 2019-01-10 00:05:54 -05:00
Travis Burtrum f15edf8315 Tweak keyboard device finding 2018-01-10 01:25:47 -05:00
Travis Burtrum 966cebf42a No more shelling out to grep, parse /proc/bus/input/devices manually 2018-01-10 01:17:46 -05:00
Travis Burtrum 0e58664f67 Send SYN_REPORT with shift modifying keys to make libinput happy 2017-12-06 00:06:44 -05:00
Travis Burtrum d0fdea6ff6 Update readme 2017-11-17 00:03:45 -05:00
Travis Burtrum e7fc77773c Refactor out InputDevice module 2017-11-16 23:21:21 -05:00
Travis Burtrum 84b176a75e Add shortcut for mapping 1 device 2017-11-16 22:16:13 -05:00
Travis Burtrum 99cf9c452f Add sample systemd service 2017-11-16 00:31:56 -05:00
Travis Burtrum ca28b96982 All threads now send input to a single mapping/output device 2017-11-16 00:13:46 -05:00
Travis Burtrum 47f8231d33 Share keymap with Arc 2017-11-15 22:59:17 -05:00
Travis Burtrum 363ede21d3 More refactoring 2017-11-15 22:03:52 -05:00
Travis Burtrum 75e340f28a First go at working inotify support 2017-11-15 02:39:03 -05:00
Travis Burtrum 5bfc286d19 Panic cleanup part 3 2017-11-15 01:42:54 -05:00
Travis Burtrum 49fd65a4b0 Panic cleanup part 2 2017-11-15 01:08:38 -05:00
Travis Burtrum f9a6ed8d00 Panic cleanup part 1 2017-11-15 00:39:00 -05:00
Travis Burtrum 98ffe1b17e Implement start of better thread waiting 2017-11-15 00:03:42 -05:00
Travis Burtrum f67973c932 Support multiple devices with threads, hackily 2017-11-14 21:50:34 -05:00
Travis Burtrum 74d3350743 Try /dev/uinput first 2017-11-14 20:42:04 -05:00
Travis Burtrum 5c812eba8f Release 0.0.2 2017-11-06 23:22:47 -05:00
Travis Burtrum 3dfeed33e0 Try all possible uinput paths 2017-11-06 23:18:54 -05:00
Travis Burtrum 00c617307f Implement simpler CodeKeyMap for keymaps with no shift modifying behavior 2017-09-23 01:29:23 -04:00
Travis Burtrum 80d2d09ae2 Switch from Vec<Key> to [Key] 2017-09-23 00:47:20 -04:00
Travis Burtrum ef4ee29518 Change from Vec<Box<KeyMapper>> to Vec<Key> from trait to enum 2017-09-23 00:46:42 -04:00
Travis Burtrum 628b832af1 Only pass references around 2017-09-22 23:57:26 -04:00
29 changed files with 4657 additions and 1292 deletions

46
.ci/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,46 @@
properties(
[
disableConcurrentBuilds()
]
)
node('linux && docker') {
try {
stage('Checkout') {
//branch name from Jenkins environment variables
echo "My branch is: ${env.BRANCH_NAME}"
// this doesn't grab tags pointing to this branch
//checkout scm
// this hack does... https://issues.jenkins.io/browse/JENKINS-45164
checkout([
$class: 'GitSCM',
branches: [[name: 'refs/heads/'+env.BRANCH_NAME]],
extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']],
userRemoteConfigs: scm.userRemoteConfigs,
])
sh '''
set -euxo pipefail
git checkout "$BRANCH_NAME" --
git reset --hard "origin/$BRANCH_NAME"
'''
}
stage('Build + Deploy') {
sh '''
mkdir -p release
cp keymap.toml release
curl --compressed -sL https://code.moparisthebest.com/moparisthebest/self-ci/raw/branch/master/build-ci.sh | bash
'''
}
currentBuild.result = 'SUCCESS'
} catch (Exception err) {
currentBuild.result = 'FAILURE'
} finally {
stage('Email') {
step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'admin.jenkins@moparisthebest.com', sendToIndividuals: true])
}
deleteDir()
}
}

45
.ci/build.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash
set -exo pipefail
echo "starting build for TARGET $TARGET"
export CRATE_NAME=rusty-keys
DISABLE_TESTS=${DISABLE_TESTS:-0}
SUFFIX=""
echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && SUFFIX=".exe"
[ "$TARGET" == 'riscv64gc-unknown-linux-gnu' ] && echo 'riscv64gc-unknown-linux-gnu is not yet supported by inotify, skipping build...' && exit 0
# no main impl for these platforms
echo "$TARGET" | grep -E '(android|solaris$)' >/dev/null && DISABLE_TESTS=1
cross build --target $TARGET --release
# to check how they are built
file "target/$TARGET/release/rusty-keys$SUFFIX"
if [ $DISABLE_TESTS -ne 1 ]
then
# only going to run --help I guess
cross run --target $TARGET --release --bin rusty-keys -- -h
fi
# if this commit has a tag, upload artifact to release
strip "target/$TARGET/release/rusty-keys$SUFFIX" || true # if strip fails, it's fine
mkdir -p release
mv "target/$TARGET/release/rusty-keys$SUFFIX" "release/rusty-keys-$TARGET$SUFFIX"
if [ "$TARGET" == 'x86_64-unknown-linux-musl' ]
then
# for this arch only, we are going to build with each feature combo to test that the build succeeds, but not archive them
# the default for now is all features, so that's already tested above
cross build --target $TARGET --release --no-default-features
cross build --target $TARGET --release --no-default-features --features epoll_inotify
cross build --target $TARGET --release --no-default-features --features toml_serde
fi
echo 'build success!'
exit 0

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
target
Cargo.lock
.idea/
*.zip

265
Cargo.lock generated Normal file
View File

@ -0,0 +1,265 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cc"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "core-foundation"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
[[package]]
name = "core-graphics"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86"
dependencies = [
"bitflags",
"core-foundation",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [
"bitflags",
"core-foundation",
"foreign-types",
"libc",
]
[[package]]
name = "epoll"
version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "inotify"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5fc8f41dbaa9c8492a96c8afffda4f76896ee041d6a57606e70581b80c901f"
dependencies = [
"bitflags",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "proc-macro2"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rusty-keys"
version = "0.0.3"
dependencies = [
"core-foundation-sys",
"core-graphics",
"epoll",
"getopts",
"inotify",
"lazy_static",
"libc",
"nix",
"serde",
"toml",
"winapi",
]
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,6 +1,6 @@
[package]
name = "rusty-keys"
version = "0.0.1"
version = "0.0.3"
authors = ["moparisthebest <admin@moparisthebest.com>"]
license = "AGPL-3.0"
@ -9,23 +9,39 @@ description = "Linux keyboard mapper"
repository = "https://code.moparisthebest.com/moparisthebest/rusty-keys"
keywords = ["linux", "input", "keyboard", "keymapper"]
exclude = [
"keymap.orig.toml",
edition = "2018"
include = [
"src/**",
"Cargo.toml",
"Cargo.lock",
"keymap.toml",
"LICENSE.md",
"README.md",
]
[dependencies]
libc = "0.2"
nix = "0.5"
uinput-sys = "0.1"
getopts = "0.2.15"
toml = "0.4.5"
serde_derive = "1.0.11"
serde = "1.0.11"
getopts = "0.2.21"
toml = { version = "0.5.8", optional = true }
serde = { version = "1.0.130", features = ["derive"], optional = true }
[dependencies.libudev]
optional = true
version = "0.2"
[target.'cfg(target_os="macos")'.dependencies]
core-graphics = "0.22"
core-foundation-sys = "0.8"
#rustkit = "0.0.1"
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["winuser", "wincon"] }
lazy_static = "1.4.0"
[target.'cfg(target_os="linux")'.dependencies]
libc = "0.2.102"
nix = "0.22.1"
epoll = { version = "4.3.1", optional = true }
inotify = { version = "0.9.3", default-features = false, features = [], optional = true }
[features]
default = ["udev"]
udev = ["libudev"]
default = ["epoll_inotify", "toml_serde"]
toml_serde = ["toml", "serde"]
epoll_inotify = ["epoll", "inotify"]

View File

@ -1,48 +1,58 @@
rusty-keys
======
uinput level keyboard mapper for linux, with advanced caps lock and shift swapping behavior
==========
This is the only keymapper I am aware of capable of implementing this layout:
[![Build Status](https://ci.moparisthe.best/job/moparisthebest/job/rusty-keys/job/master/badge/icon%3Fstyle=plastic)](https://ci.moparisthe.best/job/moparisthebest/job/rusty-keys/job/master/)
low level keyboard mapper for linux and windows, with advanced caps lock and shift swapping behavior
This is the only keymapper I am aware of capable of implementing this layout, which I call Unix Programmer's Dvorak, which has been my daily driver since 2014:
![Unix Programmer's Dvorak](https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg)
The Problem
-----------
If you ever have mapped keys on linux, you know that there is the console keymap (loadkeys) and the X keymap (setxkbmap)
If you ever have mapped keys on linux, you know that there is the console keymap (loadkeys) and the X keymap (setxkbmap),
also things like SDL and Virtualbox grab the input directly and respect no maps. Lastly I want to revert to QWERTY when
holding ctrl so ctrl+c works just like normal, without remapping all programs to ctrl+j. Linux keymaps cannot do this either.
The Solution
------------
1. Grab a keyboard device directly so only we can read events from it.
2. Create a new keyboard input device with uinput, this is identical to any other keyboard device to anything running on the box.
3. Read input_events from real device, map them, send them to our created device.
2. Create a new keyboard input device with uinput, this looks identical to any other keyboard device to anything running on the box.
3. Read input_events from the real device, map them, send them to our created device.
This solution is what rusty-keys implements, it works in ttys, in X, in virtualbox even running windows or whatever,
This solution is what rusty-keys implements, it works in ttys, in X, in Wayland, in virtualbox even running windows or whatever,
on SDL games, it will work literally everywhere, because rusty-keys just creates a regular keyboard.
How to run
----------
When ran, it will read a keymap.toml file from your current working directory, refer to example and tweak to suit.
When ran, it will read a keymap.toml configuration file, refer to example and tweak to suit.
```
Usage: rusty-keys [options]
Usage: rusty-keys [options] [device_files...]
Options:
-h, --help prints this help message
-v, --version prints the version
-d, --device DEVICE specify the keyboard input device file
-c, --config FILE specify the keymap config file to use
-c, --config FILE specify the keymap config file to use (default:
/etc/rusty-keys/keymap.toml)
```
with only one keyboard attached:
when ran without specifying input devices, it maps all currently connected keyboards, and watches /dev/input/ with
inotify and starts mapping any new keyboards that are plugged in forever, until you kill it:
`rusty-keys`
with multiple keyboards, currently you must specify one:
`rusty-keys -d /dev/input/event0`
or you can specify one or multiple input devices, and it will run until all are disconnected, then stop:
`rusty-keys /dev/input/event0` or `rusty-keys /dev/input/event0 /dev/input/event2`
find all eligible keyboard devices like:
`grep -E 'Handlers|EV' /proc/bus/input/devices | grep -B1 120013 | grep -Eo event[0-9]+`
An example systemd service is in systemd/rusty-keys.service, enable it to have mapped keyboards all the time.
How to install
--------------
* `cargo install rusty-keys`
* Arch Linux [rusty-keys](https://aur.archlinux.org/packages/rusty-keys/) [rusty-keys-git](https://aur.archlinux.org/packages/rusty-keys-git/)
* Download a static binary for your system from the [releases](https://code.moparisthebest.com/moparisthebest/rusty-keys/releases) section. [github mirror](https://github.com/moparisthebest/rusty-keys/releases)
License
-------

90
appveyor.yml Normal file
View File

@ -0,0 +1,90 @@
# Based on the "trust" template v0.1.2
# https://github.com/japaric/trust/tree/v0.1.2
environment:
global:
# TODO This is the Rust channel that build jobs will use by default but can be
# overridden on a case by case basis down below
RUST_VERSION: stable
# TODO Update this to match the name of your project.
CRATE_NAME: rusty-keys
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
# don't need
matrix:
# MinGW
- TARGET: i686-pc-windows-gnu
- TARGET: x86_64-pc-windows-gnu
# MSVC
- TARGET: i686-pc-windows-msvc
- TARGET: x86_64-pc-windows-msvc
# Testing other channels
- TARGET: x86_64-pc-windows-gnu
RUST_VERSION: nightly
- TARGET: x86_64-pc-windows-msvc
RUST_VERSION: nightly
install:
- ps: >-
If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw64\bin'
} ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw32\bin'
}
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
# TODO This is the "test phase", tweak it as you see fit
test_script:
# we don't run the "test phase" when doing deploys
- if [%APPVEYOR_REPO_TAG%]==[false] (
cargo build --target %TARGET% --release &&
cargo run --target %TARGET% --release --bin rusty-keys -- -h
)
before_deploy:
# TODO Update this to build the artifacts that matter to you
- cargo rustc --target %TARGET% --release --bin rusty-keys -- -C lto
- ps: ci\before_deploy.ps1
deploy:
artifact: /rusty-keys-.*\.exe/
# TODO update `auth_token.secure`
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
# - Encrypt it. Go to https://ci.appveyor.com/tools/encrypt
# - Paste the output down here
auth_token:
secure: gyQW6TqUY94X8IpcQeezbngBQA/PROaCPpr8K+8IxGBG5gf2iHra2CLlp/QJJZYx
description: ''
on:
# TODO Here you can pick which targets will generate binary releases
# In this example, there are some targets that are tested using the stable
# and nightly channels. This condition makes sure there is only one release
# for such targets and that's generated using the stable channel
RUST_VERSION: stable
appveyor_repo_tag: true
provider: GitHub
cache:
- C:\Users\appveyor\.cargo\registry
- target
branches:
only:
# Release tags
- /^v\d+\.\d+\.\d+.*$/
- master
- dev
notifications:
- provider: Email
on_build_success: false
# Building is done in the test phase, so we disable Appveyor's build phase.
build: false

View File

@ -1,76 +0,0 @@
#!/usr/bin/env python
# pressing all of these keys along with a number key representing the index of keymaps changes the layout
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
switch_layout_keys = ['LEFTSHIFT','RIGHTSHIFT']
# pressing QWERTY reverts to the index specified in revert_keymap_index for only the duration of the pressing
# used so QWERTY shortcuts like Ctrl+C still work
revert_default_key = 'LEFTCTRL'
revert_keymap_index = 0
# this is the default index to use when the program first starts
# in this case, 2 means modified Progammer Dvorak
default_keymap_index = 2
# these are keys that caps_lock doesn't modify by default, but that you would like it to, affects all keymaps
caps_lock_modify = """
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, PSLS,PAST,PMNS,
LBRC,RBRC,BSLS, P7, P8, P9,
SCLN,QUOT, P4, P5, P6, PPLS,
COMM,DOT, SLSH, P1, P2, P3,
P0, PDOT
"""
# these are the keymaps available, you can add as many as you want or re-order them, just be aware the mapping is
# always done from the first one to all subsequent ones, so you probably want to leave QWERTY or similar up top
keymaps = [
# default key layout, QWERTY in this case
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9,
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT, ENT, P4, P5, P6, PPLS,
LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3,
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
""",
# Dvorak http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard
# https://www.moparisthebest.com/kbs/standard-dvorak-QwertySecondary.svg
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, LBRC,RBRC,BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
TAB, QUOT,COMM,DOT, P, Y, F, G, C, R, L, SLSH,EQL, BSLS, DEL, END, PGDN, P7, P8, P9,
CAPS,A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4, P5, P6, PPLS,
LSFT,SCLN,Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1, P2, P3,
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
""",
# Unix Dvorak Programmer Dvorak - for unix developers who are switching from dvorak
# https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
^4:^GRV, ^7:1, LBRC:2, ^LBRC:3, ^RBRC:4, ^9:5, ^2:6, ^8:7, ^0:8, ^EQL:9, RBRC:0, ^1:^5, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, EQL:^6, ^BSLS:BSLS, DEL, END, PGDN, P7:^A, P8:^B, P9:^C,
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1:EQL, P2:X, P3:^SCLN,
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
""",
# Unix Dvorak Programmer Dvorak - for unix developers who are switching from dvorak - phone numpad
# https://www.moparisthebest.com/kbs/programmer-dvorak-QwertySecondary-NumpadPhone-NoSwap-StandardNums-SwapAt-SwapPipe.svg
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
^4:^GRV, ^7:1, LBRC:2, ^LBRC:3, ^RBRC:4, ^9:5, ^2:6, ^8:7, ^0:8, ^EQL:9, RBRC:0, ^1:^5, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, EQL:^6, ^BSLS:BSLS, DEL, END, PGDN, P1:^A, P2:^B, P3:^C,
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P7:EQL, P8:X, P9:^SCLN,
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
""",
# Programmer Dvorak http://www.kaufmann.no/roland/dvorak/
# https://www.moparisthebest.com/kbs/programmer-dvorak-QwertySecondary-NumpadPhone-StrictSwap-StrictNums-StrictAt-StrictPipe.svg
"""
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
^4:^GRV, ^7:^5, LBRC:7, ^LBRC:5, ^RBRC:3, ^9:1, EQL:9, ^8:0, ^0:2, ^EQL:4, RBRC:6, ^1:8, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
TAB, SCLN, COMM, DOT, P, Y, F, G, C, R, L, SLSH, ^2:^6, BSLS, DEL, END, PGDN, P1:^A, P2:^B, P3:^C,
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
LSFT, QUOT, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P7:EQL, P8:X, P9:^SCLN,
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
""",
]

View File

@ -3,9 +3,9 @@
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
switch_layout_keys = ['LEFTSHIFT','RIGHTSHIFT']
# pressing QWERTY reverts to the index specified in revert_keymap_index for only the duration of the pressing
# pressing any of these keys reverts to the index specified in revert_keymap_index for only the duration of the pressing
# used so QWERTY shortcuts like Ctrl+C still work
revert_default_key = 'LEFTCTRL'
revert_default_keys = ['LCTL','LGUI','LALT']
revert_keymap_index = 0
# this is the default index to use when the program first starts

40
notes.txt Normal file
View File

@ -0,0 +1,40 @@
After all these years of this technique working perfectly, libinput goes and breaks it (along with apparantly many other things):
https://bugs.freedesktop.org/show_bug.cgi?id=104030
A normal USB keyboard pressing shift+3:
E: 0.000001 0004 0004 458977 # EV_MSC / MSC_SCAN 458977
E: 0.000001 0001 002a 0001 # EV_KEY / KEY_LEFTSHIFT 1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.151990 0004 0004 458784 # EV_MSC / MSC_SCAN 458784
E: 0.151990 0001 0004 0001 # EV_KEY / KEY_3 1
E: 0.151990 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +151ms
#E: 0.327930 0004 0004 458784 # EV_MSC / MSC_SCAN 458784
E: 0.327930 0001 0004 0000 # EV_KEY / KEY_3 0
E: 0.327930 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +176ms
E: 0.400020 0004 0004 458977 # EV_MSC / MSC_SCAN 458977
E: 0.400020 0001 002a 0000 # EV_KEY / KEY_LEFTSHIFT 0
E: 0.400020 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +73ms
rusty-keys pre SYN_REPORT fix:
E: 0.000001 0001 002a 0001 # EV_KEY / KEY_LEFTSHIFT 1
E: 0.000001 0001 001a 0001 # EV_KEY / KEY_LEFTBRACE 1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
{E: 0.031945 0001 001a 0000 # EV_KEY / KEY_LEFTBRACE 0
E: 0.031945 0001 002a 0000 # EV_KEY / KEY_LEFTSHIFT 0
E: 0.031945 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +31ms
rusty-keys post SYN_REPORT fix:
E: 0.000001 0001 002a 0001 # EV_KEY / KEY_LEFTSHIFT 1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.000032 0001 001a 0001 # EV_KEY / KEY_LEFTBRACE 1
E: 0.000032 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
{E: 0.096031 0001 001a 0000 # EV_KEY / KEY_LEFTBRACE 0
E: 0.096031 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +96ms
E: 0.096091 0001 002a 0000 # EV_KEY / KEY_LEFTSHIFT 0
E: 0.096091 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
Seems to be good enough for now...

View File

@ -1,287 +0,0 @@
use std::path::Path;
use std::{mem, slice};
use std::ffi::CString;
use libc::c_int;
use nix::{self, fcntl, unistd};
use nix::sys::stat;
use ffi::*;
use {Result as Res, Error, Device};
use std::collections::hash_map::Values;
#[cfg(feature = "udev")]
use udev;
/// Device builder.
pub struct Builder {
fd: c_int,
def: uinput_user_dev,
abs: Option<c_int>,
}
impl Builder {
/// Create a builder from the specified path.
pub fn open<P: AsRef<Path>>(path: P) -> Res<Self> {
Ok(Builder {
fd: try!(fcntl::open(path.as_ref(), fcntl::O_WRONLY | fcntl::O_NONBLOCK, stat::Mode::empty())),
def: unsafe { mem::zeroed() },
abs: None,
})
}
#[cfg(feature = "udev")]
/// Create a builder from the default path taken from udev.
pub fn default() -> Res<Self> {
let context = try!(udev::Context::new());
let mut enumerator = try!(udev::Enumerator::new(&context));
try!(enumerator.match_subsystem("misc"));
try!(enumerator.match_sysname("uinput"));
let device = try!(try!(enumerator.scan_devices())
.next().ok_or(Error::NotFound));
Builder::open(try!(device.devnode().ok_or(Error::NotFound)))
}
#[cfg(not(feature = "udev"))]
/// Create a builder from `/dev/uinput`.
pub fn default() -> Res<Self> {
Builder::open("/dev/uinput")
}
/// Set the name.
pub fn name<T: AsRef<str>>(mut self, value: T) -> Res<Self> {
let string = try!(CString::new(value.as_ref()));
let bytes = string.as_bytes_with_nul();
if bytes.len() > UINPUT_MAX_NAME_SIZE as usize {
try!(Err(nix::Error::from_errno(nix::Errno::EINVAL)));
}
(&mut self.def.name)[..bytes.len()]
.clone_from_slice(unsafe { mem::transmute(bytes) });
Ok(self)
}
/// Set the bus type.
pub fn bus(mut self, value: u16) -> Self {
self.def.id.bustype = value;
self
}
/// Set the vendor ID.
pub fn vendor(mut self, value: u16) -> Self {
self.def.id.vendor = value;
self
}
/// Set the product ID.
pub fn product(mut self, value: u16) -> Self {
self.def.id.product = value;
self
}
/// Set the version.
pub fn version(mut self, value: u16) -> Self {
self.def.id.version = value;
self
}
pub fn event(mut self, key_codes: Values<&str, *const c_int>) -> Res<Self> {
self.abs = None;
//let test_ev_key : c_int = EV_KEY as c_int;
unsafe {
//try!(Errno::result(ui_set_evbit(self.fd, EV_KEY)));
//try!(Errno::result(ui_set_keybit(self.fd, KEY_H)));
ui_set_evbit(self.fd, EV_KEY as *const c_int)?;
//ui_set_keybit(self.fd, KEY_H as *const c_int)?;
for key_code in key_codes {
ui_set_keybit(self.fd, *key_code)?;
}
//try!(ui_set_keybit(self.fd, &KEY_H));
}
Ok(self)
}
/*
/// Enable the given event.
pub fn event<T: Into<Event>>(mut self, value: T) -> Res<Self> {
self.abs = None;
match value.into() {
Event::All => {
try!(self.event(Event::Keyboard(event::Keyboard::All)))
.event(Event::Controller(event::Controller::All))
}
Event::Keyboard(value) => {
match value {
event::Keyboard::All => {
let mut builder = self;
for item in event::keyboard::Key::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::KeyPad::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Misc::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::InputAssist::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Function::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Braille::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Numeric::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::TouchPad::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Camera::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::keyboard::Attendant::iter_variants() {
builder = try!(builder.event(item));
}
Ok(builder)
}
value => {
unsafe {
try!(Errno::result(ui_set_evbit(self.fd, value.kind())));
try!(Errno::result(ui_set_keybit(self.fd, value.code())));
}
Ok(self)
}
}
}
Event::Controller(value) => {
match value {
event::Controller::All => {
let mut builder = self;
for item in event::controller::Misc::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::Mouse::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::JoyStick::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::GamePad::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::Digi::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::Wheel::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::DPad::iter_variants() {
builder = try!(builder.event(item));
}
for item in event::controller::TriggerHappy::iter_variants() {
builder = try!(builder.event(item));
}
Ok(builder)
}
value => {
unsafe {
try!(Errno::result(ui_set_evbit(self.fd, value.kind())));
try!(Errno::result(ui_set_keybit(self.fd, value.code())));
}
Ok(self)
}
}
}
Event::Relative(value) => {
unsafe {
try!(Errno::result(ui_set_evbit(self.fd, value.kind())));
try!(Errno::result(ui_set_relbit(self.fd, value.code())));
}
Ok(self)
}
Event::Absolute(value) => {
unsafe {
try!(Errno::result(ui_set_evbit(self.fd, value.kind())));
try!(Errno::result(ui_set_absbit(self.fd, value.code())));
}
self.abs = Some(value.code());
Ok(self)
}
}
}
*/
/// Set the maximum value for the previously enabled absolute event.
pub fn max(mut self, value: i32) -> Self {
self.def.absmax[self.abs.unwrap() as usize] = value;
self
}
/// Set the minimum value for the previously enabled absolute event.
pub fn min(mut self, value: i32) -> Self {
self.def.absmin[self.abs.unwrap() as usize] = value;
self
}
/// Set the fuzz value for the previously enabled absolute event.
pub fn fuzz(mut self, value: i32) -> Self {
self.def.absfuzz[self.abs.unwrap() as usize] = value;
self
}
/// Set the flat value for the previously enabled absolute event.
pub fn flat(mut self, value: i32) -> Self {
self.def.absflat[self.abs.unwrap() as usize] = value;
self
}
/// Create the defined device.
pub fn create(self) -> Res<Device> {
unsafe {
let ptr = &self.def as *const _ as *const u8;
let size = mem::size_of_val(&self.def);
try!(unistd::write(self.fd, slice::from_raw_parts(ptr, size)));
//todo: try!(Errno::result(ui_dev_create(self.fd)));
// try1: Errno::result(ui_dev_create(self.fd)).unwrap();
try!(ui_dev_create(self.fd));
}
Ok(Device::new(self.fd))
}
}

View File

@ -1,87 +0,0 @@
use std::{mem, ptr, slice};
use libc::c_int;
use libc::{timeval, gettimeofday, input_event};
use nix::unistd;
use ffi::*;
use {Result as Res};
/// The virtual device.
pub struct Device {
fd: c_int,
}
impl Device {
/// Wrap a file descriptor in a `Device`.
pub fn new(fd: c_int) -> Self {
Device {
fd: fd
}
}
#[doc(hidden)]
pub fn write(&mut self, kind: c_int, code: c_int, value: c_int) -> Res<()> {
let event = input_event {
time: timeval { tv_sec: 0, tv_usec: 0 },
type_: kind as u16,
code: code as u16,
value: value as i32,
};
self.write_event(event)
}
#[doc(hidden)]
pub fn write_event(&self, mut event: input_event) -> Res<()> {
unsafe {
gettimeofday(&mut event.time, ptr::null_mut());
let ptr = &event as *const _ as *const u8;
let size = mem::size_of_val(&event);
try!(unistd::write(self.fd, slice::from_raw_parts(ptr, size)));
}
Ok(())
}
/// Synchronize the device.
pub fn synchronize(&mut self) -> Res<()> {
self.write(EV_SYN, SYN_REPORT, 0)
}
/// Send an event.
pub fn send(&mut self, kind: c_int, code: c_int, value: i32) -> Res<()> {
self.write(kind, code, value)
}
/// Send a press event.
pub fn press(&mut self, kind: c_int, code: c_int) -> Res<()> {
self.write(kind, code, 1)
}
/// Send a release event.
pub fn release(&mut self, kind: c_int, code: c_int) -> Res<()> {
self.write(kind, code, 0)
}
/// Send a press and release event.
pub fn click(&mut self, kind: c_int, code: c_int) -> Res<()> {
try!(self.press(kind, code));
try!(self.release(kind, code));
Ok(())
}
/// Send a relative or absolute positioning event.
pub fn position(&mut self, kind: c_int, code: c_int, value: i32) -> Res<()> {
self.write(kind, code, value)
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
ui_dev_destroy(self.fd).unwrap();
}
}
}

View File

@ -1,5 +0,0 @@
mod builder;
pub use self::builder::Builder;
mod device;
pub use self::device::Device;

View File

@ -1,26 +1,33 @@
use std::fmt;
use std::error;
use std::ffi;
use nix;
use std::io;
#[cfg(feature = "udev")]
use udev;
#[cfg(target_os = "linux")]
use nix;
/// UInput error.
#[derive(Debug)]
pub enum Error {
/// System errors.
#[cfg(target_os = "linux")]
Nix(nix::Error),
/// Errors with internal nulls in names.
Nul(ffi::NulError),
#[cfg(feature = "udev")]
/// Errors coming from udev.
Udev(udev::Error),
Io(io::Error),
#[cfg(feature = "toml_serde")]
Toml(toml::de::Error),
/// The uinput file could not be found.
NotFound,
NotAKeyboard,
/// error reading input_event
ShortRead,
/// epoll already added
EpollAlreadyAdded,
}
impl From<ffi::NulError> for Error {
@ -29,40 +36,39 @@ impl From<ffi::NulError> for Error {
}
}
#[cfg(target_os = "linux")]
impl From<nix::Error> for Error {
fn from(value: nix::Error) -> Self {
Error::Nix(value)
}
}
#[cfg(feature = "udev")]
impl From<udev::Error> for Error {
fn from(value: udev::Error) -> Self {
Error::Udev(value)
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Error::Io(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(error::Error::description(self))
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::Nix(ref err) =>
err.description(),
#[cfg(target_os = "linux")]
&Error::Nix(ref err) => err.fmt(f),
&Error::Nul(ref err) =>
err.description(),
&Error::Nul(ref err) => err.fmt(f),
#[cfg(feature = "udev")]
&Error::Udev(ref err) =>
err.description(),
&Error::Io(ref err) => err.fmt(f),
&Error::NotFound =>
"Device not found.",
#[cfg(feature = "toml_serde")]
&Error::Toml(ref err) => err.fmt(f),
&Error::NotAKeyboard => f.write_str("This device file is not a keyboard"),
&Error::ShortRead => f.write_str("Error while reading from device file."),
&Error::EpollAlreadyAdded => f.write_str("epoll already added, delete first"),
}
}
}
impl error::Error for Error {}

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,27 @@
#![recursion_limit = "1000"]
extern crate libc;
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
extern crate uinput_sys as ffi;
extern crate nix;
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "udev")]
extern crate libudev as udev;
use std::path::Path;
mod error;
pub mod error;
pub use error::Error;
pub type Result<T> = ::std::result::Result<T, Error>;
pub mod keymapper;
pub use keymapper::KeyMaps;
pub use keymapper::*;
pub mod device;
pub use device::Device;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::*;
/// Open the default uinput device.
pub fn default() -> Result<device::Builder> {
device::Builder::default()
}
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
pub use linux::*;
/// Open the specified uinput device.
pub fn open<P: AsRef<Path>>(path: P) -> Result<device::Builder> {
device::Builder::open(path)
}
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::*;

151
src/linux/device/builder.rs Normal file
View File

@ -0,0 +1,151 @@
use std::path::Path;
use std::{mem, slice};
use std::ffi::CString;
use libc::c_int;
use nix::{self, fcntl, unistd, ioctl_write_ptr, ioctl_none};
use nix::sys::stat;
use crate::{Result, Device};
use std::collections::hash_map::Values;
use std::os::raw::c_char;
use crate::linux::device::codes::*;
ioctl_write_ptr!(ui_set_evbit, b'U', 100, c_int);
ioctl_write_ptr!(ui_set_keybit, b'U', 101, c_int);
ioctl_none!(ui_dev_create, b'U', 1);
pub const UINPUT_MAX_NAME_SIZE: c_int = 80;
pub const ABS_MAX: c_int = 0x3f;
pub const ABS_CNT: c_int = ABS_MAX + 1;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct input_id {
pub bustype: u16,
pub vendor: u16,
pub product: u16,
pub version: u16,
}
#[repr(C)]
pub struct uinput_user_dev {
pub name: [c_char; UINPUT_MAX_NAME_SIZE as usize],
pub id: input_id,
pub ff_effects_max: u32,
pub absmax: [i32; ABS_CNT as usize],
pub absmin: [i32; ABS_CNT as usize],
pub absfuzz: [i32; ABS_CNT as usize],
pub absflat: [i32; ABS_CNT as usize],
}
/// Device builder.
pub struct Builder {
fd: c_int,
def: uinput_user_dev,
abs: Option<c_int>,
}
impl Builder {
/// Create a builder from the specified path.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Builder {
fd: fcntl::open(path.as_ref(), fcntl::OFlag::O_WRONLY | fcntl::OFlag::O_NONBLOCK, stat::Mode::empty())?,
def: unsafe { mem::zeroed() },
abs: None,
})
}
/// Create a builder from `/dev/uinput`.
pub fn default() -> Result<Self> {
Builder::open("/dev/uinput")
}
/// Set the name.
pub fn name<T: AsRef<str>>(mut self, value: T) -> Result<Self> {
let string = CString::new(value.as_ref())?;
let bytes = string.as_bytes_with_nul();
if bytes.len() > UINPUT_MAX_NAME_SIZE as usize {
Err(nix::Error::EINVAL)?;
}
(&mut self.def.name)[..bytes.len()]
.clone_from_slice(unsafe { mem::transmute(bytes) });
Ok(self)
}
/// Set the bus type.
pub fn bus(mut self, value: u16) -> Self {
self.def.id.bustype = value;
self
}
/// Set the vendor ID.
pub fn vendor(mut self, value: u16) -> Self {
self.def.id.vendor = value;
self
}
/// Set the product ID.
pub fn product(mut self, value: u16) -> Self {
self.def.id.product = value;
self
}
/// Set the version.
pub fn version(mut self, value: u16) -> Self {
self.def.id.version = value;
self
}
pub fn event(mut self, key_codes: Values<&str, u16>) -> Result<Self> {
self.abs = None;
unsafe {
ui_set_evbit(self.fd, EV_KEY as *const c_int)?;
for key_code in key_codes {
ui_set_keybit(self.fd, *key_code as *const c_int)?;
}
}
Ok(self)
}
/// Set the maximum value for the previously enabled absolute event.
pub fn max(mut self, value: i32) -> Self {
self.def.absmax[self.abs.unwrap() as usize] = value;
self
}
/// Set the minimum value for the previously enabled absolute event.
pub fn min(mut self, value: i32) -> Self {
self.def.absmin[self.abs.unwrap() as usize] = value;
self
}
/// Set the fuzz value for the previously enabled absolute event.
pub fn fuzz(mut self, value: i32) -> Self {
self.def.absfuzz[self.abs.unwrap() as usize] = value;
self
}
/// Set the flat value for the previously enabled absolute event.
pub fn flat(mut self, value: i32) -> Self {
self.def.absflat[self.abs.unwrap() as usize] = value;
self
}
/// Create the defined device.
pub fn create(self) -> Result<Device> {
unsafe {
let ptr = &self.def as *const _ as *const u8;
let size = mem::size_of_val(&self.def);
unistd::write(self.fd, slice::from_raw_parts(ptr, size))?;
ui_dev_create(self.fd)?;
}
Ok(Device::new(self.fd))
}
}

780
src/linux/device/codes.rs Normal file
View File

@ -0,0 +1,780 @@
use libc::{c_int};
pub const INPUT_PROP_POINTER: c_int = 0x00; /* needs a pointer */
pub const INPUT_PROP_DIRECT: c_int = 0x01; /* direct input devices */
pub const INPUT_PROP_BUTTONPAD: c_int = 0x02; /* has button: c_int = s under pad */
pub const INPUT_PROP_SEMI_MT: c_int = 0x03; /* touch rectangle only */
pub const INPUT_PROP_TOPBUTTONPAD: c_int = 0x04; /* softbuttons at top of pad */
pub const INPUT_PROP_POINTING_STICK: c_int = 0x05; /* is a pointing stick */
pub const INPUT_PROP_ACCELEROMETER: c_int = 0x06; /* has accelerometer */
pub const INPUT_PROP_MAX: c_int = 0x1f;
pub const INPUT_PROP_CNT: c_int = INPUT_PROP_MAX + 1;
/*
* Event types
*/
pub const EV_SYN: c_int = 0x00;
pub const EV_KEY: c_int = 0x01;
pub const EV_REL: c_int = 0x02;
pub const EV_ABS: c_int = 0x03;
pub const EV_MSC: c_int = 0x04;
pub const EV_SW: c_int = 0x05;
pub const EV_LED: c_int = 0x11;
pub const EV_SND: c_int = 0x12;
pub const EV_REP: c_int = 0x14;
pub const EV_FF: c_int = 0x15;
pub const EV_PWR: c_int = 0x16;
pub const EV_FF_STATUS: c_int = 0x17;
pub const EV_MAX: c_int = 0x1f;
pub const EV_CNT: c_int = EV_MAX + 1;
/*
* Synchronization events.
*/
pub const SYN_REPORT: c_int = 0;
pub const SYN_CONFIG: c_int = 1;
pub const SYN_MT_REPORT: c_int = 2;
pub const SYN_DROPPED: c_int = 3;
pub const SYN_MAX: c_int = 0xf;
pub const SYN_CNT: c_int = SYN_MAX + 1;
/*
* Keys and buttons
*
* Most of the keys/buttons are modeled after USB HUT 1.12 (see
* http://www.usb.org/developers/hidpage).
* Abbreviations in the comments:
* AC - Application Control
* AL - Application Launch Button
* SC - System Control
*/
pub const KEY_RESERVED: c_int = 0;
pub const KEY_ESC: c_int = 1;
pub const KEY_1: c_int = 2;
pub const KEY_2: c_int = 3;
pub const KEY_3: c_int = 4;
pub const KEY_4: c_int = 5;
pub const KEY_5: c_int = 6;
pub const KEY_6: c_int = 7;
pub const KEY_7: c_int = 8;
pub const KEY_8: c_int = 9;
pub const KEY_9: c_int = 10;
pub const KEY_10: c_int = 11;
pub const KEY_MINUS: c_int = 12;
pub const KEY_EQUAL: c_int = 13;
pub const KEY_BACKSPACE: c_int = 14;
pub const KEY_TAB: c_int = 15;
pub const KEY_Q: c_int = 16;
pub const KEY_W: c_int = 17;
pub const KEY_E: c_int = 18;
pub const KEY_R: c_int = 19;
pub const KEY_T: c_int = 20;
pub const KEY_Y: c_int = 21;
pub const KEY_U: c_int = 22;
pub const KEY_I: c_int = 23;
pub const KEY_O: c_int = 24;
pub const KEY_P: c_int = 25;
pub const KEY_LEFTBRACE: c_int = 26;
pub const KEY_RIGHTBRACE: c_int = 27;
pub const KEY_ENTER: c_int = 28;
pub const KEY_LEFTCTRL: c_int = 29;
pub const KEY_A: c_int = 30;
pub const KEY_S: c_int = 31;
pub const KEY_D: c_int = 32;
pub const KEY_F: c_int = 33;
pub const KEY_G: c_int = 34;
pub const KEY_H: c_int = 35;
pub const KEY_J: c_int = 36;
pub const KEY_K: c_int = 37;
pub const KEY_L: c_int = 38;
pub const KEY_SEMICOLON: c_int = 39;
pub const KEY_APOSTROPHE: c_int = 40;
pub const KEY_GRAVE: c_int = 41;
pub const KEY_LEFTSHIFT: c_int = 42;
pub const KEY_BACKSLASH: c_int = 43;
pub const KEY_Z: c_int = 44;
pub const KEY_X: c_int = 45;
pub const KEY_C: c_int = 46;
pub const KEY_V: c_int = 47;
pub const KEY_B: c_int = 48;
pub const KEY_N: c_int = 49;
pub const KEY_M: c_int = 50;
pub const KEY_COMMA: c_int = 51;
pub const KEY_DOT: c_int = 52;
pub const KEY_SLASH: c_int = 53;
pub const KEY_RIGHTSHIFT: c_int = 54;
pub const KEY_KPASTERISK: c_int = 55;
pub const KEY_LEFTALT: c_int = 56;
pub const KEY_SPACE: c_int = 57;
pub const KEY_CAPSLOCK: c_int = 58;
pub const KEY_F1: c_int = 59;
pub const KEY_F2: c_int = 60;
pub const KEY_F3: c_int = 61;
pub const KEY_F4: c_int = 62;
pub const KEY_F5: c_int = 63;
pub const KEY_F6: c_int = 64;
pub const KEY_F7: c_int = 65;
pub const KEY_F8: c_int = 66;
pub const KEY_F9: c_int = 67;
pub const KEY_F10: c_int = 68;
pub const KEY_NUMLOCK: c_int = 69;
pub const KEY_SCROLLLOCK: c_int = 70;
pub const KEY_KP7: c_int = 71;
pub const KEY_KP8: c_int = 72;
pub const KEY_KP9: c_int = 73;
pub const KEY_KPMINUS: c_int = 74;
pub const KEY_KP4: c_int = 75;
pub const KEY_KP5: c_int = 76;
pub const KEY_KP6: c_int = 77;
pub const KEY_KPPLUS: c_int = 78;
pub const KEY_KP1: c_int = 79;
pub const KEY_KP2: c_int = 80;
pub const KEY_KP3: c_int = 81;
pub const KEY_KP0: c_int = 82;
pub const KEY_KPDOT: c_int = 83;
pub const KEY_ZENKAKUHANKAKU: c_int = 85;
pub const KEY_102ND: c_int = 86;
pub const KEY_F11: c_int = 87;
pub const KEY_F12: c_int = 88;
pub const KEY_RO: c_int = 89;
pub const KEY_KATAKANA: c_int = 90;
pub const KEY_HIRAGANA: c_int = 91;
pub const KEY_HENKAN: c_int = 92;
pub const KEY_KATAKANAHIRAGANA: c_int = 93;
pub const KEY_MUHENKAN: c_int = 94;
pub const KEY_KPJPCOMMA: c_int = 95;
pub const KEY_KPENTER: c_int = 96;
pub const KEY_RIGHTCTRL: c_int = 97;
pub const KEY_KPSLASH: c_int = 98;
pub const KEY_SYSRQ: c_int = 99;
pub const KEY_RIGHTALT: c_int = 100;
pub const KEY_LINEFEED: c_int = 101;
pub const KEY_HOME: c_int = 102;
pub const KEY_UP: c_int = 103;
pub const KEY_PAGEUP: c_int = 104;
pub const KEY_LEFT: c_int = 105;
pub const KEY_RIGHT: c_int = 106;
pub const KEY_END: c_int = 107;
pub const KEY_DOWN: c_int = 108;
pub const KEY_PAGEDOWN: c_int = 109;
pub const KEY_INSERT: c_int = 110;
pub const KEY_DELETE: c_int = 111;
pub const KEY_MACRO: c_int = 112;
pub const KEY_MUTE: c_int = 113;
pub const KEY_VOLUMEDOWN: c_int = 114;
pub const KEY_VOLUMEUP: c_int = 115;
pub const KEY_POWER: c_int = 116; /* SC System Power Down */
pub const KEY_KPEQUAL: c_int = 117;
pub const KEY_KPPLUSMINUS: c_int = 118;
pub const KEY_PAUSE: c_int = 119;
pub const KEY_SCALE: c_int = 120; /* AL Compiz Scale : c_int = Expose */
pub const KEY_KPCOMMA: c_int = 121;
pub const KEY_HANGEUL: c_int = 122;
pub const KEY_HANGUEL: c_int = KEY_HANGEUL;
pub const KEY_HANJA: c_int = 123;
pub const KEY_YEN: c_int = 124;
pub const KEY_LEFTMETA: c_int = 125;
pub const KEY_RIGHTMETA: c_int = 126;
pub const KEY_COMPOSE: c_int = 127;
pub const KEY_STOP: c_int = 128; /* AC Stop */
pub const KEY_AGAIN: c_int = 129;
pub const KEY_PROPS: c_int = 130; /* AC Properties */
pub const KEY_UNDO: c_int = 131; /* AC Undo */
pub const KEY_FRONT: c_int = 132;
pub const KEY_COPY: c_int = 133; /* AC Copy */
pub const KEY_OPEN: c_int = 134; /* AC Open */
pub const KEY_PASTE: c_int = 135; /* AC Paste */
pub const KEY_FIND: c_int = 136; /* AC Search */
pub const KEY_CUT: c_int = 137; /* AC Cut */
pub const KEY_HELP: c_int = 138; /* AL Integrated Help Center */
pub const KEY_MENU: c_int = 139; /* Menu : c_int = show menu */
pub const KEY_CALC: c_int = 140; /* AL Calculator */
pub const KEY_SETUP: c_int = 141;
pub const KEY_SLEEP: c_int = 142; /* SC System Sleep */
pub const KEY_WAKEUP: c_int = 143; /* System Wake Up */
pub const KEY_FILE: c_int = 144; /* AL Local Machine Browser */
pub const KEY_SENDFILE: c_int = 145;
pub const KEY_DELETEFILE: c_int = 146;
pub const KEY_XFER: c_int = 147;
pub const KEY_PROG1: c_int = 148;
pub const KEY_PROG2: c_int = 149;
pub const KEY_WWW: c_int = 150; /* AL Internet Browser */
pub const KEY_MSDOS: c_int = 151;
pub const KEY_COFFEE: c_int = 152; /* AL Terminal Lock/Screensaver */
pub const KEY_SCREENLOCK: c_int = KEY_COFFEE;
pub const KEY_ROTATE_DISPLAY: c_int = 153; /* Display orientation for e.g. tablets */
pub const KEY_DIRECTION: c_int = KEY_ROTATE_DISPLAY;
pub const KEY_CYCLEWINDOWS: c_int = 154;
pub const KEY_MAIL: c_int = 155;
pub const KEY_BOOKMARKS: c_int = 156; /* AC Bookmarks */
pub const KEY_COMPUTER: c_int = 157;
pub const KEY_BACK: c_int = 158; /* AC Back */
pub const KEY_FORWARD: c_int = 159; /* AC Forward */
pub const KEY_CLOSECD: c_int = 160;
pub const KEY_EJECTCD: c_int = 161;
pub const KEY_EJECTCLOSECD: c_int = 162;
pub const KEY_NEXTSONG: c_int = 163;
pub const KEY_PLAYPAUSE: c_int = 164;
pub const KEY_PREVIOUSSONG: c_int = 165;
pub const KEY_STOPCD: c_int = 166;
pub const KEY_RECORD: c_int = 167;
pub const KEY_REWIND: c_int = 168;
pub const KEY_PHONE: c_int = 169; /* Media Select Telephone */
pub const KEY_ISO: c_int = 170;
pub const KEY_CONFIG: c_int = 171; /* AL Consumer Control Configuration */
pub const KEY_HOMEPAGE: c_int = 172; /* AC Home */
pub const KEY_REFRESH: c_int = 173; /* AC Refresh */
pub const KEY_EXIT: c_int = 174; /* AC Exit */
pub const KEY_MOVE: c_int = 175;
pub const KEY_EDIT: c_int = 176;
pub const KEY_SCROLLUP: c_int = 177;
pub const KEY_SCROLLDOWN: c_int = 178;
pub const KEY_KPLEFTPAREN: c_int = 179;
pub const KEY_KPRIGHTPAREN: c_int = 180;
pub const KEY_NEW: c_int = 181; /* AC New */
pub const KEY_REDO: c_int = 182; /* AC Redo/Repeat */
pub const KEY_F13: c_int = 183;
pub const KEY_F14: c_int = 184;
pub const KEY_F15: c_int = 185;
pub const KEY_F16: c_int = 186;
pub const KEY_F17: c_int = 187;
pub const KEY_F18: c_int = 188;
pub const KEY_F19: c_int = 189;
pub const KEY_F20: c_int = 190;
pub const KEY_F21: c_int = 191;
pub const KEY_F22: c_int = 192;
pub const KEY_F23: c_int = 193;
pub const KEY_F24: c_int = 194;
pub const KEY_PLAYCD: c_int = 200;
pub const KEY_PAUSECD: c_int = 201;
pub const KEY_PROG3: c_int = 202;
pub const KEY_PROG4: c_int = 203;
pub const KEY_DASHBOARD: c_int = 204; /* AL Dashboard */
pub const KEY_SUSPEND: c_int = 205;
pub const KEY_CLOSE: c_int = 206; /* AC Close */
pub const KEY_PLAY: c_int = 207;
pub const KEY_FASTFORWARD: c_int = 208;
pub const KEY_BASSBOOST: c_int = 209;
pub const KEY_PRINT: c_int = 210; /* AC Print */
pub const KEY_HP: c_int = 211;
pub const KEY_CAMERA: c_int = 212;
pub const KEY_SOUND: c_int = 213;
pub const KEY_QUESTION: c_int = 214;
pub const KEY_EMAIL: c_int = 215;
pub const KEY_CHAT: c_int = 216;
pub const KEY_SEARCH: c_int = 217;
pub const KEY_CONNECT: c_int = 218;
pub const KEY_FINANCE: c_int = 219; /* AL Checkbook/Finance */
pub const KEY_SPORT: c_int = 220;
pub const KEY_SHOP: c_int = 221;
pub const KEY_ALTERASE: c_int = 222;
pub const KEY_CANCEL: c_int = 223; /* AC Cancel */
pub const KEY_BRIGHTNESSDOWN: c_int = 224;
pub const KEY_BRIGHTNESSUP: c_int = 225;
pub const KEY_MEDIA: c_int = 226;
pub const KEY_SWITCHVIDEOMODE: c_int = 227; /* Cycle between available video outputs (Monitor/LCD/TV-out/etc) */
pub const KEY_KBDILLUMTOGGLE: c_int = 228;
pub const KEY_KBDILLUMDOWN: c_int = 229;
pub const KEY_KBDILLUMUP: c_int = 230;
pub const KEY_SEND: c_int = 231; /* AC Send */
pub const KEY_REPLY: c_int = 232; /* AC Reply */
pub const KEY_FORWARDMAIL: c_int = 233; /* AC Forward Msg */
pub const KEY_SAVE: c_int = 234; /* AC Save */
pub const KEY_DOCUMENTS: c_int = 235;
pub const KEY_BATTERY: c_int = 236;
pub const KEY_BLUETOOTH: c_int = 237;
pub const KEY_WLAN: c_int = 238;
pub const KEY_UWB: c_int = 239;
pub const KEY_UNKNOWN: c_int = 240;
pub const KEY_VIDEO_NEXT: c_int = 241; /* drive next video source */
pub const KEY_VIDEO_PREV: c_int = 242; /* drive previous video source */
pub const KEY_BRIGHTNESS_CYCLE: c_int = 243; /* brightness up, after max is min */
pub const KEY_BRIGHTNESS_AUTO: c_int = 244; /* Set Auto Brightness: manual brightness control is off, rely on ambient */
pub const KEY_BRIGHTNESS_ZERO: c_int = KEY_BRIGHTNESS_AUTO;
pub const KEY_DISPLAY_OFF: c_int = 245; /* display device to off state */
pub const KEY_WWAN: c_int = 246; /* Wireless WAN : c_int = LTE, UMTS, GSM, etc. */
pub const KEY_WIMAX: c_int = KEY_WWAN;
pub const KEY_RFKILL: c_int = 247; /* Key that controls all radios */
pub const KEY_MICMUTE: c_int = 248; /* Mute / unmute the microphone */
/* Code 255 is reserved for special needs of AT keyboard driver */
pub const BTN_MISC: c_int = 0x100;
pub const BTN_0: c_int = 0x100;
pub const BTN_1: c_int = 0x101;
pub const BTN_2: c_int = 0x102;
pub const BTN_3: c_int = 0x103;
pub const BTN_4: c_int = 0x104;
pub const BTN_5: c_int = 0x105;
pub const BTN_6: c_int = 0x106;
pub const BTN_7: c_int = 0x107;
pub const BTN_8: c_int = 0x108;
pub const BTN_9: c_int = 0x109;
pub const BTN_MOUSE: c_int = 0x110;
pub const BTN_LEFT: c_int = 0x110;
pub const BTN_RIGHT: c_int = 0x111;
pub const BTN_MIDDLE: c_int = 0x112;
pub const BTN_SIDE: c_int = 0x113;
pub const BTN_EXTRA: c_int = 0x114;
pub const BTN_FORWARD: c_int = 0x115;
pub const BTN_BACK: c_int = 0x116;
pub const BTN_TASK: c_int = 0x117;
pub const BTN_JOYSTICK: c_int = 0x120;
pub const BTN_TRIGGER: c_int = 0x120;
pub const BTN_THUMB: c_int = 0x121;
pub const BTN_THUMB2: c_int = 0x122;
pub const BTN_TOP: c_int = 0x123;
pub const BTN_TOP2: c_int = 0x124;
pub const BTN_PINKIE: c_int = 0x125;
pub const BTN_BASE: c_int = 0x126;
pub const BTN_BASE2: c_int = 0x127;
pub const BTN_BASE3: c_int = 0x128;
pub const BTN_BASE4: c_int = 0x129;
pub const BTN_BASE5: c_int = 0x12a;
pub const BTN_BASE6: c_int = 0x12b;
pub const BTN_DEAD: c_int = 0x12f;
pub const BTN_GAMEPAD: c_int = 0x130;
pub const BTN_SOUTH: c_int = 0x130;
pub const BTN_A: c_int = BTN_SOUTH;
pub const BTN_EAST: c_int = 0x131;
pub const BTN_B: c_int = BTN_EAST;
pub const BTN_C: c_int = 0x132;
pub const BTN_NORTH: c_int = 0x133;
pub const BTN_X: c_int = BTN_NORTH;
pub const BTN_WEST: c_int = 0x134;
pub const BTN_Y: c_int = BTN_WEST;
pub const BTN_Z: c_int = 0x135;
pub const BTN_TL: c_int = 0x136;
pub const BTN_TR: c_int = 0x137;
pub const BTN_TL2: c_int = 0x138;
pub const BTN_TR2: c_int = 0x139;
pub const BTN_SELECT: c_int = 0x13a;
pub const BTN_START: c_int = 0x13b;
pub const BTN_MODE: c_int = 0x13c;
pub const BTN_THUMBL: c_int = 0x13d;
pub const BTN_THUMBR: c_int = 0x13e;
pub const BTN_DIGI: c_int = 0x140;
pub const BTN_TOOL_PEN: c_int = 0x140;
pub const BTN_TOOL_RUBBER: c_int = 0x141;
pub const BTN_TOOL_BRUSH: c_int = 0x142;
pub const BTN_TOOL_PENCIL: c_int = 0x143;
pub const BTN_TOOL_AIRBRUSH: c_int = 0x144;
pub const BTN_TOOL_FINGER: c_int = 0x145;
pub const BTN_TOOL_MOUSE: c_int = 0x146;
pub const BTN_TOOL_LENS: c_int = 0x147;
pub const BTN_TOOL_QUINTTAP: c_int = 0x148; /* Five fingers on trackpad */
pub const BTN_TOUCH: c_int = 0x14a;
pub const BTN_STYLUS: c_int = 0x14b;
pub const BTN_STYLUS2: c_int = 0x14c;
pub const BTN_TOOL_DOUBLETAP: c_int = 0x14d;
pub const BTN_TOOL_TRIPLETAP: c_int = 0x14e;
pub const BTN_TOOL_QUADTAP: c_int = 0x14f; /* Four fingers on trackpad */
pub const BTN_WHEEL: c_int = 0x150;
pub const BTN_GEAR_DOWN: c_int = 0x150;
pub const BTN_GEAR_UP: c_int = 0x151;
pub const KEY_OK: c_int = 0x160;
pub const KEY_SELECT: c_int = 0x161;
pub const KEY_GOTO: c_int = 0x162;
pub const KEY_CLEAR: c_int = 0x163;
pub const KEY_POWER2: c_int = 0x164;
pub const KEY_OPTION: c_int = 0x165;
pub const KEY_INFO: c_int = 0x166; /* AL OEM Features/Tips/Tutorial */
pub const KEY_TIME: c_int = 0x167;
pub const KEY_VENDOR: c_int = 0x168;
pub const KEY_ARCHIVE: c_int = 0x169;
pub const KEY_PROGRAM: c_int = 0x16a; /* Media Select Program Guide */
pub const KEY_CHANNEL: c_int = 0x16b;
pub const KEY_FAVORITES: c_int = 0x16c;
pub const KEY_EPG: c_int = 0x16d;
pub const KEY_PVR: c_int = 0x16e; /* Media Select Home */
pub const KEY_MHP: c_int = 0x16f;
pub const KEY_LANGUAGE: c_int = 0x170;
pub const KEY_TITLE: c_int = 0x171;
pub const KEY_SUBTITLE: c_int = 0x172;
pub const KEY_ANGLE: c_int = 0x173;
pub const KEY_ZOOM: c_int = 0x174;
pub const KEY_MODE: c_int = 0x175;
pub const KEY_KEYBOARD: c_int = 0x176;
pub const KEY_SCREEN: c_int = 0x177;
pub const KEY_PC: c_int = 0x178; /* Media Select Computer */
pub const KEY_TV: c_int = 0x179; /* Media Select TV */
pub const KEY_TV2: c_int = 0x17a; /* Media Select Cable */
pub const KEY_VCR: c_int = 0x17b; /* Media Select VCR */
pub const KEY_VCR2: c_int = 0x17c; /* VCR Plus */
pub const KEY_SAT: c_int = 0x17d; /* Media Select Satellite */
pub const KEY_SAT2: c_int = 0x17e;
pub const KEY_CD: c_int = 0x17f; /* Media Select CD */
pub const KEY_TAPE: c_int = 0x180; /* Media Select Tape */
pub const KEY_RADIO: c_int = 0x181;
pub const KEY_TUNER: c_int = 0x182; /* Media Select Tuner */
pub const KEY_PLAYER: c_int = 0x183;
pub const KEY_TEXT: c_int = 0x184;
pub const KEY_DVD: c_int = 0x185; /* Media Select DVD */
pub const KEY_AUX: c_int = 0x186;
pub const KEY_MP3: c_int = 0x187;
pub const KEY_AUDIO: c_int = 0x188; /* AL Audio Browser */
pub const KEY_VIDEO: c_int = 0x189; /* AL Movie Browser */
pub const KEY_DIRECTORY: c_int = 0x18a;
pub const KEY_LIST: c_int = 0x18b;
pub const KEY_MEMO: c_int = 0x18c; /* Media Select Messages */
pub const KEY_CALENDAR: c_int = 0x18d;
pub const KEY_RED: c_int = 0x18e;
pub const KEY_GREEN: c_int = 0x18f;
pub const KEY_YELLOW: c_int = 0x190;
pub const KEY_BLUE: c_int = 0x191;
pub const KEY_CHANNELUP: c_int = 0x192; /* Channel Increment */
pub const KEY_CHANNELDOWN: c_int = 0x193; /* Channel Decrement */
pub const KEY_FIRST: c_int = 0x194;
pub const KEY_LAST: c_int = 0x195; /* Recall Last */
pub const KEY_AB: c_int = 0x196;
pub const KEY_NEXT: c_int = 0x197;
pub const KEY_RESTART: c_int = 0x198;
pub const KEY_SLOW: c_int = 0x199;
pub const KEY_SHUFFLE: c_int = 0x19a;
pub const KEY_BREAK: c_int = 0x19b;
pub const KEY_PREVIOUS: c_int = 0x19c;
pub const KEY_DIGITS: c_int = 0x19d;
pub const KEY_TEEN: c_int = 0x19e;
pub const KEY_TWEN: c_int = 0x19f;
pub const KEY_VIDEOPHONE: c_int = 0x1a0; /* Media Select Video Phone */
pub const KEY_GAMES: c_int = 0x1a1; /* Media Select Games */
pub const KEY_ZOOMIN: c_int = 0x1a2; /* AC Zoom In */
pub const KEY_ZOOMOUT: c_int = 0x1a3; /* AC Zoom Out */
pub const KEY_ZOOMRESET: c_int = 0x1a4; /* AC Zoom */
pub const KEY_WORDPROCESSOR: c_int = 0x1a5; /* AL Word Processor */
pub const KEY_EDITOR: c_int = 0x1a6; /* AL Text Editor */
pub const KEY_SPREADSHEET: c_int = 0x1a7; /* AL Spreadsheet */
pub const KEY_GRAPHICSEDITOR: c_int = 0x1a8; /* AL Graphics Editor */
pub const KEY_PRESENTATION: c_int = 0x1a9; /* AL Presentation App */
pub const KEY_DATABASE: c_int = 0x1aa; /* AL Database App */
pub const KEY_NEWS: c_int = 0x1ab; /* AL Newsreader */
pub const KEY_VOICEMAIL: c_int = 0x1ac; /* AL Voicemail */
pub const KEY_ADDRESSBOOK: c_int = 0x1ad; /* AL Contacts/Address Book */
pub const KEY_MESSENGER: c_int = 0x1ae; /* AL Instant Messaging */
pub const KEY_DISPLAYTOGGLE: c_int = 0x1af; /* Turn display : c_int = LCD on and off */
pub const KEY_BRIGHTNESS_TOGGLE: c_int = KEY_DISPLAYTOGGLE;
pub const KEY_SPELLCHECK: c_int = 0x1b0; /* AL Spell Check */
pub const KEY_LOGOFF: c_int = 0x1b1; /* AL Logoff */
pub const KEY_DOLLAR: c_int = 0x1b2;
pub const KEY_EURO: c_int = 0x1b3;
pub const KEY_FRAMEBACK: c_int = 0x1b4; /* Consumer - transport controls */
pub const KEY_FRAMEFORWARD: c_int = 0x1b5;
pub const KEY_CONTEXT_MENU: c_int = 0x1b6; /* GenDesc - system context menu */
pub const KEY_MEDIA_REPEAT: c_int = 0x1b7; /* Consumer - transport control */
pub const KEY_10CHANNELSUP: c_int = 0x1b8; /* 10 channels up : c_int = 10+ */
pub const KEY_10CHANNELSDOWN: c_int = 0x1b9; /* 10 channels down : c_int = 10- */
pub const KEY_IMAGES: c_int = 0x1ba; /* AL Image Browser */
pub const KEY_DEL_EOL: c_int = 0x1c0;
pub const KEY_DEL_EOS: c_int = 0x1c1;
pub const KEY_INS_LINE: c_int = 0x1c2;
pub const KEY_DEL_LINE: c_int = 0x1c3;
pub const KEY_FN: c_int = 0x1d0;
pub const KEY_FN_ESC: c_int = 0x1d1;
pub const KEY_FN_F1: c_int = 0x1d2;
pub const KEY_FN_F2: c_int = 0x1d3;
pub const KEY_FN_F3: c_int = 0x1d4;
pub const KEY_FN_F4: c_int = 0x1d5;
pub const KEY_FN_F5: c_int = 0x1d6;
pub const KEY_FN_F6: c_int = 0x1d7;
pub const KEY_FN_F7: c_int = 0x1d8;
pub const KEY_FN_F8: c_int = 0x1d9;
pub const KEY_FN_F9: c_int = 0x1da;
pub const KEY_FN_F10: c_int = 0x1db;
pub const KEY_FN_F11: c_int = 0x1dc;
pub const KEY_FN_F12: c_int = 0x1dd;
pub const KEY_FN_1: c_int = 0x1de;
pub const KEY_FN_2: c_int = 0x1df;
pub const KEY_FN_D: c_int = 0x1e0;
pub const KEY_FN_E: c_int = 0x1e1;
pub const KEY_FN_F: c_int = 0x1e2;
pub const KEY_FN_S: c_int = 0x1e3;
pub const KEY_FN_B: c_int = 0x1e4;
pub const KEY_BRL_DOT1: c_int = 0x1f1;
pub const KEY_BRL_DOT2: c_int = 0x1f2;
pub const KEY_BRL_DOT3: c_int = 0x1f3;
pub const KEY_BRL_DOT4: c_int = 0x1f4;
pub const KEY_BRL_DOT5: c_int = 0x1f5;
pub const KEY_BRL_DOT6: c_int = 0x1f6;
pub const KEY_BRL_DOT7: c_int = 0x1f7;
pub const KEY_BRL_DOT8: c_int = 0x1f8;
pub const KEY_BRL_DOT9: c_int = 0x1f9;
pub const KEY_BRL_DOT10: c_int = 0x1fa;
pub const KEY_NUMERIC_0: c_int = 0x200; /* used by phones, remote controls, */
pub const KEY_NUMERIC_1: c_int = 0x201; /* and other keypads */
pub const KEY_NUMERIC_2: c_int = 0x202;
pub const KEY_NUMERIC_3: c_int = 0x203;
pub const KEY_NUMERIC_4: c_int = 0x204;
pub const KEY_NUMERIC_5: c_int = 0x205;
pub const KEY_NUMERIC_6: c_int = 0x206;
pub const KEY_NUMERIC_7: c_int = 0x207;
pub const KEY_NUMERIC_8: c_int = 0x208;
pub const KEY_NUMERIC_9: c_int = 0x209;
pub const KEY_NUMERIC_STAR: c_int = 0x20a;
pub const KEY_NUMERIC_POUND: c_int = 0x20b;
pub const KEY_NUMERIC_A: c_int = 0x20c; /* Phone key A - HUT Telephony 0xb9 */
pub const KEY_NUMERIC_B: c_int = 0x20d;
pub const KEY_NUMERIC_C: c_int = 0x20e;
pub const KEY_NUMERIC_D: c_int = 0x20f;
pub const KEY_CAMERA_FOCUS: c_int = 0x210;
pub const KEY_WPS_BUTTON: c_int = 0x211; /* WiFi Protected Setup key */
pub const KEY_TOUCHPAD_TOGGLE: c_int = 0x212; /* Request switch touchpad on or off */
pub const KEY_TOUCHPAD_ON: c_int = 0x213;
pub const KEY_TOUCHPAD_OFF: c_int = 0x214;
pub const KEY_CAMERA_ZOOMIN: c_int = 0x215;
pub const KEY_CAMERA_ZOOMOUT: c_int = 0x216;
pub const KEY_CAMERA_UP: c_int = 0x217;
pub const KEY_CAMERA_DOWN: c_int = 0x218;
pub const KEY_CAMERA_LEFT: c_int = 0x219;
pub const KEY_CAMERA_RIGHT: c_int = 0x21a;
pub const KEY_ATTENDANT_ON: c_int = 0x21b;
pub const KEY_ATTENDANT_OFF: c_int = 0x21c;
pub const KEY_ATTENDANT_TOGGLE: c_int = 0x21d; /* Attendant call on or off */
pub const KEY_LIGHTS_TOGGLE: c_int = 0x21e; /* Reading light on or off */
pub const BTN_DPAD_UP: c_int = 0x220;
pub const BTN_DPAD_DOWN: c_int = 0x221;
pub const BTN_DPAD_LEFT: c_int = 0x222;
pub const BTN_DPAD_RIGHT: c_int = 0x223;
pub const KEY_ALS_TOGGLE: c_int = 0x230; /* Ambient light sensor */
pub const KEY_BUTTONCONFIG: c_int = 0x240; /* AL Button Configuration */
pub const KEY_TASKMANAGER: c_int = 0x241; /* AL Task/Project Manager */
pub const KEY_JOURNAL: c_int = 0x242; /* AL Log/Journal/Timecard */
pub const KEY_CONTROLPANEL: c_int = 0x243; /* AL Control Panel */
pub const KEY_APPSELECT: c_int = 0x244; /* AL Select Task/Application */
pub const KEY_SCREENSAVER: c_int = 0x245; /* AL Screen Saver */
pub const KEY_VOICECOMMAND: c_int = 0x246; /* Listening Voice Command */
pub const KEY_BRIGHTNESS_MIN: c_int = 0x250; /* Set Brightness to Minimum */
pub const KEY_BRIGHTNESS_MAX: c_int = 0x251; /* Set Brightness to Maximum */
pub const KEY_KBDINPUTASSIST_PREV: c_int = 0x260;
pub const KEY_KBDINPUTASSIST_NEXT: c_int = 0x261;
pub const KEY_KBDINPUTASSIST_PREVGROUP: c_int = 0x262;
pub const KEY_KBDINPUTASSIST_NEXTGROUP: c_int = 0x263;
pub const KEY_KBDINPUTASSIST_ACCEPT: c_int = 0x264;
pub const KEY_KBDINPUTASSIST_CANCEL: c_int = 0x265;
pub const BTN_TRIGGER_HAPPY: c_int = 0x2c0;
pub const BTN_TRIGGER_HAPPY1: c_int = 0x2c0;
pub const BTN_TRIGGER_HAPPY2: c_int = 0x2c1;
pub const BTN_TRIGGER_HAPPY3: c_int = 0x2c2;
pub const BTN_TRIGGER_HAPPY4: c_int = 0x2c3;
pub const BTN_TRIGGER_HAPPY5: c_int = 0x2c4;
pub const BTN_TRIGGER_HAPPY6: c_int = 0x2c5;
pub const BTN_TRIGGER_HAPPY7: c_int = 0x2c6;
pub const BTN_TRIGGER_HAPPY8: c_int = 0x2c7;
pub const BTN_TRIGGER_HAPPY9: c_int = 0x2c8;
pub const BTN_TRIGGER_HAPPY10: c_int = 0x2c9;
pub const BTN_TRIGGER_HAPPY11: c_int = 0x2ca;
pub const BTN_TRIGGER_HAPPY12: c_int = 0x2cb;
pub const BTN_TRIGGER_HAPPY13: c_int = 0x2cc;
pub const BTN_TRIGGER_HAPPY14: c_int = 0x2cd;
pub const BTN_TRIGGER_HAPPY15: c_int = 0x2ce;
pub const BTN_TRIGGER_HAPPY16: c_int = 0x2cf;
pub const BTN_TRIGGER_HAPPY17: c_int = 0x2d0;
pub const BTN_TRIGGER_HAPPY18: c_int = 0x2d1;
pub const BTN_TRIGGER_HAPPY19: c_int = 0x2d2;
pub const BTN_TRIGGER_HAPPY20: c_int = 0x2d3;
pub const BTN_TRIGGER_HAPPY21: c_int = 0x2d4;
pub const BTN_TRIGGER_HAPPY22: c_int = 0x2d5;
pub const BTN_TRIGGER_HAPPY23: c_int = 0x2d6;
pub const BTN_TRIGGER_HAPPY24: c_int = 0x2d7;
pub const BTN_TRIGGER_HAPPY25: c_int = 0x2d8;
pub const BTN_TRIGGER_HAPPY26: c_int = 0x2d9;
pub const BTN_TRIGGER_HAPPY27: c_int = 0x2da;
pub const BTN_TRIGGER_HAPPY28: c_int = 0x2db;
pub const BTN_TRIGGER_HAPPY29: c_int = 0x2dc;
pub const BTN_TRIGGER_HAPPY30: c_int = 0x2dd;
pub const BTN_TRIGGER_HAPPY31: c_int = 0x2de;
pub const BTN_TRIGGER_HAPPY32: c_int = 0x2df;
pub const BTN_TRIGGER_HAPPY33: c_int = 0x2e0;
pub const BTN_TRIGGER_HAPPY34: c_int = 0x2e1;
pub const BTN_TRIGGER_HAPPY35: c_int = 0x2e2;
pub const BTN_TRIGGER_HAPPY36: c_int = 0x2e3;
pub const BTN_TRIGGER_HAPPY37: c_int = 0x2e4;
pub const BTN_TRIGGER_HAPPY38: c_int = 0x2e5;
pub const BTN_TRIGGER_HAPPY39: c_int = 0x2e6;
pub const BTN_TRIGGER_HAPPY40: c_int = 0x2e7;
/* We avoid low common keys in module aliases so they don't get huge. */
pub const KEY_MIN_INTERESTING: c_int = KEY_MUTE;
pub const KEY_MAX: c_int = 0x2ff;
pub const KEY_CNT: c_int = KEY_MAX + 1;
/*
* Relative axes
*/
pub const REL_X: c_int = 0x00;
pub const REL_Y: c_int = 0x01;
pub const REL_Z: c_int = 0x02;
pub const REL_RX: c_int = 0x03;
pub const REL_RY: c_int = 0x04;
pub const REL_RZ: c_int = 0x05;
pub const REL_HWHEEL: c_int = 0x06;
pub const REL_DIAL: c_int = 0x07;
pub const REL_WHEEL: c_int = 0x08;
pub const REL_MISC: c_int = 0x09;
pub const REL_MAX: c_int = 0x0f;
pub const REL_CNT: c_int = REL_MAX + 1;
/*
* Absolute axes
*/
pub const ABS_X: c_int = 0x00;
pub const ABS_Y: c_int = 0x01;
pub const ABS_Z: c_int = 0x02;
pub const ABS_RX: c_int = 0x03;
pub const ABS_RY: c_int = 0x04;
pub const ABS_RZ: c_int = 0x05;
pub const ABS_THROTTLE: c_int = 0x06;
pub const ABS_RUDDER: c_int = 0x07;
pub const ABS_WHEEL: c_int = 0x08;
pub const ABS_GAS: c_int = 0x09;
pub const ABS_BRAKE: c_int = 0x0a;
pub const ABS_HAT0X: c_int = 0x10;
pub const ABS_HAT0Y: c_int = 0x11;
pub const ABS_HAT1X: c_int = 0x12;
pub const ABS_HAT1Y: c_int = 0x13;
pub const ABS_HAT2X: c_int = 0x14;
pub const ABS_HAT2Y: c_int = 0x15;
pub const ABS_HAT3X: c_int = 0x16;
pub const ABS_HAT3Y: c_int = 0x17;
pub const ABS_PRESSURE: c_int = 0x18;
pub const ABS_DISTANCE: c_int = 0x19;
pub const ABS_TILT_X: c_int = 0x1a;
pub const ABS_TILT_Y: c_int = 0x1b;
pub const ABS_TOOL_WIDTH: c_int = 0x1c;
pub const ABS_VOLUME: c_int = 0x20;
pub const ABS_MISC: c_int = 0x28;
pub const ABS_MT_SLOT: c_int = 0x2f; /* MT slot being modified */
pub const ABS_MT_TOUCH_MAJOR: c_int = 0x30; /* Major axis of touching ellipse */
pub const ABS_MT_TOUCH_MINOR: c_int = 0x31; /* Minor axis : c_int = omit if circular */
pub const ABS_MT_WIDTH_MAJOR: c_int = 0x32; /* Major axis of approaching ellipse */
pub const ABS_MT_WIDTH_MINOR: c_int = 0x33; /* Minor axis : c_int = omit if circular */
pub const ABS_MT_ORIENTATION: c_int = 0x34; /* Ellipse orientation */
pub const ABS_MT_POSITION_X: c_int = 0x35; /* Center X touch position */
pub const ABS_MT_POSITION_Y: c_int = 0x36; /* Center Y touch position */
pub const ABS_MT_TOOL_TYPE: c_int = 0x37; /* Type of touching device */
pub const ABS_MT_BLOB_ID: c_int = 0x38; /* Group a set of packets as a blob */
pub const ABS_MT_TRACKING_ID: c_int = 0x39; /* Unique ID of initiated contact */
pub const ABS_MT_PRESSURE: c_int = 0x3a; /* Pressure on contact area */
pub const ABS_MT_DISTANCE: c_int = 0x3b; /* Contact hover distance */
pub const ABS_MT_TOOL_X: c_int = 0x3c; /* Center X tool position */
pub const ABS_MT_TOOL_Y: c_int = 0x3d; /* Center Y tool position */
pub const ABS_MAX: c_int = 0x3f;
pub const ABS_CNT: c_int = ABS_MAX + 1;
/*
* Switch events
*/
pub const SW_LID: c_int = 0x00; /* set = lid shut */
pub const SW_TABLET_MODE: c_int = 0x01; /* set = tablet mode */
pub const SW_HEADPHONE_INSERT: c_int = 0x02; /* set = inserted */
pub const SW_RFKILL_ALL: c_int = 0x03; /* rfkill master switch, type "any" set = radio enabled */
pub const SW_RADIO: c_int = SW_RFKILL_ALL; /* deprecated */
pub const SW_MICROPHONE_INSERT: c_int = 0x04; /* set = inserted */
pub const SW_DOCK: c_int = 0x05; /* set = plugged into dock */
pub const SW_LINEOUT_INSERT: c_int = 0x06; /* set = inserted */
pub const SW_JACK_PHYSICAL_INSERT: c_int = 0x07; /* set = mechanical switch set */
pub const SW_VIDEOOUT_INSERT: c_int = 0x08; /* set = inserted */
pub const SW_CAMERA_LENS_COVER: c_int = 0x09; /* set = lens covered */
pub const SW_KEYPAD_SLIDE: c_int = 0x0a; /* set = keypad slide out */
pub const SW_FRONT_PROXIMITY: c_int = 0x0b; /* set = front proximity sensor active */
pub const SW_ROTATE_LOCK: c_int = 0x0c; /* set = rotate locked/disabled */
pub const SW_LINEIN_INSERT: c_int = 0x0d; /* set = inserted */
pub const SW_MUTE_DEVICE: c_int = 0x0e; /* set = device disabled */
pub const SW_MAX: c_int = 0x0f;
pub const SW_CNT: c_int = SW_MAX + 1;
/*
* Misc events
*/
pub const MSC_SERIAL: c_int = 0x00;
pub const MSC_PULSELED: c_int = 0x01;
pub const MSC_GESTURE: c_int = 0x02;
pub const MSC_RAW: c_int = 0x03;
pub const MSC_SCAN: c_int = 0x04;
pub const MSC_TIMESTAMP: c_int = 0x05;
pub const MSC_MAX: c_int = 0x07;
pub const MSC_CNT: c_int = MSC_MAX + 1;
/*
* LEDs
*/
pub const LED_NUML: c_int = 0x00;
pub const LED_CAPSL: c_int = 0x01;
pub const LED_SCROLLL: c_int = 0x02;
pub const LED_COMPOSE: c_int = 0x03;
pub const LED_KANA: c_int = 0x04;
pub const LED_SLEEP: c_int = 0x05;
pub const LED_SUSPEND: c_int = 0x06;
pub const LED_MUTE: c_int = 0x07;
pub const LED_MISC: c_int = 0x08;
pub const LED_MAIL: c_int = 0x09;
pub const LED_CHARGING: c_int = 0x0a;
pub const LED_MAX: c_int = 0x0f;
pub const LED_CNT: c_int = LED_MAX + 1;
/*
* Autorepeat values
*/
pub const REP_DELAY: c_int = 0x00;
pub const REP_PERIOD: c_int = 0x01;
pub const REP_MAX: c_int = 0x01;
pub const REP_CNT: c_int = REP_MAX + 1;
/*
* Sounds
*/
pub const SND_CLICK: c_int = 0x00;
pub const SND_BELL: c_int = 0x01;
pub const SND_TONE: c_int = 0x02;
pub const SND_MAX: c_int = 0x07;
pub const SND_CNT: c_int = SND_MAX + 1;

View File

@ -0,0 +1,88 @@
use std::{mem, ptr, slice};
use libc::{timeval, gettimeofday, input_event, c_int};
use nix::{unistd, ioctl_none};
use crate::Result;
use crate::linux::device::codes::*;
ioctl_none!(ui_dev_destroy, b'U', 2);
/// The virtual device.
pub struct Device {
fd: c_int,
}
impl Device {
/// Wrap a file descriptor in a `Device`.
pub fn new(fd: c_int) -> Self {
Device {
fd: fd
}
}
#[doc(hidden)]
pub fn write(&self, kind: c_int, code: c_int, value: c_int) -> Result<()> {
let mut event = input_event {
time: timeval { tv_sec: 0, tv_usec: 0 },
type_: kind as u16,
code: code as u16,
value: value as i32,
};
self.write_event(&mut event)
}
#[doc(hidden)]
pub fn write_event(&self, event: &mut input_event) -> Result<()> {
unsafe {
gettimeofday(&mut event.time, ptr::null_mut());
let ptr = event as *const _ as *const u8;
let size = mem::size_of_val(event);
unistd::write(self.fd, slice::from_raw_parts(ptr, size))?;
}
Ok(())
}
/// Synchronize the device.
pub fn synchronize(&self) -> Result<()> {
self.write(EV_SYN, SYN_REPORT, 0)
}
/// Send an event.
pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
self.write(kind, code, value)
}
/// Send a press event.
pub fn press(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 1)
}
/// Send a release event.
pub fn release(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 0)
}
/// Send a press and release event.
pub fn click(&self, kind: c_int, code: c_int) -> Result<()> {
self.press(kind, code)?;
self.release(kind, code)
}
/// Send a relative or absolute positioning event.
pub fn position(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
self.write(kind, code, value)
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
// ignore error here so as to not panic in a drop
ui_dev_destroy(self.fd).ok();
}
}
}

View File

@ -0,0 +1,150 @@
use std::mem;
use std::fs::File;
use std::io::Read;
use std::os::unix::io::AsRawFd;
use libc::{input_event, c_int};
use nix::{ioctl_write_ptr, ioctl_read_buf};
#[cfg(feature = "epoll_inotify")]
use std::os::unix::prelude::RawFd;
use crate::{Error,Result};
use crate::linux::{EV_KEY, KEY_MAX, NAME, KEY_W, KEY_A, KEY_S, KEY_D, BTN_LEFT};
ioctl_write_ptr!(eviocgrab, b'E', 0x90, c_int);
ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
ioctl_read_buf!(eviocgbit, b'E', 0x20, u8);
ioctl_read_buf!(eviocgbit_ev_key, b'E', 0x20 + EV_KEY, u8);
const SIZE_OF_INPUT_EVENT: usize = mem::size_of::<input_event>();
pub struct InputDevice {
device_file: File,
grabbed: bool,
#[cfg(feature = "epoll_inotify")]
epoll_fd: Option<RawFd>,
}
impl InputDevice {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
Ok(InputDevice {
device_file: File::open(path)?,
grabbed: false,
#[cfg(feature = "epoll_inotify")]
epoll_fd: None,
})
}
pub fn new_input_event_buf() -> [u8; SIZE_OF_INPUT_EVENT] {
[0u8; SIZE_OF_INPUT_EVENT]
}
pub fn read_event(&mut self, buf: &mut [u8; SIZE_OF_INPUT_EVENT]) -> Result<input_event> {
let num_bytes = self.device_file.read(buf)?;
if num_bytes != SIZE_OF_INPUT_EVENT {
return Err(Error::ShortRead);
}
let event: input_event = unsafe { mem::transmute(*buf) };
Ok(event)
}
pub fn valid_keyboard_device(self) -> Result<Self> {
use std::os::unix::fs::FileTypeExt;
// must be a character device
if !self.device_file.metadata()?.file_type().is_char_device() {
return Err(Error::NotAKeyboard);
}
let raw_fd = self.device_file.as_raw_fd();
// does it support EV_KEY
let mut evbit = [0u8; 8];
unsafe {
eviocgbit(raw_fd, &mut evbit)?;
};
let evbit = u64::from_ne_bytes(evbit);
if (evbit & (1 << EV_KEY)) == 0 {
return Err(Error::NotAKeyboard);
}
// does it support all keys WASD and *not* LEFT mouse button ? (yes this is fairly random but probably good enough, could make configuration probably)
let mut key_bits = [0u8; (KEY_MAX as usize / 8) + 1];
unsafe {
eviocgbit_ev_key(raw_fd, &mut key_bits)?;
};
let key_unsupported = |key : c_int| (key_bits[key as usize / 8] & (1 << (key % 8))) == 0;
if key_unsupported(KEY_W) || key_unsupported(KEY_A) || key_unsupported(KEY_S) || key_unsupported(KEY_D) || !key_unsupported(BTN_LEFT) {
return Err(Error::NotAKeyboard);
}
// is it another running copy of rusty-keys ?
let mut name = [0u8; NAME.len()];
unsafe {
eviocgname(raw_fd, &mut name)?
};
// exclude anything starting with "Yubico" also
if NAME.as_bytes() == &name || "Yubico".as_bytes() == &name[0..6] {
return Err(Error::NotAKeyboard);
}
return Ok(self);
}
pub fn grab(mut self) -> Result<Self> {
unsafe {
eviocgrab(self.device_file.as_raw_fd(), 1 as *const c_int)?;
}
self.grabbed = true;
Ok(self)
}
pub fn release(&mut self) -> Result<()> {
if self.grabbed {
unsafe {
eviocgrab(self.device_file.as_raw_fd(), 0 as *const c_int)?;
}
self.grabbed = false;
}
Ok(())
}
#[cfg(feature = "epoll_inotify")]
pub fn epoll_add(mut self, epoll_fd: RawFd, data: u64) -> Result<Self> {
use nix::fcntl::{OFlag, fcntl, FcntlArg};
if None != self.epoll_fd {
return Err(Error::EpollAlreadyAdded);
}
let raw_fd = self.device_file.as_raw_fd();
let flags = unsafe {
// https://github.com/nix-rust/nix/issues/1102
OFlag::from_bits_unchecked(fcntl(raw_fd, FcntlArg::F_GETFL)?)
};
fcntl(raw_fd, FcntlArg::F_SETFL(flags | OFlag::O_NONBLOCK))?;
let epoll_event = epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLET, data);
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_ADD, raw_fd, epoll_event)?;
self.epoll_fd = Some(epoll_fd);
Ok(self)
}
#[cfg(feature = "epoll_inotify")]
pub fn epoll_del(&mut self) -> Result<&mut Self> {
if let Some(epoll_fd) = self.epoll_fd {
// set this to None first, if we end up returning an Err early, we can't do anything else anyway...
self.epoll_fd = None;
let empty_event = epoll::Event::new(epoll::Events::empty(), 0);
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_DEL, self.device_file.as_raw_fd(), empty_event)?;
}
Ok(self)
}
}
impl Drop for InputDevice {
fn drop(&mut self) {
// ignore any errors here, what could we do anyhow?
self.release().ok();
#[cfg(feature = "epoll_inotify")]
self.epoll_del().ok();
}
}

11
src/linux/device/mod.rs Normal file
View File

@ -0,0 +1,11 @@
mod builder;
pub use self::builder::Builder;
pub mod codes;
pub use codes::*;
mod device;
pub use self::device::Device;
mod input_device;
pub use self::input_device::InputDevice;

599
src/linux/mod.rs Normal file
View File

@ -0,0 +1,599 @@
use crate::*;
use crate::linux::device::codes::*;
pub mod device;
pub use device::{Device,InputDevice, Builder};
use libc::input_event;
use std::process::exit;
use std::env;
use std::collections::HashMap;
#[cfg(feature = "epoll_inotify")]
const INPUT_FOLDER: &str = "/dev/input/";
// 1 is down, 0 is up
const DOWN: i32 = 1;
const UP: i32 = 0;
use getopts::Options;
const EV_KEY_U16: u16 = EV_KEY as u16;
type LinuxKeyMaps = KeyMaps<Device, u16, input_event>;
impl KeyEvent<u16> for input_event {
fn code(&self) -> u16 {
self.code
}
fn value(&self) -> KeyState {
match self.value {
UP => KeyState::UP,
DOWN => KeyState::DOWN,
_ => KeyState::OTHER,
}
}
}
impl Keyboard<u16, input_event> for Device {
fn send(&self, event: &mut input_event) -> Result<()> {
self.write_event(event)
}
fn send_mod_code(&self, code: u16, event: &mut input_event) -> Result<()> {
event.code = code;
Keyboard::send(self, event)
}
fn send_mod_code_value(&self, code: u16, up_not_down: bool, event: &mut input_event) -> Result<()> {
event.code = code;
let value = event.value;
if up_not_down {
event.value = UP;
} else {
event.value = DOWN;
}
Keyboard::send(self, event)?;
// set it back
event.value = value;
Ok(())
}
fn synchronize(&self) -> Result<()> {
Device::synchronize(self)
}
fn left_shift_code(&self) -> u16 {
KEY_LEFTSHIFT as u16
}
fn right_shift_code(&self) -> u16 {
KEY_RIGHTSHIFT as u16
}
fn caps_lock_code(&self) -> u16 {
KEY_CAPSLOCK as u16
}
fn block_key(&self) -> Result<()> {
Ok(()) // we don't actually use/need this here
}
}
#[derive(Debug)]
struct Config {
device_files: Vec<String>,
config_file: String
}
impl Config {
fn new(device_files: Vec<String>, config_file: String) -> Self {
Config { device_files: device_files, config_file: config_file }
}
}
pub fn main_res() -> Result<()> {
let config = parse_args();
//println!("Config: {:?}", config);
let key_map = key_map();
//println!("key_map: {:?}", key_map);
let device = Builder::open("/dev/uinput")
.or_else(|_| Builder::open("/dev/input/uinput"))
.or_else(|_| Builder::default())?
.name(NAME)?
.event(key_map.values())?
.create()?;
#[cfg(not(feature = "toml_serde"))]
let mut key_map = LinuxKeyMaps::new(&key_map, KeymapConfig::default());
#[cfg(feature = "toml_serde")]
let mut key_map = LinuxKeyMaps::from_cfg(&key_map, &config.config_file);
//println!("keymaps: {:?}", keymaps);
let mut input_event_buf = InputDevice::new_input_event_buf();
if config.device_files.len() == 1 {
// shortcut, don't bother with epoll
let mut input_device = InputDevice::open(&config.device_files[0])?.grab()?;
loop {
let event = input_device.read_event(&mut input_event_buf)?;
send_event(&mut key_map, event, &device)?
}
} else {
#[cfg(not(feature = "epoll_inotify"))]
panic!("without epoll_inotify feature, only exactly 1 device is supported");
#[cfg(feature = "epoll_inotify")]
{
use inotify::{Inotify, WatchMask};
let epoll_fd = epoll::create(true)?;
const INOTIFY_DATA: u64 = u64::max_value();
let (device_files, mut inotify) = if config.device_files.len() > 0 {
// we operate on exactly the devices sent in and never watch for new devices
(config.device_files.iter().map(|device_file| InputDevice::open(&device_file).expect("device_file does not exist!")).collect(), None)
} else {
use std::os::unix::io::AsRawFd;
// we want to wait forever starting new threads for any new keyboard devices
// there is a slight race condition here, if a keyboard is plugged in between the time we
// enumerate the devices and set up the inotify watch, we'll miss it, doing it the other way
// can bring duplicates though, todo: think about this...
let device_files = get_keyboard_devices();
let mut inotify = Inotify::init()?;
inotify.add_watch(INPUT_FOLDER, WatchMask::CREATE)?;
let epoll_event = epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLET, INOTIFY_DATA);
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_ADD, inotify.as_raw_fd(), epoll_event)?;
(device_files, Some(inotify))
};
let mut input_devices = Vec::with_capacity(device_files.len());
for (idx, device_file) in device_files.into_iter().enumerate() {
input_devices.push(Some(device_file.grab()?.epoll_add(epoll_fd, idx as u64)?));
}
let mut epoll_buf = [epoll::Event::new(epoll::Events::empty(), 0); 4];
let mut inotify_buf = [0u8; 256];
loop {
let num_events = epoll::wait(epoll_fd, -1, &mut epoll_buf)?;
for event in &epoll_buf[0..num_events] {
let idx = event.data as usize;
if let Some(Some(input_device)) = &mut input_devices.get_mut(idx) {
loop {
match input_device.read_event(&mut input_event_buf) {
Ok(event) => {
//println!("input event: {:?}", event);
send_event(&mut key_map, event, &device)?
}
Err(err) => {
if let Error::Io(ref err) = err {
if err.kind() == std::io::ErrorKind::WouldBlock {
// go back to epoll event loop
break;
}
}
// otherwise it's some other error, don't read anything from this again
println!("input err: {:?}", err);
// remove it from input_devices and drop it
let _ = std::mem::replace(&mut input_devices[idx], None);
if inotify.is_none() {
// if we aren't watching with inotify, and the last device is removed (Vec only has None's in it), exit the program
if input_devices.iter().all(|id| id.is_none()) {
println!("last device went away, exiting...");
return Ok(());
}
}
break;
}
}
}
} else if event.data == INOTIFY_DATA {
if let Some(inotify) = &mut inotify {
for event in inotify.read_events(&mut inotify_buf)? {
if let Some(device_file) = event.name.and_then(|name| name.to_str()) {
// check if this is an eligible keyboard device
let mut path = std::path::PathBuf::new();
path.push(INPUT_FOLDER);
path.push(device_file);
if let Ok(input_device) = InputDevice::open(path).and_then(|id| id.valid_keyboard_device()) {
println!("starting mapping for new keyboard: {}", device_file);
let idx = input_devices.iter().position(|id| id.is_none()).unwrap_or(input_devices.len());
let input_device = input_device.grab()?.epoll_add(epoll_fd, idx as u64)?;
if idx == input_devices.len() {
input_devices.push(Some(input_device));
} else {
// simply replacing None here
let _ = std::mem::replace(&mut input_devices[idx], Some(input_device));
}
}
}
}
}
}
}
}
}
}
}
fn send_event(key_map: &mut LinuxKeyMaps, mut event: input_event, device: &Device) -> Result<()> {
if event.type_ == EV_KEY_U16 {
// println!("type: {} code: {:?} value: {:?}", event.type_, event.code(), event.value());
key_map.send_event(&mut event, &device)?
} else {
device.write_event(&mut event)?
}
Ok(())
}
fn parse_args() -> Config {
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options] [device_files...]", program);
println!("{}", opts.usage(&brief));
}
let args: Vec<_> = env::args().collect();
let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message");
opts.optflag("v", "version", "prints the version");
opts.optopt("c", "config", "specify the keymap config file to use (default: /etc/rusty-keys/keymap.toml)", "FILE");
let matches = opts.parse(&args[1..]);
if matches.is_err() {
print_usage(&args[0], opts);
exit(0);
}
let matches = matches.unwrap();
if matches.opt_present("h") {
print_usage(&args[0], opts);
exit(0);
}
if matches.opt_present("v") {
println!("{} {}", NAME, VERSION);
exit(0);
}
let config_file = matches.opt_str("c").unwrap_or("/etc/rusty-keys/keymap.toml".to_owned());
Config::new(matches.free, config_file)
}
#[cfg(feature = "epoll_inotify")]
fn get_keyboard_devices() -> Vec<InputDevice> {
let mut res = Vec::new();
if let Ok(entries) = std::fs::read_dir(INPUT_FOLDER) {
for entry in entries {
if let Ok(entry) = entry {
if let Ok(input_device) = InputDevice::open(entry.path()).and_then(|id|id.valid_keyboard_device()) {
res.push(input_device);
}
}
}
}
res
}
pub fn key_map() -> HashMap<&'static str, u16> {
[
// generated like:
// grep -o 'KEY_[^ :;]*' ~/.cargo/registry/src/github.com-1ecc6299db9ec823/uinput-sys-0.1.3/src/codes | sed 's/^KEY_//' | awk '{print "(\""$1"\", KEY_"$1"),"}'
("RESERVED", KEY_RESERVED),
("ESC", KEY_ESC),
("1", KEY_1),
("2", KEY_2),
("3", KEY_3),
("4", KEY_4),
("5", KEY_5),
("6", KEY_6),
("7", KEY_7),
("8", KEY_8),
("9", KEY_9),
("10", KEY_10),
("MINUS", KEY_MINUS),
("EQUAL", KEY_EQUAL),
("BACKSPACE", KEY_BACKSPACE),
("TAB", KEY_TAB),
("Q", KEY_Q),
("W", KEY_W),
("E", KEY_E),
("R", KEY_R),
("T", KEY_T),
("Y", KEY_Y),
("U", KEY_U),
("I", KEY_I),
("O", KEY_O),
("P", KEY_P),
("LEFTBRACE", KEY_LEFTBRACE),
("RIGHTBRACE", KEY_RIGHTBRACE),
("ENTER", KEY_ENTER),
("LEFTCTRL", KEY_LEFTCTRL),
("A", KEY_A),
("S", KEY_S),
("D", KEY_D),
("F", KEY_F),
("G", KEY_G),
("H", KEY_H),
("J", KEY_J),
("K", KEY_K),
("L", KEY_L),
("SEMICOLON", KEY_SEMICOLON),
("APOSTROPHE", KEY_APOSTROPHE),
("GRAVE", KEY_GRAVE),
("LEFTSHIFT", KEY_LEFTSHIFT),
("BACKSLASH", KEY_BACKSLASH),
("Z", KEY_Z),
("X", KEY_X),
("C", KEY_C),
("V", KEY_V),
("B", KEY_B),
("N", KEY_N),
("M", KEY_M),
("COMMA", KEY_COMMA),
("DOT", KEY_DOT),
("SLASH", KEY_SLASH),
("RIGHTSHIFT", KEY_RIGHTSHIFT),
("KPASTERISK", KEY_KPASTERISK),
("LEFTALT", KEY_LEFTALT),
("SPACE", KEY_SPACE),
("CAPSLOCK", KEY_CAPSLOCK),
("F1", KEY_F1),
("F2", KEY_F2),
("F3", KEY_F3),
("F4", KEY_F4),
("F5", KEY_F5),
("F6", KEY_F6),
("F7", KEY_F7),
("F8", KEY_F8),
("F9", KEY_F9),
("F10", KEY_F10),
("NUMLOCK", KEY_NUMLOCK),
("SCROLLLOCK", KEY_SCROLLLOCK),
("KP7", KEY_KP7),
("KP8", KEY_KP8),
("KP9", KEY_KP9),
("KPMINUS", KEY_KPMINUS),
("KP4", KEY_KP4),
("KP5", KEY_KP5),
("KP6", KEY_KP6),
("KPPLUS", KEY_KPPLUS),
("KP1", KEY_KP1),
("KP2", KEY_KP2),
("KP3", KEY_KP3),
("KP0", KEY_KP0),
("KPDOT", KEY_KPDOT),
("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU),
("102ND", KEY_102ND),
("F11", KEY_F11),
("F12", KEY_F12),
("RO", KEY_RO),
("KATAKANA", KEY_KATAKANA),
("HIRAGANA", KEY_HIRAGANA),
("HENKAN", KEY_HENKAN),
("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA),
("MUHENKAN", KEY_MUHENKAN),
("KPJPCOMMA", KEY_KPJPCOMMA),
("KPENTER", KEY_KPENTER),
("RIGHTCTRL", KEY_RIGHTCTRL),
("KPSLASH", KEY_KPSLASH),
("SYSRQ", KEY_SYSRQ),
("RIGHTALT", KEY_RIGHTALT),
("LINEFEED", KEY_LINEFEED),
("HOME", KEY_HOME),
("UP", KEY_UP),
("PAGEUP", KEY_PAGEUP),
("LEFT", KEY_LEFT),
("RIGHT", KEY_RIGHT),
("END", KEY_END),
("DOWN", KEY_DOWN),
("PAGEDOWN", KEY_PAGEDOWN),
("INSERT", KEY_INSERT),
("DELETE", KEY_DELETE),
("MACRO", KEY_MACRO),
("MUTE", KEY_MUTE),
("VOLUMEDOWN", KEY_VOLUMEDOWN),
("VOLUMEUP", KEY_VOLUMEUP),
("POWER", KEY_POWER),
("KPEQUAL", KEY_KPEQUAL),
("KPPLUSMINUS", KEY_KPPLUSMINUS),
("PAUSE", KEY_PAUSE),
("SCALE", KEY_SCALE),
("KPCOMMA", KEY_KPCOMMA),
("HANGEUL", KEY_HANGEUL),
("HANGUEL", KEY_HANGUEL),
("HANGEUL", KEY_HANGEUL),
("HANJA", KEY_HANJA),
("YEN", KEY_YEN),
("LEFTMETA", KEY_LEFTMETA),
("RIGHTMETA", KEY_RIGHTMETA),
("COMPOSE", KEY_COMPOSE),
("STOP", KEY_STOP),
("AGAIN", KEY_AGAIN),
("PROPS", KEY_PROPS),
("UNDO", KEY_UNDO),
("FRONT", KEY_FRONT),
("COPY", KEY_COPY),
("OPEN", KEY_OPEN),
("PASTE", KEY_PASTE),
("FIND", KEY_FIND),
("CUT", KEY_CUT),
("HELP", KEY_HELP),
("MENU", KEY_MENU),
("CALC", KEY_CALC),
("SETUP", KEY_SETUP),
("SLEEP", KEY_SLEEP),
("WAKEUP", KEY_WAKEUP),
("FILE", KEY_FILE),
("SENDFILE", KEY_SENDFILE),
("DELETEFILE", KEY_DELETEFILE),
("XFER", KEY_XFER),
("PROG1", KEY_PROG1),
("PROG2", KEY_PROG2),
("WWW", KEY_WWW),
("MSDOS", KEY_MSDOS),
("COFFEE", KEY_COFFEE),
("SCREENLOCK", KEY_SCREENLOCK),
("COFFEE", KEY_COFFEE),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("DIRECTION", KEY_DIRECTION),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("CYCLEWINDOWS", KEY_CYCLEWINDOWS),
("MAIL", KEY_MAIL),
("BOOKMARKS", KEY_BOOKMARKS),
("COMPUTER", KEY_COMPUTER),
("BACK", KEY_BACK),
("FORWARD", KEY_FORWARD),
("CLOSECD", KEY_CLOSECD),
("EJECTCD", KEY_EJECTCD),
("EJECTCLOSECD", KEY_EJECTCLOSECD),
("NEXTSONG", KEY_NEXTSONG),
("PLAYPAUSE", KEY_PLAYPAUSE),
("PREVIOUSSONG", KEY_PREVIOUSSONG),
("STOPCD", KEY_STOPCD),
("RECORD", KEY_RECORD),
("REWIND", KEY_REWIND),
("PHONE", KEY_PHONE),
("ISO", KEY_ISO),
("CONFIG", KEY_CONFIG),
("HOMEPAGE", KEY_HOMEPAGE),
("REFRESH", KEY_REFRESH),
("EXIT", KEY_EXIT),
("MOVE", KEY_MOVE),
("EDIT", KEY_EDIT),
("SCROLLUP", KEY_SCROLLUP),
("SCROLLDOWN", KEY_SCROLLDOWN),
("KPLEFTPAREN", KEY_KPLEFTPAREN),
("KPRIGHTPAREN", KEY_KPRIGHTPAREN),
("NEW", KEY_NEW),
("REDO", KEY_REDO),
("F13", KEY_F13),
("F14", KEY_F14),
("F15", KEY_F15),
("F16", KEY_F16),
("F17", KEY_F17),
("F18", KEY_F18),
("F19", KEY_F19),
("F20", KEY_F20),
("F21", KEY_F21),
("F22", KEY_F22),
("F23", KEY_F23),
("F24", KEY_F24),
("PLAYCD", KEY_PLAYCD),
("PAUSECD", KEY_PAUSECD),
("PROG3", KEY_PROG3),
("PROG4", KEY_PROG4),
("DASHBOARD", KEY_DASHBOARD),
("SUSPEND", KEY_SUSPEND),
("CLOSE", KEY_CLOSE),
("PLAY", KEY_PLAY),
("FASTFORWARD", KEY_FASTFORWARD),
("BASSBOOST", KEY_BASSBOOST),
("PRINT", KEY_PRINT),
("HP", KEY_HP),
("CAMERA", KEY_CAMERA),
("SOUND", KEY_SOUND),
("QUESTION", KEY_QUESTION),
("EMAIL", KEY_EMAIL),
("CHAT", KEY_CHAT),
("SEARCH", KEY_SEARCH),
("CONNECT", KEY_CONNECT),
("FINANCE", KEY_FINANCE),
("SPORT", KEY_SPORT),
("SHOP", KEY_SHOP),
("ALTERASE", KEY_ALTERASE),
("CANCEL", KEY_CANCEL),
("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN),
("BRIGHTNESSUP", KEY_BRIGHTNESSUP),
("MEDIA", KEY_MEDIA),
("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE),
("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE),
("KBDILLUMDOWN", KEY_KBDILLUMDOWN),
("KBDILLUMUP", KEY_KBDILLUMUP),
("SEND", KEY_SEND),
("REPLY", KEY_REPLY),
("FORWARDMAIL", KEY_FORWARDMAIL),
("SAVE", KEY_SAVE),
("DOCUMENTS", KEY_DOCUMENTS),
("BATTERY", KEY_BATTERY),
("BLUETOOTH", KEY_BLUETOOTH),
("WLAN", KEY_WLAN),
("UWB", KEY_UWB),
("UNKNOWN", KEY_UNKNOWN),
("VIDEO_NEXT", KEY_VIDEO_NEXT),
("VIDEO_PREV", KEY_VIDEO_PREV),
("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("DISPLAY_OFF", KEY_DISPLAY_OFF),
("WWAN", KEY_WWAN),
("WIMAX", KEY_WIMAX),
("WWAN", KEY_WWAN),
("RFKILL", KEY_RFKILL),
("MICMUTE", KEY_MICMUTE),
// below manual shortcuts
("PSCR", KEY_SYSRQ),
("SLCK", KEY_SCROLLLOCK),
("BRK", KEY_PAUSE),
("GRV", KEY_GRAVE),
("0", KEY_10), // dumb or named wrong?
("MINS", KEY_MINUS),
("EQL", KEY_EQUAL),
("BSPC", KEY_BACKSPACE),
("LBRC", KEY_LEFTBRACE),
("RBRC", KEY_RIGHTBRACE),
("BSLS", KEY_BACKSLASH),
("SCLN", KEY_SEMICOLON),
("QUOT", KEY_APOSTROPHE),
("ENT", KEY_ENTER),
("COMM", KEY_COMMA),
("DOT", KEY_DOT),
("SLSH", KEY_SLASH),
("CAPS", KEY_CAPSLOCK),
("LSFT", KEY_LEFTSHIFT),
("RSFT", KEY_RIGHTSHIFT),
("SPC", KEY_SPACE),
("APP", KEY_COMPOSE),
("LCTL", KEY_LEFTCTRL),
("RCTL", KEY_RIGHTCTRL),
("LALT", KEY_LEFTALT),
("RALT", KEY_RIGHTALT),
("LGUI", KEY_LEFTMETA),
("RGUI", KEY_RIGHTMETA),
("INS", KEY_INSERT),
("PGUP", KEY_PAGEUP),
("PGDN", KEY_PAGEDOWN),
("DEL", KEY_DELETE),
("RGHT", KEY_RIGHT),
("NLCK", KEY_NUMLOCK),
("PSLS", KEY_KPSLASH),
("PAST", KEY_KPASTERISK),
("PMNS", KEY_KPMINUS),
("P7", KEY_KP7),
("P8", KEY_KP8),
("P9", KEY_KP9),
("P4", KEY_KP4),
("P5", KEY_KP5),
("P6", KEY_KP6),
("PPLS", KEY_KPPLUS),
("P1", KEY_KP1),
("P2", KEY_KP2),
("P3", KEY_KP3),
("P0", KEY_KP0),
("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER),
("TOUCHPAD_TOGGLE", KEY_TOUCHPAD_TOGGLE),
].iter().cloned().map(|(m, v)| (m, v as u16)).collect()
}

568
src/macos/codes.rs Normal file
View File

@ -0,0 +1,568 @@
use std::collections::HashMap;
use core_graphics::event::CGKeyCode;
pub const KEY_RESERVED: CGKeyCode = 0x31;
pub const KEY_ESC: CGKeyCode = 0x00;
pub const KEY_1: CGKeyCode = 0x0012;
pub const KEY_2: CGKeyCode = 0x0013;
pub const KEY_3: CGKeyCode = 0x0014;
pub const KEY_4: CGKeyCode = 0x0015;
pub const KEY_5: CGKeyCode = 0x0017;
pub const KEY_6: CGKeyCode = 0x0016;
pub const KEY_7: CGKeyCode = 0x001A;
pub const KEY_8: CGKeyCode = 0x001C;
pub const KEY_9: CGKeyCode = 0x0019;
pub const KEY_10: CGKeyCode = 0x001D;
pub const KEY_MINUS: CGKeyCode = 0x001B;
pub const KEY_EQUAL: CGKeyCode = 0x0018;
pub const KEY_BACKSPACE: CGKeyCode = 0x0033;
pub const KEY_TAB: CGKeyCode = 0x0030;
pub const KEY_Q: CGKeyCode = 0x000C;
pub const KEY_W: CGKeyCode = 0x000D;
pub const KEY_E: CGKeyCode = 0x000E;
pub const KEY_R: CGKeyCode = 0x000F;
pub const KEY_T: CGKeyCode = 0x0011;
pub const KEY_Y: CGKeyCode = 0x0010;
pub const KEY_U: CGKeyCode = 0x0020;
pub const KEY_I: CGKeyCode = 0x0022;
pub const KEY_O: CGKeyCode = 0x001F;
pub const KEY_P: CGKeyCode = 0x0023;
pub const KEY_LEFTBRACE: CGKeyCode = 0x0021;
pub const KEY_RIGHTBRACE: CGKeyCode = 0x001E;
pub const KEY_ENTER: CGKeyCode = 0x0024;
pub const KEY_LEFTCTRL: CGKeyCode = 0x003B;
pub const KEY_A: CGKeyCode = 0x0000;
pub const KEY_S: CGKeyCode = 0x0001;
pub const KEY_D: CGKeyCode = 0x0002;
pub const KEY_F: CGKeyCode = 0x0003;
pub const KEY_G: CGKeyCode = 0x0005;
pub const KEY_H: CGKeyCode = 0x0004;
pub const KEY_J: CGKeyCode = 0x0026;
pub const KEY_K: CGKeyCode = 0x0028;
pub const KEY_L: CGKeyCode = 0x0025;
pub const KEY_SEMICOLON: CGKeyCode = 0x0029;
pub const KEY_APOSTROPHE: CGKeyCode = 0x0027;
pub const KEY_GRAVE: CGKeyCode = 0x0032;
pub const KEY_LEFTSHIFT: CGKeyCode = 0x0038;
pub const KEY_BACKSLASH: CGKeyCode = 0x002A;
pub const KEY_Z: CGKeyCode = 0x0006;
pub const KEY_X: CGKeyCode = 0x0007;
pub const KEY_C: CGKeyCode = 0x0008;
pub const KEY_V: CGKeyCode = 0x0009;
pub const KEY_B: CGKeyCode = 0x000B;
pub const KEY_N: CGKeyCode = 0x002D;
pub const KEY_M: CGKeyCode = 0x002E;
pub const KEY_COMMA: CGKeyCode = 0x002B;
pub const KEY_DOT: CGKeyCode = 0x002F;
pub const KEY_SLASH: CGKeyCode = 0x002C;
pub const KEY_RIGHTSHIFT: CGKeyCode = 0x003C;
pub const KEY_KPASTERISK: CGKeyCode = 0x0043;
pub const KEY_LEFTALT: CGKeyCode = 0x003A;
pub const KEY_SPACE: CGKeyCode = 0x0031;
pub const KEY_CAPSLOCK: CGKeyCode = 0x0039;
pub const KEY_F1: CGKeyCode = 0x007A;
pub const KEY_F2: CGKeyCode = 0x0078;
pub const KEY_F3: CGKeyCode = 0x0063;
pub const KEY_F4: CGKeyCode = 0x0076;
pub const KEY_F5: CGKeyCode = 0x0060;
pub const KEY_F6: CGKeyCode = 0x0061;
pub const KEY_F7: CGKeyCode = 0x0062;
pub const KEY_F8: CGKeyCode = 0x0064;
pub const KEY_F9: CGKeyCode = 0x0065;
pub const KEY_F10: CGKeyCode = 0x006D;
pub const KEY_NUMLOCK: CGKeyCode = 0x0047;
pub const KEY_SCROLLLOCK: CGKeyCode = 0x006B;
pub const KEY_KP7: CGKeyCode = 0x0059;
pub const KEY_KP8: CGKeyCode = 0x005B;
pub const KEY_KP9: CGKeyCode = 0x005C;
pub const KEY_KPMINUS: CGKeyCode = 0x004E;
pub const KEY_KP4: CGKeyCode = 0x0056;
pub const KEY_KP5: CGKeyCode = 0x0057;
pub const KEY_KP6: CGKeyCode = 0x0058;
pub const KEY_KPPLUS: CGKeyCode = 0x0045;
pub const KEY_KP1: CGKeyCode = 0x0053;
pub const KEY_KP2: CGKeyCode = 0x0054;
pub const KEY_KP3: CGKeyCode = 0x0055;
pub const KEY_KP0: CGKeyCode = 0x0052; // https://code-with-me.jetbrains.com/UCsz0dzSd1QAbmOi8g0V3w#p=IC&fp=6298EDAF97FA62E9897E2556D1A6631FB66974568C7252E696472EE85078E8A0
pub const KEY_KPDOT: CGKeyCode = 0x0041;
// pub const KEY_ZENKAKUHANKAKU: CGKeyCode = NOT_FOUND;
pub const KEY_102ND: CGKeyCode = 0x000A;
pub const KEY_F11: CGKeyCode = 0x0067;
pub const KEY_F12: CGKeyCode = 0x006F;
// pub const KEY_RO: CGKeyCode = NOT_FOUND;
// pub const KEY_KATAKANA: CGKeyCode = NOT_FOUND;
// pub const KEY_HIRAGANA: CGKeyCode = NOT_FOUND;
// pub const KEY_HENKAN: CGKeyCode = NOT_FOUND;
// pub const KEY_KATAKANAHIRAGANA: CGKeyCode = NOT_FOUND;
// pub const KEY_MUHENKAN: CGKeyCode = NOT_FOUND;
// pub const KEY_KPJPCOMMA: CGKeyCode = NOT_FOUND; // https://code-with-me.jetbrains.com/Mf59EFUeJZQ2mpGCCCjNWw#p=IC&fp=6298EDAF97FA62E9897E2556D1A6631FB66974568C7252E696472EE85078E8A0
pub const KEY_KPENTER: CGKeyCode = 0x004C;
pub const KEY_RIGHTCTRL: CGKeyCode = 0x003E;
pub const KEY_KPSLASH: CGKeyCode = 0x004B;
pub const KEY_SYSRQ: CGKeyCode = 0x0069;
pub const KEY_RIGHTALT: CGKeyCode = 0x003D;
pub const KEY_LINEFEED: CGKeyCode = 0x0071;
pub const KEY_HOME: CGKeyCode = 0x0073;
pub const KEY_UP: CGKeyCode = 0x007E;
pub const KEY_PAGEUP: CGKeyCode = 0x0074;
pub const KEY_LEFT: CGKeyCode = 0x007B;
pub const KEY_RIGHT: CGKeyCode = 0x007C;
pub const KEY_END: CGKeyCode = 0x0077;
pub const KEY_DOWN: CGKeyCode = 0x007D;
pub const KEY_PAGEDOWN: CGKeyCode = 0x0079;
pub const KEY_INSERT: CGKeyCode = 0x0072;
pub const KEY_DELETE: CGKeyCode = 0x0075;
// pub const KEY_MACRO: CGKeyCode = NOT_FOUND;
// pub const KEY_MUTE: CGKeyCode = NOT_FOUND;
// pub const KEY_VOLUMEDOWN: CGKeyCode = NOT_FOUND;
// pub const KEY_VOLUMEUP: CGKeyCode = NOT_FOUND;
// pub const KEY_POWER: CGKeyCode = NOT_FOUND;
pub const KEY_KPEQUAL: CGKeyCode = 0x0069;
// pub const KEY_KPPLUSMINUS: CGKeyCode = NOT_FOUND;
pub const KEY_PAUSE: CGKeyCode = KEY_LINEFEED;
pub const KEY_SCALE: CGKeyCode = 0x0047;
pub const KEY_KPCOMMA: CGKeyCode = 0x0036;
// pub const KEY_HANGEUL: CGKeyCode = NOT_FOUND;
// pub const KEY_HANGUEL: CGKeyCode = NOT_FOUND;
// pub const KEY_HANGEUL: CGKeyCode = NOT_FOUND;
// pub const KEY_HANJA: CGKeyCode = NOT_FOUND;
//pub const KEY_YEN: CGKeyCode = NOT_FOUND;
pub const KEY_LEFTMETA: CGKeyCode = 0x0037;
pub const KEY_RIGHTMETA: CGKeyCode = 0x0036;
pub const KEY_COMPOSE: CGKeyCode = 0x006E;
// pub const KEY_STOP: CGKeyCode = NOT_FOUND;
// pub const KEY_AGAIN: CGKeyCode = NOT_FOUND;
// pub const KEY_PROPS: CGKeyCode = NOT_FOUND;
// pub const KEY_UNDO: CGKeyCode = NOT_FOUND;
// pub const KEY_FRONT: CGKeyCode = NOT_FOUND;
// pub const KEY_COPY: CGKeyCode = NOT_FOUND;
// pub const KEY_OPEN: CGKeyCode = NOT_FOUND;
// pub const KEY_PASTE: CGKeyCode = NOT_FOUND;
// pub const KEY_FIND: CGKeyCode = NOT_FOUND;
// pub const KEY_CUT: CGKeyCode = NOT_FOUND;
// pub const KEY_HELP: CGKeyCode = NOT_FOUND;
// pub const KEY_MENU: CGKeyCode = NOT_FOUND;
// pub const KEY_CALC: CGKeyCode = NOT_FOUND;
// pub const KEY_SETUP: CGKeyCode = NOT_FOUND;
// pub const KEY_SLEEP: CGKeyCode = NOT_FOUND;
// pub const KEY_WAKEUP: CGKeyCode = NOT_FOUND;
// pub const KEY_FILE: CGKeyCode = NOT_FOUND;
// pub const KEY_SENDFILE: CGKeyCode = NOT_FOUND;
// pub const KEY_DELETEFILE: CGKeyCode = NOT_FOUND;
// pub const KEY_XFER: CGKeyCode = NOT_FOUND;
// pub const KEY_PROG1: CGKeyCode = NOT_FOUND;
// pub const KEY_PROG2: CGKeyCode = NOT_FOUND;
// pub const KEY_WWW: CGKeyCode = NOT_FOUND;
// pub const KEY_MSDOS: CGKeyCode = NOT_FOUND;
// pub const KEY_COFFEE: CGKeyCode = NOT_FOUND;
// pub const KEY_SCREENLOCK: CGKeyCode = NOT_FOUND;
// pub const KEY_COFFEE: CGKeyCode = NOT_FOUND;
// pub const KEY_ROTATE_DISPLAY: CGKeyCode = NOT_FOUND;
// pub const KEY_DIRECTION: CGKeyCode = NOT_FOUND;
// pub const KEY_ROTATE_DISPLAY: CGKeyCode = NOT_FOUND;
// pub const KEY_CYCLEWINDOWS: CGKeyCode = NOT_FOUND;
// pub const KEY_MAIL: CGKeyCode = NOT_FOUND;
// pub const KEY_BOOKMARKS: CGKeyCode = NOT_FOUND;
// pub const KEY_COMPUTER: CGKeyCode = NOT_FOUND;
// pub const KEY_BACK: CGKeyCode = NOT_FOUND;
// pub const KEY_FORWARD: CGKeyCode = NOT_FOUND;
// pub const KEY_CLOSECD: CGKeyCode = NOT_FOUND;
// pub const KEY_EJECTCD: CGKeyCode = NOT_FOUND;
// pub const KEY_EJECTCLOSECD: CGKeyCode = NOT_FOUND;
// pub const KEY_NEXTSONG: CGKeyCode = NOT_FOUND;
// pub const KEY_PLAYPAUSE: CGKeyCode = NOT_FOUND;
// pub const KEY_PREVIOUSSONG: CGKeyCode = NOT_FOUND;
// pub const KEY_STOPCD: CGKeyCode = NOT_FOUND;
// pub const KEY_RECORD: CGKeyCode = NOT_FOUND;
// pub const KEY_REWIND: CGKeyCode = NOT_FOUND;
// pub const KEY_PHONE: CGKeyCode = NOT_FOUND;
// pub const KEY_ISO: CGKeyCode = NOT_FOUND;
// pub const KEY_CONFIG: CGKeyCode = NOT_FOUND;
// pub const KEY_HOMEPAGE: CGKeyCode = NOT_FOUND;
// pub const KEY_REFRESH: CGKeyCode = NOT_FOUND;
// pub const KEY_EXIT: CGKeyCode = NOT_FOUND;
// pub const KEY_MOVE: CGKeyCode = NOT_FOUND;
// pub const KEY_EDIT: CGKeyCode = NOT_FOUND;
// pub const KEY_SCROLLUP: CGKeyCode = NOT_FOUND;
// pub const KEY_SCROLLDOWN: CGKeyCode = NOT_FOUND;
// pub const KEY_KPLEFTPAREN: CGKeyCode = NOT_FOUND;
// pub const KEY_KPRIGHTPAREN: CGKeyCode = NOT_FOUND;
// pub const KEY_NEW: CGKeyCode = NOT_FOUND;
// pub const KEY_REDO: CGKeyCode = NOT_FOUND;
// pub const KEY_F13: CGKeyCode = NOT_FOUND;
// pub const KEY_F14: CGKeyCode = NOT_FOUND;
// pub const KEY_F15: CGKeyCode = NOT_FOUND;
// pub const KEY_F16: CGKeyCode = NOT_FOUND;
// pub const KEY_F17: CGKeyCode = NOT_FOUND;
// pub const KEY_F18: CGKeyCode = NOT_FOUND;
// pub const KEY_F19: CGKeyCode = NOT_FOUND;
// pub const KEY_F20: CGKeyCode = NOT_FOUND;
// pub const KEY_F21: CGKeyCode = NOT_FOUND;
// pub const KEY_F22: CGKeyCode = NOT_FOUND;
// pub const KEY_F23: CGKeyCode = NOT_FOUND;
// pub const KEY_F24: CGKeyCode = NOT_FOUND;
// pub const KEY_PLAYCD: CGKeyCode = NOT_FOUND;
// pub const KEY_PAUSECD: CGKeyCode = NOT_FOUND;
// pub const KEY_PROG3: CGKeyCode = NOT_FOUND;
// pub const KEY_PROG4: CGKeyCode = NOT_FOUND;
// pub const KEY_DASHBOARD: CGKeyCode = NOT_FOUND;
// pub const KEY_SUSPEND: CGKeyCode = NOT_FOUND;
// pub const KEY_CLOSE: CGKeyCode = NOT_FOUND;
// pub const KEY_PLAY: CGKeyCode = NOT_FOUND;
// pub const KEY_FASTFORWARD: CGKeyCode = NOT_FOUND;
// pub const KEY_BASSBOOST: CGKeyCode = NOT_FOUND;
// pub const KEY_PRINT: CGKeyCode = NOT_FOUND;
// pub const KEY_HP: CGKeyCode = NOT_FOUND;
// pub const KEY_CAMERA: CGKeyCode = NOT_FOUND;
// pub const KEY_SOUND: CGKeyCode = NOT_FOUND;
// pub const KEY_QUESTION: CGKeyCode = NOT_FOUND;
// pub const KEY_EMAIL: CGKeyCode = NOT_FOUND;
// pub const KEY_CHAT: CGKeyCode = NOT_FOUND;
// pub const KEY_SEARCH: CGKeyCode = NOT_FOUND;
// pub const KEY_CONNECT: CGKeyCode = NOT_FOUND;
// pub const KEY_FINANCE: CGKeyCode = NOT_FOUND;
// pub const KEY_SPORT: CGKeyCode = NOT_FOUND;
//pub const KEY_SHOP: CGKeyCode = NOT_FOUND;
pub const KEY_ALTERASE: CGKeyCode = 0x0047;
// pub const KEY_CANCEL: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESSDOWN: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESSUP: CGKeyCode = NOT_FOUND;
// pub const KEY_MEDIA: CGKeyCode = NOT_FOUND;
// pub const KEY_SWITCHVIDEOMODE: CGKeyCode = NOT_FOUND;
// pub const KEY_KBDILLUMTOGGLE: CGKeyCode = NOT_FOUND;
// pub const KEY_KBDILLUMDOWN: CGKeyCode = NOT_FOUND;
// pub const KEY_KBDILLUMUP: CGKeyCode = NOT_FOUND;
// pub const KEY_SEND: CGKeyCode = NOT_FOUND;
// pub const KEY_REPLY: CGKeyCode = NOT_FOUND;
// pub const KEY_FORWARDMAIL: CGKeyCode = NOT_FOUND;
// pub const KEY_SAVE: CGKeyCode = NOT_FOUND;
// pub const KEY_DOCUMENTS: CGKeyCode = NOT_FOUND;
// pub const KEY_BATTERY: CGKeyCode = NOT_FOUND;
// pub const KEY_BLUETOOTH: CGKeyCode = NOT_FOUND;
// pub const KEY_UWB: CGKeyCode = NOT_FOUND;
// pub const KEY_UNKNOWN: CGKeyCode = NOT_FOUND;
// pub const KEY_VIDEO_NEXT: CGKeyCode = NOT_FOUND;
// pub const KEY_VIDEO_PREV: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESS_CYCLE: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESS_AUTO: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESS_ZERO: CGKeyCode = NOT_FOUND;
// pub const KEY_BRIGHTNESS_AUTO: CGKeyCode = NOT_FOUND;
// pub const KEY_DISPLAY_OFF: CGKeyCode = NOT_FOUND;
pub fn key_map() -> HashMap<&'static str, CGKeyCode> {
[
// grep 'Key => 0x' ../rusty-keys-win/src/windows/inputs.rs | tr '[a-z]' '[A-Z]' | sed -r -e 's/KEY => 0X/: CGKeyCode = 0x/' -e 's/^[ ]+/pub const KEY_/' | tr ',' ';'
("ESC", KEY_ESC),
("1", KEY_1),
("2", KEY_2),
("3", KEY_3),
("4", KEY_4),
("5", KEY_5),
("6", KEY_6),
("7", KEY_7),
("8", KEY_8),
("9", KEY_9),
("10", KEY_10),
("MINUS", KEY_MINUS),
("EQUAL", KEY_EQUAL),
("BACKSPACE", KEY_BACKSPACE),
("TAB", KEY_TAB),
("Q", KEY_Q),
("W", KEY_W),
("E", KEY_E),
("R", KEY_R),
("T", KEY_T),
("Y", KEY_Y),
("U", KEY_U),
("I", KEY_I),
("O", KEY_O),
("P", KEY_P),
("LEFTBRACE", KEY_LEFTBRACE),
("RIGHTBRACE", KEY_RIGHTBRACE),
("ENTER", KEY_ENTER),
("LEFTCTRL", KEY_LEFTCTRL),
("A", KEY_A),
("S", KEY_S),
("D", KEY_D),
("F", KEY_F),
("G", KEY_G),
("H", KEY_H),
("J", KEY_J),
("K", KEY_K),
("L", KEY_L),
("SEMICOLON", KEY_SEMICOLON),
("APOSTROPHE", KEY_APOSTROPHE),
("GRAVE", KEY_GRAVE),
("LEFTSHIFT", KEY_LEFTSHIFT),
("BACKSLASH", KEY_BACKSLASH),
("Z", KEY_Z),
("X", KEY_X),
("C", KEY_C),
("V", KEY_V),
("B", KEY_B),
("N", KEY_N),
("M", KEY_M),
("COMMA", KEY_COMMA),
("DOT", KEY_DOT),
("SLASH", KEY_SLASH),
("RIGHTSHIFT", KEY_RIGHTSHIFT),
("KPASTERISK", KEY_KPASTERISK),
("LEFTALT", KEY_LEFTALT),
("SPACE", KEY_SPACE),
("CAPSLOCK", KEY_CAPSLOCK),
("F1", KEY_F1),
("F2", KEY_F2),
("F3", KEY_F3),
("F4", KEY_F4),
("F5", KEY_F5),
("F6", KEY_F6),
("F7", KEY_F7),
("F8", KEY_F8),
("F9", KEY_F9),
("F10", KEY_F10),
("NUMLOCK", KEY_NUMLOCK),
("SCROLLLOCK", KEY_SCROLLLOCK),
("KP7", KEY_KP7),
("KP8", KEY_KP8),
("KP9", KEY_KP9),
("KPMINUS", KEY_KPMINUS),
("KP4", KEY_KP4),
("KP5", KEY_KP5),
("KP6", KEY_KP6),
("KPPLUS", KEY_KPPLUS),
("KP1", KEY_KP1),
("KP2", KEY_KP2),
("KP3", KEY_KP3),
("KP0", KEY_KP0),
("KPDOT", KEY_KPDOT),
/*
("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU),
("102ND", KEY_102ND),
*/
("F11", KEY_F11),
("F12", KEY_F12),
/*
("RO", KEY_RO),
("KATAKANA", KEY_KATAKANA),
("HIRAGANA", KEY_HIRAGANA),
("HENKAN", KEY_HENKAN),
("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA),
("MUHENKAN", KEY_MUHENKAN),
("KPJPCOMMA", KEY_KPJPCOMMA),
*/
("KPENTER", KEY_KPENTER),
("RIGHTCTRL", KEY_RIGHTCTRL),
("KPSLASH", KEY_KPSLASH),
("SYSRQ", KEY_SYSRQ),
("RIGHTALT", KEY_RIGHTALT),
/*
("LINEFEED", KEY_LINEFEED),
*/
("HOME", KEY_HOME),
("UP", KEY_UP),
("PAGEUP", KEY_PAGEUP),
("LEFT", KEY_LEFT),
("RIGHT", KEY_RIGHT),
("END", KEY_END),
("DOWN", KEY_DOWN),
("PAGEDOWN", KEY_PAGEDOWN),
("INSERT", KEY_INSERT),
("DELETE", KEY_DELETE),
/*
("MACRO", KEY_MACRO),
("MUTE", KEY_MUTE),
("VOLUMEDOWN", KEY_VOLUMEDOWN),
("VOLUMEUP", KEY_VOLUMEUP),
("POWER", KEY_POWER),
("KPEQUAL", KEY_KPEQUAL),
("KPPLUSMINUS", KEY_KPPLUSMINUS),
("PAUSE", KEY_PAUSE),
("SCALE", KEY_SCALE),
("KPCOMMA", KEY_KPCOMMA),
("HANGEUL", KEY_HANGEUL),
("HANGUEL", KEY_HANGUEL),
("HANGEUL", KEY_HANGEUL),
("HANJA", KEY_HANJA),
("YEN", KEY_YEN),
("LEFTMETA", KEY_LEFTMETA),
("RIGHTMETA", KEY_RIGHTMETA),
("COMPOSE", KEY_COMPOSE),
("STOP", KEY_STOP),
("AGAIN", KEY_AGAIN),
("PROPS", KEY_PROPS),
("UNDO", KEY_UNDO),
("FRONT", KEY_FRONT),
("COPY", KEY_COPY),
("OPEN", KEY_OPEN),
("PASTE", KEY_PASTE),
("FIND", KEY_FIND),
("CUT", KEY_CUT),
("HELP", KEY_HELP),
("MENU", KEY_MENU),
("CALC", KEY_CALC),
("SETUP", KEY_SETUP),
("SLEEP", KEY_SLEEP),
("WAKEUP", KEY_WAKEUP),
("FILE", KEY_FILE),
("SENDFILE", KEY_SENDFILE),
("DELETEFILE", KEY_DELETEFILE),
("XFER", KEY_XFER),
("PROG1", KEY_PROG1),
("PROG2", KEY_PROG2),
("WWW", KEY_WWW),
("MSDOS", KEY_MSDOS),
("COFFEE", KEY_COFFEE),
("SCREENLOCK", KEY_SCREENLOCK),
("COFFEE", KEY_COFFEE),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("DIRECTION", KEY_DIRECTION),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("CYCLEWINDOWS", KEY_CYCLEWINDOWS),
("MAIL", KEY_MAIL),
("BOOKMARKS", KEY_BOOKMARKS),
("COMPUTER", KEY_COMPUTER),
("BACK", KEY_BACK),
("FORWARD", KEY_FORWARD),
("CLOSECD", KEY_CLOSECD),
("EJECTCD", KEY_EJECTCD),
("EJECTCLOSECD", KEY_EJECTCLOSECD),
("NEXTSONG", KEY_NEXTSONG),
("PLAYPAUSE", KEY_PLAYPAUSE),
("PREVIOUSSONG", KEY_PREVIOUSSONG),
("STOPCD", KEY_STOPCD),
("RECORD", KEY_RECORD),
("REWIND", KEY_REWIND),
("PHONE", KEY_PHONE),
("ISO", KEY_ISO),
("CONFIG", KEY_CONFIG),
("HOMEPAGE", KEY_HOMEPAGE),
("REFRESH", KEY_REFRESH),
("EXIT", KEY_EXIT),
("MOVE", KEY_MOVE),
("EDIT", KEY_EDIT),
("SCROLLUP", KEY_SCROLLUP),
("SCROLLDOWN", KEY_SCROLLDOWN),
("KPLEFTPAREN", KEY_KPLEFTPAREN),
("KPRIGHTPAREN", KEY_KPRIGHTPAREN),
("NEW", KEY_NEW),
("REDO", KEY_REDO),
("F13", KEY_F13),
("F14", KEY_F14),
("F15", KEY_F15),
("F16", KEY_F16),
("F17", KEY_F17),
("F18", KEY_F18),
("F19", KEY_F19),
("F20", KEY_F20),
("F21", KEY_F21),
("F22", KEY_F22),
("F23", KEY_F23),
("F24", KEY_F24),
("PLAYCD", KEY_PLAYCD),
("PAUSECD", KEY_PAUSECD),
("PROG3", KEY_PROG3),
("PROG4", KEY_PROG4),
("DASHBOARD", KEY_DASHBOARD),
("SUSPEND", KEY_SUSPEND),
("CLOSE", KEY_CLOSE),
("PLAY", KEY_PLAY),
("FASTFORWARD", KEY_FASTFORWARD),
("BASSBOOST", KEY_BASSBOOST),
("PRINT", KEY_PRINT),
("HP", KEY_HP),
("CAMERA", KEY_CAMERA),
("SOUND", KEY_SOUND),
("QUESTION", KEY_QUESTION),
("EMAIL", KEY_EMAIL),
("CHAT", KEY_CHAT),
("SEARCH", KEY_SEARCH),
("CONNECT", KEY_CONNECT),
("FINANCE", KEY_FINANCE),
("SPORT", KEY_SPORT),
("SHOP", KEY_SHOP),
("ALTERASE", KEY_ALTERASE),
("CANCEL", KEY_CANCEL),
("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN),
("BRIGHTNESSUP", KEY_BRIGHTNESSUP),
("MEDIA", KEY_MEDIA),
("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE),
("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE),
("KBDILLUMDOWN", KEY_KBDILLUMDOWN),
("KBDILLUMUP", KEY_KBDILLUMUP),
("SEND", KEY_SEND),
("REPLY", KEY_REPLY),
("FORWARDMAIL", KEY_FORWARDMAIL),
("SAVE", KEY_SAVE),
("DOCUMENTS", KEY_DOCUMENTS),
("BATTERY", KEY_BATTERY),
("BLUETOOTH", KEY_BLUETOOTH),
("WLAN", KEY_WLAN),
("UWB", KEY_UWB),
("UNKNOWN", KEY_UNKNOWN),
("VIDEO_NEXT", KEY_VIDEO_NEXT),
("VIDEO_PREV", KEY_VIDEO_PREV),
("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("DISPLAY_OFF", KEY_DISPLAY_OFF),
("WWAN", KEY_WWAN),
("WIMAX", KEY_WIMAX),
("WWAN", KEY_WWAN),
("RFKILL", KEY_RFKILL),
("MICMUTE", KEY_MICMUTE),
*/
// below manual shortcuts
("PSCR", KEY_SYSRQ),
("SLCK", KEY_SCROLLLOCK),
("BRK", KEY_PAUSE),
("GRV", KEY_GRAVE),
("0", KEY_10), // dumb or named wrong?
("MINS", KEY_MINUS),
("EQL", KEY_EQUAL),
("BSPC", KEY_BACKSPACE),
("LBRC", KEY_LEFTBRACE),
("RBRC", KEY_RIGHTBRACE),
("BSLS", KEY_BACKSLASH),
("SCLN", KEY_SEMICOLON),
("QUOT", KEY_APOSTROPHE),
("ENT", KEY_ENTER),
("COMM", KEY_COMMA),
("DOT", KEY_DOT),
("SLSH", KEY_SLASH),
("CAPS", KEY_CAPSLOCK),
("LSFT", KEY_LEFTSHIFT),
("RSFT", KEY_RIGHTSHIFT),
("SPC", KEY_SPACE),
("APP", KEY_COMPOSE),
("LCTL", KEY_LEFTCTRL),
("RCTL", KEY_RIGHTCTRL),
("LALT", KEY_LEFTALT),
("RALT", KEY_RIGHTALT),
("LGUI", KEY_LEFTMETA),
("RGUI", KEY_RIGHTMETA),
("INS", KEY_INSERT),
("PGUP", KEY_PAGEUP),
("PGDN", KEY_PAGEDOWN),
("DEL", KEY_DELETE),
("RGHT", KEY_RIGHT),
("NLCK", KEY_NUMLOCK),
("PSLS", KEY_KPSLASH),
("PAST", KEY_KPASTERISK),
("PMNS", KEY_KPMINUS),
("P7", KEY_KP7),
("P8", KEY_KP8),
("P9", KEY_KP9),
("P4", KEY_KP4),
("P5", KEY_KP5),
("P6", KEY_KP6),
("PPLS", KEY_KPPLUS),
("P1", KEY_KP1),
("P2", KEY_KP2),
("P3", KEY_KP3),
("P0", KEY_KP0),
("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER),
].iter().cloned().map(|(m, v)| (m, v)).collect()
}

414
src/macos/mod.rs Normal file
View File

@ -0,0 +1,414 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(improper_ctypes)]
use crate::*;
use std::env;
use std::process::exit;
use getopts::Options;
use std::fs::File;
pub mod codes;
use codes::*;
use core_graphics::event::CGKeyCode;
use core_graphics::event::*;
use core_graphics::event_source::*;
use core_graphics::event::{CGEventTapLocation, CGEventType};
type MacOSKeyMaps = KeyMaps<CGEventSource, CGKeyCode, CGEvent, Option<CGEvent>>;
type CallbackPointer = (MacOSKeyMaps, CGEventSource);
/*
// possible types for event_source
Private = -1,
CombinedSessionState = 0,
HIDSystemState = 1,
// possible types for tapLocation
HID,
Session,
AnnotatedSession,
*/
// macOS seems to require this, or it ignores shift, WHY?
const delay: std::time::Duration = std::time::Duration::from_millis(20);
const tapLocation: CGEventTapLocation = CGEventTapLocation::Session;
// this is only used if tapLocation is HID, to prevent us from mapping our own key inputs
const uniqueHIDUserData: i64 = 45;
impl KeyEvent<CGKeyCode> for CGEvent {
fn code(&self) -> CGKeyCode {
self.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE) as CGKeyCode
}
fn value(&self) -> KeyState {
let event_type = self.get_type();
match event_type {
CGEventType::FlagsChanged => {
let flags = self.get_flags().bits(); // todo: fix cast?
let mask = match self.code() {
KEY_LEFTCTRL => NX_DEVICELCTLKEYMASK,
KEY_RIGHTCTRL => NX_DEVICERCTLKEYMASK,
KEY_LEFTSHIFT => NX_DEVICELSHIFTKEYMASK,
KEY_RIGHTSHIFT => NX_DEVICERSHIFTKEYMASK,
KEY_LEFTMETA => NX_DEVICELCMDKEYMASK,
KEY_RIGHTMETA => NX_DEVICERCMDKEYMASK,
KEY_LEFTALT => NX_DEVICELALTKEYMASK,
KEY_RIGHTALT => NX_DEVICERALTKEYMASK,
KEY_CAPSLOCK => NX_DEVICECAPSLOCKMASK,
_ => panic!("unhandled key: {}", self.code()),
};
if (flags & mask) != 0 { KeyState::DOWN } else { KeyState::UP }
},
CGEventType::KeyDown => KeyState::DOWN,
CGEventType::KeyUp => KeyState::UP,
CGEventType::TapDisabledByTimeout => {
println!("Quartz event tap disabled because of timeout; attempting to reregister.");
todo!("implement register listener");
//register_listener(channel);
//KeyState::OTHER
},
_ => {
println!("Received unknown EventType: {:?}", event_type);
KeyState::OTHER
},
}
}
}
impl Keyboard<CGKeyCode, CGEvent, Option<CGEvent>> for CGEventSource {
fn send(&self, event: &mut CGEvent) -> Result<Option<CGEvent>> {
//println!("send orig: {}", event.code());
//Ok(Some(event.event))
self.send_mod_code_value(event.code(), event.value() == KeyState::UP, event)
}
fn send_mod_code(&self, code: CGKeyCode, event: &mut CGEvent) -> Result<Option<CGEvent>> {
// event.value should only ever be UP/DOWN when this method is called
//println!("send_mod_code orig: {} code: {}", event.code(), code);
//unsafe { CGEventSetIntegerValueField(event.event, kCGKeyboardEventKeycode, code as i64) };
//Ok(Some(event.event))
self.send_mod_code_value(code, event.value() == KeyState::UP, event)
}
fn send_mod_code_value(&self, code: CGKeyCode, up_not_down: bool, _event: &mut CGEvent) -> Result<Option<CGEvent>> {
//println!("send_mod_code_value orig: {} code: {}, up_not_down: {}", event.code(), code, up_not_down);
//return Ok(None);
let event =
CGEvent::new_keyboard_event(self.clone(), code, !up_not_down)
.expect("Failed creating event");
match tapLocation {
CGEventTapLocation::HID => event.set_integer_value_field(EventField::EVENT_SOURCE_USER_DATA, uniqueHIDUserData),
_ => {}
};
event.post(tapLocation);
Ok(None)
}
fn synchronize(&self) -> Result<Option<CGEvent>> {
std::thread::sleep(delay);
Ok(None)
}
fn left_shift_code(&self) -> CGKeyCode {
KEY_LEFTSHIFT
}
fn right_shift_code(&self) -> CGKeyCode {
KEY_RIGHTSHIFT
}
fn caps_lock_code(&self) -> CGKeyCode {
KEY_CAPSLOCK
}
fn block_key(&self) -> Result<Option<CGEvent>> {
Ok(None)
}
}
pub fn main_res() -> Result<()> {
let config = parse_args();
//println!("Config: {:?}", config);
let key_map = key_map();
//println!("key_map: {:?}", key_map);
let key_maps = MacOSKeyMaps::from_cfg(&key_map, &config.config_file);
//println!("key_maps: {}", key_maps);
let callback_pointer: CallbackPointer = (key_maps, CGEventSource::new(CGEventSourceStateID::Private).expect("Failed creating event source"));
let mask = CGEventMaskBit(CGEventType::KeyDown)
| CGEventMaskBit(CGEventType::KeyUp)
| CGEventMaskBit(CGEventType::FlagsChanged)
;
unsafe {
let options = 0;
// Create the event tap
let event_tap = CGEventTapCreate(
kCGSessionEventTap,
kCGHeadInsertEventTap,
options,
mask,
callback,
&callback_pointer,
);
if event_tap.is_null() {
panic!("Unable to create event tap. Please make sure you have the correct permissions");
}
println!("Created event tap...");
let allocator = kCFAllocatorDefault;
let current_event_loop = CFRunLoopGetCurrent();
let mode = kCFRunLoopCommonModes;
// Create Run Loop Source
let run_loop_source = CFMachPortCreateRunLoopSource(allocator, event_tap, 0);
// Add Run Loop Source to the current event loop
CFRunLoopAddSource(current_event_loop, run_loop_source, mode);
// Enable the tap
CGEventTapEnable(event_tap, true);
CFRunLoopRun();
}
Ok(())
}
#[derive(Debug)]
struct Config {
config_file: String
}
impl Config {
fn new(config_file: String) -> Self {
Config { config_file: config_file }
}
}
fn get_env_push(key: &str, to_push: &str, vec: &mut Vec<String>) {
if let Some(var) = env::var_os(key) {
if let Ok(str) = var.into_string() {
let mut str = str.to_owned();
str.push_str(to_push);
vec.push(str);
}
}
}
fn parse_args() -> Config {
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options] [keymap.toml]", program);
println!("{}", opts.usage(&brief));
}
let args: Vec<_> = env::args().collect();
let mut default_configs = Vec::new();
get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs);
get_env_push("APPDATA", "\\keymap.toml", &mut default_configs);
default_configs.push("keymap.toml".to_string());
let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs);
let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message");
opts.optflag("v", "version", "prints the version");
opts.optopt("c", "config", &c_msg, "FILE");
let matches = opts.parse(&args[1..]);
if matches.is_err() {
print_usage(&args[0], opts);
exit(1);
}
let matches = matches.unwrap();
if matches.opt_present("h") {
print_usage(&args[0], opts);
exit(0);
}
if matches.opt_present("v") {
println!("rusty-keys {}", VERSION);
exit(0);
}
let config_file = matches.opt_str("c").unwrap_or_else(|| {
let remaining_args = matches.free;
if remaining_args.len() > 0 {
remaining_args[0].clone()
} else {
for keymap in default_configs.drain(..) {
if File::open(&keymap).is_ok() {
return keymap;
}
}
println!("Error: no keymap.toml found...");
print_usage(&args[0], opts);
exit(1);
}
});
Config::new(config_file)
}
use libc;
// Opaque Pointer Types
type Pointer = *mut libc::c_void;
type CFMachPortRef = Pointer;
// Integer Types
type CGEventMask = u64;
type CGEventTapOptions = u32;
type CGEventTapPlacement = u32;
// Callback Type
type CGEventTapCallBack = extern "C" fn(Pointer, CGEventType, CGEvent, &mut CallbackPointer) -> CGEvent;
// Constants
const kCGSessionEventTap: CGEventTapLocation = CGEventTapLocation::HID;
const kCGHeadInsertEventTap: CGEventTapPlacement = 0;
// Link to ApplicationServices/ApplicationServices.h and Carbon/Carbon.h
#[link(name = "ApplicationServices", kind = "framework")]
#[link(name = "Carbon", kind = "framework")]
extern {
/// Pass through to the default loop modes
pub static kCFRunLoopCommonModes: Pointer;
/// Pass through to the default allocator
pub static kCFAllocatorDefault: Pointer;
/// Run the current threads loop in default mode
pub fn CFRunLoopRun();
/// Obtain the current threads loop
pub fn CFRunLoopGetCurrent() -> Pointer;
/// Create an event tap
///
/// # Arguments
///
/// * `place` - The location of the new event tap. Pass one of
/// the constants listed in Event Tap Locations. Only
/// processes running as the root user may locate an
/// event tap at the point where HID events enter the
/// window server; for other users, this function
/// returns NULL.
///
/// * `options` - The placement of the new event tap in the
/// list of active event taps. Pass one of the
/// constants listed in Event Tap Placement.
///
/// * `eventsOfInterest` - A constant that specifies whether
/// the new event tap is a passive listener or an
/// active filter.
///
/// * `callback` - A bit mask that specifies the set of events
/// to be observed. For a list of possible events,
/// see Event Types. For information on how to
/// specify the mask, see CGEventMask. If the event
/// tap is not permitted to monitor one or more of
/// the events specified in the eventsOfInterest
/// parameter, then the appropriate bits in the mask
/// are cleared. If that action results in an empty
/// mask, this function returns NULL. callback
///
/// * `refcon` - An event tap callback function that you
/// provide. Your callback function is invoked from
/// the run loop to which the event tap is added as a
/// source. The thread safety of the callback is
/// defined by the run loops environment. To learn
/// more about event tap callbacks, see
/// CGEventTapCallBack. refcon
///
/// * `channel` - A pointer to user-defined data. This pointer
/// is passed into the callback function specified in
/// the callback parameter. Here we use it as a mpsc
/// channel.
pub fn CGEventTapCreate(
tap: CGEventTapLocation,
place: CGEventTapPlacement,
options: CGEventTapOptions,
eventsOfInterest: CGEventMask,
callback: CGEventTapCallBack,
channel: &CallbackPointer,
) -> CFMachPortRef;
/// Creates a CFRunLoopSource object for a CFMachPort
/// object.
///
/// The run loop source is not automatically added to
/// a run loop. To add the source to a run loop, use
/// CFRunLoopAddSource
pub fn CFMachPortCreateRunLoopSource(
allocator: Pointer,
port: CFMachPortRef,
order: libc::c_int,
) -> Pointer;
/// Adds a CFRunLoopSource object to a run loop mode.
pub fn CFRunLoopAddSource(
run_loop: Pointer,
run_loop_source: Pointer,
mode: Pointer,
);
pub fn CGEventTapEnable(port: CFMachPortRef, enable: bool);
}
const NX_DEVICELCTLKEYMASK: u64 = 0x00000001;
const NX_DEVICERCTLKEYMASK: u64 = 0x00002000;
const NX_DEVICELSHIFTKEYMASK: u64 = 0x00000002;
const NX_DEVICERSHIFTKEYMASK: u64 = 0x00000004;
const NX_DEVICELCMDKEYMASK: u64 = 0x00000008;
const NX_DEVICERCMDKEYMASK: u64 = 0x00000010;
const NX_DEVICELALTKEYMASK: u64 = 0x00000020;
const NX_DEVICERALTKEYMASK: u64 = 0x00000040;
const NX_DEVICECAPSLOCKMASK: u64 = 1 << 16;
/// This callback will be registered to be invoked from the run loop
/// to which the event tap is added as a source.
#[no_mangle]
#[allow(unused_variables, improper_ctypes_definitions)]
pub extern fn callback(proxy: Pointer, event_type: CGEventType, mut event: CGEvent, callback_pointer: &mut CallbackPointer) -> CGEvent {
let (key_maps, event_source) = callback_pointer;
match tapLocation {
CGEventTapLocation::HID => {
let user_data = event.get_integer_value_field(EventField::EVENT_SOURCE_USER_DATA);
if user_data == uniqueHIDUserData {
return event;
}
}
_ => {}
};
key_maps.send_event(&mut event, &event_source).expect("macos shouldn't error...")
.unwrap_or_else(|| {
event.set_type(CGEventType::Null);
event
}) // None means return NULL
}
/// Redefine macro for bitshifting from header as function here
pub fn CGEventMaskBit(eventType: CGEventType) -> CGEventMask {
1 << (eventType as u32)
}

View File

@ -1,193 +1,13 @@
extern crate rusty_keys;
extern crate uinput_sys as ffi;
extern crate libc;
extern crate getopts;
#[macro_use]
extern crate nix;
use rusty_keys::KeyMaps;
use ffi::*;
use libc::{c_int, input_event};
//use std::thread;
//use std::time::Duration;
use std::process::{exit, Command};
use std::fs::File;
use std::io::Read;
use std::{env, mem};
use std::os::unix::io::AsRawFd;
use getopts::Options;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const EV_KEY_U16: u16 = EV_KEY as u16;
#[derive(Debug)]
struct Config {
device_file: String,
config_file: String
}
impl Config {
fn new(device_file: String, config_file: String) -> Self {
Config { device_file: device_file, config_file: config_file }
}
}
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn main() {
let key_map = KeyMaps::key_map();
//println!("key_map: {:?}", key_map);
let device = rusty_keys::default().expect("1")
.name("test").expect("2")
.event(key_map.values()).expect("3")
//.event(uinput::event::Keyboard::All).unwrap()
.create().expect("4");
//thread::sleep(Duration::from_secs(1));
let config = parse_args();
//println!("Config: {:?}", config);
let mut input_device = InputDevice::open(&config.device_file);
input_device.grab();
let mut key_map = KeyMaps::from_cfg(&key_map, config.config_file);
//println!("keymaps: {:?}", keymaps);
loop {
let event = input_device.read_event();
if event.type_ == EV_KEY_U16 {
key_map.send_event(event, &device);
/*
println!("type: {} code: {}", event.type_, event.code);
if event.code == KEY_A as u16 {
event.code = KEY_B as u16;
}
*/
} else {
device.write_event(event).expect("could not write event?");
}
let ret = rusty_keys::main_res();
if let Err(e) = ret {
println!("fatal error: {}", e);
}
}
fn parse_args() -> Config {
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options]", program);
println!("{}", opts.usage(&brief));
}
let args: Vec<_> = env::args().collect();
let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message");
opts.optflag("v", "version", "prints the version");
opts.optopt("d", "device", "specify the keyboard input device file", "DEVICE");
opts.optopt("c", "config", "specify the keymap config file to use", "FILE");
let matches = opts.parse(&args[1..]).unwrap_or_else(|e| panic!("{}", e));
if matches.opt_present("h") {
print_usage(&args[0], opts);
exit(0);
}
if matches.opt_present("v") {
println!("rusty-keys {}", VERSION);
exit(0);
}
let device_file = matches.opt_str("d").unwrap_or_else(|| get_default_device());
let config_file = matches.opt_str("c").unwrap_or("keymap.toml".to_owned());
Config::new(device_file, config_file)
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
fn main() {
panic!("sorry no main impl for this platform");
}
fn get_default_device() -> String {
let mut filenames = get_keyboard_device_filenames();
println!("Detected devices: {:?}", filenames);
if filenames.len() == 1 {
filenames.swap_remove(0)
} else {
panic!("The following keyboard devices were detected: {:?}. Please select one using \
the `-d` flag", filenames);
}
}
// Detects and returns the name of the keyboard device file. This function uses
// the fact that all device information is shown in /proc/bus/input/devices and
// the keyboard device file should always have an EV of 120013
fn get_keyboard_device_filenames() -> Vec<String> {
let mut command_str = "grep -E 'Handlers|EV' /proc/bus/input/devices".to_string();
command_str.push_str("| grep -B1 120013");
command_str.push_str("| grep -Eo event[0-9]+");
let res = Command::new("sh").arg("-c").arg(command_str).output().unwrap_or_else(|e| {
panic!("{}", e);
});
let res_str = std::str::from_utf8(&res.stdout).unwrap();
let mut filenames = Vec::new();
for file in res_str.trim().split('\n') {
let mut filename = "/dev/input/".to_string();
filename.push_str(file);
filenames.push(filename);
}
filenames
}
// inputdevice stuff
ioctl!(write eviocgrab with b'E', 0x90; c_int);
// TODO: use size_of_input_event instead of hard-coding 24.
const SIZE_OF_INPUT_EVENT: usize = 24;//mem::size_of::<input_event>();
struct InputDevice {
device_file: File,
buf: [u8; SIZE_OF_INPUT_EVENT],
}
impl InputDevice {
pub fn open(device_file: &str) -> Self {
let device_file = File::open(device_file).unwrap_or_else(|e| panic!("{}", e));
InputDevice {
device_file: device_file,
buf: [0u8; SIZE_OF_INPUT_EVENT],
}
}
pub fn read_event(&mut self) -> input_event {
let num_bytes = self.device_file.read(&mut self.buf).unwrap_or_else(|e| panic!("{}", e));
if num_bytes != SIZE_OF_INPUT_EVENT {
panic!("Error while reading from device file");
}
let event: input_event = unsafe { mem::transmute(self.buf) };
event
}
pub fn grab(&mut self) {
unsafe {
eviocgrab(self.device_file.as_raw_fd(), 1 as *const c_int).expect("no grab?");
}
}
pub fn release(&mut self) {
unsafe {
eviocgrab(self.device_file.as_raw_fd(), 0 as *const c_int).expect("no release?");
}
}
}
impl Drop for InputDevice {
fn drop(&mut self) {
self.release();
}
}

579
src/windows/codes.rs Normal file
View File

@ -0,0 +1,579 @@
use winapi::shared::minwindef::DWORD;
use std::collections::HashMap;
use std::convert::TryFrom;
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
pub const KEY_BACKSPACE: DWORD = 0x08;
pub const KEY_TAB: DWORD = 0x09;
pub const KEY_ENTER: DWORD = 0x0D;
pub const KEY_KPENTER: DWORD = 0x0D; // on windows, same as KEY_ENTER..
pub const KEY_ESC: DWORD = 0x1B;
pub const KEY_SPACE: DWORD = 0x20;
pub const KEY_HOME: DWORD = 0x24;
pub const KEY_LEFT: DWORD = 0x25;
pub const KEY_UP: DWORD = 0x26;
pub const KEY_RIGHT: DWORD = 0x27;
pub const KEY_DOWN: DWORD = 0x28;
pub const KEY_INSERT: DWORD = 0x2D;
pub const KEY_DELETE: DWORD = 0x2E;
pub const KEY_10: DWORD = 0x30; // named 10 on linux, 0 on windows..
pub const KEY_0: DWORD = 0x30;
pub const KEY_1: DWORD = 0x31;
pub const KEY_2: DWORD = 0x32;
pub const KEY_3: DWORD = 0x33;
pub const KEY_4: DWORD = 0x34;
pub const KEY_5: DWORD = 0x35;
pub const KEY_6: DWORD = 0x36;
pub const KEY_7: DWORD = 0x37;
pub const KEY_8: DWORD = 0x38;
pub const KEY_9: DWORD = 0x39;
pub const KEY_A: DWORD = 0x41;
pub const KEY_B: DWORD = 0x42;
pub const KEY_C: DWORD = 0x43;
pub const KEY_D: DWORD = 0x44;
pub const KEY_E: DWORD = 0x45;
pub const KEY_F: DWORD = 0x46;
pub const KEY_G: DWORD = 0x47;
pub const KEY_H: DWORD = 0x48;
pub const KEY_I: DWORD = 0x49;
pub const KEY_J: DWORD = 0x4A;
pub const KEY_K: DWORD = 0x4B;
pub const KEY_L: DWORD = 0x4C;
pub const KEY_M: DWORD = 0x4D;
pub const KEY_N: DWORD = 0x4E;
pub const KEY_O: DWORD = 0x4F;
pub const KEY_P: DWORD = 0x50;
pub const KEY_Q: DWORD = 0x51;
pub const KEY_R: DWORD = 0x52;
pub const KEY_S: DWORD = 0x53;
pub const KEY_T: DWORD = 0x54;
pub const KEY_U: DWORD = 0x55;
pub const KEY_V: DWORD = 0x56;
pub const KEY_W: DWORD = 0x57;
pub const KEY_X: DWORD = 0x58;
pub const KEY_Y: DWORD = 0x59;
pub const KEY_Z: DWORD = 0x5A;
pub const KEY_KP0: DWORD = 0x60;
pub const KEY_KP1: DWORD = 0x61;
pub const KEY_KP2: DWORD = 0x62;
pub const KEY_KP3: DWORD = 0x63;
pub const KEY_KP4: DWORD = 0x64;
pub const KEY_KP5: DWORD = 0x65;
pub const KEY_KP6: DWORD = 0x66;
pub const KEY_KP7: DWORD = 0x67;
pub const KEY_KP8: DWORD = 0x68;
pub const KEY_KP9: DWORD = 0x69;
pub const KEY_F1: DWORD = 0x70;
pub const KEY_F2: DWORD = 0x71;
pub const KEY_F3: DWORD = 0x72;
pub const KEY_F4: DWORD = 0x73;
pub const KEY_F5: DWORD = 0x74;
pub const KEY_F6: DWORD = 0x75;
pub const KEY_F7: DWORD = 0x76;
pub const KEY_F8: DWORD = 0x77;
pub const KEY_F9: DWORD = 0x78;
pub const KEY_F10: DWORD = 0x79;
pub const KEY_F11: DWORD = 0x7A;
pub const KEY_F12: DWORD = 0x7B;
pub const KEY_NUMLOCK: DWORD = 0x90;
pub const KEY_SCROLLLOCK: DWORD = 0x91;
pub const KEY_CAPSLOCK: DWORD = 0x14;
pub const KEY_LEFTSHIFT: DWORD = 0xA0;
pub const KEY_RIGHTSHIFT: DWORD = 0xA1;
pub const KEY_LEFTCTRL: DWORD = 0xA2;
pub const KEY_RIGHTCTRL: DWORD = 0xA3;
pub const KEY_LBUTTON: DWORD = 0x01;
pub const KEY_RBUTTON: DWORD = 0x02;
pub const KEY_CANCEL: DWORD = 0x03;
pub const KEY_MBUTTON: DWORD = 0x04;
pub const KEY_XBUTTON1: DWORD = 0x05;
pub const KEY_XBUTTON2: DWORD = 0x06;
pub const KEY_BACK: DWORD = 0x08;
pub const KEY_CLEAR: DWORD = 0x0C;
pub const KEY_RETURN: DWORD = 0x0D;
pub const KEY_SHIFT: DWORD = 0x10;
pub const KEY_CONTROL: DWORD = 0x11;
pub const KEY_MENU: DWORD = 0x12;
pub const KEY_PAUSE: DWORD = 0x13;
pub const KEY_CAPITAL: DWORD = 0x14;
pub const KEY_KANA: DWORD = 0x15;
pub const KEY_HANGEUL: DWORD = 0x15;
pub const KEY_HANGUL: DWORD = 0x15;
pub const KEY_JUNJA: DWORD = 0x17;
pub const KEY_FINAL: DWORD = 0x18;
pub const KEY_HANJA: DWORD = 0x19;
pub const KEY_KANJI: DWORD = 0x19;
pub const KEY_ESCAPE: DWORD = 0x1B;
pub const KEY_CONVERT: DWORD = 0x1C;
pub const KEY_NONCONVERT: DWORD = 0x1D;
pub const KEY_ACCEPT: DWORD = 0x1E;
pub const KEY_MODECHANGE: DWORD = 0x1F;
pub const KEY_PAGEUP: DWORD = 0x21;
pub const KEY_PAGEDOWN: DWORD = 0x22;
pub const KEY_END: DWORD = 0x23;
pub const KEY_SELECT: DWORD = 0x29;
pub const KEY_PRINT: DWORD = 0x2A;
pub const KEY_EXECUTE: DWORD = 0x2B;
pub const KEY_SYSRQ: DWORD = 0x2C;
pub const KEY_HELP: DWORD = 0x2F;
pub const KEY_LEFTMETA: DWORD = 0x5B;
pub const KEY_RIGHTMETA: DWORD = 0x5C;
pub const KEY_COMPOSE: DWORD = 0x5D;
pub const KEY_SLEEP: DWORD = 0x5F;
pub const KEY_KPASTERISK: DWORD = 0x6A;
pub const KEY_KPPLUS: DWORD = 0x6B;
pub const KEY_SEPARATOR: DWORD = 0x6C;
pub const KEY_KPMINUS: DWORD = 0x6D;
pub const KEY_KPDOT: DWORD = 0x6E;
pub const KEY_KPSLASH: DWORD = 0x6F;
pub const KEY_F13: DWORD = 0x7C;
pub const KEY_F14: DWORD = 0x7D;
pub const KEY_F15: DWORD = 0x7E;
pub const KEY_F16: DWORD = 0x7F;
pub const KEY_F17: DWORD = 0x80;
pub const KEY_F18: DWORD = 0x81;
pub const KEY_F19: DWORD = 0x82;
pub const KEY_F20: DWORD = 0x83;
pub const KEY_F21: DWORD = 0x84;
pub const KEY_F22: DWORD = 0x85;
pub const KEY_F23: DWORD = 0x86;
pub const KEY_F24: DWORD = 0x87;
pub const KEY_NAVIGATION_VIEW: DWORD = 0x88;
pub const KEY_NAVIGATION_MENU: DWORD = 0x89;
pub const KEY_NAVIGATION_UP: DWORD = 0x8A;
pub const KEY_NAVIGATION_DOWN: DWORD = 0x8B;
pub const KEY_NAVIGATION_LEFT: DWORD = 0x8C;
pub const KEY_NAVIGATION_RIGHT: DWORD = 0x8D;
pub const KEY_NAVIGATION_ACCEPT: DWORD = 0x8E;
pub const KEY_NAVIGATION_CANCEL: DWORD = 0x8F;
pub const KEY_SCROLL: DWORD = 0x91;
pub const KEY_OEM_NEC_EQUAL: DWORD = 0x92;
pub const KEY_OEM_FJ_JISHO: DWORD = 0x92;
pub const KEY_OEM_FJ_MASSHOU: DWORD = 0x93;
pub const KEY_OEM_FJ_TOUROKU: DWORD = 0x94;
pub const KEY_OEM_FJ_LOYA: DWORD = 0x95;
pub const KEY_OEM_FJ_ROYA: DWORD = 0x96;
pub const KEY_LEFTALT: DWORD = 0xA4;
pub const KEY_RIGHTALT: DWORD = 0xA5;
pub const KEY_BROWSER_BACK: DWORD = 0xA6;
pub const KEY_BROWSER_FORWARD: DWORD = 0xA7;
pub const KEY_BROWSER_REFRESH: DWORD = 0xA8;
pub const KEY_BROWSER_STOP: DWORD = 0xA9;
pub const KEY_BROWSER_SEARCH: DWORD = 0xAA;
pub const KEY_BROWSER_FAVORITES: DWORD = 0xAB;
pub const KEY_BROWSER_HOME: DWORD = 0xAC;
pub const KEY_VOLUME_MUTE: DWORD = 0xAD;
pub const KEY_VOLUME_DOWN: DWORD = 0xAE;
pub const KEY_VOLUME_UP: DWORD = 0xAF;
pub const KEY_MEDIA_NEXT_TRACK: DWORD = 0xB0;
pub const KEY_MEDIA_PREV_TRACK: DWORD = 0xB1;
pub const KEY_MEDIA_STOP: DWORD = 0xB2;
pub const KEY_MEDIA_PLAY_PAUSE: DWORD = 0xB3;
pub const KEY_LAUNCH_MAIL: DWORD = 0xB4;
pub const KEY_LAUNCH_MEDIA_SELECT: DWORD = 0xB5;
pub const KEY_LAUNCH_APP1: DWORD = 0xB6;
pub const KEY_LAUNCH_APP2: DWORD = 0xB7;
pub const KEY_SEMICOLON: DWORD = 0xBA;
pub const KEY_EQUAL: DWORD = 0xBB;
pub const KEY_COMMA: DWORD = 0xBC;
pub const KEY_MINUS: DWORD = 0xBD;
pub const KEY_DOT: DWORD = 0xBE;
pub const KEY_SLASH: DWORD = 0xBF;
pub const KEY_GRAVE: DWORD = 0xC0;
pub const KEY_GAMEPAD_A: DWORD = 0xC3;
pub const KEY_GAMEPAD_B: DWORD = 0xC4;
pub const KEY_GAMEPAD_X: DWORD = 0xC5;
pub const KEY_GAMEPAD_Y: DWORD = 0xC6;
pub const KEY_GAMEPAD_RIGHT_SHOULDER: DWORD = 0xC7;
pub const KEY_GAMEPAD_LEFT_SHOULDER: DWORD = 0xC8;
pub const KEY_GAMEPAD_LEFT_TRIGGER: DWORD = 0xC9;
pub const KEY_GAMEPAD_RIGHT_TRIGGER: DWORD = 0xCA;
pub const KEY_GAMEPAD_DPAD_UP: DWORD = 0xCB;
pub const KEY_GAMEPAD_DPAD_DOWN: DWORD = 0xCC;
pub const KEY_GAMEPAD_DPAD_LEFT: DWORD = 0xCD;
pub const KEY_GAMEPAD_DPAD_RIGHT: DWORD = 0xCE;
pub const KEY_GAMEPAD_MENU: DWORD = 0xCF;
pub const KEY_GAMEPAD_VIEW: DWORD = 0xD0;
pub const KEY_GAMEPAD_LEFT_THUMBSTICK_BUTTON: DWORD = 0xD1;
pub const KEY_GAMEPAD_RIGHT_THUMBSTICK_BUTTON: DWORD = 0xD2;
pub const KEY_GAMEPAD_LEFT_THUMBSTICK_UP: DWORD = 0xD3;
pub const KEY_GAMEPAD_LEFT_THUMBSTICK_DOWN: DWORD = 0xD4;
pub const KEY_GAMEPAD_LEFT_THUMBSTICK_RIGHT: DWORD = 0xD5;
pub const KEY_GAMEPAD_LEFT_THUMBSTICK_LEFT: DWORD = 0xD6;
pub const KEY_GAMEPAD_RIGHT_THUMBSTICK_UP: DWORD = 0xD7;
pub const KEY_GAMEPAD_RIGHT_THUMBSTICK_DOWN: DWORD = 0xD8;
pub const KEY_GAMEPAD_RIGHT_THUMBSTICK_RIGHT: DWORD = 0xD9;
pub const KEY_GAMEPAD_RIGHT_THUMBSTICK_LEFT: DWORD = 0xDA;
pub const KEY_LEFTBRACE: DWORD = 0xDB;
pub const KEY_BACKSLASH: DWORD = 0xDC;
pub const KEY_RIGHTBRACE: DWORD = 0xDD;
pub const KEY_APOSTROPHE: DWORD = 0xDE;
pub const KEY_OEM_8: DWORD = 0xDF;
pub const KEY_OEM_AX: DWORD = 0xE1;
pub const KEY_OEM_102: DWORD = 0xE2;
pub const KEY_ICO_HELP: DWORD = 0xE3;
pub const KEY_ICO_00: DWORD = 0xE4;
pub const KEY_PROCESSKEY: DWORD = 0xE5;
pub const KEY_ICO_CLEAR: DWORD = 0xE6;
pub const KEY_PACKET: DWORD = 0xE7;
pub const KEY_OEM_RESET: DWORD = 0xE9;
pub const KEY_OEM_JUMP: DWORD = 0xEA;
pub const KEY_OEM_PA1: DWORD = 0xEB;
pub const KEY_OEM_PA2: DWORD = 0xEC;
pub const KEY_OEM_PA3: DWORD = 0xED;
pub const KEY_OEM_WSCTRL: DWORD = 0xEE;
pub const KEY_OEM_CUSEL: DWORD = 0xEF;
pub const KEY_OEM_ATTN: DWORD = 0xF0;
pub const KEY_OEM_FINISH: DWORD = 0xF1;
pub const KEY_OEM_COPY: DWORD = 0xF2;
pub const KEY_OEM_AUTO: DWORD = 0xF3;
pub const KEY_OEM_ENLW: DWORD = 0xF4;
pub const KEY_OEM_BACKTAB: DWORD = 0xF5;
pub const KEY_ATTN: DWORD = 0xF6;
pub const KEY_CRSEL: DWORD = 0xF7;
pub const KEY_EXSEL: DWORD = 0xF8;
pub const KEY_EREOF: DWORD = 0xF9;
pub const KEY_PLAY: DWORD = 0xFA;
pub const KEY_ZOOM: DWORD = 0xFB;
pub const KEY_NONAME: DWORD = 0xFC;
pub const KEY_PA1: DWORD = 0xFD;
pub const KEY_OEM_CLEAR: DWORD = 0xFE;
pub fn key_map() -> HashMap<&'static str, USizeableDWORD> {
[
// grep 'Key => 0x' ../rusty-keys-win/src/windows/inputs.rs | tr '[a-z]' '[A-Z]' | sed -r -e 's/KEY => 0X/: DWORD = 0x/' -e 's/^[ ]+/pub const KEY_/' | tr ',' ';'
("ESC", KEY_ESC),
("1", KEY_1),
("2", KEY_2),
("3", KEY_3),
("4", KEY_4),
("5", KEY_5),
("6", KEY_6),
("7", KEY_7),
("8", KEY_8),
("9", KEY_9),
("10", KEY_10),
("MINUS", KEY_MINUS),
("EQUAL", KEY_EQUAL),
("BACKSPACE", KEY_BACKSPACE),
("TAB", KEY_TAB),
("Q", KEY_Q),
("W", KEY_W),
("E", KEY_E),
("R", KEY_R),
("T", KEY_T),
("Y", KEY_Y),
("U", KEY_U),
("I", KEY_I),
("O", KEY_O),
("P", KEY_P),
("LEFTBRACE", KEY_LEFTBRACE),
("RIGHTBRACE", KEY_RIGHTBRACE),
("ENTER", KEY_ENTER),
("LEFTCTRL", KEY_LEFTCTRL),
("A", KEY_A),
("S", KEY_S),
("D", KEY_D),
("F", KEY_F),
("G", KEY_G),
("H", KEY_H),
("J", KEY_J),
("K", KEY_K),
("L", KEY_L),
("SEMICOLON", KEY_SEMICOLON),
("APOSTROPHE", KEY_APOSTROPHE),
("GRAVE", KEY_GRAVE),
("LEFTSHIFT", KEY_LEFTSHIFT),
("BACKSLASH", KEY_BACKSLASH),
("Z", KEY_Z),
("X", KEY_X),
("C", KEY_C),
("V", KEY_V),
("B", KEY_B),
("N", KEY_N),
("M", KEY_M),
("COMMA", KEY_COMMA),
("DOT", KEY_DOT),
("SLASH", KEY_SLASH),
("RIGHTSHIFT", KEY_RIGHTSHIFT),
("KPASTERISK", KEY_KPASTERISK),
("LEFTALT", KEY_LEFTALT),
("SPACE", KEY_SPACE),
("CAPSLOCK", KEY_CAPSLOCK),
("F1", KEY_F1),
("F2", KEY_F2),
("F3", KEY_F3),
("F4", KEY_F4),
("F5", KEY_F5),
("F6", KEY_F6),
("F7", KEY_F7),
("F8", KEY_F8),
("F9", KEY_F9),
("F10", KEY_F10),
("NUMLOCK", KEY_NUMLOCK),
("SCROLLLOCK", KEY_SCROLLLOCK),
("KP7", KEY_KP7),
("KP8", KEY_KP8),
("KP9", KEY_KP9),
("KPMINUS", KEY_KPMINUS),
("KP4", KEY_KP4),
("KP5", KEY_KP5),
("KP6", KEY_KP6),
("KPPLUS", KEY_KPPLUS),
("KP1", KEY_KP1),
("KP2", KEY_KP2),
("KP3", KEY_KP3),
("KP0", KEY_KP0),
("KPDOT", KEY_KPDOT),
/*
("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU),
("102ND", KEY_102ND),
*/
("F11", KEY_F11),
("F12", KEY_F12),
/*
("RO", KEY_RO),
("KATAKANA", KEY_KATAKANA),
("HIRAGANA", KEY_HIRAGANA),
("HENKAN", KEY_HENKAN),
("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA),
("MUHENKAN", KEY_MUHENKAN),
("KPJPCOMMA", KEY_KPJPCOMMA),
*/
("KPENTER", KEY_KPENTER),
("RIGHTCTRL", KEY_RIGHTCTRL),
("KPSLASH", KEY_KPSLASH),
("SYSRQ", KEY_SYSRQ),
("RIGHTALT", KEY_RIGHTALT),
/*
("LINEFEED", KEY_LINEFEED),
*/
("HOME", KEY_HOME),
("UP", KEY_UP),
("PAGEUP", KEY_PAGEUP),
("LEFT", KEY_LEFT),
("RIGHT", KEY_RIGHT),
("END", KEY_END),
("DOWN", KEY_DOWN),
("PAGEDOWN", KEY_PAGEDOWN),
("INSERT", KEY_INSERT),
("DELETE", KEY_DELETE),
/*
("MACRO", KEY_MACRO),
("MUTE", KEY_MUTE),
("VOLUMEDOWN", KEY_VOLUMEDOWN),
("VOLUMEUP", KEY_VOLUMEUP),
("POWER", KEY_POWER),
("KPEQUAL", KEY_KPEQUAL),
("KPPLUSMINUS", KEY_KPPLUSMINUS),
("PAUSE", KEY_PAUSE),
("SCALE", KEY_SCALE),
("KPCOMMA", KEY_KPCOMMA),
("HANGEUL", KEY_HANGEUL),
("HANGUEL", KEY_HANGUEL),
("HANGEUL", KEY_HANGEUL),
("HANJA", KEY_HANJA),
("YEN", KEY_YEN),
("LEFTMETA", KEY_LEFTMETA),
("RIGHTMETA", KEY_RIGHTMETA),
("COMPOSE", KEY_COMPOSE),
("STOP", KEY_STOP),
("AGAIN", KEY_AGAIN),
("PROPS", KEY_PROPS),
("UNDO", KEY_UNDO),
("FRONT", KEY_FRONT),
("COPY", KEY_COPY),
("OPEN", KEY_OPEN),
("PASTE", KEY_PASTE),
("FIND", KEY_FIND),
("CUT", KEY_CUT),
("HELP", KEY_HELP),
("MENU", KEY_MENU),
("CALC", KEY_CALC),
("SETUP", KEY_SETUP),
("SLEEP", KEY_SLEEP),
("WAKEUP", KEY_WAKEUP),
("FILE", KEY_FILE),
("SENDFILE", KEY_SENDFILE),
("DELETEFILE", KEY_DELETEFILE),
("XFER", KEY_XFER),
("PROG1", KEY_PROG1),
("PROG2", KEY_PROG2),
("WWW", KEY_WWW),
("MSDOS", KEY_MSDOS),
("COFFEE", KEY_COFFEE),
("SCREENLOCK", KEY_SCREENLOCK),
("COFFEE", KEY_COFFEE),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("DIRECTION", KEY_DIRECTION),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("CYCLEWINDOWS", KEY_CYCLEWINDOWS),
("MAIL", KEY_MAIL),
("BOOKMARKS", KEY_BOOKMARKS),
("COMPUTER", KEY_COMPUTER),
("BACK", KEY_BACK),
("FORWARD", KEY_FORWARD),
("CLOSECD", KEY_CLOSECD),
("EJECTCD", KEY_EJECTCD),
("EJECTCLOSECD", KEY_EJECTCLOSECD),
("NEXTSONG", KEY_NEXTSONG),
("PLAYPAUSE", KEY_PLAYPAUSE),
("PREVIOUSSONG", KEY_PREVIOUSSONG),
("STOPCD", KEY_STOPCD),
("RECORD", KEY_RECORD),
("REWIND", KEY_REWIND),
("PHONE", KEY_PHONE),
("ISO", KEY_ISO),
("CONFIG", KEY_CONFIG),
("HOMEPAGE", KEY_HOMEPAGE),
("REFRESH", KEY_REFRESH),
("EXIT", KEY_EXIT),
("MOVE", KEY_MOVE),
("EDIT", KEY_EDIT),
("SCROLLUP", KEY_SCROLLUP),
("SCROLLDOWN", KEY_SCROLLDOWN),
("KPLEFTPAREN", KEY_KPLEFTPAREN),
("KPRIGHTPAREN", KEY_KPRIGHTPAREN),
("NEW", KEY_NEW),
("REDO", KEY_REDO),
*/
("F13", KEY_F13),
("F14", KEY_F14),
("F15", KEY_F15),
("F16", KEY_F16),
("F17", KEY_F17),
("F18", KEY_F18),
("F19", KEY_F19),
("F20", KEY_F20),
("F21", KEY_F21),
("F22", KEY_F22),
("F23", KEY_F23),
("F24", KEY_F24),
/*
("PLAYCD", KEY_PLAYCD),
("PAUSECD", KEY_PAUSECD),
("PROG3", KEY_PROG3),
("PROG4", KEY_PROG4),
("DASHBOARD", KEY_DASHBOARD),
("SUSPEND", KEY_SUSPEND),
("CLOSE", KEY_CLOSE),
("PLAY", KEY_PLAY),
("FASTFORWARD", KEY_FASTFORWARD),
("BASSBOOST", KEY_BASSBOOST),
("PRINT", KEY_PRINT),
("HP", KEY_HP),
("CAMERA", KEY_CAMERA),
("SOUND", KEY_SOUND),
("QUESTION", KEY_QUESTION),
("EMAIL", KEY_EMAIL),
("CHAT", KEY_CHAT),
("SEARCH", KEY_SEARCH),
("CONNECT", KEY_CONNECT),
("FINANCE", KEY_FINANCE),
("SPORT", KEY_SPORT),
("SHOP", KEY_SHOP),
("ALTERASE", KEY_ALTERASE),
("CANCEL", KEY_CANCEL),
("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN),
("BRIGHTNESSUP", KEY_BRIGHTNESSUP),
("MEDIA", KEY_MEDIA),
("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE),
("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE),
("KBDILLUMDOWN", KEY_KBDILLUMDOWN),
("KBDILLUMUP", KEY_KBDILLUMUP),
("SEND", KEY_SEND),
("REPLY", KEY_REPLY),
("FORWARDMAIL", KEY_FORWARDMAIL),
("SAVE", KEY_SAVE),
("DOCUMENTS", KEY_DOCUMENTS),
("BATTERY", KEY_BATTERY),
("BLUETOOTH", KEY_BLUETOOTH),
("WLAN", KEY_WLAN),
("UWB", KEY_UWB),
("UNKNOWN", KEY_UNKNOWN),
("VIDEO_NEXT", KEY_VIDEO_NEXT),
("VIDEO_PREV", KEY_VIDEO_PREV),
("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("DISPLAY_OFF", KEY_DISPLAY_OFF),
("WWAN", KEY_WWAN),
("WIMAX", KEY_WIMAX),
("WWAN", KEY_WWAN),
("RFKILL", KEY_RFKILL),
("MICMUTE", KEY_MICMUTE),
*/
// below manual shortcuts
("PSCR", KEY_SYSRQ),
("SLCK", KEY_SCROLLLOCK),
("BRK", KEY_PAUSE),
("GRV", KEY_GRAVE),
("0", KEY_10), // dumb or named wrong?
("MINS", KEY_MINUS),
("EQL", KEY_EQUAL),
("BSPC", KEY_BACKSPACE),
("LBRC", KEY_LEFTBRACE),
("RBRC", KEY_RIGHTBRACE),
("BSLS", KEY_BACKSLASH),
("SCLN", KEY_SEMICOLON),
("QUOT", KEY_APOSTROPHE),
("ENT", KEY_ENTER),
("COMM", KEY_COMMA),
("DOT", KEY_DOT),
("SLSH", KEY_SLASH),
("CAPS", KEY_CAPSLOCK),
("LSFT", KEY_LEFTSHIFT),
("RSFT", KEY_RIGHTSHIFT),
("SPC", KEY_SPACE),
("APP", KEY_COMPOSE),
("LCTL", KEY_LEFTCTRL),
("RCTL", KEY_RIGHTCTRL),
("LALT", KEY_LEFTALT),
("RALT", KEY_RIGHTALT),
("LGUI", KEY_LEFTMETA),
("RGUI", KEY_RIGHTMETA),
("INS", KEY_INSERT),
("PGUP", KEY_PAGEUP),
("PGDN", KEY_PAGEDOWN),
("DEL", KEY_DELETE),
("RGHT", KEY_RIGHT),
("NLCK", KEY_NUMLOCK),
("PSLS", KEY_KPSLASH),
("PAST", KEY_KPASTERISK),
("PMNS", KEY_KPMINUS),
("P7", KEY_KP7),
("P8", KEY_KP8),
("P9", KEY_KP9),
("P4", KEY_KP4),
("P5", KEY_KP5),
("P6", KEY_KP6),
("PPLS", KEY_KPPLUS),
("P1", KEY_KP1),
("P2", KEY_KP2),
("P3", KEY_KP3),
("P0", KEY_KP0),
("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER),
].iter().cloned().map(|(m, v)| (m, USizeableDWORD(v))).collect()
}
// https://www.reddit.com/r/rust/comments/9xth8g/why_no_from_conversion_from_u32_to_usize/
#[derive(Copy, Clone, Hash, Eq, PartialEq, Default)]
pub struct USizeableDWORD(pub DWORD);
impl From<USizeableDWORD> for usize {
fn from(u: USizeableDWORD) -> Self {
u.0 as usize
}
}
impl TryFrom<usize> for USizeableDWORD {
type Error = ();
fn try_from(value: usize) -> ::std::result::Result<Self, Self::Error> {
Ok(USizeableDWORD(value as DWORD))
}
}

277
src/windows/mod.rs Normal file
View File

@ -0,0 +1,277 @@
#![windows_subsystem = "windows"]
use crate::*;
use std::env;
use std::process::exit;
use getopts::Options;
use std::fs::File;
use winapi::um::winuser::{KBDLLHOOKSTRUCT, WH_KEYBOARD_LL, MSG, GetMessageW, CallNextHookEx, SetWindowsHookExW, INPUT_KEYBOARD, MapVirtualKeyW, LPINPUT, INPUT, KEYBDINPUT, SendInput, KEYEVENTF_SCANCODE, KEYEVENTF_KEYUP, WM_KEYUP, WM_KEYDOWN, MAPVK_VK_TO_VSC, ShowWindow, SW_HIDE};
use winapi::shared::windef::{HHOOK__, HWND};
use winapi::shared::minwindef::{LRESULT, WPARAM, LPARAM, HINSTANCE};
use winapi::shared::basetsd::ULONG_PTR;
use winapi::ctypes::c_int;
pub mod codes;
use codes::*;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::mem::{zeroed, size_of};
use winapi::_core::ptr::null_mut;
use winapi::_core::mem::transmute_copy;
use lazy_static::lazy_static;
use std::sync::Mutex;
use winapi::um::wincon::GetConsoleWindow;
type WindowsKeyMaps = KeyMaps<Device, USizeableDWORD, InputEvent, LRESULT>;
// this is used for identifying the fake keypresses we insert, so we don't process them in an infinite loop
const FAKE_EXTRA_INFO: ULONG_PTR = 332;
// non-zero means don't send on, I think https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
const BLOCK_KEY: LRESULT = 1;
struct InputEvent {
code: c_int,
value: WPARAM,
kb_hook_pointer: LPARAM,
kb_hook_struct: KBDLLHOOKSTRUCT,
}
impl KeyEvent<USizeableDWORD> for InputEvent {
fn code(&self) -> USizeableDWORD {
USizeableDWORD(self.kb_hook_struct.vkCode)
}
fn value(&self) -> KeyState {
match self.value as u32 {
WM_KEYUP => KeyState::UP,
WM_KEYDOWN => KeyState::DOWN,
_ => KeyState::OTHER,
}
}
}
struct Device;
impl Keyboard<USizeableDWORD, InputEvent, LRESULT> for Device {
fn send(&self, event: &mut InputEvent) -> Result<LRESULT> {
Ok(unsafe { CallNextHookEx(null_mut(), event.code, event.value, event.kb_hook_pointer) })
}
fn send_mod_code(&self, code: USizeableDWORD, event: &mut InputEvent) -> Result<LRESULT> {
// event.value should only ever be UP/DOWN when this method is called
self.send_mod_code_value(code, event.value as u32 == WM_KEYUP, event)
}
fn send_mod_code_value(&self, code: USizeableDWORD, up_not_down: bool, _event: &mut InputEvent) -> Result<LRESULT> {
let flags = if up_not_down {
KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
} else {
KEYEVENTF_SCANCODE
};
send_keybd_input(flags, code);
Ok(BLOCK_KEY)
}
fn synchronize(&self) -> Result<LRESULT> {
// no-op here
Ok(0)
}
fn left_shift_code(&self) -> USizeableDWORD {
USizeableDWORD(KEY_LEFTSHIFT)
}
fn right_shift_code(&self) -> USizeableDWORD {
USizeableDWORD(KEY_RIGHTSHIFT)
}
fn caps_lock_code(&self) -> USizeableDWORD {
USizeableDWORD(KEY_CAPSLOCK)
}
fn block_key(&self) -> Result<LRESULT> {
Ok(BLOCK_KEY)
}
}
unsafe impl Send for WindowsKeyMaps {
// windows promises us keybd_proc will only be called by a single thread at a time
// but rust makes us wrap in mutex anyway, so we are extra safe...
}
const DEVICE: Device = Device;
lazy_static! {
static ref KEY_MAPPER: Mutex<WindowsKeyMaps> = {
let config = parse_args();
//println!("Config: {:?}", config);
let key_map = key_map();
//println!("key_map: {:?}", key_map);
println!("chosen config file: {}", config.config_file);
Mutex::new(WindowsKeyMaps::from_cfg(&key_map, &config.config_file))
};
}
unsafe extern "system" fn keybd_proc(code: c_int, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
let kb_struct = *(l_param as *const KBDLLHOOKSTRUCT);
//println!("value: {:X}, key: {:X}", w_param, kb_struct.vkCode);
if kb_struct.dwExtraInfo == FAKE_EXTRA_INFO {
return CallNextHookEx(null_mut(), code, w_param, l_param);
}
/*
println!("code: {}, w_param: {}, vkCode: {}, scanCode: {}, flags: {}, time: {}, dwExtraInfo: {}",
code, w_param,
kb_struct.vkCode, kb_struct.scanCode, kb_struct.flags, kb_struct.time, kb_struct.dwExtraInfo);
*/
let mut input_event = InputEvent{
code,
value: w_param,
kb_hook_pointer: l_param,
kb_hook_struct: kb_struct,
};
// .unwrap() is ok because windows impl can actually never can fail
//DEVICE.send(&mut input_event).unwrap()
//KEY_MAPPER.send_event(&mut input_event, &DEVICE).unwrap()
KEY_MAPPER.lock().unwrap().send_event(&mut input_event, &DEVICE).unwrap()
}
fn set_hook(
hook_id: i32,
hook_ptr: &AtomicPtr<HHOOK__>,
hook_proc: unsafe extern "system" fn (c_int, WPARAM, LPARAM) -> LRESULT,
) {
hook_ptr.store(
unsafe { SetWindowsHookExW(hook_id, Some(hook_proc), 0 as HINSTANCE, 0) },
Ordering::Relaxed,
);
}
fn send_keybd_input(flags: u32, key_code: USizeableDWORD) {
let mut input = INPUT {
type_: INPUT_KEYBOARD,
u: unsafe {
transmute_copy(&KEYBDINPUT {
wVk: 0,
wScan: MapVirtualKeyW(key_code.0, MAPVK_VK_TO_VSC) as u16,
dwFlags: flags,
time: 0,
dwExtraInfo: FAKE_EXTRA_INFO,
})
},
};
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) };
}
pub fn main_res() -> Result<()> {
// this is just to cause the lazy_static init to run first, so if -h or -v is wanted, we do that
// and exit immediately... todo: how to avoid mutex/lazy_static entirely???
let _ = KEY_MAPPER.lock().unwrap();
// now start actually intercepting keypresses
let keybd_hhook: AtomicPtr<HHOOK__> = AtomicPtr::default();
set_hook(WH_KEYBOARD_LL, &keybd_hhook, keybd_proc);
println!("rusty-keys {} keyboard hook registered, now for some reason you *sometimes* have to type in this window once to activate it, thanks windows!", VERSION);
unsafe {
// hide window
// todo: probably should be tray icon someplace in future to quit, and error messages as windows popups etc...
let hwnd = GetConsoleWindow();
ShowWindow( hwnd, SW_HIDE );
}
let mut msg: MSG = unsafe { zeroed() };
unsafe { GetMessageW(&mut msg, 0 as HWND, 0, 0) };
//std::thread::sleep(std::time::Duration::from_millis(400000));
Ok(())
}
#[derive(Debug)]
struct Config {
config_file: String
}
impl Config {
fn new(config_file: String) -> Self {
Config { config_file: config_file }
}
}
fn get_env_push(key: &str, to_push: &str, vec: &mut Vec<String>) {
if let Some(var) = env::var_os(key) {
if let Ok(str) = var.into_string() {
let mut str = str.to_owned();
str.push_str(to_push);
vec.push(str);
}
}
}
fn parse_args() -> Config {
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options] [keymap.toml]", program);
println!("{}", opts.usage(&brief));
}
let args: Vec<_> = env::args().collect();
let mut default_configs = Vec::new();
get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs);
get_env_push("APPDATA", "\\keymap.toml", &mut default_configs);
default_configs.push("keymap.toml".to_string());
let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs);
let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message");
opts.optflag("v", "version", "prints the version");
opts.optopt("c", "config", &c_msg, "FILE");
let matches = opts.parse(&args[1..]);
if matches.is_err() {
print_usage(&args[0], opts);
exit(1);
}
let matches = matches.unwrap();
if matches.opt_present("h") {
print_usage(&args[0], opts);
exit(0);
}
if matches.opt_present("v") {
println!("rusty-keys {}", VERSION);
exit(0);
}
let config_file = matches.opt_str("c").unwrap_or_else(|| {
let remaining_args = matches.free;
if remaining_args.len() > 0 {
remaining_args[0].clone()
} else {
for keymap in default_configs.drain(..) {
if File::open(&keymap).is_ok() {
return keymap;
}
}
println!("Error: no keymap.toml found...");
print_usage(&args[0], opts);
exit(1);
}
});
Config::new(config_file)
}

View File

@ -0,0 +1,11 @@
[Unit]
Description=rusty-keys keymapper service
After=systemd-udevd.service
[Service]
ExecStart=/usr/bin/rusty-keys
Restart=always
RestartSec=1s
[Install]
WantedBy=default.target

32
win.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/sh
set -euxo pipefail
target="${1-x86_64-pc-windows-gnu}"
cargo build --target "$target" --release
cp keymap.toml target/"$target"/release/
cd target/"$target"/release/
strip rusty-keys.exe
zip -9 rusty-keys-"$target".zip rusty-keys.exe keymap.toml
mv rusty-keys-"$target".zip ../../..
exit 0
# on arch linux, run these to enable cross compiling to win64:
pacman -S mingw-w64-gcc
# you can change these, if you want
CHANNEL=stable
ARCH=x86_64
rustup install $CHANNEL-$ARCH-pc-windows-gnu && rustup target add $ARCH-pc-windows-gnu
for lib in crt2.o dllcrt2.o libmsvcrt.a; do cp -v /usr/x86_64-w64-mingw32/lib/$lib $HOME/.rustup/toolchains/$CHANNEL-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu/lib/; done
cat >> ~/.cargo/config <<EOF
[target.$ARCH-pc-windows-gnu]
linker = "/usr/bin/$ARCH-w64-mingw32-gcc"
ar = "/usr/$ARCH-w64-mingw32/bin/ar"
EOF