Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
270cdca14f | |||
e9705db5ba | |||
10096b2709 | |||
e6f4653570 | |||
552cd50266 | |||
b0f9dfddf2 | |||
b0543d3f93 | |||
527ab97a04 | |||
d0e38d0e55 | |||
8ffe640c4b | |||
bcea441630 | |||
17a8f0c995 | |||
60a3f24c86 | |||
c420323cdf | |||
a454b2a2a1 | |||
877a35861a | |||
9317d31153 | |||
d785d341cb | |||
fa202be7da | |||
95833e3a33 | |||
b1bb55766d | |||
bd87beb211 | |||
965c446b51 | |||
4a816737c7 | |||
95f9c731e9 | |||
6c4e4392b4 | |||
432e35cb6a | |||
24786356c4 | |||
62501febb9 | |||
4554e19f28 | |||
f7afe198c7 | |||
f47d9e4be7 | |||
50b84dea0a | |||
e02b59f55d | |||
9281922c72 | |||
c2efbb6fd1 | |||
f4083343da | |||
95cfd637b9 | |||
d3aeededda | |||
0ffc1695aa | |||
f15edf8315 | |||
966cebf42a | |||
0e58664f67 | |||
d0fdea6ff6 | |||
e7fc77773c | |||
84b176a75e | |||
99cf9c452f | |||
ca28b96982 | |||
47f8231d33 | |||
363ede21d3 | |||
75e340f28a | |||
5bfc286d19 | |||
49fd65a4b0 | |||
f9a6ed8d00 | |||
98ffe1b17e | |||
f67973c932 | |||
74d3350743 |
46
.ci/Jenkinsfile
vendored
Normal file
46
.ci/Jenkinsfile
vendored
Normal 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
45
.ci/build.sh
Executable 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
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
target
|
target
|
||||||
Cargo.lock
|
.idea/
|
||||||
|
*.zip
|
||||||
|
265
Cargo.lock
generated
Normal file
265
Cargo.lock
generated
Normal 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"
|
46
Cargo.toml
46
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusty-keys"
|
name = "rusty-keys"
|
||||||
version = "0.0.2"
|
version = "0.0.3"
|
||||||
|
|
||||||
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
authors = ["moparisthebest <admin@moparisthebest.com>"]
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
@ -9,23 +9,39 @@ description = "Linux keyboard mapper"
|
|||||||
repository = "https://code.moparisthebest.com/moparisthebest/rusty-keys"
|
repository = "https://code.moparisthebest.com/moparisthebest/rusty-keys"
|
||||||
keywords = ["linux", "input", "keyboard", "keymapper"]
|
keywords = ["linux", "input", "keyboard", "keymapper"]
|
||||||
|
|
||||||
exclude = [
|
edition = "2018"
|
||||||
"keymap.orig.toml",
|
|
||||||
|
include = [
|
||||||
|
"src/**",
|
||||||
|
"Cargo.toml",
|
||||||
|
"Cargo.lock",
|
||||||
|
"keymap.toml",
|
||||||
|
"LICENSE.md",
|
||||||
|
"README.md",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
getopts = "0.2.21"
|
||||||
nix = "0.5"
|
toml = { version = "0.5.8", optional = true }
|
||||||
uinput-sys = "0.1"
|
serde = { version = "1.0.130", features = ["derive"], optional = true }
|
||||||
getopts = "0.2.15"
|
|
||||||
toml = "0.4.5"
|
|
||||||
serde_derive = "1.0.11"
|
|
||||||
serde = "1.0.11"
|
|
||||||
|
|
||||||
[dependencies.libudev]
|
[target.'cfg(target_os="macos")'.dependencies]
|
||||||
optional = true
|
core-graphics = "0.22"
|
||||||
version = "0.2"
|
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]
|
[features]
|
||||||
default = ["udev"]
|
default = ["epoll_inotify", "toml_serde"]
|
||||||
udev = ["libudev"]
|
toml_serde = ["toml", "serde"]
|
||||||
|
epoll_inotify = ["epoll", "inotify"]
|
||||||
|
45
README.md
45
README.md
@ -1,59 +1,58 @@
|
|||||||
rusty-keys
|
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)
|
![Unix Programmer's Dvorak](https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg)
|
||||||
|
|
||||||
The Problem
|
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
|
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.
|
holding ctrl so ctrl+c works just like normal, without remapping all programs to ctrl+j. Linux keymaps cannot do this either.
|
||||||
|
|
||||||
The Solution
|
The Solution
|
||||||
------------
|
------------
|
||||||
1. Grab a keyboard device directly so only we can read events from it.
|
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.
|
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 real device, map them, send them to our created device.
|
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.
|
on SDL games, it will work literally everywhere, because rusty-keys just creates a regular keyboard.
|
||||||
|
|
||||||
How to run
|
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:
|
Options:
|
||||||
-h, --help prints this help message
|
-h, --help prints this help message
|
||||||
-v, --version prints the version
|
-v, --version prints the version
|
||||||
-d, --device DEVICE specify the keyboard input device file
|
-c, --config FILE specify the keymap config file to use (default:
|
||||||
-c, --config FILE specify the keymap config file to use
|
/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`
|
`rusty-keys`
|
||||||
|
|
||||||
with multiple keyboards, currently you must specify one:
|
or you can specify one or multiple input devices, and it will run until all are disconnected, then stop:
|
||||||
`rusty-keys -d /dev/input/event0`
|
`rusty-keys /dev/input/event0` or `rusty-keys /dev/input/event0 /dev/input/event2`
|
||||||
|
|
||||||
find all eligible keyboard devices like:
|
An example systemd service is in systemd/rusty-keys.service, enable it to have mapped keyboards all the time.
|
||||||
`grep -E 'Handlers|EV' /proc/bus/input/devices | grep -B1 120013 | grep -Eo event[0-9]+`
|
|
||||||
|
|
||||||
For using the systemd unit with by-id or by-path:
|
|
||||||
```
|
|
||||||
$ systemd-escape --template=rusty-keys@.service by-id/usb-04c8_USB_Keyboard-event-kbd
|
|
||||||
rusty-keys@by\x2did-usb\x2d04c8_USB_Keyboard\x2devent\x2dkbd.service
|
|
||||||
```
|
|
||||||
|
|
||||||
How to install
|
How to install
|
||||||
--------------
|
--------------
|
||||||
* `cargo install rusty-keys`
|
* `cargo install rusty-keys`
|
||||||
* Arch Linux [AUR PKGBUILD](https://aur.archlinux.org/packages/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
|
License
|
||||||
-------
|
-------
|
||||||
|
90
appveyor.yml
Normal file
90
appveyor.yml
Normal 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
|
@ -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
|
|
||||||
""",
|
|
||||||
]
|
|
@ -3,9 +3,9 @@
|
|||||||
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
|
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
|
||||||
switch_layout_keys = ['LEFTSHIFT','RIGHTSHIFT']
|
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
|
# used so QWERTY shortcuts like Ctrl+C still work
|
||||||
revert_default_key = 'LEFTCTRL'
|
revert_default_keys = ['LCTL','LGUI','LALT']
|
||||||
revert_keymap_index = 0
|
revert_keymap_index = 0
|
||||||
|
|
||||||
# this is the default index to use when the program first starts
|
# this is the default index to use when the program first starts
|
||||||
|
40
notes.txt
Normal file
40
notes.txt
Normal 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...
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
mod builder;
|
|
||||||
pub use self::builder::Builder;
|
|
||||||
|
|
||||||
mod device;
|
|
||||||
pub use self::device::Device;
|
|
60
src/error.rs
60
src/error.rs
@ -1,26 +1,33 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
use nix;
|
use std::io;
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
#[cfg(target_os = "linux")]
|
||||||
use udev;
|
use nix;
|
||||||
|
|
||||||
/// UInput error.
|
/// UInput error.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// System errors.
|
/// System errors.
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
Nix(nix::Error),
|
Nix(nix::Error),
|
||||||
|
|
||||||
/// Errors with internal nulls in names.
|
/// Errors with internal nulls in names.
|
||||||
Nul(ffi::NulError),
|
Nul(ffi::NulError),
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
Io(io::Error),
|
||||||
/// Errors coming from udev.
|
|
||||||
Udev(udev::Error),
|
|
||||||
|
|
||||||
/// The uinput file could not be found.
|
#[cfg(feature = "toml_serde")]
|
||||||
NotFound,
|
Toml(toml::de::Error),
|
||||||
|
|
||||||
|
NotAKeyboard,
|
||||||
|
|
||||||
|
/// error reading input_event
|
||||||
|
ShortRead,
|
||||||
|
|
||||||
|
/// epoll already added
|
||||||
|
EpollAlreadyAdded,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ffi::NulError> for Error {
|
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 {
|
impl From<nix::Error> for Error {
|
||||||
fn from(value: nix::Error) -> Self {
|
fn from(value: nix::Error) -> Self {
|
||||||
Error::Nix(value)
|
Error::Nix(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
impl From<io::Error> for Error {
|
||||||
impl From<udev::Error> for Error {
|
fn from(value: io::Error) -> Self {
|
||||||
fn from(value: udev::Error) -> Self {
|
Error::Io(value)
|
||||||
Error::Udev(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::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 {
|
match self {
|
||||||
&Error::Nix(ref err) =>
|
#[cfg(target_os = "linux")]
|
||||||
err.description(),
|
&Error::Nix(ref err) => err.fmt(f),
|
||||||
|
|
||||||
&Error::Nul(ref err) =>
|
&Error::Nul(ref err) => err.fmt(f),
|
||||||
err.description(),
|
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
&Error::Io(ref err) => err.fmt(f),
|
||||||
&Error::Udev(ref err) =>
|
|
||||||
err.description(),
|
|
||||||
|
|
||||||
&Error::NotFound =>
|
#[cfg(feature = "toml_serde")]
|
||||||
"Device not found.",
|
&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 {}
|
||||||
|
849
src/keymapper.rs
849
src/keymapper.rs
File diff suppressed because it is too large
Load Diff
41
src/lib.rs
41
src/lib.rs
@ -1,36 +1,27 @@
|
|||||||
#![recursion_limit = "1000"]
|
#![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;
|
pub mod error;
|
||||||
|
|
||||||
extern crate nix;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(feature = "udev")]
|
|
||||||
extern crate libudev as udev;
|
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
pub mod keymapper;
|
pub mod keymapper;
|
||||||
pub use keymapper::KeyMaps;
|
pub use keymapper::*;
|
||||||
|
|
||||||
pub mod device;
|
#[cfg(target_os = "windows")]
|
||||||
pub use device::Device;
|
mod windows;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use windows::*;
|
||||||
|
|
||||||
/// Open the default uinput device.
|
#[cfg(target_os = "linux")]
|
||||||
pub fn default() -> Result<device::Builder> {
|
mod linux;
|
||||||
device::Builder::default()
|
#[cfg(target_os = "linux")]
|
||||||
}
|
pub use linux::*;
|
||||||
|
|
||||||
/// Open the specified uinput device.
|
#[cfg(target_os = "macos")]
|
||||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<device::Builder> {
|
mod macos;
|
||||||
device::Builder::open(path)
|
#[cfg(target_os = "macos")]
|
||||||
}
|
pub use macos::*;
|
||||||
|
151
src/linux/device/builder.rs
Normal file
151
src/linux/device/builder.rs
Normal 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
780
src/linux/device/codes.rs
Normal 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;
|
@ -1,9 +1,11 @@
|
|||||||
use std::{mem, ptr, slice};
|
use std::{mem, ptr, slice};
|
||||||
use libc::c_int;
|
use libc::{timeval, gettimeofday, input_event, c_int};
|
||||||
use libc::{timeval, gettimeofday, input_event};
|
use nix::{unistd, ioctl_none};
|
||||||
use nix::unistd;
|
use crate::Result;
|
||||||
use ffi::*;
|
|
||||||
use {Result as Res};
|
use crate::linux::device::codes::*;
|
||||||
|
|
||||||
|
ioctl_none!(ui_dev_destroy, b'U', 2);
|
||||||
|
|
||||||
/// The virtual device.
|
/// The virtual device.
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
@ -19,7 +21,7 @@ impl Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn write(&mut self, kind: c_int, code: c_int, value: c_int) -> Res<()> {
|
pub fn write(&self, kind: c_int, code: c_int, value: c_int) -> Result<()> {
|
||||||
let mut event = input_event {
|
let mut event = input_event {
|
||||||
time: timeval { tv_sec: 0, tv_usec: 0 },
|
time: timeval { tv_sec: 0, tv_usec: 0 },
|
||||||
type_: kind as u16,
|
type_: kind as u16,
|
||||||
@ -31,49 +33,47 @@ impl Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn write_event(&self, event: &mut input_event) -> Res<()> {
|
pub fn write_event(&self, event: &mut input_event) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
gettimeofday(&mut event.time, ptr::null_mut());
|
gettimeofday(&mut event.time, ptr::null_mut());
|
||||||
|
|
||||||
let ptr = event as *const _ as *const u8;
|
let ptr = event as *const _ as *const u8;
|
||||||
let size = mem::size_of_val(event);
|
let size = mem::size_of_val(event);
|
||||||
|
|
||||||
try!(unistd::write(self.fd, slice::from_raw_parts(ptr, size)));
|
unistd::write(self.fd, slice::from_raw_parts(ptr, size))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronize the device.
|
/// Synchronize the device.
|
||||||
pub fn synchronize(&mut self) -> Res<()> {
|
pub fn synchronize(&self) -> Result<()> {
|
||||||
self.write(EV_SYN, SYN_REPORT, 0)
|
self.write(EV_SYN, SYN_REPORT, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send an event.
|
/// Send an event.
|
||||||
pub fn send(&mut self, kind: c_int, code: c_int, value: i32) -> Res<()> {
|
pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
|
||||||
self.write(kind, code, value)
|
self.write(kind, code, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a press event.
|
/// Send a press event.
|
||||||
pub fn press(&mut self, kind: c_int, code: c_int) -> Res<()> {
|
pub fn press(&self, kind: c_int, code: c_int) -> Result<()> {
|
||||||
self.write(kind, code, 1)
|
self.write(kind, code, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a release event.
|
/// Send a release event.
|
||||||
pub fn release(&mut self, kind: c_int, code: c_int) -> Res<()> {
|
pub fn release(&self, kind: c_int, code: c_int) -> Result<()> {
|
||||||
self.write(kind, code, 0)
|
self.write(kind, code, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a press and release event.
|
/// Send a press and release event.
|
||||||
pub fn click(&mut self, kind: c_int, code: c_int) -> Res<()> {
|
pub fn click(&self, kind: c_int, code: c_int) -> Result<()> {
|
||||||
try!(self.press(kind, code));
|
self.press(kind, code)?;
|
||||||
try!(self.release(kind, code));
|
self.release(kind, code)
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a relative or absolute positioning event.
|
/// Send a relative or absolute positioning event.
|
||||||
pub fn position(&mut self, kind: c_int, code: c_int, value: i32) -> Res<()> {
|
pub fn position(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
|
||||||
self.write(kind, code, value)
|
self.write(kind, code, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +81,8 @@ impl Device {
|
|||||||
impl Drop for Device {
|
impl Drop for Device {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ui_dev_destroy(self.fd).unwrap();
|
// ignore error here so as to not panic in a drop
|
||||||
|
ui_dev_destroy(self.fd).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
150
src/linux/device/input_device.rs
Normal file
150
src/linux/device/input_device.rs
Normal 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
11
src/linux/device/mod.rs
Normal 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
599
src/linux/mod.rs
Normal 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
568
src/macos/codes.rs
Normal 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
414
src/macos/mod.rs
Normal 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 loop’s 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)
|
||||||
|
}
|
||||||
|
|
196
src/main.rs
196
src/main.rs
@ -1,195 +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() {
|
fn main() {
|
||||||
let key_map = KeyMaps::key_map();
|
let ret = rusty_keys::main_res();
|
||||||
//println!("key_map: {:?}", key_map);
|
if let Err(e) = ret {
|
||||||
|
println!("fatal error: {}", e);
|
||||||
let device = rusty_keys::default()
|
|
||||||
.or_else(|_| rusty_keys::open("/dev/uinput"))
|
|
||||||
.or_else(|_| rusty_keys::open("/dev/input/uinput"))
|
|
||||||
.expect("cannot open uinput device")
|
|
||||||
.name("test").expect("cannot name uinput device")
|
|
||||||
.event(key_map.values()).expect("cannot register events on uinput device")
|
|
||||||
.create().expect("cannot create uinput device");
|
|
||||||
|
|
||||||
//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 mut event = input_device.read_event();
|
|
||||||
if event.type_ == EV_KEY_U16 {
|
|
||||||
key_map.send_event(&mut 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(&mut event).expect("could not write event?");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_args() -> Config {
|
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
|
||||||
fn print_usage(program: &str, opts: Options) {
|
fn main() {
|
||||||
let brief = format!("Usage: {} [options]", program);
|
panic!("sorry no main impl for this platform");
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
579
src/windows/codes.rs
Normal 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
277
src/windows/mod.rs
Normal 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)
|
||||||
|
}
|
11
systemd/rusty-keys.service
Normal file
11
systemd/rusty-keys.service
Normal 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
32
win.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user