Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
270cdca14f | |||
e9705db5ba | |||
10096b2709 | |||
e6f4653570 | |||
552cd50266 | |||
b0f9dfddf2 | |||
b0543d3f93 | |||
527ab97a04 | |||
d0e38d0e55 | |||
8ffe640c4b | |||
bcea441630 | |||
17a8f0c995 | |||
60a3f24c86 | |||
c420323cdf | |||
a454b2a2a1 | |||
877a35861a | |||
9317d31153 | |||
d785d341cb | |||
fa202be7da | |||
95833e3a33 | |||
b1bb55766d | |||
bd87beb211 | |||
965c446b51 | |||
4a816737c7 | |||
95f9c731e9 | |||
6c4e4392b4 | |||
432e35cb6a | |||
24786356c4 |
46
.ci/Jenkinsfile
vendored
Normal file
46
.ci/Jenkinsfile
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
properties(
|
||||
[
|
||||
disableConcurrentBuilds()
|
||||
]
|
||||
)
|
||||
|
||||
node('linux && docker') {
|
||||
try {
|
||||
stage('Checkout') {
|
||||
//branch name from Jenkins environment variables
|
||||
echo "My branch is: ${env.BRANCH_NAME}"
|
||||
|
||||
// this doesn't grab tags pointing to this branch
|
||||
//checkout scm
|
||||
// this hack does... https://issues.jenkins.io/browse/JENKINS-45164
|
||||
checkout([
|
||||
$class: 'GitSCM',
|
||||
branches: [[name: 'refs/heads/'+env.BRANCH_NAME]],
|
||||
extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']],
|
||||
userRemoteConfigs: scm.userRemoteConfigs,
|
||||
])
|
||||
sh '''
|
||||
set -euxo pipefail
|
||||
git checkout "$BRANCH_NAME" --
|
||||
git reset --hard "origin/$BRANCH_NAME"
|
||||
'''
|
||||
}
|
||||
|
||||
stage('Build + Deploy') {
|
||||
sh '''
|
||||
mkdir -p release
|
||||
cp keymap.toml release
|
||||
curl --compressed -sL https://code.moparisthebest.com/moparisthebest/self-ci/raw/branch/master/build-ci.sh | bash
|
||||
'''
|
||||
}
|
||||
|
||||
currentBuild.result = 'SUCCESS'
|
||||
} catch (Exception err) {
|
||||
currentBuild.result = 'FAILURE'
|
||||
} finally {
|
||||
stage('Email') {
|
||||
step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'admin.jenkins@moparisthebest.com', sendToIndividuals: true])
|
||||
}
|
||||
deleteDir()
|
||||
}
|
||||
}
|
45
.ci/build.sh
Executable file
45
.ci/build.sh
Executable file
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
set -exo pipefail
|
||||
|
||||
echo "starting build for TARGET $TARGET"
|
||||
|
||||
export CRATE_NAME=rusty-keys
|
||||
|
||||
DISABLE_TESTS=${DISABLE_TESTS:-0}
|
||||
|
||||
SUFFIX=""
|
||||
|
||||
echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && SUFFIX=".exe"
|
||||
|
||||
[ "$TARGET" == 'riscv64gc-unknown-linux-gnu' ] && echo 'riscv64gc-unknown-linux-gnu is not yet supported by inotify, skipping build...' && exit 0
|
||||
|
||||
# no main impl for these platforms
|
||||
echo "$TARGET" | grep -E '(android|solaris$)' >/dev/null && DISABLE_TESTS=1
|
||||
|
||||
cross build --target $TARGET --release
|
||||
|
||||
# to check how they are built
|
||||
file "target/$TARGET/release/rusty-keys$SUFFIX"
|
||||
|
||||
if [ $DISABLE_TESTS -ne 1 ]
|
||||
then
|
||||
# only going to run --help I guess
|
||||
cross run --target $TARGET --release --bin rusty-keys -- -h
|
||||
fi
|
||||
|
||||
# if this commit has a tag, upload artifact to release
|
||||
strip "target/$TARGET/release/rusty-keys$SUFFIX" || true # if strip fails, it's fine
|
||||
mkdir -p release
|
||||
mv "target/$TARGET/release/rusty-keys$SUFFIX" "release/rusty-keys-$TARGET$SUFFIX"
|
||||
|
||||
if [ "$TARGET" == 'x86_64-unknown-linux-musl' ]
|
||||
then
|
||||
# for this arch only, we are going to build with each feature combo to test that the build succeeds, but not archive them
|
||||
# the default for now is all features, so that's already tested above
|
||||
cross build --target $TARGET --release --no-default-features
|
||||
cross build --target $TARGET --release --no-default-features --features epoll_inotify
|
||||
cross build --target $TARGET --release --no-default-features --features toml_serde
|
||||
fi
|
||||
|
||||
echo 'build success!'
|
||||
exit 0
|
243
Cargo.lock
generated
243
Cargo.lock
generated
@ -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"
|
||||
|
25
Cargo.toml
25
Cargo.toml
@ -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"]
|
||||
|
12
README.md
12
README.md
@ -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
90
appveyor.yml
Normal file
@ -0,0 +1,90 @@
|
||||
# Based on the "trust" template v0.1.2
|
||||
# https://github.com/japaric/trust/tree/v0.1.2
|
||||
|
||||
environment:
|
||||
global:
|
||||
# TODO This is the Rust channel that build jobs will use by default but can be
|
||||
# overridden on a case by case basis down below
|
||||
RUST_VERSION: stable
|
||||
|
||||
# TODO Update this to match the name of your project.
|
||||
CRATE_NAME: rusty-keys
|
||||
|
||||
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
|
||||
# don't need
|
||||
matrix:
|
||||
# MinGW
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
- TARGET: x86_64-pc-windows-gnu
|
||||
|
||||
# MSVC
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
|
||||
# Testing other channels
|
||||
- TARGET: x86_64-pc-windows-gnu
|
||||
RUST_VERSION: nightly
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
RUST_VERSION: nightly
|
||||
|
||||
install:
|
||||
- ps: >-
|
||||
If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') {
|
||||
$Env:PATH += ';C:\msys64\mingw64\bin'
|
||||
} ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') {
|
||||
$Env:PATH += ';C:\msys64\mingw32\bin'
|
||||
}
|
||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
|
||||
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- rustc -Vv
|
||||
- cargo -V
|
||||
|
||||
# TODO This is the "test phase", tweak it as you see fit
|
||||
test_script:
|
||||
# we don't run the "test phase" when doing deploys
|
||||
- if [%APPVEYOR_REPO_TAG%]==[false] (
|
||||
cargo build --target %TARGET% --release &&
|
||||
cargo run --target %TARGET% --release --bin rusty-keys -- -h
|
||||
)
|
||||
|
||||
before_deploy:
|
||||
# TODO Update this to build the artifacts that matter to you
|
||||
- cargo rustc --target %TARGET% --release --bin rusty-keys -- -C lto
|
||||
- ps: ci\before_deploy.ps1
|
||||
|
||||
deploy:
|
||||
artifact: /rusty-keys-.*\.exe/
|
||||
# TODO update `auth_token.secure`
|
||||
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
|
||||
# - Encrypt it. Go to https://ci.appveyor.com/tools/encrypt
|
||||
# - Paste the output down here
|
||||
auth_token:
|
||||
secure: gyQW6TqUY94X8IpcQeezbngBQA/PROaCPpr8K+8IxGBG5gf2iHra2CLlp/QJJZYx
|
||||
description: ''
|
||||
on:
|
||||
# TODO Here you can pick which targets will generate binary releases
|
||||
# In this example, there are some targets that are tested using the stable
|
||||
# and nightly channels. This condition makes sure there is only one release
|
||||
# for such targets and that's generated using the stable channel
|
||||
RUST_VERSION: stable
|
||||
appveyor_repo_tag: true
|
||||
provider: GitHub
|
||||
|
||||
cache:
|
||||
- C:\Users\appveyor\.cargo\registry
|
||||
- target
|
||||
|
||||
branches:
|
||||
only:
|
||||
# Release tags
|
||||
- /^v\d+\.\d+\.\d+.*$/
|
||||
- master
|
||||
- dev
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
|
||||
# Building is done in the test phase, so we disable Appveyor's build phase.
|
||||
build: false
|
@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# pressing all of these keys along with a number key representing the index of keymaps changes the layout
|
||||
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
|
||||
switch_layout_keys = ['LEFTSHIFT','RIGHTSHIFT']
|
||||
|
||||
# pressing QWERTY reverts to the index specified in revert_keymap_index for only the duration of the pressing
|
||||
# used so QWERTY shortcuts like Ctrl+C still work
|
||||
revert_default_key = 'LEFTCTRL'
|
||||
revert_keymap_index = 0
|
||||
|
||||
# this is the default index to use when the program first starts
|
||||
# in this case, 2 means modified Progammer Dvorak
|
||||
default_keymap_index = 2
|
||||
|
||||
# these are keys that caps_lock doesn't modify by default, but that you would like it to, affects all keymaps
|
||||
caps_lock_modify = """
|
||||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, PSLS,PAST,PMNS,
|
||||
LBRC,RBRC,BSLS, P7, P8, P9,
|
||||
SCLN,QUOT, P4, P5, P6, PPLS,
|
||||
COMM,DOT, SLSH, P1, P2, P3,
|
||||
P0, PDOT
|
||||
"""
|
||||
|
||||
# these are the keymaps available, you can add as many as you want or re-order them, just be aware the mapping is
|
||||
# always done from the first one to all subsequent ones, so you probably want to leave QWERTY or similar up top
|
||||
keymaps = [
|
||||
# default key layout, QWERTY in this case
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
|
||||
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSLS, DEL, END, PGDN, P7, P8, P9,
|
||||
CAPS,A, S, D, F, G, H, J, K, L, SCLN,QUOT, ENT, P4, P5, P6, PPLS,
|
||||
LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH, RSFT, UP, P1, P2, P3,
|
||||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
|
||||
""",
|
||||
# Dvorak http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard
|
||||
# https://www.moparisthebest.com/kbs/standard-dvorak-QwertySecondary.svg
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
GRV, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, LBRC,RBRC,BSPC, INS, HOME,PGUP, NLCK,PSLS,PAST,PMNS,
|
||||
TAB, QUOT,COMM,DOT, P, Y, F, G, C, R, L, SLSH,EQL, BSLS, DEL, END, PGDN, P7, P8, P9,
|
||||
CAPS,A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4, P5, P6, PPLS,
|
||||
LSFT,SCLN,Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1, P2, P3,
|
||||
LCTL,LGUI,LALT, SPC, RALT,RGUI,APP, RCTL, LEFT,DOWN,RGHT, P0, PDOT,PENT
|
||||
""",
|
||||
# Unix Dvorak Programmer Dvorak - for unix developers who are switching from dvorak
|
||||
# https://www.moparisthebest.com/kbs/programmer-dvorak-NoSecondary-NumpadStandard-NoSwap-StandardNums-SwapAt-SwapPipe.svg
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
^4:^GRV, ^7:1, LBRC:2, ^LBRC:3, ^RBRC:4, ^9:5, ^2:6, ^8:7, ^0:8, ^EQL:9, RBRC:0, ^1:^5, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
|
||||
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, EQL:^6, ^BSLS:BSLS, DEL, END, PGDN, P7:^A, P8:^B, P9:^C,
|
||||
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
|
||||
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P1:EQL, P2:X, P3:^SCLN,
|
||||
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
|
||||
""",
|
||||
# Unix Dvorak Programmer Dvorak - for unix developers who are switching from dvorak - phone numpad
|
||||
# https://www.moparisthebest.com/kbs/programmer-dvorak-QwertySecondary-NumpadPhone-NoSwap-StandardNums-SwapAt-SwapPipe.svg
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
^4:^GRV, ^7:1, LBRC:2, ^LBRC:3, ^RBRC:4, ^9:5, ^2:6, ^8:7, ^0:8, ^EQL:9, RBRC:0, ^1:^5, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
|
||||
TAB, QUOT, COMM, DOT, P, Y, F, G, C, R, L, SLSH, EQL:^6, ^BSLS:BSLS, DEL, END, PGDN, P1:^A, P2:^B, P3:^C,
|
||||
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
|
||||
LSFT, SCLN, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P7:EQL, P8:X, P9:^SCLN,
|
||||
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
|
||||
""",
|
||||
# Programmer Dvorak http://www.kaufmann.no/roland/dvorak/
|
||||
# https://www.moparisthebest.com/kbs/programmer-dvorak-QwertySecondary-NumpadPhone-StrictSwap-StrictNums-StrictAt-StrictPipe.svg
|
||||
"""
|
||||
ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PSCR,SLCK,BRK,
|
||||
^4:^GRV, ^7:^5, LBRC:7, ^LBRC:5, ^RBRC:3, ^9:1, EQL:9, ^8:0, ^0:2, ^EQL:4, RBRC:6, ^1:8, ^3:GRV, BSPC, INS, HOME,PGUP, NLCK, PSLS:^9, PAST:^0, PMNS:^4,
|
||||
TAB, SCLN, COMM, DOT, P, Y, F, G, C, R, L, SLSH, ^2:^6, BSLS, DEL, END, PGDN, P1:^A, P2:^B, P3:^C,
|
||||
CAPS, A, O, E, U, I, D, H, T, N, S, MINS, ENT, P4:^D, P5:^E, P6:^F, PPLS:COMM,
|
||||
LSFT, QUOT, Q, J, K, X, B, M, W, V, Z, RSFT, UP, P7:EQL, P8:X, P9:^SCLN,
|
||||
LCTL, LGUI, LALT, SPC, RALT, RGUI, APP, RCTL, LEFT,DOWN,RGHT, P0:BSLS, PDOT:SCLN, PENT
|
||||
""",
|
||||
]
|
@ -3,9 +3,9 @@
|
||||
# ie, in this case pressing both and 0 would go QWERTY, while both and 1 would go dvorak
|
||||
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
|
||||
|
59
src/error.rs
59
src/error.rs
@ -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 {}
|
||||
|
300
src/keymapper.rs
300
src/keymapper.rs
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
||||
|
@ -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
780
src/linux/device/codes.rs
Normal file
@ -0,0 +1,780 @@
|
||||
use libc::{c_int};
|
||||
|
||||
pub const INPUT_PROP_POINTER: c_int = 0x00; /* needs a pointer */
|
||||
pub const INPUT_PROP_DIRECT: c_int = 0x01; /* direct input devices */
|
||||
pub const INPUT_PROP_BUTTONPAD: c_int = 0x02; /* has button: c_int = s under pad */
|
||||
pub const INPUT_PROP_SEMI_MT: c_int = 0x03; /* touch rectangle only */
|
||||
pub const INPUT_PROP_TOPBUTTONPAD: c_int = 0x04; /* softbuttons at top of pad */
|
||||
pub const INPUT_PROP_POINTING_STICK: c_int = 0x05; /* is a pointing stick */
|
||||
pub const INPUT_PROP_ACCELEROMETER: c_int = 0x06; /* has accelerometer */
|
||||
|
||||
pub const INPUT_PROP_MAX: c_int = 0x1f;
|
||||
pub const INPUT_PROP_CNT: c_int = INPUT_PROP_MAX + 1;
|
||||
|
||||
/*
|
||||
* Event types
|
||||
*/
|
||||
|
||||
pub const EV_SYN: c_int = 0x00;
|
||||
pub const EV_KEY: c_int = 0x01;
|
||||
pub const EV_REL: c_int = 0x02;
|
||||
pub const EV_ABS: c_int = 0x03;
|
||||
pub const EV_MSC: c_int = 0x04;
|
||||
pub const EV_SW: c_int = 0x05;
|
||||
pub const EV_LED: c_int = 0x11;
|
||||
pub const EV_SND: c_int = 0x12;
|
||||
pub const EV_REP: c_int = 0x14;
|
||||
pub const EV_FF: c_int = 0x15;
|
||||
pub const EV_PWR: c_int = 0x16;
|
||||
pub const EV_FF_STATUS: c_int = 0x17;
|
||||
pub const EV_MAX: c_int = 0x1f;
|
||||
pub const EV_CNT: c_int = EV_MAX + 1;
|
||||
|
||||
/*
|
||||
* Synchronization events.
|
||||
*/
|
||||
|
||||
pub const SYN_REPORT: c_int = 0;
|
||||
pub const SYN_CONFIG: c_int = 1;
|
||||
pub const SYN_MT_REPORT: c_int = 2;
|
||||
pub const SYN_DROPPED: c_int = 3;
|
||||
pub const SYN_MAX: c_int = 0xf;
|
||||
pub const SYN_CNT: c_int = SYN_MAX + 1;
|
||||
|
||||
/*
|
||||
* Keys and buttons
|
||||
*
|
||||
* Most of the keys/buttons are modeled after USB HUT 1.12 (see
|
||||
* http://www.usb.org/developers/hidpage).
|
||||
* Abbreviations in the comments:
|
||||
* AC - Application Control
|
||||
* AL - Application Launch Button
|
||||
* SC - System Control
|
||||
*/
|
||||
|
||||
pub const KEY_RESERVED: c_int = 0;
|
||||
pub const KEY_ESC: c_int = 1;
|
||||
pub const KEY_1: c_int = 2;
|
||||
pub const KEY_2: c_int = 3;
|
||||
pub const KEY_3: c_int = 4;
|
||||
pub const KEY_4: c_int = 5;
|
||||
pub const KEY_5: c_int = 6;
|
||||
pub const KEY_6: c_int = 7;
|
||||
pub const KEY_7: c_int = 8;
|
||||
pub const KEY_8: c_int = 9;
|
||||
pub const KEY_9: c_int = 10;
|
||||
pub const KEY_10: c_int = 11;
|
||||
pub const KEY_MINUS: c_int = 12;
|
||||
pub const KEY_EQUAL: c_int = 13;
|
||||
pub const KEY_BACKSPACE: c_int = 14;
|
||||
pub const KEY_TAB: c_int = 15;
|
||||
pub const KEY_Q: c_int = 16;
|
||||
pub const KEY_W: c_int = 17;
|
||||
pub const KEY_E: c_int = 18;
|
||||
pub const KEY_R: c_int = 19;
|
||||
pub const KEY_T: c_int = 20;
|
||||
pub const KEY_Y: c_int = 21;
|
||||
pub const KEY_U: c_int = 22;
|
||||
pub const KEY_I: c_int = 23;
|
||||
pub const KEY_O: c_int = 24;
|
||||
pub const KEY_P: c_int = 25;
|
||||
pub const KEY_LEFTBRACE: c_int = 26;
|
||||
pub const KEY_RIGHTBRACE: c_int = 27;
|
||||
pub const KEY_ENTER: c_int = 28;
|
||||
pub const KEY_LEFTCTRL: c_int = 29;
|
||||
pub const KEY_A: c_int = 30;
|
||||
pub const KEY_S: c_int = 31;
|
||||
pub const KEY_D: c_int = 32;
|
||||
pub const KEY_F: c_int = 33;
|
||||
pub const KEY_G: c_int = 34;
|
||||
pub const KEY_H: c_int = 35;
|
||||
pub const KEY_J: c_int = 36;
|
||||
pub const KEY_K: c_int = 37;
|
||||
pub const KEY_L: c_int = 38;
|
||||
pub const KEY_SEMICOLON: c_int = 39;
|
||||
pub const KEY_APOSTROPHE: c_int = 40;
|
||||
pub const KEY_GRAVE: c_int = 41;
|
||||
pub const KEY_LEFTSHIFT: c_int = 42;
|
||||
pub const KEY_BACKSLASH: c_int = 43;
|
||||
pub const KEY_Z: c_int = 44;
|
||||
pub const KEY_X: c_int = 45;
|
||||
pub const KEY_C: c_int = 46;
|
||||
pub const KEY_V: c_int = 47;
|
||||
pub const KEY_B: c_int = 48;
|
||||
pub const KEY_N: c_int = 49;
|
||||
pub const KEY_M: c_int = 50;
|
||||
pub const KEY_COMMA: c_int = 51;
|
||||
pub const KEY_DOT: c_int = 52;
|
||||
pub const KEY_SLASH: c_int = 53;
|
||||
pub const KEY_RIGHTSHIFT: c_int = 54;
|
||||
pub const KEY_KPASTERISK: c_int = 55;
|
||||
pub const KEY_LEFTALT: c_int = 56;
|
||||
pub const KEY_SPACE: c_int = 57;
|
||||
pub const KEY_CAPSLOCK: c_int = 58;
|
||||
pub const KEY_F1: c_int = 59;
|
||||
pub const KEY_F2: c_int = 60;
|
||||
pub const KEY_F3: c_int = 61;
|
||||
pub const KEY_F4: c_int = 62;
|
||||
pub const KEY_F5: c_int = 63;
|
||||
pub const KEY_F6: c_int = 64;
|
||||
pub const KEY_F7: c_int = 65;
|
||||
pub const KEY_F8: c_int = 66;
|
||||
pub const KEY_F9: c_int = 67;
|
||||
pub const KEY_F10: c_int = 68;
|
||||
pub const KEY_NUMLOCK: c_int = 69;
|
||||
pub const KEY_SCROLLLOCK: c_int = 70;
|
||||
pub const KEY_KP7: c_int = 71;
|
||||
pub const KEY_KP8: c_int = 72;
|
||||
pub const KEY_KP9: c_int = 73;
|
||||
pub const KEY_KPMINUS: c_int = 74;
|
||||
pub const KEY_KP4: c_int = 75;
|
||||
pub const KEY_KP5: c_int = 76;
|
||||
pub const KEY_KP6: c_int = 77;
|
||||
pub const KEY_KPPLUS: c_int = 78;
|
||||
pub const KEY_KP1: c_int = 79;
|
||||
pub const KEY_KP2: c_int = 80;
|
||||
pub const KEY_KP3: c_int = 81;
|
||||
pub const KEY_KP0: c_int = 82;
|
||||
pub const KEY_KPDOT: c_int = 83;
|
||||
|
||||
pub const KEY_ZENKAKUHANKAKU: c_int = 85;
|
||||
pub const KEY_102ND: c_int = 86;
|
||||
pub const KEY_F11: c_int = 87;
|
||||
pub const KEY_F12: c_int = 88;
|
||||
pub const KEY_RO: c_int = 89;
|
||||
pub const KEY_KATAKANA: c_int = 90;
|
||||
pub const KEY_HIRAGANA: c_int = 91;
|
||||
pub const KEY_HENKAN: c_int = 92;
|
||||
pub const KEY_KATAKANAHIRAGANA: c_int = 93;
|
||||
pub const KEY_MUHENKAN: c_int = 94;
|
||||
pub const KEY_KPJPCOMMA: c_int = 95;
|
||||
pub const KEY_KPENTER: c_int = 96;
|
||||
pub const KEY_RIGHTCTRL: c_int = 97;
|
||||
pub const KEY_KPSLASH: c_int = 98;
|
||||
pub const KEY_SYSRQ: c_int = 99;
|
||||
pub const KEY_RIGHTALT: c_int = 100;
|
||||
pub const KEY_LINEFEED: c_int = 101;
|
||||
pub const KEY_HOME: c_int = 102;
|
||||
pub const KEY_UP: c_int = 103;
|
||||
pub const KEY_PAGEUP: c_int = 104;
|
||||
pub const KEY_LEFT: c_int = 105;
|
||||
pub const KEY_RIGHT: c_int = 106;
|
||||
pub const KEY_END: c_int = 107;
|
||||
pub const KEY_DOWN: c_int = 108;
|
||||
pub const KEY_PAGEDOWN: c_int = 109;
|
||||
pub const KEY_INSERT: c_int = 110;
|
||||
pub const KEY_DELETE: c_int = 111;
|
||||
pub const KEY_MACRO: c_int = 112;
|
||||
pub const KEY_MUTE: c_int = 113;
|
||||
pub const KEY_VOLUMEDOWN: c_int = 114;
|
||||
pub const KEY_VOLUMEUP: c_int = 115;
|
||||
pub const KEY_POWER: c_int = 116; /* SC System Power Down */
|
||||
pub const KEY_KPEQUAL: c_int = 117;
|
||||
pub const KEY_KPPLUSMINUS: c_int = 118;
|
||||
pub const KEY_PAUSE: c_int = 119;
|
||||
pub const KEY_SCALE: c_int = 120; /* AL Compiz Scale : c_int = Expose */
|
||||
|
||||
pub const KEY_KPCOMMA: c_int = 121;
|
||||
pub const KEY_HANGEUL: c_int = 122;
|
||||
pub const KEY_HANGUEL: c_int = KEY_HANGEUL;
|
||||
pub const KEY_HANJA: c_int = 123;
|
||||
pub const KEY_YEN: c_int = 124;
|
||||
pub const KEY_LEFTMETA: c_int = 125;
|
||||
pub const KEY_RIGHTMETA: c_int = 126;
|
||||
pub const KEY_COMPOSE: c_int = 127;
|
||||
|
||||
pub const KEY_STOP: c_int = 128; /* AC Stop */
|
||||
pub const KEY_AGAIN: c_int = 129;
|
||||
pub const KEY_PROPS: c_int = 130; /* AC Properties */
|
||||
pub const KEY_UNDO: c_int = 131; /* AC Undo */
|
||||
pub const KEY_FRONT: c_int = 132;
|
||||
pub const KEY_COPY: c_int = 133; /* AC Copy */
|
||||
pub const KEY_OPEN: c_int = 134; /* AC Open */
|
||||
pub const KEY_PASTE: c_int = 135; /* AC Paste */
|
||||
pub const KEY_FIND: c_int = 136; /* AC Search */
|
||||
pub const KEY_CUT: c_int = 137; /* AC Cut */
|
||||
pub const KEY_HELP: c_int = 138; /* AL Integrated Help Center */
|
||||
pub const KEY_MENU: c_int = 139; /* Menu : c_int = show menu */
|
||||
pub const KEY_CALC: c_int = 140; /* AL Calculator */
|
||||
pub const KEY_SETUP: c_int = 141;
|
||||
pub const KEY_SLEEP: c_int = 142; /* SC System Sleep */
|
||||
pub const KEY_WAKEUP: c_int = 143; /* System Wake Up */
|
||||
pub const KEY_FILE: c_int = 144; /* AL Local Machine Browser */
|
||||
pub const KEY_SENDFILE: c_int = 145;
|
||||
pub const KEY_DELETEFILE: c_int = 146;
|
||||
pub const KEY_XFER: c_int = 147;
|
||||
pub const KEY_PROG1: c_int = 148;
|
||||
pub const KEY_PROG2: c_int = 149;
|
||||
pub const KEY_WWW: c_int = 150; /* AL Internet Browser */
|
||||
pub const KEY_MSDOS: c_int = 151;
|
||||
pub const KEY_COFFEE: c_int = 152; /* AL Terminal Lock/Screensaver */
|
||||
pub const KEY_SCREENLOCK: c_int = KEY_COFFEE;
|
||||
pub const KEY_ROTATE_DISPLAY: c_int = 153; /* Display orientation for e.g. tablets */
|
||||
pub const KEY_DIRECTION: c_int = KEY_ROTATE_DISPLAY;
|
||||
pub const KEY_CYCLEWINDOWS: c_int = 154;
|
||||
pub const KEY_MAIL: c_int = 155;
|
||||
pub const KEY_BOOKMARKS: c_int = 156; /* AC Bookmarks */
|
||||
pub const KEY_COMPUTER: c_int = 157;
|
||||
pub const KEY_BACK: c_int = 158; /* AC Back */
|
||||
pub const KEY_FORWARD: c_int = 159; /* AC Forward */
|
||||
pub const KEY_CLOSECD: c_int = 160;
|
||||
pub const KEY_EJECTCD: c_int = 161;
|
||||
pub const KEY_EJECTCLOSECD: c_int = 162;
|
||||
pub const KEY_NEXTSONG: c_int = 163;
|
||||
pub const KEY_PLAYPAUSE: c_int = 164;
|
||||
pub const KEY_PREVIOUSSONG: c_int = 165;
|
||||
pub const KEY_STOPCD: c_int = 166;
|
||||
pub const KEY_RECORD: c_int = 167;
|
||||
pub const KEY_REWIND: c_int = 168;
|
||||
pub const KEY_PHONE: c_int = 169; /* Media Select Telephone */
|
||||
pub const KEY_ISO: c_int = 170;
|
||||
pub const KEY_CONFIG: c_int = 171; /* AL Consumer Control Configuration */
|
||||
pub const KEY_HOMEPAGE: c_int = 172; /* AC Home */
|
||||
pub const KEY_REFRESH: c_int = 173; /* AC Refresh */
|
||||
pub const KEY_EXIT: c_int = 174; /* AC Exit */
|
||||
pub const KEY_MOVE: c_int = 175;
|
||||
pub const KEY_EDIT: c_int = 176;
|
||||
pub const KEY_SCROLLUP: c_int = 177;
|
||||
pub const KEY_SCROLLDOWN: c_int = 178;
|
||||
pub const KEY_KPLEFTPAREN: c_int = 179;
|
||||
pub const KEY_KPRIGHTPAREN: c_int = 180;
|
||||
pub const KEY_NEW: c_int = 181; /* AC New */
|
||||
pub const KEY_REDO: c_int = 182; /* AC Redo/Repeat */
|
||||
|
||||
pub const KEY_F13: c_int = 183;
|
||||
pub const KEY_F14: c_int = 184;
|
||||
pub const KEY_F15: c_int = 185;
|
||||
pub const KEY_F16: c_int = 186;
|
||||
pub const KEY_F17: c_int = 187;
|
||||
pub const KEY_F18: c_int = 188;
|
||||
pub const KEY_F19: c_int = 189;
|
||||
pub const KEY_F20: c_int = 190;
|
||||
pub const KEY_F21: c_int = 191;
|
||||
pub const KEY_F22: c_int = 192;
|
||||
pub const KEY_F23: c_int = 193;
|
||||
pub const KEY_F24: c_int = 194;
|
||||
|
||||
pub const KEY_PLAYCD: c_int = 200;
|
||||
pub const KEY_PAUSECD: c_int = 201;
|
||||
pub const KEY_PROG3: c_int = 202;
|
||||
pub const KEY_PROG4: c_int = 203;
|
||||
pub const KEY_DASHBOARD: c_int = 204; /* AL Dashboard */
|
||||
pub const KEY_SUSPEND: c_int = 205;
|
||||
pub const KEY_CLOSE: c_int = 206; /* AC Close */
|
||||
pub const KEY_PLAY: c_int = 207;
|
||||
pub const KEY_FASTFORWARD: c_int = 208;
|
||||
pub const KEY_BASSBOOST: c_int = 209;
|
||||
pub const KEY_PRINT: c_int = 210; /* AC Print */
|
||||
pub const KEY_HP: c_int = 211;
|
||||
pub const KEY_CAMERA: c_int = 212;
|
||||
pub const KEY_SOUND: c_int = 213;
|
||||
pub const KEY_QUESTION: c_int = 214;
|
||||
pub const KEY_EMAIL: c_int = 215;
|
||||
pub const KEY_CHAT: c_int = 216;
|
||||
pub const KEY_SEARCH: c_int = 217;
|
||||
pub const KEY_CONNECT: c_int = 218;
|
||||
pub const KEY_FINANCE: c_int = 219; /* AL Checkbook/Finance */
|
||||
pub const KEY_SPORT: c_int = 220;
|
||||
pub const KEY_SHOP: c_int = 221;
|
||||
pub const KEY_ALTERASE: c_int = 222;
|
||||
pub const KEY_CANCEL: c_int = 223; /* AC Cancel */
|
||||
pub const KEY_BRIGHTNESSDOWN: c_int = 224;
|
||||
pub const KEY_BRIGHTNESSUP: c_int = 225;
|
||||
pub const KEY_MEDIA: c_int = 226;
|
||||
|
||||
pub const KEY_SWITCHVIDEOMODE: c_int = 227; /* Cycle between available video outputs (Monitor/LCD/TV-out/etc) */
|
||||
pub const KEY_KBDILLUMTOGGLE: c_int = 228;
|
||||
pub const KEY_KBDILLUMDOWN: c_int = 229;
|
||||
pub const KEY_KBDILLUMUP: c_int = 230;
|
||||
|
||||
pub const KEY_SEND: c_int = 231; /* AC Send */
|
||||
pub const KEY_REPLY: c_int = 232; /* AC Reply */
|
||||
pub const KEY_FORWARDMAIL: c_int = 233; /* AC Forward Msg */
|
||||
pub const KEY_SAVE: c_int = 234; /* AC Save */
|
||||
pub const KEY_DOCUMENTS: c_int = 235;
|
||||
|
||||
pub const KEY_BATTERY: c_int = 236;
|
||||
|
||||
pub const KEY_BLUETOOTH: c_int = 237;
|
||||
pub const KEY_WLAN: c_int = 238;
|
||||
pub const KEY_UWB: c_int = 239;
|
||||
|
||||
pub const KEY_UNKNOWN: c_int = 240;
|
||||
|
||||
pub const KEY_VIDEO_NEXT: c_int = 241; /* drive next video source */
|
||||
pub const KEY_VIDEO_PREV: c_int = 242; /* drive previous video source */
|
||||
pub const KEY_BRIGHTNESS_CYCLE: c_int = 243; /* brightness up, after max is min */
|
||||
pub const KEY_BRIGHTNESS_AUTO: c_int = 244; /* Set Auto Brightness: manual brightness control is off, rely on ambient */
|
||||
pub const KEY_BRIGHTNESS_ZERO: c_int = KEY_BRIGHTNESS_AUTO;
|
||||
pub const KEY_DISPLAY_OFF: c_int = 245; /* display device to off state */
|
||||
|
||||
pub const KEY_WWAN: c_int = 246; /* Wireless WAN : c_int = LTE, UMTS, GSM, etc. */
|
||||
pub const KEY_WIMAX: c_int = KEY_WWAN;
|
||||
pub const KEY_RFKILL: c_int = 247; /* Key that controls all radios */
|
||||
|
||||
pub const KEY_MICMUTE: c_int = 248; /* Mute / unmute the microphone */
|
||||
|
||||
/* Code 255 is reserved for special needs of AT keyboard driver */
|
||||
|
||||
pub const BTN_MISC: c_int = 0x100;
|
||||
pub const BTN_0: c_int = 0x100;
|
||||
pub const BTN_1: c_int = 0x101;
|
||||
pub const BTN_2: c_int = 0x102;
|
||||
pub const BTN_3: c_int = 0x103;
|
||||
pub const BTN_4: c_int = 0x104;
|
||||
pub const BTN_5: c_int = 0x105;
|
||||
pub const BTN_6: c_int = 0x106;
|
||||
pub const BTN_7: c_int = 0x107;
|
||||
pub const BTN_8: c_int = 0x108;
|
||||
pub const BTN_9: c_int = 0x109;
|
||||
|
||||
pub const BTN_MOUSE: c_int = 0x110;
|
||||
pub const BTN_LEFT: c_int = 0x110;
|
||||
pub const BTN_RIGHT: c_int = 0x111;
|
||||
pub const BTN_MIDDLE: c_int = 0x112;
|
||||
pub const BTN_SIDE: c_int = 0x113;
|
||||
pub const BTN_EXTRA: c_int = 0x114;
|
||||
pub const BTN_FORWARD: c_int = 0x115;
|
||||
pub const BTN_BACK: c_int = 0x116;
|
||||
pub const BTN_TASK: c_int = 0x117;
|
||||
|
||||
pub const BTN_JOYSTICK: c_int = 0x120;
|
||||
pub const BTN_TRIGGER: c_int = 0x120;
|
||||
pub const BTN_THUMB: c_int = 0x121;
|
||||
pub const BTN_THUMB2: c_int = 0x122;
|
||||
pub const BTN_TOP: c_int = 0x123;
|
||||
pub const BTN_TOP2: c_int = 0x124;
|
||||
pub const BTN_PINKIE: c_int = 0x125;
|
||||
pub const BTN_BASE: c_int = 0x126;
|
||||
pub const BTN_BASE2: c_int = 0x127;
|
||||
pub const BTN_BASE3: c_int = 0x128;
|
||||
pub const BTN_BASE4: c_int = 0x129;
|
||||
pub const BTN_BASE5: c_int = 0x12a;
|
||||
pub const BTN_BASE6: c_int = 0x12b;
|
||||
pub const BTN_DEAD: c_int = 0x12f;
|
||||
|
||||
pub const BTN_GAMEPAD: c_int = 0x130;
|
||||
pub const BTN_SOUTH: c_int = 0x130;
|
||||
pub const BTN_A: c_int = BTN_SOUTH;
|
||||
pub const BTN_EAST: c_int = 0x131;
|
||||
pub const BTN_B: c_int = BTN_EAST;
|
||||
pub const BTN_C: c_int = 0x132;
|
||||
pub const BTN_NORTH: c_int = 0x133;
|
||||
pub const BTN_X: c_int = BTN_NORTH;
|
||||
pub const BTN_WEST: c_int = 0x134;
|
||||
pub const BTN_Y: c_int = BTN_WEST;
|
||||
pub const BTN_Z: c_int = 0x135;
|
||||
pub const BTN_TL: c_int = 0x136;
|
||||
pub const BTN_TR: c_int = 0x137;
|
||||
pub const BTN_TL2: c_int = 0x138;
|
||||
pub const BTN_TR2: c_int = 0x139;
|
||||
pub const BTN_SELECT: c_int = 0x13a;
|
||||
pub const BTN_START: c_int = 0x13b;
|
||||
pub const BTN_MODE: c_int = 0x13c;
|
||||
pub const BTN_THUMBL: c_int = 0x13d;
|
||||
pub const BTN_THUMBR: c_int = 0x13e;
|
||||
|
||||
pub const BTN_DIGI: c_int = 0x140;
|
||||
pub const BTN_TOOL_PEN: c_int = 0x140;
|
||||
pub const BTN_TOOL_RUBBER: c_int = 0x141;
|
||||
pub const BTN_TOOL_BRUSH: c_int = 0x142;
|
||||
pub const BTN_TOOL_PENCIL: c_int = 0x143;
|
||||
pub const BTN_TOOL_AIRBRUSH: c_int = 0x144;
|
||||
pub const BTN_TOOL_FINGER: c_int = 0x145;
|
||||
pub const BTN_TOOL_MOUSE: c_int = 0x146;
|
||||
pub const BTN_TOOL_LENS: c_int = 0x147;
|
||||
pub const BTN_TOOL_QUINTTAP: c_int = 0x148; /* Five fingers on trackpad */
|
||||
pub const BTN_TOUCH: c_int = 0x14a;
|
||||
pub const BTN_STYLUS: c_int = 0x14b;
|
||||
pub const BTN_STYLUS2: c_int = 0x14c;
|
||||
pub const BTN_TOOL_DOUBLETAP: c_int = 0x14d;
|
||||
pub const BTN_TOOL_TRIPLETAP: c_int = 0x14e;
|
||||
pub const BTN_TOOL_QUADTAP: c_int = 0x14f; /* Four fingers on trackpad */
|
||||
|
||||
pub const BTN_WHEEL: c_int = 0x150;
|
||||
pub const BTN_GEAR_DOWN: c_int = 0x150;
|
||||
pub const BTN_GEAR_UP: c_int = 0x151;
|
||||
|
||||
pub const KEY_OK: c_int = 0x160;
|
||||
pub const KEY_SELECT: c_int = 0x161;
|
||||
pub const KEY_GOTO: c_int = 0x162;
|
||||
pub const KEY_CLEAR: c_int = 0x163;
|
||||
pub const KEY_POWER2: c_int = 0x164;
|
||||
pub const KEY_OPTION: c_int = 0x165;
|
||||
pub const KEY_INFO: c_int = 0x166; /* AL OEM Features/Tips/Tutorial */
|
||||
pub const KEY_TIME: c_int = 0x167;
|
||||
pub const KEY_VENDOR: c_int = 0x168;
|
||||
pub const KEY_ARCHIVE: c_int = 0x169;
|
||||
pub const KEY_PROGRAM: c_int = 0x16a; /* Media Select Program Guide */
|
||||
pub const KEY_CHANNEL: c_int = 0x16b;
|
||||
pub const KEY_FAVORITES: c_int = 0x16c;
|
||||
pub const KEY_EPG: c_int = 0x16d;
|
||||
pub const KEY_PVR: c_int = 0x16e; /* Media Select Home */
|
||||
pub const KEY_MHP: c_int = 0x16f;
|
||||
pub const KEY_LANGUAGE: c_int = 0x170;
|
||||
pub const KEY_TITLE: c_int = 0x171;
|
||||
pub const KEY_SUBTITLE: c_int = 0x172;
|
||||
pub const KEY_ANGLE: c_int = 0x173;
|
||||
pub const KEY_ZOOM: c_int = 0x174;
|
||||
pub const KEY_MODE: c_int = 0x175;
|
||||
pub const KEY_KEYBOARD: c_int = 0x176;
|
||||
pub const KEY_SCREEN: c_int = 0x177;
|
||||
pub const KEY_PC: c_int = 0x178; /* Media Select Computer */
|
||||
pub const KEY_TV: c_int = 0x179; /* Media Select TV */
|
||||
pub const KEY_TV2: c_int = 0x17a; /* Media Select Cable */
|
||||
pub const KEY_VCR: c_int = 0x17b; /* Media Select VCR */
|
||||
pub const KEY_VCR2: c_int = 0x17c; /* VCR Plus */
|
||||
pub const KEY_SAT: c_int = 0x17d; /* Media Select Satellite */
|
||||
pub const KEY_SAT2: c_int = 0x17e;
|
||||
pub const KEY_CD: c_int = 0x17f; /* Media Select CD */
|
||||
pub const KEY_TAPE: c_int = 0x180; /* Media Select Tape */
|
||||
pub const KEY_RADIO: c_int = 0x181;
|
||||
pub const KEY_TUNER: c_int = 0x182; /* Media Select Tuner */
|
||||
pub const KEY_PLAYER: c_int = 0x183;
|
||||
pub const KEY_TEXT: c_int = 0x184;
|
||||
pub const KEY_DVD: c_int = 0x185; /* Media Select DVD */
|
||||
pub const KEY_AUX: c_int = 0x186;
|
||||
pub const KEY_MP3: c_int = 0x187;
|
||||
pub const KEY_AUDIO: c_int = 0x188; /* AL Audio Browser */
|
||||
pub const KEY_VIDEO: c_int = 0x189; /* AL Movie Browser */
|
||||
pub const KEY_DIRECTORY: c_int = 0x18a;
|
||||
pub const KEY_LIST: c_int = 0x18b;
|
||||
pub const KEY_MEMO: c_int = 0x18c; /* Media Select Messages */
|
||||
pub const KEY_CALENDAR: c_int = 0x18d;
|
||||
pub const KEY_RED: c_int = 0x18e;
|
||||
pub const KEY_GREEN: c_int = 0x18f;
|
||||
pub const KEY_YELLOW: c_int = 0x190;
|
||||
pub const KEY_BLUE: c_int = 0x191;
|
||||
pub const KEY_CHANNELUP: c_int = 0x192; /* Channel Increment */
|
||||
pub const KEY_CHANNELDOWN: c_int = 0x193; /* Channel Decrement */
|
||||
pub const KEY_FIRST: c_int = 0x194;
|
||||
pub const KEY_LAST: c_int = 0x195; /* Recall Last */
|
||||
pub const KEY_AB: c_int = 0x196;
|
||||
pub const KEY_NEXT: c_int = 0x197;
|
||||
pub const KEY_RESTART: c_int = 0x198;
|
||||
pub const KEY_SLOW: c_int = 0x199;
|
||||
pub const KEY_SHUFFLE: c_int = 0x19a;
|
||||
pub const KEY_BREAK: c_int = 0x19b;
|
||||
pub const KEY_PREVIOUS: c_int = 0x19c;
|
||||
pub const KEY_DIGITS: c_int = 0x19d;
|
||||
pub const KEY_TEEN: c_int = 0x19e;
|
||||
pub const KEY_TWEN: c_int = 0x19f;
|
||||
pub const KEY_VIDEOPHONE: c_int = 0x1a0; /* Media Select Video Phone */
|
||||
pub const KEY_GAMES: c_int = 0x1a1; /* Media Select Games */
|
||||
pub const KEY_ZOOMIN: c_int = 0x1a2; /* AC Zoom In */
|
||||
pub const KEY_ZOOMOUT: c_int = 0x1a3; /* AC Zoom Out */
|
||||
pub const KEY_ZOOMRESET: c_int = 0x1a4; /* AC Zoom */
|
||||
pub const KEY_WORDPROCESSOR: c_int = 0x1a5; /* AL Word Processor */
|
||||
pub const KEY_EDITOR: c_int = 0x1a6; /* AL Text Editor */
|
||||
pub const KEY_SPREADSHEET: c_int = 0x1a7; /* AL Spreadsheet */
|
||||
pub const KEY_GRAPHICSEDITOR: c_int = 0x1a8; /* AL Graphics Editor */
|
||||
pub const KEY_PRESENTATION: c_int = 0x1a9; /* AL Presentation App */
|
||||
pub const KEY_DATABASE: c_int = 0x1aa; /* AL Database App */
|
||||
pub const KEY_NEWS: c_int = 0x1ab; /* AL Newsreader */
|
||||
pub const KEY_VOICEMAIL: c_int = 0x1ac; /* AL Voicemail */
|
||||
pub const KEY_ADDRESSBOOK: c_int = 0x1ad; /* AL Contacts/Address Book */
|
||||
pub const KEY_MESSENGER: c_int = 0x1ae; /* AL Instant Messaging */
|
||||
pub const KEY_DISPLAYTOGGLE: c_int = 0x1af; /* Turn display : c_int = LCD on and off */
|
||||
pub const KEY_BRIGHTNESS_TOGGLE: c_int = KEY_DISPLAYTOGGLE;
|
||||
pub const KEY_SPELLCHECK: c_int = 0x1b0; /* AL Spell Check */
|
||||
pub const KEY_LOGOFF: c_int = 0x1b1; /* AL Logoff */
|
||||
|
||||
pub const KEY_DOLLAR: c_int = 0x1b2;
|
||||
pub const KEY_EURO: c_int = 0x1b3;
|
||||
|
||||
pub const KEY_FRAMEBACK: c_int = 0x1b4; /* Consumer - transport controls */
|
||||
pub const KEY_FRAMEFORWARD: c_int = 0x1b5;
|
||||
pub const KEY_CONTEXT_MENU: c_int = 0x1b6; /* GenDesc - system context menu */
|
||||
pub const KEY_MEDIA_REPEAT: c_int = 0x1b7; /* Consumer - transport control */
|
||||
pub const KEY_10CHANNELSUP: c_int = 0x1b8; /* 10 channels up : c_int = 10+ */
|
||||
pub const KEY_10CHANNELSDOWN: c_int = 0x1b9; /* 10 channels down : c_int = 10- */
|
||||
pub const KEY_IMAGES: c_int = 0x1ba; /* AL Image Browser */
|
||||
|
||||
pub const KEY_DEL_EOL: c_int = 0x1c0;
|
||||
pub const KEY_DEL_EOS: c_int = 0x1c1;
|
||||
pub const KEY_INS_LINE: c_int = 0x1c2;
|
||||
pub const KEY_DEL_LINE: c_int = 0x1c3;
|
||||
|
||||
pub const KEY_FN: c_int = 0x1d0;
|
||||
pub const KEY_FN_ESC: c_int = 0x1d1;
|
||||
pub const KEY_FN_F1: c_int = 0x1d2;
|
||||
pub const KEY_FN_F2: c_int = 0x1d3;
|
||||
pub const KEY_FN_F3: c_int = 0x1d4;
|
||||
pub const KEY_FN_F4: c_int = 0x1d5;
|
||||
pub const KEY_FN_F5: c_int = 0x1d6;
|
||||
pub const KEY_FN_F6: c_int = 0x1d7;
|
||||
pub const KEY_FN_F7: c_int = 0x1d8;
|
||||
pub const KEY_FN_F8: c_int = 0x1d9;
|
||||
pub const KEY_FN_F9: c_int = 0x1da;
|
||||
pub const KEY_FN_F10: c_int = 0x1db;
|
||||
pub const KEY_FN_F11: c_int = 0x1dc;
|
||||
pub const KEY_FN_F12: c_int = 0x1dd;
|
||||
pub const KEY_FN_1: c_int = 0x1de;
|
||||
pub const KEY_FN_2: c_int = 0x1df;
|
||||
pub const KEY_FN_D: c_int = 0x1e0;
|
||||
pub const KEY_FN_E: c_int = 0x1e1;
|
||||
pub const KEY_FN_F: c_int = 0x1e2;
|
||||
pub const KEY_FN_S: c_int = 0x1e3;
|
||||
pub const KEY_FN_B: c_int = 0x1e4;
|
||||
|
||||
pub const KEY_BRL_DOT1: c_int = 0x1f1;
|
||||
pub const KEY_BRL_DOT2: c_int = 0x1f2;
|
||||
pub const KEY_BRL_DOT3: c_int = 0x1f3;
|
||||
pub const KEY_BRL_DOT4: c_int = 0x1f4;
|
||||
pub const KEY_BRL_DOT5: c_int = 0x1f5;
|
||||
pub const KEY_BRL_DOT6: c_int = 0x1f6;
|
||||
pub const KEY_BRL_DOT7: c_int = 0x1f7;
|
||||
pub const KEY_BRL_DOT8: c_int = 0x1f8;
|
||||
pub const KEY_BRL_DOT9: c_int = 0x1f9;
|
||||
pub const KEY_BRL_DOT10: c_int = 0x1fa;
|
||||
|
||||
pub const KEY_NUMERIC_0: c_int = 0x200; /* used by phones, remote controls, */
|
||||
pub const KEY_NUMERIC_1: c_int = 0x201; /* and other keypads */
|
||||
pub const KEY_NUMERIC_2: c_int = 0x202;
|
||||
pub const KEY_NUMERIC_3: c_int = 0x203;
|
||||
pub const KEY_NUMERIC_4: c_int = 0x204;
|
||||
pub const KEY_NUMERIC_5: c_int = 0x205;
|
||||
pub const KEY_NUMERIC_6: c_int = 0x206;
|
||||
pub const KEY_NUMERIC_7: c_int = 0x207;
|
||||
pub const KEY_NUMERIC_8: c_int = 0x208;
|
||||
pub const KEY_NUMERIC_9: c_int = 0x209;
|
||||
pub const KEY_NUMERIC_STAR: c_int = 0x20a;
|
||||
pub const KEY_NUMERIC_POUND: c_int = 0x20b;
|
||||
pub const KEY_NUMERIC_A: c_int = 0x20c; /* Phone key A - HUT Telephony 0xb9 */
|
||||
pub const KEY_NUMERIC_B: c_int = 0x20d;
|
||||
pub const KEY_NUMERIC_C: c_int = 0x20e;
|
||||
pub const KEY_NUMERIC_D: c_int = 0x20f;
|
||||
|
||||
pub const KEY_CAMERA_FOCUS: c_int = 0x210;
|
||||
pub const KEY_WPS_BUTTON: c_int = 0x211; /* WiFi Protected Setup key */
|
||||
|
||||
pub const KEY_TOUCHPAD_TOGGLE: c_int = 0x212; /* Request switch touchpad on or off */
|
||||
pub const KEY_TOUCHPAD_ON: c_int = 0x213;
|
||||
pub const KEY_TOUCHPAD_OFF: c_int = 0x214;
|
||||
|
||||
pub const KEY_CAMERA_ZOOMIN: c_int = 0x215;
|
||||
pub const KEY_CAMERA_ZOOMOUT: c_int = 0x216;
|
||||
pub const KEY_CAMERA_UP: c_int = 0x217;
|
||||
pub const KEY_CAMERA_DOWN: c_int = 0x218;
|
||||
pub const KEY_CAMERA_LEFT: c_int = 0x219;
|
||||
pub const KEY_CAMERA_RIGHT: c_int = 0x21a;
|
||||
|
||||
pub const KEY_ATTENDANT_ON: c_int = 0x21b;
|
||||
pub const KEY_ATTENDANT_OFF: c_int = 0x21c;
|
||||
pub const KEY_ATTENDANT_TOGGLE: c_int = 0x21d; /* Attendant call on or off */
|
||||
pub const KEY_LIGHTS_TOGGLE: c_int = 0x21e; /* Reading light on or off */
|
||||
|
||||
pub const BTN_DPAD_UP: c_int = 0x220;
|
||||
pub const BTN_DPAD_DOWN: c_int = 0x221;
|
||||
pub const BTN_DPAD_LEFT: c_int = 0x222;
|
||||
pub const BTN_DPAD_RIGHT: c_int = 0x223;
|
||||
|
||||
pub const KEY_ALS_TOGGLE: c_int = 0x230; /* Ambient light sensor */
|
||||
|
||||
pub const KEY_BUTTONCONFIG: c_int = 0x240; /* AL Button Configuration */
|
||||
pub const KEY_TASKMANAGER: c_int = 0x241; /* AL Task/Project Manager */
|
||||
pub const KEY_JOURNAL: c_int = 0x242; /* AL Log/Journal/Timecard */
|
||||
pub const KEY_CONTROLPANEL: c_int = 0x243; /* AL Control Panel */
|
||||
pub const KEY_APPSELECT: c_int = 0x244; /* AL Select Task/Application */
|
||||
pub const KEY_SCREENSAVER: c_int = 0x245; /* AL Screen Saver */
|
||||
pub const KEY_VOICECOMMAND: c_int = 0x246; /* Listening Voice Command */
|
||||
|
||||
pub const KEY_BRIGHTNESS_MIN: c_int = 0x250; /* Set Brightness to Minimum */
|
||||
pub const KEY_BRIGHTNESS_MAX: c_int = 0x251; /* Set Brightness to Maximum */
|
||||
|
||||
pub const KEY_KBDINPUTASSIST_PREV: c_int = 0x260;
|
||||
pub const KEY_KBDINPUTASSIST_NEXT: c_int = 0x261;
|
||||
pub const KEY_KBDINPUTASSIST_PREVGROUP: c_int = 0x262;
|
||||
pub const KEY_KBDINPUTASSIST_NEXTGROUP: c_int = 0x263;
|
||||
pub const KEY_KBDINPUTASSIST_ACCEPT: c_int = 0x264;
|
||||
pub const KEY_KBDINPUTASSIST_CANCEL: c_int = 0x265;
|
||||
|
||||
pub const BTN_TRIGGER_HAPPY: c_int = 0x2c0;
|
||||
pub const BTN_TRIGGER_HAPPY1: c_int = 0x2c0;
|
||||
pub const BTN_TRIGGER_HAPPY2: c_int = 0x2c1;
|
||||
pub const BTN_TRIGGER_HAPPY3: c_int = 0x2c2;
|
||||
pub const BTN_TRIGGER_HAPPY4: c_int = 0x2c3;
|
||||
pub const BTN_TRIGGER_HAPPY5: c_int = 0x2c4;
|
||||
pub const BTN_TRIGGER_HAPPY6: c_int = 0x2c5;
|
||||
pub const BTN_TRIGGER_HAPPY7: c_int = 0x2c6;
|
||||
pub const BTN_TRIGGER_HAPPY8: c_int = 0x2c7;
|
||||
pub const BTN_TRIGGER_HAPPY9: c_int = 0x2c8;
|
||||
pub const BTN_TRIGGER_HAPPY10: c_int = 0x2c9;
|
||||
pub const BTN_TRIGGER_HAPPY11: c_int = 0x2ca;
|
||||
pub const BTN_TRIGGER_HAPPY12: c_int = 0x2cb;
|
||||
pub const BTN_TRIGGER_HAPPY13: c_int = 0x2cc;
|
||||
pub const BTN_TRIGGER_HAPPY14: c_int = 0x2cd;
|
||||
pub const BTN_TRIGGER_HAPPY15: c_int = 0x2ce;
|
||||
pub const BTN_TRIGGER_HAPPY16: c_int = 0x2cf;
|
||||
pub const BTN_TRIGGER_HAPPY17: c_int = 0x2d0;
|
||||
pub const BTN_TRIGGER_HAPPY18: c_int = 0x2d1;
|
||||
pub const BTN_TRIGGER_HAPPY19: c_int = 0x2d2;
|
||||
pub const BTN_TRIGGER_HAPPY20: c_int = 0x2d3;
|
||||
pub const BTN_TRIGGER_HAPPY21: c_int = 0x2d4;
|
||||
pub const BTN_TRIGGER_HAPPY22: c_int = 0x2d5;
|
||||
pub const BTN_TRIGGER_HAPPY23: c_int = 0x2d6;
|
||||
pub const BTN_TRIGGER_HAPPY24: c_int = 0x2d7;
|
||||
pub const BTN_TRIGGER_HAPPY25: c_int = 0x2d8;
|
||||
pub const BTN_TRIGGER_HAPPY26: c_int = 0x2d9;
|
||||
pub const BTN_TRIGGER_HAPPY27: c_int = 0x2da;
|
||||
pub const BTN_TRIGGER_HAPPY28: c_int = 0x2db;
|
||||
pub const BTN_TRIGGER_HAPPY29: c_int = 0x2dc;
|
||||
pub const BTN_TRIGGER_HAPPY30: c_int = 0x2dd;
|
||||
pub const BTN_TRIGGER_HAPPY31: c_int = 0x2de;
|
||||
pub const BTN_TRIGGER_HAPPY32: c_int = 0x2df;
|
||||
pub const BTN_TRIGGER_HAPPY33: c_int = 0x2e0;
|
||||
pub const BTN_TRIGGER_HAPPY34: c_int = 0x2e1;
|
||||
pub const BTN_TRIGGER_HAPPY35: c_int = 0x2e2;
|
||||
pub const BTN_TRIGGER_HAPPY36: c_int = 0x2e3;
|
||||
pub const BTN_TRIGGER_HAPPY37: c_int = 0x2e4;
|
||||
pub const BTN_TRIGGER_HAPPY38: c_int = 0x2e5;
|
||||
pub const BTN_TRIGGER_HAPPY39: c_int = 0x2e6;
|
||||
pub const BTN_TRIGGER_HAPPY40: c_int = 0x2e7;
|
||||
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
pub const KEY_MIN_INTERESTING: c_int = KEY_MUTE;
|
||||
pub const KEY_MAX: c_int = 0x2ff;
|
||||
pub const KEY_CNT: c_int = KEY_MAX + 1;
|
||||
|
||||
/*
|
||||
* Relative axes
|
||||
*/
|
||||
|
||||
pub const REL_X: c_int = 0x00;
|
||||
pub const REL_Y: c_int = 0x01;
|
||||
pub const REL_Z: c_int = 0x02;
|
||||
pub const REL_RX: c_int = 0x03;
|
||||
pub const REL_RY: c_int = 0x04;
|
||||
pub const REL_RZ: c_int = 0x05;
|
||||
pub const REL_HWHEEL: c_int = 0x06;
|
||||
pub const REL_DIAL: c_int = 0x07;
|
||||
pub const REL_WHEEL: c_int = 0x08;
|
||||
pub const REL_MISC: c_int = 0x09;
|
||||
pub const REL_MAX: c_int = 0x0f;
|
||||
pub const REL_CNT: c_int = REL_MAX + 1;
|
||||
|
||||
/*
|
||||
* Absolute axes
|
||||
*/
|
||||
|
||||
pub const ABS_X: c_int = 0x00;
|
||||
pub const ABS_Y: c_int = 0x01;
|
||||
pub const ABS_Z: c_int = 0x02;
|
||||
pub const ABS_RX: c_int = 0x03;
|
||||
pub const ABS_RY: c_int = 0x04;
|
||||
pub const ABS_RZ: c_int = 0x05;
|
||||
pub const ABS_THROTTLE: c_int = 0x06;
|
||||
pub const ABS_RUDDER: c_int = 0x07;
|
||||
pub const ABS_WHEEL: c_int = 0x08;
|
||||
pub const ABS_GAS: c_int = 0x09;
|
||||
pub const ABS_BRAKE: c_int = 0x0a;
|
||||
pub const ABS_HAT0X: c_int = 0x10;
|
||||
pub const ABS_HAT0Y: c_int = 0x11;
|
||||
pub const ABS_HAT1X: c_int = 0x12;
|
||||
pub const ABS_HAT1Y: c_int = 0x13;
|
||||
pub const ABS_HAT2X: c_int = 0x14;
|
||||
pub const ABS_HAT2Y: c_int = 0x15;
|
||||
pub const ABS_HAT3X: c_int = 0x16;
|
||||
pub const ABS_HAT3Y: c_int = 0x17;
|
||||
pub const ABS_PRESSURE: c_int = 0x18;
|
||||
pub const ABS_DISTANCE: c_int = 0x19;
|
||||
pub const ABS_TILT_X: c_int = 0x1a;
|
||||
pub const ABS_TILT_Y: c_int = 0x1b;
|
||||
pub const ABS_TOOL_WIDTH: c_int = 0x1c;
|
||||
|
||||
pub const ABS_VOLUME: c_int = 0x20;
|
||||
|
||||
pub const ABS_MISC: c_int = 0x28;
|
||||
|
||||
pub const ABS_MT_SLOT: c_int = 0x2f; /* MT slot being modified */
|
||||
pub const ABS_MT_TOUCH_MAJOR: c_int = 0x30; /* Major axis of touching ellipse */
|
||||
pub const ABS_MT_TOUCH_MINOR: c_int = 0x31; /* Minor axis : c_int = omit if circular */
|
||||
pub const ABS_MT_WIDTH_MAJOR: c_int = 0x32; /* Major axis of approaching ellipse */
|
||||
pub const ABS_MT_WIDTH_MINOR: c_int = 0x33; /* Minor axis : c_int = omit if circular */
|
||||
pub const ABS_MT_ORIENTATION: c_int = 0x34; /* Ellipse orientation */
|
||||
pub const ABS_MT_POSITION_X: c_int = 0x35; /* Center X touch position */
|
||||
pub const ABS_MT_POSITION_Y: c_int = 0x36; /* Center Y touch position */
|
||||
pub const ABS_MT_TOOL_TYPE: c_int = 0x37; /* Type of touching device */
|
||||
pub const ABS_MT_BLOB_ID: c_int = 0x38; /* Group a set of packets as a blob */
|
||||
pub const ABS_MT_TRACKING_ID: c_int = 0x39; /* Unique ID of initiated contact */
|
||||
pub const ABS_MT_PRESSURE: c_int = 0x3a; /* Pressure on contact area */
|
||||
pub const ABS_MT_DISTANCE: c_int = 0x3b; /* Contact hover distance */
|
||||
pub const ABS_MT_TOOL_X: c_int = 0x3c; /* Center X tool position */
|
||||
pub const ABS_MT_TOOL_Y: c_int = 0x3d; /* Center Y tool position */
|
||||
|
||||
|
||||
pub const ABS_MAX: c_int = 0x3f;
|
||||
pub const ABS_CNT: c_int = ABS_MAX + 1;
|
||||
|
||||
/*
|
||||
* Switch events
|
||||
*/
|
||||
|
||||
pub const SW_LID: c_int = 0x00; /* set = lid shut */
|
||||
pub const SW_TABLET_MODE: c_int = 0x01; /* set = tablet mode */
|
||||
pub const SW_HEADPHONE_INSERT: c_int = 0x02; /* set = inserted */
|
||||
pub const SW_RFKILL_ALL: c_int = 0x03; /* rfkill master switch, type "any" set = radio enabled */
|
||||
pub const SW_RADIO: c_int = SW_RFKILL_ALL; /* deprecated */
|
||||
pub const SW_MICROPHONE_INSERT: c_int = 0x04; /* set = inserted */
|
||||
pub const SW_DOCK: c_int = 0x05; /* set = plugged into dock */
|
||||
pub const SW_LINEOUT_INSERT: c_int = 0x06; /* set = inserted */
|
||||
pub const SW_JACK_PHYSICAL_INSERT: c_int = 0x07; /* set = mechanical switch set */
|
||||
pub const SW_VIDEOOUT_INSERT: c_int = 0x08; /* set = inserted */
|
||||
pub const SW_CAMERA_LENS_COVER: c_int = 0x09; /* set = lens covered */
|
||||
pub const SW_KEYPAD_SLIDE: c_int = 0x0a; /* set = keypad slide out */
|
||||
pub const SW_FRONT_PROXIMITY: c_int = 0x0b; /* set = front proximity sensor active */
|
||||
pub const SW_ROTATE_LOCK: c_int = 0x0c; /* set = rotate locked/disabled */
|
||||
pub const SW_LINEIN_INSERT: c_int = 0x0d; /* set = inserted */
|
||||
pub const SW_MUTE_DEVICE: c_int = 0x0e; /* set = device disabled */
|
||||
pub const SW_MAX: c_int = 0x0f;
|
||||
pub const SW_CNT: c_int = SW_MAX + 1;
|
||||
|
||||
/*
|
||||
* Misc events
|
||||
*/
|
||||
|
||||
pub const MSC_SERIAL: c_int = 0x00;
|
||||
pub const MSC_PULSELED: c_int = 0x01;
|
||||
pub const MSC_GESTURE: c_int = 0x02;
|
||||
pub const MSC_RAW: c_int = 0x03;
|
||||
pub const MSC_SCAN: c_int = 0x04;
|
||||
pub const MSC_TIMESTAMP: c_int = 0x05;
|
||||
pub const MSC_MAX: c_int = 0x07;
|
||||
pub const MSC_CNT: c_int = MSC_MAX + 1;
|
||||
|
||||
/*
|
||||
* LEDs
|
||||
*/
|
||||
|
||||
pub const LED_NUML: c_int = 0x00;
|
||||
pub const LED_CAPSL: c_int = 0x01;
|
||||
pub const LED_SCROLLL: c_int = 0x02;
|
||||
pub const LED_COMPOSE: c_int = 0x03;
|
||||
pub const LED_KANA: c_int = 0x04;
|
||||
pub const LED_SLEEP: c_int = 0x05;
|
||||
pub const LED_SUSPEND: c_int = 0x06;
|
||||
pub const LED_MUTE: c_int = 0x07;
|
||||
pub const LED_MISC: c_int = 0x08;
|
||||
pub const LED_MAIL: c_int = 0x09;
|
||||
pub const LED_CHARGING: c_int = 0x0a;
|
||||
pub const LED_MAX: c_int = 0x0f;
|
||||
pub const LED_CNT: c_int = LED_MAX + 1;
|
||||
|
||||
/*
|
||||
* Autorepeat values
|
||||
*/
|
||||
|
||||
pub const REP_DELAY: c_int = 0x00;
|
||||
pub const REP_PERIOD: c_int = 0x01;
|
||||
pub const REP_MAX: c_int = 0x01;
|
||||
pub const REP_CNT: c_int = REP_MAX + 1;
|
||||
|
||||
/*
|
||||
* Sounds
|
||||
*/
|
||||
|
||||
pub const SND_CLICK: c_int = 0x00;
|
||||
pub const SND_BELL: c_int = 0x01;
|
||||
pub const SND_TONE: c_int = 0x02;
|
||||
pub const SND_MAX: c_int = 0x07;
|
||||
pub const SND_CNT: c_int = SND_MAX + 1;
|
@ -1,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
mod builder;
|
||||
pub use self::builder::Builder;
|
||||
|
||||
pub mod codes;
|
||||
pub use codes::*;
|
||||
|
||||
mod device;
|
||||
pub use self::device::Device;
|
||||
|
||||
|
250
src/linux/mod.rs
250
src/linux/mod.rs
@ -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
568
src/macos/codes.rs
Normal file
@ -0,0 +1,568 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use core_graphics::event::CGKeyCode;
|
||||
|
||||
pub const KEY_RESERVED: CGKeyCode = 0x31;
|
||||
pub const KEY_ESC: CGKeyCode = 0x00;
|
||||
pub const KEY_1: CGKeyCode = 0x0012;
|
||||
pub const KEY_2: CGKeyCode = 0x0013;
|
||||
pub const KEY_3: CGKeyCode = 0x0014;
|
||||
pub const KEY_4: CGKeyCode = 0x0015;
|
||||
pub const KEY_5: CGKeyCode = 0x0017;
|
||||
pub const KEY_6: CGKeyCode = 0x0016;
|
||||
pub const KEY_7: CGKeyCode = 0x001A;
|
||||
pub const KEY_8: CGKeyCode = 0x001C;
|
||||
pub const KEY_9: CGKeyCode = 0x0019;
|
||||
pub const KEY_10: CGKeyCode = 0x001D;
|
||||
pub const KEY_MINUS: CGKeyCode = 0x001B;
|
||||
pub const KEY_EQUAL: CGKeyCode = 0x0018;
|
||||
pub const KEY_BACKSPACE: CGKeyCode = 0x0033;
|
||||
pub const KEY_TAB: CGKeyCode = 0x0030;
|
||||
pub const KEY_Q: CGKeyCode = 0x000C;
|
||||
pub const KEY_W: CGKeyCode = 0x000D;
|
||||
pub const KEY_E: CGKeyCode = 0x000E;
|
||||
pub const KEY_R: CGKeyCode = 0x000F;
|
||||
pub const KEY_T: CGKeyCode = 0x0011;
|
||||
pub const KEY_Y: CGKeyCode = 0x0010;
|
||||
pub const KEY_U: CGKeyCode = 0x0020;
|
||||
pub const KEY_I: CGKeyCode = 0x0022;
|
||||
pub const KEY_O: CGKeyCode = 0x001F;
|
||||
pub const KEY_P: CGKeyCode = 0x0023;
|
||||
pub const KEY_LEFTBRACE: CGKeyCode = 0x0021;
|
||||
pub const KEY_RIGHTBRACE: CGKeyCode = 0x001E;
|
||||
pub const KEY_ENTER: CGKeyCode = 0x0024;
|
||||
pub const KEY_LEFTCTRL: CGKeyCode = 0x003B;
|
||||
pub const KEY_A: CGKeyCode = 0x0000;
|
||||
pub const KEY_S: CGKeyCode = 0x0001;
|
||||
pub const KEY_D: CGKeyCode = 0x0002;
|
||||
pub const KEY_F: CGKeyCode = 0x0003;
|
||||
pub const KEY_G: CGKeyCode = 0x0005;
|
||||
pub const KEY_H: CGKeyCode = 0x0004;
|
||||
pub const KEY_J: CGKeyCode = 0x0026;
|
||||
pub const KEY_K: CGKeyCode = 0x0028;
|
||||
pub const KEY_L: CGKeyCode = 0x0025;
|
||||
pub const KEY_SEMICOLON: CGKeyCode = 0x0029;
|
||||
pub const KEY_APOSTROPHE: CGKeyCode = 0x0027;
|
||||
pub const KEY_GRAVE: CGKeyCode = 0x0032;
|
||||
pub const KEY_LEFTSHIFT: CGKeyCode = 0x0038;
|
||||
pub const KEY_BACKSLASH: CGKeyCode = 0x002A;
|
||||
pub const KEY_Z: CGKeyCode = 0x0006;
|
||||
pub const KEY_X: CGKeyCode = 0x0007;
|
||||
pub const KEY_C: CGKeyCode = 0x0008;
|
||||
pub const KEY_V: CGKeyCode = 0x0009;
|
||||
pub const KEY_B: CGKeyCode = 0x000B;
|
||||
pub const KEY_N: CGKeyCode = 0x002D;
|
||||
pub const KEY_M: CGKeyCode = 0x002E;
|
||||
pub const KEY_COMMA: CGKeyCode = 0x002B;
|
||||
pub const KEY_DOT: CGKeyCode = 0x002F;
|
||||
pub const KEY_SLASH: CGKeyCode = 0x002C;
|
||||
pub const KEY_RIGHTSHIFT: CGKeyCode = 0x003C;
|
||||
pub const KEY_KPASTERISK: CGKeyCode = 0x0043;
|
||||
pub const KEY_LEFTALT: CGKeyCode = 0x003A;
|
||||
pub const KEY_SPACE: CGKeyCode = 0x0031;
|
||||
pub const KEY_CAPSLOCK: CGKeyCode = 0x0039;
|
||||
pub const KEY_F1: CGKeyCode = 0x007A;
|
||||
pub const KEY_F2: CGKeyCode = 0x0078;
|
||||
pub const KEY_F3: CGKeyCode = 0x0063;
|
||||
pub const KEY_F4: CGKeyCode = 0x0076;
|
||||
pub const KEY_F5: CGKeyCode = 0x0060;
|
||||
pub const KEY_F6: CGKeyCode = 0x0061;
|
||||
pub const KEY_F7: CGKeyCode = 0x0062;
|
||||
pub const KEY_F8: CGKeyCode = 0x0064;
|
||||
pub const KEY_F9: CGKeyCode = 0x0065;
|
||||
pub const KEY_F10: CGKeyCode = 0x006D;
|
||||
pub const KEY_NUMLOCK: CGKeyCode = 0x0047;
|
||||
pub const KEY_SCROLLLOCK: CGKeyCode = 0x006B;
|
||||
pub const KEY_KP7: CGKeyCode = 0x0059;
|
||||
pub const KEY_KP8: CGKeyCode = 0x005B;
|
||||
pub const KEY_KP9: CGKeyCode = 0x005C;
|
||||
pub const KEY_KPMINUS: CGKeyCode = 0x004E;
|
||||
pub const KEY_KP4: CGKeyCode = 0x0056;
|
||||
pub const KEY_KP5: CGKeyCode = 0x0057;
|
||||
pub const KEY_KP6: CGKeyCode = 0x0058;
|
||||
pub const KEY_KPPLUS: CGKeyCode = 0x0045;
|
||||
pub const KEY_KP1: CGKeyCode = 0x0053;
|
||||
pub const KEY_KP2: CGKeyCode = 0x0054;
|
||||
pub const KEY_KP3: CGKeyCode = 0x0055;
|
||||
pub const KEY_KP0: CGKeyCode = 0x0052; // https://code-with-me.jetbrains.com/UCsz0dzSd1QAbmOi8g0V3w#p=IC&fp=6298EDAF97FA62E9897E2556D1A6631FB66974568C7252E696472EE85078E8A0
|
||||
pub const KEY_KPDOT: CGKeyCode = 0x0041;
|
||||
// pub const KEY_ZENKAKUHANKAKU: CGKeyCode = NOT_FOUND;
|
||||
pub const KEY_102ND: CGKeyCode = 0x000A;
|
||||
pub const KEY_F11: CGKeyCode = 0x0067;
|
||||
pub const KEY_F12: CGKeyCode = 0x006F;
|
||||
// pub const KEY_RO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KATAKANA: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HIRAGANA: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HENKAN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KATAKANAHIRAGANA: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MUHENKAN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KPJPCOMMA: CGKeyCode = NOT_FOUND; // https://code-with-me.jetbrains.com/Mf59EFUeJZQ2mpGCCCjNWw#p=IC&fp=6298EDAF97FA62E9897E2556D1A6631FB66974568C7252E696472EE85078E8A0
|
||||
pub const KEY_KPENTER: CGKeyCode = 0x004C;
|
||||
pub const KEY_RIGHTCTRL: CGKeyCode = 0x003E;
|
||||
pub const KEY_KPSLASH: CGKeyCode = 0x004B;
|
||||
pub const KEY_SYSRQ: CGKeyCode = 0x0069;
|
||||
pub const KEY_RIGHTALT: CGKeyCode = 0x003D;
|
||||
pub const KEY_LINEFEED: CGKeyCode = 0x0071;
|
||||
pub const KEY_HOME: CGKeyCode = 0x0073;
|
||||
pub const KEY_UP: CGKeyCode = 0x007E;
|
||||
pub const KEY_PAGEUP: CGKeyCode = 0x0074;
|
||||
pub const KEY_LEFT: CGKeyCode = 0x007B;
|
||||
pub const KEY_RIGHT: CGKeyCode = 0x007C;
|
||||
pub const KEY_END: CGKeyCode = 0x0077;
|
||||
pub const KEY_DOWN: CGKeyCode = 0x007D;
|
||||
pub const KEY_PAGEDOWN: CGKeyCode = 0x0079;
|
||||
pub const KEY_INSERT: CGKeyCode = 0x0072;
|
||||
pub const KEY_DELETE: CGKeyCode = 0x0075;
|
||||
// pub const KEY_MACRO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MUTE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_VOLUMEDOWN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_VOLUMEUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_POWER: CGKeyCode = NOT_FOUND;
|
||||
pub const KEY_KPEQUAL: CGKeyCode = 0x0069;
|
||||
// pub const KEY_KPPLUSMINUS: CGKeyCode = NOT_FOUND;
|
||||
pub const KEY_PAUSE: CGKeyCode = KEY_LINEFEED;
|
||||
pub const KEY_SCALE: CGKeyCode = 0x0047;
|
||||
pub const KEY_KPCOMMA: CGKeyCode = 0x0036;
|
||||
// pub const KEY_HANGEUL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HANGUEL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HANGEUL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HANJA: CGKeyCode = NOT_FOUND;
|
||||
//pub const KEY_YEN: CGKeyCode = NOT_FOUND;
|
||||
pub const KEY_LEFTMETA: CGKeyCode = 0x0037;
|
||||
pub const KEY_RIGHTMETA: CGKeyCode = 0x0036;
|
||||
pub const KEY_COMPOSE: CGKeyCode = 0x006E;
|
||||
// pub const KEY_STOP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_AGAIN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PROPS: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_UNDO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FRONT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_COPY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_OPEN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PASTE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FIND: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CUT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HELP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MENU: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CALC: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SETUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SLEEP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_WAKEUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FILE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SENDFILE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_DELETEFILE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_XFER: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PROG1: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PROG2: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_WWW: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MSDOS: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_COFFEE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SCREENLOCK: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_COFFEE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_ROTATE_DISPLAY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_DIRECTION: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_ROTATE_DISPLAY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CYCLEWINDOWS: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MAIL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BOOKMARKS: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_COMPUTER: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BACK: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FORWARD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CLOSECD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_EJECTCD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_EJECTCLOSECD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_NEXTSONG: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PLAYPAUSE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PREVIOUSSONG: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_STOPCD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_RECORD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_REWIND: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PHONE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_ISO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CONFIG: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HOMEPAGE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_REFRESH: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_EXIT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MOVE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_EDIT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SCROLLUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SCROLLDOWN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KPLEFTPAREN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KPRIGHTPAREN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_NEW: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_REDO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F13: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F14: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F15: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F16: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F17: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F18: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F19: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F20: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F21: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F22: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F23: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_F24: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PLAYCD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PAUSECD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PROG3: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PROG4: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_DASHBOARD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SUSPEND: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CLOSE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PLAY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FASTFORWARD: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BASSBOOST: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_PRINT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_HP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CAMERA: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SOUND: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_QUESTION: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_EMAIL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CHAT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SEARCH: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_CONNECT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FINANCE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SPORT: CGKeyCode = NOT_FOUND;
|
||||
//pub const KEY_SHOP: CGKeyCode = NOT_FOUND;
|
||||
pub const KEY_ALTERASE: CGKeyCode = 0x0047;
|
||||
// pub const KEY_CANCEL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESSDOWN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESSUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_MEDIA: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SWITCHVIDEOMODE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KBDILLUMTOGGLE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KBDILLUMDOWN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_KBDILLUMUP: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SEND: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_REPLY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_FORWARDMAIL: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_SAVE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_DOCUMENTS: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BATTERY: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BLUETOOTH: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_UWB: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_UNKNOWN: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_VIDEO_NEXT: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_VIDEO_PREV: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESS_CYCLE: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESS_AUTO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESS_ZERO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_BRIGHTNESS_AUTO: CGKeyCode = NOT_FOUND;
|
||||
// pub const KEY_DISPLAY_OFF: CGKeyCode = NOT_FOUND;
|
||||
|
||||
pub fn key_map() -> HashMap<&'static str, CGKeyCode> {
|
||||
[
|
||||
// grep 'Key => 0x' ../rusty-keys-win/src/windows/inputs.rs | tr '[a-z]' '[A-Z]' | sed -r -e 's/KEY => 0X/: CGKeyCode = 0x/' -e 's/^[ ]+/pub const KEY_/' | tr ',' ';'
|
||||
("ESC", KEY_ESC),
|
||||
("1", KEY_1),
|
||||
("2", KEY_2),
|
||||
("3", KEY_3),
|
||||
("4", KEY_4),
|
||||
("5", KEY_5),
|
||||
("6", KEY_6),
|
||||
("7", KEY_7),
|
||||
("8", KEY_8),
|
||||
("9", KEY_9),
|
||||
("10", KEY_10),
|
||||
("MINUS", KEY_MINUS),
|
||||
("EQUAL", KEY_EQUAL),
|
||||
("BACKSPACE", KEY_BACKSPACE),
|
||||
("TAB", KEY_TAB),
|
||||
("Q", KEY_Q),
|
||||
("W", KEY_W),
|
||||
("E", KEY_E),
|
||||
("R", KEY_R),
|
||||
("T", KEY_T),
|
||||
("Y", KEY_Y),
|
||||
("U", KEY_U),
|
||||
("I", KEY_I),
|
||||
("O", KEY_O),
|
||||
("P", KEY_P),
|
||||
("LEFTBRACE", KEY_LEFTBRACE),
|
||||
("RIGHTBRACE", KEY_RIGHTBRACE),
|
||||
("ENTER", KEY_ENTER),
|
||||
("LEFTCTRL", KEY_LEFTCTRL),
|
||||
("A", KEY_A),
|
||||
("S", KEY_S),
|
||||
("D", KEY_D),
|
||||
("F", KEY_F),
|
||||
("G", KEY_G),
|
||||
("H", KEY_H),
|
||||
("J", KEY_J),
|
||||
("K", KEY_K),
|
||||
("L", KEY_L),
|
||||
("SEMICOLON", KEY_SEMICOLON),
|
||||
("APOSTROPHE", KEY_APOSTROPHE),
|
||||
("GRAVE", KEY_GRAVE),
|
||||
("LEFTSHIFT", KEY_LEFTSHIFT),
|
||||
("BACKSLASH", KEY_BACKSLASH),
|
||||
("Z", KEY_Z),
|
||||
("X", KEY_X),
|
||||
("C", KEY_C),
|
||||
("V", KEY_V),
|
||||
("B", KEY_B),
|
||||
("N", KEY_N),
|
||||
("M", KEY_M),
|
||||
("COMMA", KEY_COMMA),
|
||||
("DOT", KEY_DOT),
|
||||
("SLASH", KEY_SLASH),
|
||||
("RIGHTSHIFT", KEY_RIGHTSHIFT),
|
||||
("KPASTERISK", KEY_KPASTERISK),
|
||||
("LEFTALT", KEY_LEFTALT),
|
||||
("SPACE", KEY_SPACE),
|
||||
("CAPSLOCK", KEY_CAPSLOCK),
|
||||
("F1", KEY_F1),
|
||||
("F2", KEY_F2),
|
||||
("F3", KEY_F3),
|
||||
("F4", KEY_F4),
|
||||
("F5", KEY_F5),
|
||||
("F6", KEY_F6),
|
||||
("F7", KEY_F7),
|
||||
("F8", KEY_F8),
|
||||
("F9", KEY_F9),
|
||||
("F10", KEY_F10),
|
||||
("NUMLOCK", KEY_NUMLOCK),
|
||||
("SCROLLLOCK", KEY_SCROLLLOCK),
|
||||
("KP7", KEY_KP7),
|
||||
("KP8", KEY_KP8),
|
||||
("KP9", KEY_KP9),
|
||||
("KPMINUS", KEY_KPMINUS),
|
||||
("KP4", KEY_KP4),
|
||||
("KP5", KEY_KP5),
|
||||
("KP6", KEY_KP6),
|
||||
("KPPLUS", KEY_KPPLUS),
|
||||
("KP1", KEY_KP1),
|
||||
("KP2", KEY_KP2),
|
||||
("KP3", KEY_KP3),
|
||||
("KP0", KEY_KP0),
|
||||
("KPDOT", KEY_KPDOT),
|
||||
/*
|
||||
("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU),
|
||||
("102ND", KEY_102ND),
|
||||
*/
|
||||
("F11", KEY_F11),
|
||||
("F12", KEY_F12),
|
||||
/*
|
||||
("RO", KEY_RO),
|
||||
("KATAKANA", KEY_KATAKANA),
|
||||
("HIRAGANA", KEY_HIRAGANA),
|
||||
("HENKAN", KEY_HENKAN),
|
||||
("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA),
|
||||
("MUHENKAN", KEY_MUHENKAN),
|
||||
("KPJPCOMMA", KEY_KPJPCOMMA),
|
||||
*/
|
||||
("KPENTER", KEY_KPENTER),
|
||||
("RIGHTCTRL", KEY_RIGHTCTRL),
|
||||
("KPSLASH", KEY_KPSLASH),
|
||||
("SYSRQ", KEY_SYSRQ),
|
||||
("RIGHTALT", KEY_RIGHTALT),
|
||||
/*
|
||||
("LINEFEED", KEY_LINEFEED),
|
||||
*/
|
||||
("HOME", KEY_HOME),
|
||||
("UP", KEY_UP),
|
||||
("PAGEUP", KEY_PAGEUP),
|
||||
("LEFT", KEY_LEFT),
|
||||
("RIGHT", KEY_RIGHT),
|
||||
("END", KEY_END),
|
||||
("DOWN", KEY_DOWN),
|
||||
("PAGEDOWN", KEY_PAGEDOWN),
|
||||
("INSERT", KEY_INSERT),
|
||||
("DELETE", KEY_DELETE),
|
||||
/*
|
||||
("MACRO", KEY_MACRO),
|
||||
("MUTE", KEY_MUTE),
|
||||
("VOLUMEDOWN", KEY_VOLUMEDOWN),
|
||||
("VOLUMEUP", KEY_VOLUMEUP),
|
||||
("POWER", KEY_POWER),
|
||||
("KPEQUAL", KEY_KPEQUAL),
|
||||
("KPPLUSMINUS", KEY_KPPLUSMINUS),
|
||||
("PAUSE", KEY_PAUSE),
|
||||
("SCALE", KEY_SCALE),
|
||||
("KPCOMMA", KEY_KPCOMMA),
|
||||
("HANGEUL", KEY_HANGEUL),
|
||||
("HANGUEL", KEY_HANGUEL),
|
||||
("HANGEUL", KEY_HANGEUL),
|
||||
("HANJA", KEY_HANJA),
|
||||
("YEN", KEY_YEN),
|
||||
("LEFTMETA", KEY_LEFTMETA),
|
||||
("RIGHTMETA", KEY_RIGHTMETA),
|
||||
("COMPOSE", KEY_COMPOSE),
|
||||
("STOP", KEY_STOP),
|
||||
("AGAIN", KEY_AGAIN),
|
||||
("PROPS", KEY_PROPS),
|
||||
("UNDO", KEY_UNDO),
|
||||
("FRONT", KEY_FRONT),
|
||||
("COPY", KEY_COPY),
|
||||
("OPEN", KEY_OPEN),
|
||||
("PASTE", KEY_PASTE),
|
||||
("FIND", KEY_FIND),
|
||||
("CUT", KEY_CUT),
|
||||
("HELP", KEY_HELP),
|
||||
("MENU", KEY_MENU),
|
||||
("CALC", KEY_CALC),
|
||||
("SETUP", KEY_SETUP),
|
||||
("SLEEP", KEY_SLEEP),
|
||||
("WAKEUP", KEY_WAKEUP),
|
||||
("FILE", KEY_FILE),
|
||||
("SENDFILE", KEY_SENDFILE),
|
||||
("DELETEFILE", KEY_DELETEFILE),
|
||||
("XFER", KEY_XFER),
|
||||
("PROG1", KEY_PROG1),
|
||||
("PROG2", KEY_PROG2),
|
||||
("WWW", KEY_WWW),
|
||||
("MSDOS", KEY_MSDOS),
|
||||
("COFFEE", KEY_COFFEE),
|
||||
("SCREENLOCK", KEY_SCREENLOCK),
|
||||
("COFFEE", KEY_COFFEE),
|
||||
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
|
||||
("DIRECTION", KEY_DIRECTION),
|
||||
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
|
||||
("CYCLEWINDOWS", KEY_CYCLEWINDOWS),
|
||||
("MAIL", KEY_MAIL),
|
||||
("BOOKMARKS", KEY_BOOKMARKS),
|
||||
("COMPUTER", KEY_COMPUTER),
|
||||
("BACK", KEY_BACK),
|
||||
("FORWARD", KEY_FORWARD),
|
||||
("CLOSECD", KEY_CLOSECD),
|
||||
("EJECTCD", KEY_EJECTCD),
|
||||
("EJECTCLOSECD", KEY_EJECTCLOSECD),
|
||||
("NEXTSONG", KEY_NEXTSONG),
|
||||
("PLAYPAUSE", KEY_PLAYPAUSE),
|
||||
("PREVIOUSSONG", KEY_PREVIOUSSONG),
|
||||
("STOPCD", KEY_STOPCD),
|
||||
("RECORD", KEY_RECORD),
|
||||
("REWIND", KEY_REWIND),
|
||||
("PHONE", KEY_PHONE),
|
||||
("ISO", KEY_ISO),
|
||||
("CONFIG", KEY_CONFIG),
|
||||
("HOMEPAGE", KEY_HOMEPAGE),
|
||||
("REFRESH", KEY_REFRESH),
|
||||
("EXIT", KEY_EXIT),
|
||||
("MOVE", KEY_MOVE),
|
||||
("EDIT", KEY_EDIT),
|
||||
("SCROLLUP", KEY_SCROLLUP),
|
||||
("SCROLLDOWN", KEY_SCROLLDOWN),
|
||||
("KPLEFTPAREN", KEY_KPLEFTPAREN),
|
||||
("KPRIGHTPAREN", KEY_KPRIGHTPAREN),
|
||||
("NEW", KEY_NEW),
|
||||
("REDO", KEY_REDO),
|
||||
("F13", KEY_F13),
|
||||
("F14", KEY_F14),
|
||||
("F15", KEY_F15),
|
||||
("F16", KEY_F16),
|
||||
("F17", KEY_F17),
|
||||
("F18", KEY_F18),
|
||||
("F19", KEY_F19),
|
||||
("F20", KEY_F20),
|
||||
("F21", KEY_F21),
|
||||
("F22", KEY_F22),
|
||||
("F23", KEY_F23),
|
||||
("F24", KEY_F24),
|
||||
("PLAYCD", KEY_PLAYCD),
|
||||
("PAUSECD", KEY_PAUSECD),
|
||||
("PROG3", KEY_PROG3),
|
||||
("PROG4", KEY_PROG4),
|
||||
("DASHBOARD", KEY_DASHBOARD),
|
||||
("SUSPEND", KEY_SUSPEND),
|
||||
("CLOSE", KEY_CLOSE),
|
||||
("PLAY", KEY_PLAY),
|
||||
("FASTFORWARD", KEY_FASTFORWARD),
|
||||
("BASSBOOST", KEY_BASSBOOST),
|
||||
("PRINT", KEY_PRINT),
|
||||
("HP", KEY_HP),
|
||||
("CAMERA", KEY_CAMERA),
|
||||
("SOUND", KEY_SOUND),
|
||||
("QUESTION", KEY_QUESTION),
|
||||
("EMAIL", KEY_EMAIL),
|
||||
("CHAT", KEY_CHAT),
|
||||
("SEARCH", KEY_SEARCH),
|
||||
("CONNECT", KEY_CONNECT),
|
||||
("FINANCE", KEY_FINANCE),
|
||||
("SPORT", KEY_SPORT),
|
||||
("SHOP", KEY_SHOP),
|
||||
("ALTERASE", KEY_ALTERASE),
|
||||
("CANCEL", KEY_CANCEL),
|
||||
("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN),
|
||||
("BRIGHTNESSUP", KEY_BRIGHTNESSUP),
|
||||
("MEDIA", KEY_MEDIA),
|
||||
("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE),
|
||||
("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE),
|
||||
("KBDILLUMDOWN", KEY_KBDILLUMDOWN),
|
||||
("KBDILLUMUP", KEY_KBDILLUMUP),
|
||||
("SEND", KEY_SEND),
|
||||
("REPLY", KEY_REPLY),
|
||||
("FORWARDMAIL", KEY_FORWARDMAIL),
|
||||
("SAVE", KEY_SAVE),
|
||||
("DOCUMENTS", KEY_DOCUMENTS),
|
||||
("BATTERY", KEY_BATTERY),
|
||||
("BLUETOOTH", KEY_BLUETOOTH),
|
||||
("WLAN", KEY_WLAN),
|
||||
("UWB", KEY_UWB),
|
||||
("UNKNOWN", KEY_UNKNOWN),
|
||||
("VIDEO_NEXT", KEY_VIDEO_NEXT),
|
||||
("VIDEO_PREV", KEY_VIDEO_PREV),
|
||||
("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE),
|
||||
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
|
||||
("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO),
|
||||
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
|
||||
("DISPLAY_OFF", KEY_DISPLAY_OFF),
|
||||
("WWAN", KEY_WWAN),
|
||||
("WIMAX", KEY_WIMAX),
|
||||
("WWAN", KEY_WWAN),
|
||||
("RFKILL", KEY_RFKILL),
|
||||
("MICMUTE", KEY_MICMUTE),
|
||||
*/
|
||||
// below manual shortcuts
|
||||
("PSCR", KEY_SYSRQ),
|
||||
("SLCK", KEY_SCROLLLOCK),
|
||||
("BRK", KEY_PAUSE),
|
||||
("GRV", KEY_GRAVE),
|
||||
("0", KEY_10), // dumb or named wrong?
|
||||
("MINS", KEY_MINUS),
|
||||
("EQL", KEY_EQUAL),
|
||||
("BSPC", KEY_BACKSPACE),
|
||||
("LBRC", KEY_LEFTBRACE),
|
||||
("RBRC", KEY_RIGHTBRACE),
|
||||
("BSLS", KEY_BACKSLASH),
|
||||
("SCLN", KEY_SEMICOLON),
|
||||
("QUOT", KEY_APOSTROPHE),
|
||||
("ENT", KEY_ENTER),
|
||||
("COMM", KEY_COMMA),
|
||||
("DOT", KEY_DOT),
|
||||
("SLSH", KEY_SLASH),
|
||||
("CAPS", KEY_CAPSLOCK),
|
||||
("LSFT", KEY_LEFTSHIFT),
|
||||
("RSFT", KEY_RIGHTSHIFT),
|
||||
("SPC", KEY_SPACE),
|
||||
("APP", KEY_COMPOSE),
|
||||
("LCTL", KEY_LEFTCTRL),
|
||||
("RCTL", KEY_RIGHTCTRL),
|
||||
("LALT", KEY_LEFTALT),
|
||||
("RALT", KEY_RIGHTALT),
|
||||
("LGUI", KEY_LEFTMETA),
|
||||
("RGUI", KEY_RIGHTMETA),
|
||||
("INS", KEY_INSERT),
|
||||
("PGUP", KEY_PAGEUP),
|
||||
("PGDN", KEY_PAGEDOWN),
|
||||
("DEL", KEY_DELETE),
|
||||
("RGHT", KEY_RIGHT),
|
||||
("NLCK", KEY_NUMLOCK),
|
||||
("PSLS", KEY_KPSLASH),
|
||||
("PAST", KEY_KPASTERISK),
|
||||
("PMNS", KEY_KPMINUS),
|
||||
("P7", KEY_KP7),
|
||||
("P8", KEY_KP8),
|
||||
("P9", KEY_KP9),
|
||||
("P4", KEY_KP4),
|
||||
("P5", KEY_KP5),
|
||||
("P6", KEY_KP6),
|
||||
("PPLS", KEY_KPPLUS),
|
||||
("P1", KEY_KP1),
|
||||
("P2", KEY_KP2),
|
||||
("P3", KEY_KP3),
|
||||
("P0", KEY_KP0),
|
||||
("PDOT", KEY_KPDOT),
|
||||
("PENT", KEY_KPENTER),
|
||||
].iter().cloned().map(|(m, v)| (m, v)).collect()
|
||||
}
|
414
src/macos/mod.rs
Normal file
414
src/macos/mod.rs
Normal file
@ -0,0 +1,414 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
use crate::*;
|
||||
use std::env;
|
||||
use std::process::exit;
|
||||
|
||||
use getopts::Options;
|
||||
use std::fs::File;
|
||||
|
||||
pub mod codes;
|
||||
use codes::*;
|
||||
|
||||
use core_graphics::event::CGKeyCode;
|
||||
use core_graphics::event::*;
|
||||
use core_graphics::event_source::*;
|
||||
|
||||
use core_graphics::event::{CGEventTapLocation, CGEventType};
|
||||
|
||||
type MacOSKeyMaps = KeyMaps<CGEventSource, CGKeyCode, CGEvent, Option<CGEvent>>;
|
||||
type CallbackPointer = (MacOSKeyMaps, CGEventSource);
|
||||
|
||||
/*
|
||||
// possible types for event_source
|
||||
Private = -1,
|
||||
CombinedSessionState = 0,
|
||||
HIDSystemState = 1,
|
||||
|
||||
// possible types for tapLocation
|
||||
HID,
|
||||
Session,
|
||||
AnnotatedSession,
|
||||
*/
|
||||
|
||||
// macOS seems to require this, or it ignores shift, WHY?
|
||||
const delay: std::time::Duration = std::time::Duration::from_millis(20);
|
||||
const tapLocation: CGEventTapLocation = CGEventTapLocation::Session;
|
||||
// this is only used if tapLocation is HID, to prevent us from mapping our own key inputs
|
||||
const uniqueHIDUserData: i64 = 45;
|
||||
|
||||
impl KeyEvent<CGKeyCode> for CGEvent {
|
||||
fn code(&self) -> CGKeyCode {
|
||||
self.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE) as CGKeyCode
|
||||
}
|
||||
|
||||
fn value(&self) -> KeyState {
|
||||
let event_type = self.get_type();
|
||||
match event_type {
|
||||
CGEventType::FlagsChanged => {
|
||||
let flags = self.get_flags().bits(); // todo: fix cast?
|
||||
let mask = match self.code() {
|
||||
KEY_LEFTCTRL => NX_DEVICELCTLKEYMASK,
|
||||
KEY_RIGHTCTRL => NX_DEVICERCTLKEYMASK,
|
||||
KEY_LEFTSHIFT => NX_DEVICELSHIFTKEYMASK,
|
||||
KEY_RIGHTSHIFT => NX_DEVICERSHIFTKEYMASK,
|
||||
KEY_LEFTMETA => NX_DEVICELCMDKEYMASK,
|
||||
KEY_RIGHTMETA => NX_DEVICERCMDKEYMASK,
|
||||
KEY_LEFTALT => NX_DEVICELALTKEYMASK,
|
||||
KEY_RIGHTALT => NX_DEVICERALTKEYMASK,
|
||||
KEY_CAPSLOCK => NX_DEVICECAPSLOCKMASK,
|
||||
_ => panic!("unhandled key: {}", self.code()),
|
||||
};
|
||||
if (flags & mask) != 0 { KeyState::DOWN } else { KeyState::UP }
|
||||
},
|
||||
CGEventType::KeyDown => KeyState::DOWN,
|
||||
CGEventType::KeyUp => KeyState::UP,
|
||||
CGEventType::TapDisabledByTimeout => {
|
||||
println!("Quartz event tap disabled because of timeout; attempting to reregister.");
|
||||
todo!("implement register listener");
|
||||
//register_listener(channel);
|
||||
//KeyState::OTHER
|
||||
},
|
||||
_ => {
|
||||
println!("Received unknown EventType: {:?}", event_type);
|
||||
KeyState::OTHER
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Keyboard<CGKeyCode, CGEvent, Option<CGEvent>> for CGEventSource {
|
||||
fn send(&self, event: &mut CGEvent) -> Result<Option<CGEvent>> {
|
||||
//println!("send orig: {}", event.code());
|
||||
|
||||
//Ok(Some(event.event))
|
||||
|
||||
self.send_mod_code_value(event.code(), event.value() == KeyState::UP, event)
|
||||
}
|
||||
|
||||
fn send_mod_code(&self, code: CGKeyCode, event: &mut CGEvent) -> Result<Option<CGEvent>> {
|
||||
// event.value should only ever be UP/DOWN when this method is called
|
||||
//println!("send_mod_code orig: {} code: {}", event.code(), code);
|
||||
|
||||
//unsafe { CGEventSetIntegerValueField(event.event, kCGKeyboardEventKeycode, code as i64) };
|
||||
//Ok(Some(event.event))
|
||||
|
||||
self.send_mod_code_value(code, event.value() == KeyState::UP, event)
|
||||
}
|
||||
|
||||
fn send_mod_code_value(&self, code: CGKeyCode, up_not_down: bool, _event: &mut CGEvent) -> Result<Option<CGEvent>> {
|
||||
//println!("send_mod_code_value orig: {} code: {}, up_not_down: {}", event.code(), code, up_not_down);
|
||||
//return Ok(None);
|
||||
|
||||
let event =
|
||||
CGEvent::new_keyboard_event(self.clone(), code, !up_not_down)
|
||||
.expect("Failed creating event");
|
||||
match tapLocation {
|
||||
CGEventTapLocation::HID => event.set_integer_value_field(EventField::EVENT_SOURCE_USER_DATA, uniqueHIDUserData),
|
||||
_ => {}
|
||||
};
|
||||
event.post(tapLocation);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn synchronize(&self) -> Result<Option<CGEvent>> {
|
||||
std::thread::sleep(delay);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn left_shift_code(&self) -> CGKeyCode {
|
||||
KEY_LEFTSHIFT
|
||||
}
|
||||
|
||||
fn right_shift_code(&self) -> CGKeyCode {
|
||||
KEY_RIGHTSHIFT
|
||||
}
|
||||
|
||||
fn caps_lock_code(&self) -> CGKeyCode {
|
||||
KEY_CAPSLOCK
|
||||
}
|
||||
|
||||
fn block_key(&self) -> Result<Option<CGEvent>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_res() -> Result<()> {
|
||||
let config = parse_args();
|
||||
//println!("Config: {:?}", config);
|
||||
|
||||
let key_map = key_map();
|
||||
//println!("key_map: {:?}", key_map);
|
||||
|
||||
let key_maps = MacOSKeyMaps::from_cfg(&key_map, &config.config_file);
|
||||
//println!("key_maps: {}", key_maps);
|
||||
let callback_pointer: CallbackPointer = (key_maps, CGEventSource::new(CGEventSourceStateID::Private).expect("Failed creating event source"));
|
||||
|
||||
let mask = CGEventMaskBit(CGEventType::KeyDown)
|
||||
| CGEventMaskBit(CGEventType::KeyUp)
|
||||
| CGEventMaskBit(CGEventType::FlagsChanged)
|
||||
;
|
||||
|
||||
unsafe {
|
||||
let options = 0;
|
||||
|
||||
// Create the event tap
|
||||
let event_tap = CGEventTapCreate(
|
||||
kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
options,
|
||||
mask,
|
||||
callback,
|
||||
&callback_pointer,
|
||||
);
|
||||
if event_tap.is_null() {
|
||||
panic!("Unable to create event tap. Please make sure you have the correct permissions");
|
||||
}
|
||||
println!("Created event tap...");
|
||||
|
||||
let allocator = kCFAllocatorDefault;
|
||||
let current_event_loop = CFRunLoopGetCurrent();
|
||||
let mode = kCFRunLoopCommonModes;
|
||||
|
||||
// Create Run Loop Source
|
||||
let run_loop_source = CFMachPortCreateRunLoopSource(allocator, event_tap, 0);
|
||||
|
||||
// Add Run Loop Source to the current event loop
|
||||
CFRunLoopAddSource(current_event_loop, run_loop_source, mode);
|
||||
|
||||
// Enable the tap
|
||||
CGEventTapEnable(event_tap, true);
|
||||
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Config {
|
||||
config_file: String
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn new(config_file: String) -> Self {
|
||||
Config { config_file: config_file }
|
||||
}
|
||||
}
|
||||
|
||||
fn get_env_push(key: &str, to_push: &str, vec: &mut Vec<String>) {
|
||||
if let Some(var) = env::var_os(key) {
|
||||
if let Ok(str) = var.into_string() {
|
||||
let mut str = str.to_owned();
|
||||
str.push_str(to_push);
|
||||
vec.push(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_args() -> Config {
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let brief = format!("Usage: {} [options] [keymap.toml]", program);
|
||||
println!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
let args: Vec<_> = env::args().collect();
|
||||
|
||||
let mut default_configs = Vec::new();
|
||||
get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs);
|
||||
get_env_push("APPDATA", "\\keymap.toml", &mut default_configs);
|
||||
|
||||
default_configs.push("keymap.toml".to_string());
|
||||
|
||||
let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs);
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.optflag("h", "help", "prints this help message");
|
||||
opts.optflag("v", "version", "prints the version");
|
||||
opts.optopt("c", "config", &c_msg, "FILE");
|
||||
|
||||
let matches = opts.parse(&args[1..]);
|
||||
if matches.is_err() {
|
||||
print_usage(&args[0], opts);
|
||||
exit(1);
|
||||
}
|
||||
let matches = matches.unwrap();
|
||||
if matches.opt_present("h") {
|
||||
print_usage(&args[0], opts);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if matches.opt_present("v") {
|
||||
println!("rusty-keys {}", VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
let config_file = matches.opt_str("c").unwrap_or_else(|| {
|
||||
let remaining_args = matches.free;
|
||||
if remaining_args.len() > 0 {
|
||||
remaining_args[0].clone()
|
||||
} else {
|
||||
for keymap in default_configs.drain(..) {
|
||||
if File::open(&keymap).is_ok() {
|
||||
return keymap;
|
||||
}
|
||||
}
|
||||
println!("Error: no keymap.toml found...");
|
||||
print_usage(&args[0], opts);
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
Config::new(config_file)
|
||||
}
|
||||
|
||||
use libc;
|
||||
|
||||
// Opaque Pointer Types
|
||||
type Pointer = *mut libc::c_void;
|
||||
type CFMachPortRef = Pointer;
|
||||
|
||||
// Integer Types
|
||||
type CGEventMask = u64;
|
||||
type CGEventTapOptions = u32;
|
||||
type CGEventTapPlacement = u32;
|
||||
|
||||
// Callback Type
|
||||
type CGEventTapCallBack = extern "C" fn(Pointer, CGEventType, CGEvent, &mut CallbackPointer) -> CGEvent;
|
||||
|
||||
// Constants
|
||||
const kCGSessionEventTap: CGEventTapLocation = CGEventTapLocation::HID;
|
||||
const kCGHeadInsertEventTap: CGEventTapPlacement = 0;
|
||||
|
||||
// Link to ApplicationServices/ApplicationServices.h and Carbon/Carbon.h
|
||||
#[link(name = "ApplicationServices", kind = "framework")]
|
||||
#[link(name = "Carbon", kind = "framework")]
|
||||
extern {
|
||||
|
||||
/// Pass through to the default loop modes
|
||||
pub static kCFRunLoopCommonModes: Pointer;
|
||||
|
||||
/// Pass through to the default allocator
|
||||
pub static kCFAllocatorDefault: Pointer;
|
||||
|
||||
/// Run the current threads loop in default mode
|
||||
pub fn CFRunLoopRun();
|
||||
|
||||
/// Obtain the current threads loop
|
||||
pub fn CFRunLoopGetCurrent() -> Pointer;
|
||||
|
||||
/// Create an event tap
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `place` - The location of the new event tap. Pass one of
|
||||
/// the constants listed in Event Tap Locations. Only
|
||||
/// processes running as the root user may locate an
|
||||
/// event tap at the point where HID events enter the
|
||||
/// window server; for other users, this function
|
||||
/// returns NULL.
|
||||
///
|
||||
/// * `options` - The placement of the new event tap in the
|
||||
/// list of active event taps. Pass one of the
|
||||
/// constants listed in Event Tap Placement.
|
||||
///
|
||||
/// * `eventsOfInterest` - A constant that specifies whether
|
||||
/// the new event tap is a passive listener or an
|
||||
/// active filter.
|
||||
///
|
||||
/// * `callback` - A bit mask that specifies the set of events
|
||||
/// to be observed. For a list of possible events,
|
||||
/// see Event Types. For information on how to
|
||||
/// specify the mask, see CGEventMask. If the event
|
||||
/// tap is not permitted to monitor one or more of
|
||||
/// the events specified in the eventsOfInterest
|
||||
/// parameter, then the appropriate bits in the mask
|
||||
/// are cleared. If that action results in an empty
|
||||
/// mask, this function returns NULL. callback
|
||||
///
|
||||
/// * `refcon` - An event tap callback function that you
|
||||
/// provide. Your callback function is invoked from
|
||||
/// the run loop to which the event tap is added as a
|
||||
/// source. The thread safety of the callback is
|
||||
/// defined by the run loop’s environment. To learn
|
||||
/// more about event tap callbacks, see
|
||||
/// CGEventTapCallBack. refcon
|
||||
///
|
||||
/// * `channel` - A pointer to user-defined data. This pointer
|
||||
/// is passed into the callback function specified in
|
||||
/// the callback parameter. Here we use it as a mpsc
|
||||
/// channel.
|
||||
pub fn CGEventTapCreate(
|
||||
tap: CGEventTapLocation,
|
||||
place: CGEventTapPlacement,
|
||||
options: CGEventTapOptions,
|
||||
eventsOfInterest: CGEventMask,
|
||||
callback: CGEventTapCallBack,
|
||||
channel: &CallbackPointer,
|
||||
) -> CFMachPortRef;
|
||||
|
||||
/// Creates a CFRunLoopSource object for a CFMachPort
|
||||
/// object.
|
||||
///
|
||||
/// The run loop source is not automatically added to
|
||||
/// a run loop. To add the source to a run loop, use
|
||||
/// CFRunLoopAddSource
|
||||
pub fn CFMachPortCreateRunLoopSource(
|
||||
allocator: Pointer,
|
||||
port: CFMachPortRef,
|
||||
order: libc::c_int,
|
||||
) -> Pointer;
|
||||
|
||||
/// Adds a CFRunLoopSource object to a run loop mode.
|
||||
pub fn CFRunLoopAddSource(
|
||||
run_loop: Pointer,
|
||||
run_loop_source: Pointer,
|
||||
mode: Pointer,
|
||||
);
|
||||
|
||||
pub fn CGEventTapEnable(port: CFMachPortRef, enable: bool);
|
||||
}
|
||||
|
||||
const NX_DEVICELCTLKEYMASK: u64 = 0x00000001;
|
||||
const NX_DEVICERCTLKEYMASK: u64 = 0x00002000;
|
||||
const NX_DEVICELSHIFTKEYMASK: u64 = 0x00000002;
|
||||
const NX_DEVICERSHIFTKEYMASK: u64 = 0x00000004;
|
||||
const NX_DEVICELCMDKEYMASK: u64 = 0x00000008;
|
||||
const NX_DEVICERCMDKEYMASK: u64 = 0x00000010;
|
||||
const NX_DEVICELALTKEYMASK: u64 = 0x00000020;
|
||||
const NX_DEVICERALTKEYMASK: u64 = 0x00000040;
|
||||
|
||||
const NX_DEVICECAPSLOCKMASK: u64 = 1 << 16;
|
||||
|
||||
/// This callback will be registered to be invoked from the run loop
|
||||
/// to which the event tap is added as a source.
|
||||
#[no_mangle]
|
||||
#[allow(unused_variables, improper_ctypes_definitions)]
|
||||
pub extern fn callback(proxy: Pointer, event_type: CGEventType, mut event: CGEvent, callback_pointer: &mut CallbackPointer) -> CGEvent {
|
||||
let (key_maps, event_source) = callback_pointer;
|
||||
match tapLocation {
|
||||
CGEventTapLocation::HID => {
|
||||
let user_data = event.get_integer_value_field(EventField::EVENT_SOURCE_USER_DATA);
|
||||
if user_data == uniqueHIDUserData {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
key_maps.send_event(&mut event, &event_source).expect("macos shouldn't error...")
|
||||
.unwrap_or_else(|| {
|
||||
event.set_type(CGEventType::Null);
|
||||
event
|
||||
}) // None means return NULL
|
||||
}
|
||||
|
||||
/// Redefine macro for bitshifting from header as function here
|
||||
pub fn CGEventMaskBit(eventType: CGEventType) -> CGEventMask {
|
||||
1 << (eventType as u32)
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ After=systemd-udevd.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/rusty-keys
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
Loading…
Reference in New Issue
Block a user