Compare commits

...

28 Commits

Author SHA1 Message Date
270cdca14f Add TOUCHPAD_TOGGLE for linux
Some checks failed
moparisthebest/rusty-keys/pipeline/head There was a failure building this commit
2023-09-05 23:36:12 -04:00
e9705db5ba Update systemd unit to restart always
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2022-04-29 08:08:50 -04:00
10096b2709 Exclude devices with a LEFT mouse button on linux
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-12-28 00:21:00 -05:00
e6f4653570 Exclude Yubico devices on Linux
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-12-12 22:29:45 -05:00
552cd50266 macOS support
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-10-09 01:39:10 -04:00
b0f9dfddf2 release all currently held down keys when any revert_default_key is released
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 02:27:39 -04:00
b0543d3f93 noop other identical keys
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 02:04:34 -04:00
527ab97a04 noop identical keys
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 01:50:24 -04:00
d0e38d0e55 Avoid index out of bounds on strange keys (Apple)
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 01:36:50 -04:00
8ffe640c4b Clean up unused warnings for all features
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 01:00:09 -04:00
bcea441630 Test compilation with each feature individually
Some checks are pending
moparisthebest/rusty-keys/pipeline/head Build queued...
2021-09-25 00:51:00 -04:00
17a8f0c995 make toml and serde optional, if not included, can hardcode config
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-25 00:45:29 -04:00
60a3f24c86 Make epoll and inotify optional dependencies enabled by default with the epoll_inotify feature
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-24 01:03:27 -04:00
c420323cdf light editorial code cleanup
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-24 00:27:10 -04:00
a454b2a2a1 fix comments and order of operations in input_device
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-24 00:13:14 -04:00
877a35861a Updated all package versions 2021-09-24 00:10:02 -04:00
9317d31153 update readme
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-23 23:29:56 -04:00
d785d341cb Rewrite linux mapper to use epoll instead of threads and blocking IO
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-23 23:18:33 -04:00
fa202be7da Determine keyboard devices by whether they support KEY_A or not
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-09-21 23:56:24 -04:00
95833e3a33 Support multiple revert_default_keys
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-04-03 17:15:38 -04:00
b1bb55766d Workaround for (buggy?) Monoprice Dark Matter Aether keyboard
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2021-03-02 23:57:26 -05:00
bd87beb211 Abandon travis-ci for jenkins
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
2020-11-26 18:24:53 -05:00
965c446b51 Remove powerpc64-unknown-linux-gnu travis build as it no longer exists 2020-07-11 13:21:10 -04:00
4a816737c7 Upgrade to all latest dependencies 2020-07-11 02:20:20 -04:00
95f9c731e9 Get rid of ancient and broken uinput-sys, proper refactoring to come 2020-07-11 02:05:25 -04:00
6c4e4392b4 Fix old todo thanks to new rust version 2020-07-11 00:59:35 -04:00
432e35cb6a Add travis-ci and appveyor builds 2020-07-11 00:49:03 -04:00
24786356c4 Cargo update and fix newly deprecated code usage 2020-07-11 00:09:43 -04:00
21 changed files with 2631 additions and 659 deletions

46
.ci/Jenkinsfile vendored Normal file
View File

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

45
.ci/build.sh Executable file
View File

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

243
Cargo.lock generated
View File

@ -1,204 +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.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cc"
version = "1.0.45"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
version = "0.1.10"
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 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width",
]
[[package]]
name = "inotify"
version = "0.7.0"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5fc8f41dbaa9c8492a96c8afffda4f76896ee041d6a57606e70581b80c901f"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
]
[[package]]
name = "ioctl-sys"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[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.62"
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.15.0"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "proc-macro2"
version = "1.0.4"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.2"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
]
[[package]]
name = "rusty-keys"
version = "0.0.3"
dependencies = [
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"uinput-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation-sys",
"core-graphics",
"epoll",
"getopts",
"inotify",
"lazy_static",
"libc",
"nix",
"serde",
"toml",
"winapi",
]
[[package]]
name = "serde"
version = "1.0.101"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.101"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.5"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "toml"
version = "0.5.3"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "uinput-sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.6"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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"
[metadata]
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
"checksum inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8"
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
"checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724"
"checksum uinput-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9aabddd8174ccadd600afeab346bb276cb1db5fafcf6a7c5c5708b8cc4b2cac7"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -22,15 +22,26 @@ include = [
[dependencies]
getopts = "0.2.21"
toml = "0.5.3"
serde = { version = "1.0.101", features = ["derive"] }
toml = { version = "0.5.8", optional = true }
serde = { version = "1.0.130", features = ["derive"], optional = true }
[target.'cfg(target_os="macos")'.dependencies]
core-graphics = "0.22"
core-foundation-sys = "0.8"
#rustkit = "0.0.1"
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser", "wincon"] }
winapi = { version = "0.3.9", features = ["winuser", "wincon"] }
lazy_static = "1.4.0"
[target.'cfg(target_os="linux")'.dependencies]
libc = "0.2.62"
nix = "0.15.0"
uinput-sys = "0.1.7"
inotify = { version = "0.7.0", default-features = false, features = [] }
libc = "0.2.102"
nix = "0.22.1"
epoll = { version = "4.3.1", optional = true }
inotify = { version = "0.9.3", default-features = false, features = [], optional = true }
[features]
default = ["epoll_inotify", "toml_serde"]
toml_serde = ["toml", "serde"]
epoll_inotify = ["epoll", "inotify"]

View File

@ -1,8 +1,11 @@
rusty-keys
======
uinput level keyboard mapper for linux, with advanced caps lock and shift swapping behavior
==========
This is the only keymapper I am aware of capable of implementing this layout:
[![Build Status](https://ci.moparisthe.best/job/moparisthebest/job/rusty-keys/job/master/badge/icon%3Fstyle=plastic)](https://ci.moparisthe.best/job/moparisthebest/job/rusty-keys/job/master/)
low level keyboard mapper for linux and windows, with advanced caps lock and shift swapping behavior
This is the only keymapper I am aware of capable of implementing this layout, which I call Unix Programmer's Dvorak, which has been my daily driver since 2014:
![Unix Programmer's Dvorak](https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg)
The Problem
@ -17,7 +20,7 @@ The Solution
2. Create a new keyboard input device with uinput, this looks identical to any other keyboard device to anything running on the box.
3. Read input_events from the real device, map them, send them to our created device.
This solution is what rusty-keys implements, it works in ttys, in X, in virtualbox even running windows or whatever,
This solution is what rusty-keys implements, it works in ttys, in X, in Wayland, in virtualbox even running windows or whatever,
on SDL games, it will work literally everywhere, because rusty-keys just creates a regular keyboard.
How to run
@ -49,6 +52,7 @@ How to install
--------------
* `cargo install rusty-keys`
* Arch Linux [rusty-keys](https://aur.archlinux.org/packages/rusty-keys/) [rusty-keys-git](https://aur.archlinux.org/packages/rusty-keys-git/)
* Download a static binary for your system from the [releases](https://code.moparisthebest.com/moparisthebest/rusty-keys/releases) section. [github mirror](https://github.com/moparisthebest/rusty-keys/releases)
License
-------

90
appveyor.yml Normal file
View File

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

View File

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

View File

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

View File

@ -3,15 +3,9 @@ use std::error;
use std::ffi;
use std::io;
#[cfg(target_os = "linux")]
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use nix;
#[cfg(target_os = "linux")]
use libc;
/// UInput error.
#[derive(Debug)]
pub enum Error {
@ -23,15 +17,17 @@ pub enum Error {
Nul(ffi::NulError),
Io(io::Error),
#[cfg(feature = "toml_serde")]
Toml(toml::de::Error),
#[cfg(target_os = "linux")]
Send(mpsc::SendError<libc::input_event>),
/// The uinput file could not be found.
NotFound,
NotAKeyboard,
/// error reading input_event
ShortRead,
/// epoll already added
EpollAlreadyAdded,
}
impl From<ffi::NulError> for Error {
@ -53,41 +49,26 @@ impl From<io::Error> for Error {
}
}
#[cfg(target_os = "linux")]
impl From<mpsc::SendError<libc::input_event>> for Error {
fn from(value: mpsc::SendError<libc::input_event>) -> Self {
Error::Send(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(error::Error::description(self))
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
#[cfg(target_os = "linux")]
&Error::Nix(ref err) =>
err.description(),
&Error::Nix(ref err) => err.fmt(f),
&Error::Nul(ref err) =>
err.description(),
&Error::Nul(ref err) => err.fmt(f),
&Error::Io(ref err) =>
err.description(),
&Error::Io(ref err) => err.fmt(f),
#[cfg(target_os = "linux")]
&Error::Send(ref err) =>
err.description(),
#[cfg(feature = "toml_serde")]
&Error::Toml(ref err) => err.fmt(f),
&Error::NotAKeyboard => f.write_str("This device file is not a keyboard"),
&Error::NotFound =>
"Device not found.",
&Error::ShortRead =>
"Error while reading from device file.",
&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 {}

View File

@ -1,11 +1,12 @@
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use std::hash::Hash;
use std::convert::TryFrom;
use crate::{Error, Result};
#[cfg(feature = "toml_serde")]
use std::path::Path;
use crate::Result;
const INVERT_KEY_FLAG: char = '^';
const CAPS_MODIFY_KEY_FLAG: char = '*';
@ -14,7 +15,7 @@ const HALF_KEY_SEPARATOR: char = ':';
// nightly only...
//pub trait KeyCode = Into<usize> + TryFrom<usize> + Copy + Clone + Eq + Hash + Default + 'static;
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum KeyState {
DOWN,
UP,
@ -31,7 +32,7 @@ pub trait KeyEvent<T>
pub trait Keyboard<T, E, R = ()>
where
T: Into<usize>,
T: Into<usize> + Copy,
E: KeyEvent<T>,
{
fn send(&self, event: &mut E) -> Result<R>;
@ -42,11 +43,52 @@ pub trait Keyboard<T, E, R = ()>
fn right_shift_code(&self) -> T;
fn caps_lock_code(&self) -> T;
fn block_key(&self) -> Result<R>;
fn send_half_inverted_key(&self, half_inverted_key: &HalfInvertedKey<T>, event: &mut E, left_shift: bool, right_shift: bool, caps_lock: bool) -> Result<R> {
let value = event.value();
let mut invert_shift = half_inverted_key.invert_shift;
if value == KeyState::DOWN {
if caps_lock && half_inverted_key.capslock_nomodify {
invert_shift = !invert_shift;
}
if invert_shift {
let (shift_code, up_not_down) = if left_shift {
(self.left_shift_code(), true)
} else if right_shift {
(self.right_shift_code(), true)
} else {
(self.left_shift_code(), false)
};
self.send_mod_code_value(shift_code, up_not_down, event)?;
// SYN_REPORT after, then key, then key's SYN_REPORT
self.synchronize()?;
}
}
let ret = self.send_mod_code(half_inverted_key.code, event)?;
if value == KeyState::UP {
if caps_lock && half_inverted_key.capslock_nomodify {
invert_shift = !invert_shift;
}
if invert_shift {
let (shift_code, up_not_down) = if left_shift {
(self.left_shift_code(), false)
} else if right_shift {
(self.right_shift_code(), false)
} else {
(self.left_shift_code(), true)
};
// SYN_REPORT first after key, then shift, then key's SYN_REPORT which will be used for shift's
self.synchronize()?;
self.send_mod_code_value(shift_code, up_not_down, event)?;
}
}
Ok(ret)
}
}
pub trait KeyMapper<K, T, E, R>
where
T: Into<usize>,
T: Into<usize> + Copy,
E: KeyEvent<T>,
K: Keyboard<T, E, R>,
{
@ -63,7 +105,7 @@ pub struct KeyMaps<K, T, E, R = ()>
keymap_index_keys: HashMap<T, usize>,
switch_layout_keys: Vec<usize>,
key_state: [bool; KEY_MAX],
revert_default_key: T,
revert_default_keys: Vec<T>,
revert_keymap_index: usize,
// above do not change, below does
chosen_keymap_index: usize,
@ -120,6 +162,7 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
E: KeyEvent<T>,
K: Keyboard<T, E, R>,
{
#[cfg(feature = "toml_serde")]
pub fn from_cfg<P: AsRef<Path>>(key_map: &HashMap<&'static str, T>, path: P) -> KeyMaps<K, T, E, R> {
let key_map_config = parse_cfg(path).expect("provided config cannot be found/parsed");
KeyMaps::new(key_map, key_map_config)
@ -147,8 +190,14 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
let mut keymap = KeyMap::new();
let mut i: usize = 0;
for key_code in v {
// todo: if these are the same, do Noop instead
keymap.map(base_keymap[i], key_code);
// if it's a direct key and it's the same, don't do any mapping
if let Key::Direct(key) = key_code {
if base_keymap[i] != key {
keymap.map(base_keymap[i], key_code);
}
} else {
keymap.map(base_keymap[i], key_code);
}
i = i + 1;
if i > base_keymap.len() {
panic!("all keymaps must be the same length, keymap index 0 length: {}, index {} length: {},", base_keymap.len(), x, i);
@ -161,7 +210,9 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
let mut keymap = CodeKeyMap::new();
let mut i: usize = 0;
for key_code in v {
keymap.map(base_keymap[i], key_code);
if base_keymap[i] != key_code {
keymap.map(base_keymap[i], key_code);
}
i = i + 1;
if i > base_keymap.len() {
panic!("all keymaps must be the same length, keymap index 0 length: {}, index {} length: {},", base_keymap.len(), x, i);
@ -173,13 +224,27 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
//println!("keymaps: {:?}", keymaps);
//println!("keymap_index_keys: {:?}", keymap_index_keys);
let mut revert_default_keys = Vec::new();
if config.revert_default_key.is_some() {
revert_default_keys.push(parse_key(key_map, &config.revert_default_key.unwrap()));
}
if config.revert_default_keys.is_some() {
for revert_default_key in config.revert_default_keys.unwrap() {
let revert_default_key = parse_key(key_map, &revert_default_key);
if !revert_default_keys.contains(&revert_default_key) {
revert_default_keys.push(revert_default_key);
}
}
}
// revert_default_keys may be empty, but that's ok
KeyMaps {
keymaps: keymaps,
keymap_index_keys: keymap_index_keys,
switch_layout_keys: config.switch_layout_keys.iter().map(|k| parse_key(key_map, k).into()).collect(),
key_state: [false; KEY_MAX],
// todo: detect key state? at least CAPSLOCK...
revert_default_key: parse_key(key_map, &config.revert_default_key),
revert_default_keys,
revert_keymap_index: config.revert_keymap_index,
chosen_keymap_index: config.default_keymap_index,
current_keymap_index: config.default_keymap_index,
@ -190,7 +255,6 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
//impl KeyMapper for KeyMaps {
//impl KeyMaps {
pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
//println!("type: {} code: {} value: {}", event.type_, event.code, event.value);
let value = event.value();
if value != KeyState::OTHER {
// todo: index check here...
@ -199,7 +263,12 @@ pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
self.key_state[device.caps_lock_code().into()] = !self.key_state[device.caps_lock_code().into()];
}
} else {
self.key_state[event.code().into()] = value == KeyState::DOWN;
let idx = event.code().into();
if idx >= KEY_MAX {
// oh well, send it directly then
return device.send(event);
}
self.key_state[idx] = value == KeyState::DOWN;
}
let mut switch_layout_keys_pressed = true;
for layout_switch_key in self.switch_layout_keys.iter_mut() {
@ -217,11 +286,27 @@ pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
return device.block_key(); // we don't want to also send this keypress, so bail
}
}
if event.code() == self.revert_default_key {
if self.revert_default_keys.contains(&event.code()) {
match value {
// todo: ctrl+c will get c stuck because code c value 1 will be sent, but then we'll let go of ctrl, and code j value 0 is sent, so c is never released... fix that...
KeyState::DOWN => self.current_keymap_index = self.revert_keymap_index,
KeyState::UP => self.current_keymap_index = self.chosen_keymap_index,
KeyState::DOWN => {
// todo: should we release currently held keys and then press them back down here, kinda the opposite of below? not for now...
self.current_keymap_index = self.revert_keymap_index
},
KeyState::UP => {
self.current_keymap_index = self.chosen_keymap_index;
#[cfg(not(target_os = "macos"))] {
// need to release all currently held down keys, except this one, otherwise ctrl+c will get c stuck because code c value 1 will be sent, but then we'll let go of ctrl, and code j value 0 is sent, so c is never released
let orig_code = event.code();
for (idx, key_down) in self.key_state.iter_mut().enumerate() {
if *key_down {
device.send_mod_code_value(T::try_from(idx).unwrap_or_else(|_| panic!("cannot convert from usize to T ????")), true, event)?;
*key_down = false;
}
}
// todo: seems like we should not send this here, and instead just set the original code back, and pass it through the keymaps?
return device.send_mod_code_value(orig_code, true, event)
}
},
_ => () // do nothing for 2
}
}
@ -234,42 +319,16 @@ pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
const KEY_MAX: usize = 249;
struct KeyMap<T: Into<usize> + Copy> {
//keymap: Vec<Key>,
keymap: [Key<T>; KEY_MAX],
}
impl<T: Into<usize> + Copy> KeyMap<T> {
pub fn new() -> Self {
//let mut keymap = [0u16; KEY_MAX];
//let mut keymap : [Box<KeyMapper>; KEY_MAX] = [Box::new(NOOP); KEY_MAX];
//let mut keymap : [Box<KeyMapper>; KEY_MAX] = [Box::new(0u16); KEY_MAX];
let keymap: [Key<T>; KEY_MAX] = [Key::Noop; KEY_MAX];
/*
let mut keymap: Vec<Key> = Vec::with_capacity(KEY_MAX);
#[allow(unused_variables)]
for x in 0..KEY_MAX {
keymap.push(Key::Noop);
}
*/
// which is rustier
/*
for x in 0..KEY_MAX {
keymap[x as usize] = x as u16;
}
for (x, v) in keymap.iter_mut().enumerate() {
*v = x as u16;
}
*/
//println!("keymap: {:?}", &keymap[..]);
KeyMap {
keymap: keymap
keymap: [Key::Noop; KEY_MAX]
}
}
/*
pub fn map(&mut self, from : u16, to: u16) {
self.keymap[from as usize] = to;
}
*/
pub fn map(&mut self, from: T, to: Key<T>) {
self.keymap[from.into()] = to;
}
@ -287,25 +346,18 @@ impl<K, T, E, R> KeyMapper<K, T, E, R> for KeyMap<T>
}
struct CodeKeyMap<T: Into<usize> + TryFrom<usize> + Copy + Default> {
//keymap: Vec<Key>,
keymap: [T; KEY_MAX],
}
impl<T: Into<usize> + TryFrom<usize> + Copy + Default> CodeKeyMap<T> {
pub fn new() -> Self {
let mut keymap = [T::default(); KEY_MAX];
// which is rustier
/*
for x in 0..KEY_MAX {
keymap[x as usize] = x as u16;
}
*/
for (x, v) in keymap.iter_mut().enumerate() {
*v = T::try_from(x).unwrap_or_else(|_| panic!("cannot convert from usize to T ????"));
}
//println!("keymap: {:?}", &keymap[..]);
CodeKeyMap {
keymap: keymap
keymap
}
}
@ -328,61 +380,15 @@ impl<K, T, E, R> KeyMapper<K, T, E, R> for CodeKeyMap<T>
// todo:capslock_nomodify is like a whole-key thing, not a half-key thing, split code/invert_shift to own struct, send into send_key from *InvertedKey, maybe anyway, consider it, maybe 1 char for whole key and another for half?
#[derive(Clone, Copy)]
struct HalfInvertedKey<T: Clone + Copy> {
code: T,
pub struct HalfInvertedKey<T: Clone + Copy> {
pub code: T,
// code this is describing
invert_shift: bool,
pub invert_shift: bool,
// true to invert shift for this code
capslock_nomodify: bool,
pub capslock_nomodify: bool,
// true means capslock does not normally modify this, but you would like it to
}
fn send_half_inverted_key<K, T, E, R>(half_inverted_key: &HalfInvertedKey<T>, event: &mut E, device: &K, left_shift: bool, right_shift: bool, caps_lock: bool) -> Result<R>
where
T: Into<usize> + Clone + Copy,
E: KeyEvent<T>,
K: Keyboard<T, E, R>,
{
let value = event.value();
let mut invert_shift = half_inverted_key.invert_shift;
if value == KeyState::DOWN {
if caps_lock && half_inverted_key.capslock_nomodify {
invert_shift = !invert_shift;
}
if invert_shift {
let (shift_code, up_not_down) = if left_shift {
(device.left_shift_code(), true)
} else if right_shift {
(device.right_shift_code(), true)
} else {
(device.left_shift_code(), false)
};
device.send_mod_code_value(shift_code, up_not_down, event)?;
// SYN_REPORT after, then key, then key's SYN_REPORT
device.synchronize()?;
}
}
let ret = device.send_mod_code(half_inverted_key.code, event)?;
if value == KeyState::UP {
if caps_lock && half_inverted_key.capslock_nomodify {
invert_shift = !invert_shift;
}
if invert_shift {
let (shift_code, up_not_down) = if left_shift {
(device.left_shift_code(), false)
} else if right_shift {
(device.right_shift_code(), false)
} else {
(device.left_shift_code(), true)
};
// SYN_REPORT first after key, then shift, then key's SYN_REPORT which will be used for shift's
device.synchronize()?;
device.send_mod_code_value(shift_code, up_not_down, event)?;
}
}
Ok(ret)
}
impl<K, T, E, R> KeyMapper<K, T, E, R> for HalfInvertedKey<T>
where
T: Into<usize> + Clone + Copy,
@ -393,7 +399,7 @@ impl<K, T, E, R> KeyMapper<K, T, E, R> for HalfInvertedKey<T>
let left_shift = key_state[device.left_shift_code().into()];
let right_shift = key_state[device.right_shift_code().into()];
let caps_lock = key_state[device.caps_lock_code().into()];
send_half_inverted_key(self, event, device, left_shift, right_shift, caps_lock)
device.send_half_inverted_key(self, event, left_shift, right_shift, caps_lock)
}
}
@ -430,34 +436,96 @@ impl<K, T, E, R> KeyMapper<K, T, E, R> for Key<T>
let right_shift = key_state[device.right_shift_code().into()];
let caps_lock = key_state[device.caps_lock_code().into()];
if caps_lock != (left_shift || right_shift) {
send_half_inverted_key(shift_half, event, device, left_shift, right_shift, caps_lock)
device.send_half_inverted_key(shift_half, event, left_shift, right_shift, caps_lock)
} else {
send_half_inverted_key(noshift_half, event, device, left_shift, right_shift, caps_lock)
device.send_half_inverted_key(noshift_half, event, left_shift, right_shift, caps_lock)
}
},
}
}
}
use std::path::Path;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[cfg(feature = "toml_serde")]
#[derive(serde::Deserialize, Debug)]
pub struct KeymapConfig {
switch_layout_keys: Vec<String>,
revert_default_key: String,
revert_default_key: Option<String>,
revert_default_keys: Option<Vec<String>>,
revert_keymap_index: usize,
default_keymap_index: usize,
keymaps: Vec<String>
}
#[cfg(feature = "toml_serde")]
fn parse_cfg<P: AsRef<Path>>(path: P) -> Result<KeymapConfig> {
let mut f = File::open(path)?;
use std::io::Read;
let mut f = std::fs::File::open(path)?;
let mut input = String::new();
f.read_to_string(&mut input)?;
//toml::from_str(&input)?
match toml::from_str(&input) {
Ok(toml) => Ok(toml),
Err(_) => Err(Error::NotFound) // todo: something better
toml::from_str(&input).map_err(|e| crate::Error::Toml(e))
}
#[cfg(not(feature = "toml_serde"))]
#[derive(Debug)]
pub struct KeymapConfig {
switch_layout_keys: Vec<&'static str>,
revert_default_key: Option<&'static str>,
revert_default_keys: Option<Vec<&'static str>>,
revert_keymap_index: usize,
default_keymap_index: usize,
keymaps: Vec<&'static str>
}
#[cfg(not(feature = "toml_serde"))]
impl Default for KeymapConfig {
fn default() -> Self {
KeymapConfig {
switch_layout_keys: vec!["LEFTSHIFT", "RIGHTSHIFT"],
// pressing any of these keys reverts to the index specified in revert_keymap_index for only the duration of the pressing
// used so QWERTY shortcuts like Ctrl+C still work
revert_default_keys: Some(vec!["LCTL", "LGUI", "LALT"]),
revert_keymap_index: 0,
// this is the default index to use when the program first starts
// in this case, 2 means Unix Programmer Dvorak
default_keymap_index: 2,
// 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: vec![
// default key layout, QWERTY in this case
r###"
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
r###"
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 Programmer Dvorak - for unix developers who are switching from dvorak
// https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg
r###"
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, 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
"###,
],
revert_default_key: None, // use revert_default_keys instead
}
}
}

View File

@ -1,5 +1,6 @@
#![recursion_limit = "1000"]
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub mod error;
@ -19,3 +20,8 @@ pub use windows::*;
mod linux;
#[cfg(target_os = "linux")]
pub use linux::*;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::*;

View File

@ -2,11 +2,42 @@ use std::path::Path;
use std::{mem, slice};
use std::ffi::CString;
use libc::c_int;
use nix::{self, fcntl, unistd, errno::Errno};
use nix::{self, fcntl, unistd, ioctl_write_ptr, ioctl_none};
use nix::sys::stat;
use uinput_sys::*;
use crate::{Result as Res, Device};
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 {
@ -17,7 +48,7 @@ pub struct Builder {
impl Builder {
/// Create a builder from the specified path.
pub fn open<P: AsRef<Path>>(path: P) -> Res<Self> {
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() },
@ -26,17 +57,17 @@ impl Builder {
}
/// Create a builder from `/dev/uinput`.
pub fn default() -> Res<Self> {
pub fn default() -> Result<Self> {
Builder::open("/dev/uinput")
}
/// Set the name.
pub fn name<T: AsRef<str>>(mut self, value: T) -> Res<Self> {
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::from_errno(Errno::EINVAL))?;
Err(nix::Error::EINVAL)?;
}
(&mut self.def.name)[..bytes.len()]
@ -69,164 +100,18 @@ impl Builder {
self
}
pub fn event(mut self, key_codes: Values<&str, u16>) -> Res<Self> {
pub fn event(mut self, key_codes: Values<&str, u16>) -> Result<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)));
Errno::result(ui_set_evbit(self.fd, EV_KEY as i32))?;
//ui_set_keybit(self.fd, KEY_H as *const c_int)?;
ui_set_evbit(self.fd, EV_KEY as *const c_int)?;
for key_code in key_codes {
Errno::result(ui_set_keybit(self.fd, *key_code as i32))?;
ui_set_keybit(self.fd, *key_code as *const c_int)?;
}
//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;
@ -252,15 +137,13 @@ impl Builder {
}
/// Create the defined device.
pub fn create(self) -> Res<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))?;
//todo: try!(Errno::result(ui_dev_create(self.fd)));
// try1: Errno::result(ui_dev_create(self.fd)).unwrap();
Errno::result(ui_dev_create(self.fd))?;
ui_dev_create(self.fd)?;
}
Ok(Device::new(self.fd))

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

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

View File

@ -1,8 +1,11 @@
use std::{mem, ptr, slice};
use libc::{timeval, gettimeofday, input_event, c_int};
use nix::{unistd, errno::Errno};
use uinput_sys::*;
use crate::{Result as Res};
use nix::{unistd, ioctl_none};
use crate::Result;
use crate::linux::device::codes::*;
ioctl_none!(ui_dev_destroy, b'U', 2);
/// The virtual device.
pub struct Device {
@ -18,7 +21,7 @@ impl Device {
}
#[doc(hidden)]
pub fn write(&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 {
time: timeval { tv_sec: 0, tv_usec: 0 },
type_: kind as u16,
@ -30,7 +33,7 @@ impl Device {
}
#[doc(hidden)]
pub fn write_event(&self, event: &mut input_event) -> Res<()> {
pub fn write_event(&self, event: &mut input_event) -> Result<()> {
unsafe {
gettimeofday(&mut event.time, ptr::null_mut());
@ -44,35 +47,33 @@ impl Device {
}
/// Synchronize the device.
pub fn synchronize(&self) -> Res<()> {
pub fn synchronize(&self) -> Result<()> {
self.write(EV_SYN, SYN_REPORT, 0)
}
/// Send an event.
pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Res<()> {
pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
self.write(kind, code, value)
}
/// Send a press event.
pub fn press(&self, kind: c_int, code: c_int) -> Res<()> {
pub fn press(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 1)
}
/// Send a release event.
pub fn release(&self, kind: c_int, code: c_int) -> Res<()> {
pub fn release(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 0)
}
/// Send a press and release event.
pub fn click(&self, kind: c_int, code: c_int) -> Res<()> {
pub fn click(&self, kind: c_int, code: c_int) -> Result<()> {
self.press(kind, code)?;
self.release(kind, code)?;
Ok(())
self.release(kind, code)
}
/// Send a relative or absolute positioning event.
pub fn position(&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)
}
}
@ -80,7 +81,8 @@ impl Device {
impl Drop for Device {
fn drop(&mut self) {
unsafe {
Errno::result(ui_dev_destroy(self.fd)).unwrap();
// ignore error here so as to not panic in a drop
ui_dev_destroy(self.fd).ok();
}
}
}

View File

@ -3,54 +3,148 @@ 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;
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);
// TODO: use size_of_input_event instead of hard-coding 24.
const SIZE_OF_INPUT_EVENT: usize = 24;//mem::size_of::<input_event>();
const SIZE_OF_INPUT_EVENT: usize = mem::size_of::<input_event>();
pub struct InputDevice {
device_file: File,
buf: [u8; SIZE_OF_INPUT_EVENT],
grabbed: bool,
#[cfg(feature = "epoll_inotify")]
epoll_fd: Option<RawFd>,
}
impl InputDevice {
pub fn open(device_file: &str) -> Result<Self> {
let device_file = File::open(device_file)?;
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
Ok(InputDevice {
device_file: device_file,
buf: [0u8; SIZE_OF_INPUT_EVENT],
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) -> Result<input_event> {
let num_bytes = self.device_file.read(&mut self.buf)?;
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(self.buf) };
let event: input_event = unsafe { mem::transmute(*buf) };
Ok(event)
}
pub fn grab(&mut self) -> Result<()> {
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)?;
}
Ok(())
self.grabbed = true;
Ok(self)
}
pub fn release(&mut self) -> Result<()> {
unsafe {
eviocgrab(self.device_file.as_raw_fd(), 0 as *const c_int)?;
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) {
self.release().ok(); // ignore any errors here, what could we do anyhow?
// ignore any errors here, what could we do anyhow?
self.release().ok();
#[cfg(feature = "epoll_inotify")]
self.epoll_del().ok();
}
}

View File

@ -1,6 +1,9 @@
mod builder;
pub use self::builder::Builder;
pub mod codes;
pub use codes::*;
mod device;
pub use self::device::Device;

View File

@ -1,26 +1,18 @@
use crate::*;
use std::path::Path;
use crate::linux::device::codes::*;
pub mod device;
pub use device::{Device,InputDevice};
pub use device::{Device,InputDevice, Builder};
/// Open the default uinput device.
pub fn default() -> Result<device::Builder> {
device::Builder::default()
}
/// Open the specified uinput device.
pub fn open<P: AsRef<Path>>(path: P) -> Result<device::Builder> {
device::Builder::open(path)
}
use uinput_sys::*;
use libc::input_event;
use std::process::exit;
use std::{env, thread};
use std::sync::mpsc;
use std::sync::mpsc::Sender;
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;
@ -28,13 +20,6 @@ const UP: i32 = 0;
use getopts::Options;
use inotify::{
EventMask,
Inotify,
WatchMask,
};
use std::collections::HashMap;
const EV_KEY_U16: u16 = EV_KEY as u16;
type LinuxKeyMaps = KeyMaps<Device, u16, input_event>;
@ -98,8 +83,6 @@ impl Keyboard<u16, input_event> for Device {
}
}
#[derive(Debug)]
struct Config {
device_files: Vec<String>,
@ -119,88 +102,133 @@ pub fn main_res() -> Result<()> {
let key_map = key_map();
//println!("key_map: {:?}", key_map);
let device = open("/dev/uinput")
.or_else(|_| open("/dev/input/uinput"))
.or_else(|_| default())?
.name("rusty-keys")?
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 threads
let mut input_device = InputDevice::open(&config.device_files[0])?;
input_device.grab()?;
// shortcut, don't bother with epoll
let mut input_device = InputDevice::open(&config.device_files[0])?.grab()?;
loop {
let event = input_device.read_event()?;
let event = input_device.read_event(&mut input_event_buf)?;
send_event(&mut key_map, event, &device)?
}
} else {
// start up some intra thread communication
let (tx, rx) = mpsc::channel();
#[cfg(not(feature = "epoll_inotify"))]
panic!("without epoll_inotify feature, only exactly 1 device is supported");
if config.device_files.len() > 0 {
// we only want to operate on device files sent in then quit
for device_file in config.device_files.iter() {
let device_file = device_file.clone();
let tx = tx.clone();
thread::spawn(move || {
let ret = spawn_map_thread(tx, &device_file);
if let Err(e) = ret {
println!("mapping for {} ended due to error: {}", device_file, e);
}
});
}
} else {
let tx = tx.clone();
thread::spawn(move || {
// we want to wait forever starting new threads for any new keyboard devices
let mut inotify = Inotify::init().expect("Failed to initialize inotify");
#[cfg(feature = "epoll_inotify")]
{
use inotify::{Inotify, WatchMask};
inotify.add_watch("/dev/input/", WatchMask::CREATE).expect("Failed to add inotify watch");
let epoll_fd = epoll::create(true)?;
const INOTIFY_DATA: u64 = u64::max_value();
let device_files = get_keyboard_device_filenames();
println!("Detected devices: {:?}", device_files);
for device_file in device_files.iter() {
inotify_spawn_thread(&tx, device_file);
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 buffer = [0u8; 4096];
loop {
let events = inotify.read_events_blocking(&mut buffer);
let mut epoll_buf = [epoll::Event::new(epoll::Events::empty(), 0); 4];
let mut inotify_buf = [0u8; 256];
if let Ok(events) = events {
for event in events {
if !event.mask.contains(EventMask::ISDIR) {
if let Some(device_file) = event.name.and_then(|name|name.to_str()) {
// check if this is an eligible keyboard device
let device_files = get_keyboard_device_filenames();
if !device_files.contains(&device_file.to_string()) {
continue;
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));
}
}
}
println!("starting mapping thread for: {}", device_file);
inotify_spawn_thread(&tx, device_file.clone());
}
}
}
}
}
});
}
drop(tx); // drop our last one, so when the threads finish, everything stops
// process all events
for event in rx {
send_event(&mut key_map, event, &device)?
}
}
}
Ok(())
}
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)?
@ -208,28 +236,6 @@ fn send_event(key_map: &mut LinuxKeyMaps, mut event: input_event, device: &Devic
Ok(())
}
fn inotify_spawn_thread(tx: &Sender<input_event>, device_file: &str) {
let mut filename = "/dev/input/".to_string();
filename.push_str(&device_file);
let tx = tx.clone();
thread::spawn(move || {
let ret = spawn_map_thread(tx, &filename);
if let Err(e) = ret {
println!("mapping for {} ended due to error: {}", filename, e);
}
});
}
fn spawn_map_thread(tx: Sender<input_event>, device_file: &str) -> Result<()> {
let mut input_device = InputDevice::open(device_file)?;
input_device.grab()?;
loop {
let event = input_device.read_event()?;
tx.send(event)?
}
}
fn parse_args() -> Config {
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options] [device_files...]", program);
@ -255,7 +261,7 @@ fn parse_args() -> Config {
}
if matches.opt_present("v") {
println!("rusty-keys {}", VERSION);
println!("{} {}", NAME, VERSION);
exit(0);
}
@ -264,43 +270,25 @@ fn parse_args() -> Config {
Config::new(matches.free, config_file)
}
// 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
// grep -E 'Handlers|EV' /proc/bus/input/devices | grep -B1 120013 | grep -Eo event[0-9]+
fn get_keyboard_device_filenames() -> Vec<String> {
use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
let f = File::open("/proc/bus/input/devices");
if f.is_err() {
return Vec::new();
}
let f = BufReader::new(f.unwrap());
let mut filename = None;
let mut filenames = Vec::new();
for line in f.lines() {
if let Ok(line) = line {
if line.starts_with("H: Handlers=") {
if let Some(event_index) = line.find("event") {
let last_index = line[event_index..line.len()-1].find(" ").and_then(|i| Some(i + event_index)).unwrap_or(line.len() - 1);
filename = Some(line[event_index..last_index].to_owned());
}
} else if line.starts_with("B: EV=") && line.contains("120013") {
if let Some(ref filename) = filename {
filenames.push(filename.clone());
#[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);
}
}
}
}
filenames
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/events.rs | sed 's/^KEY_//' | awk '{print "(\""$1"\", KEY_"$1"),"}'
// 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),
@ -605,5 +593,7 @@ pub fn key_map() -> HashMap<&'static str, u16> {
("P0", KEY_KP0),
("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER),
("TOUCHPAD_TOGGLE", KEY_TOUCHPAD_TOGGLE),
].iter().cloned().map(|(m, v)| (m, v as u16)).collect()
}
}

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

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

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

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

View File

@ -1,5 +1,5 @@
#[cfg(any(target_os = "windows", target_os = "linux"))]
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn main() {
let ret = rusty_keys::main_res();
if let Err(e) = ret {
@ -7,7 +7,7 @@ fn main() {
}
}
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
fn main() {
panic!("sorry no main impl for this platform");
}

View File

@ -4,6 +4,8 @@ After=systemd-udevd.service
[Service]
ExecStart=/usr/bin/rusty-keys
Restart=always
RestartSec=1s
[Install]
WantedBy=default.target