├── .cirrus.yml
├── .eslintrc.json
├── .gitattributes
├── .github
└── workflows
│ ├── create-tagged-release.yaml
│ └── release.yaml
├── .gitignore
├── .lintignore
├── .npmignore
├── .nvmrc
├── .prettierrc.json
├── CHANGELOG.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── _config.yml
├── binding.gyp
├── build.js
├── build_def
├── darwin
│ ├── binding.gyp
│ └── uiohook.gyp
├── linux
│ ├── binding.gyp
│ └── uiohook.gyp
└── win32
│ ├── binding.gyp
│ └── uiohook.gyp
├── helpers.js
├── index.d.ts
├── index.js
├── install.js
├── libuiohook
├── .gitignore
├── AUTHORS
├── CMakeLists.txt
├── COPYING.LESSER.md
├── COPYING.md
├── Makefile.am
├── README.md
├── bootstrap.sh
├── configure.ac
├── include
│ └── uiohook.h
├── m4
│ └── ax_pthread.m4
├── man
│ ├── hook_get_auto_repeat_delay.man
│ ├── hook_get_auto_repeat_rate.man
│ ├── hook_get_multi_click_time.man
│ ├── hook_get_pointer_acceleration_multiplier.man
│ ├── hook_get_pointer_acceleration_threshold.man
│ ├── hook_get_pointer_sensitivity.man
│ ├── hook_run.man
│ ├── hook_set_dispatch_proc.man
│ ├── hook_set_logger_proc.man
│ └── hook_stop.man
├── pc
│ └── uiohook.pc.in
├── src
│ ├── darwin
│ │ ├── input_helper.c
│ │ ├── input_helper.h
│ │ ├── input_hook.c
│ │ ├── post_event.c
│ │ └── system_properties.c
│ ├── demo_hook.c
│ ├── demo_hook_async.c
│ ├── demo_post.c
│ ├── demo_properties.c
│ ├── logger.c
│ ├── logger.h
│ ├── windows
│ │ ├── input_helper.c
│ │ ├── input_helper.h
│ │ ├── input_hook.c
│ │ ├── post_event.c
│ │ └── system_properties.c
│ └── x11
│ │ ├── input_helper.c
│ │ ├── input_helper.h
│ │ ├── input_hook.c
│ │ ├── post_event.c
│ │ └── system_properties.c
└── test
│ ├── input_helper_test.c
│ ├── minunit.h
│ ├── system_properties_test.c
│ └── uiohook_test.c
├── package-lock.json
├── package.json
├── src
├── iohook.cc
└── iohook.h
└── uiohook.gyp
/.cirrus.yml:
--------------------------------------------------------------------------------
1 | task:
2 | arm_container:
3 | image: node:lts #
4 |
5 | node_modules_cache: #
6 | folder: node_modules
7 | fingerprint_script: cat package-lock.json
8 | populate_script: npm ci --ignore-scripts
9 |
10 | macos_instance:
11 | image: ghcr.io/cirruslabs/macos-ventura-base:latest
12 |
13 | matrix: #
14 | - name: Build
15 | build_script: npm run build:ci --all
16 | binaries_artifacts:
17 | path: "build/*"
18 | # - name: Publish
19 | # depends_on:
20 | # - Build
21 | # # only_if: $BRANCH == "master" #
22 | # publish_script:
23 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": true,
5 | "shared-node-browser": true,
6 | "commonjs": true,
7 | "es2021": true
8 | },
9 | "parserOptions": {
10 | "sourceType": "module",
11 | "ecmaVersion": 12
12 | },
13 | "extends": ["eslint:recommended", "prettier"],
14 | "plugins": ["only-warn", "prettier"],
15 | "reportUnusedDisableDirectives": true,
16 | "rules": {
17 | "prettier/prettier": "error"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/create-tagged-release.yaml:
--------------------------------------------------------------------------------
1 | name: 'create-tagged-release'
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | tagged-release:
10 | name: 'Create Tagged Release'
11 | runs-on: 'ubuntu-latest'
12 |
13 | steps:
14 | - uses: 'marvinpinto/action-automatic-releases@latest'
15 | with:
16 | repo_token: '${{ secrets.GITHUB_TOKEN }}'
17 | prerelease: false
18 | draft: true
19 | files: |
20 | prebuilds/*.tar.gz
21 | prebuilds/*.zip
22 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Build iohook
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | branches:
9 | - 'main'
10 |
11 | jobs:
12 | build-windows:
13 | runs-on: windows-latest
14 |
15 | strategy:
16 | matrix:
17 | node-version: [18.x]
18 |
19 | steps:
20 | - uses: actions/checkout@v3
21 |
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | cache: 'npm'
27 | - run: npm ci --ignore-scripts
28 | - run: npm run build:ci --all --openssl_fips=''
29 |
30 | - name: Upload build artifacts
31 | uses: actions/upload-artifact@v3.1.2
32 | with:
33 | name: Windows Prebuilds
34 | path: prebuilds
35 |
36 | build-mac:
37 | runs-on: macos-latest
38 |
39 | strategy:
40 | matrix:
41 | node-version: [18.x]
42 |
43 | steps:
44 | - uses: actions/checkout@v3
45 |
46 | - name: Use Node.js ${{ matrix.node-version }}
47 | uses: actions/setup-node@v3
48 | with:
49 | node-version: ${{ matrix.node-version }}
50 | cache: 'npm'
51 | - run: npm ci --ignore-scripts
52 | - run: npm run build:ci --all
53 |
54 | - name: Upload build artifacts
55 | uses: actions/upload-artifact@v3.1.2
56 | with:
57 | name: macOS Prebuilds
58 | path: prebuilds
59 |
60 | build-linux:
61 | runs-on: ubuntu-latest
62 |
63 | strategy:
64 | matrix:
65 | node-version: [18.x]
66 |
67 | steps:
68 | - uses: actions/checkout@v3
69 |
70 | - name: Use Node.js ${{ matrix.node-version }}
71 | uses: actions/setup-node@v3
72 | with:
73 | node-version: ${{ matrix.node-version }}
74 | cache: 'npm'
75 |
76 | - name: Install dependencies
77 | run: |
78 | sudo apt-get install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
79 | sudo apt-get update
80 | - name: Install dependencies (Ubuntu)
81 | run: sudo apt-get install libxtst-dev
82 |
83 | - run: npm ci --ignore-scripts
84 | - run: npm run build:ci --all
85 |
86 | - name: Upload build artifacts
87 | uses: actions/upload-artifact@v3.1.2
88 | with:
89 | name: Linux (Ubuntu) Prebuilds
90 | path: prebuilds
91 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Serverless directories
108 | .serverless/
109 |
110 | # FuseBox cache
111 | .fusebox/
112 |
113 | # DynamoDB Local files
114 | .dynamodb/
115 |
116 | # TernJS port file
117 | .tern-port
118 |
119 | # Stores VSCode versions used for testing VSCode extensions
120 | .vscode-test
121 |
122 | # yarn v2
123 | .yarn/cache
124 | .yarn/unplugged
125 | .yarn/build-state.yml
126 | .yarn/install-state.gz
127 | .pnp.*
128 |
129 | # iohook
130 | build
131 | builds
132 | prebuilds
--------------------------------------------------------------------------------
/.lintignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | .scratch
4 | build
5 | builds
6 | docs/.vuepress/dist
7 | logs
8 | node_modules
9 | prebuilds
10 | libuiohook
11 | .env
12 | *.DS_Store
13 | *.iml
14 | *.log
15 | examples/electron-example/.DS_Store
16 | npm-debug.log*
17 | stacktrace*
18 | binding.gyp
19 | uiohook.gyp
20 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 | .scratch/
4 | build/
5 | builds/
6 | docs/.vuepress/dist/
7 | logs/
8 | node_modules/
9 | prebuilds/
10 | libuiohook/
11 | .github/
12 | CMakeFiles/
13 | docs/
14 | examples/
15 | test/
16 | .env
17 | *.DS_Store
18 | *.iml
19 | *.log
20 | npm-debug.log*
21 | stacktrace*
22 | /binding.gyp
23 | /uiohook.gyp
24 | .lintignore
25 | .eslintrc.json
26 | .prettierrc.json
27 | _config.yml
28 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 14
2 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "overrides": [
3 | {
4 | "files": [".prettierrc", ".eslintrc"],
5 | "options": {
6 | "parser": "json"
7 | }
8 | }
9 | ],
10 | "semi": true,
11 | "singleQuote": true
12 | }
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## v0.7.2
4 |
5 | - fix: Fix macOS and linux prebuilt.
6 |
7 | ## v0.7.1
8 |
9 | - fix: add missing dependency.
10 |
11 | ## v0.7.0
12 |
13 | - feat: Add support for Electron 9+ and Node 12+.
14 | - fix: drop cmake-js for node-gyp.
15 |
16 | ## v0.6.6
17 |
18 | - feat: Add support for Electron 9.
19 |
20 | ## v0.6.5
21 |
22 | - feat: Add support for Electron 8.
23 |
24 | ## v0.6.4
25 |
26 | - feat: Add support for Node 13.
27 | - chore: Fix npm vulnerabilities with audit.
28 |
29 | ## v0.6.3
30 |
31 | - fix: depreacated use of objc_msgSend.
32 | - fix: Update start() types.
33 | - feat: improve x11 use.
34 |
35 | ## v0.6.2
36 |
37 | - feat: Add support for Electron 7.
38 |
39 | ## v0.6.1
40 |
41 | - fix: Update repo name.
42 |
43 | ## v0.6.0
44 |
45 | - feat: Add support for Electron 6.
46 | - deprecation: Drop support for electron < 4.X.X .
47 |
48 | ## v0.5.1
49 |
50 | - chore: Use SSL options when dealing with prebuilts.
51 |
52 | ## v0.5.0
53 |
54 | - deprecation: Drop support for node < 8, electron < 1.8
55 |
56 | ## v0.4.6
57 |
58 | - fix: fix Win32 builds
59 |
60 | ## v0.4.5
61 |
62 | - feat: add support for Electron 5, Node 12
63 |
64 | ## v0.4.4
65 |
66 | - hotfix: fix corrupted 0.4.3 release.
67 |
68 | ## v0.4.3
69 |
70 | - feat: ability to build for a single platform/target.
71 |
72 | ## v0.4.2
73 |
74 | - fix: use cmake-js fork to support Electron 4+ on Windows.
75 |
76 | ## v0.4.1
77 |
78 | - fix: use VS C++ for Electron >= v4.
79 |
80 | ## v0.4.0
81 |
82 | - feat: add support for Electron 4.
83 |
84 | ## v0.3.1
85 |
86 | - fix: allow use of iohook.registerShortcut without a 3rd releaseCallback argument.
87 |
88 | ## v0.3.0
89 |
90 | - feat: drop segfault-handler dependency.
91 |
92 | ## v0.2.6
93 |
94 | - feat: add support for Node 10
95 |
96 | ## v0.2.5
97 |
98 | - feat: git@github.com:wilix-team/iohook.git
99 | - fix: fix documentation link.
100 | - chore : repo transfered to wilix-team org.
101 |
102 | ## v0.2.4
103 |
104 | - feat: ability to listen for when a keyboard shortcut has been released
105 | - feat: ability to unregister a shortcut via the shortcut's keys
106 |
107 | ## v0.2.3
108 |
109 | - feat: Bump libuiohook version
110 | - chore: many travis build improvements
111 |
112 | ## v0.2.2
113 |
114 | - feat: Add support for Node 10 (for real this time...)
115 |
116 | ## v0.2.1
117 |
118 | - feat: Add support for Electron 2.0
119 | - feat: Drop support for node < 6, electron < 1.2
120 |
121 | ## v0.2.0
122 |
123 | - feat: Add modifier keys support
124 | - feat: Add online documentation
125 | - feat: Add tests (reverted for now)
126 |
127 | ## v0.1.16
128 |
129 | - feat: Add support for Electron 2.0
130 | - feat: Add support for NodeJS 10.x
131 | - deprecation: Drop support for Linux ia32 (like NodeJS itself)
132 |
133 | ## v0.1.15
134 |
135 | - feat: Bump libiohook to version 1.1
136 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | macro(use_c99)
4 | if (CMAKE_VERSION VERSION_LESS "3.1")
5 | if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
6 | set (CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}")
7 | endif ()
8 | else ()
9 | set (CMAKE_C_STANDARD 99)
10 | endif ()
11 | endmacro(use_c99)
12 |
13 | use_c99()
14 |
15 | set (CMAKE_CXX_STANDARD 11)
16 |
17 | project(iohook)
18 |
19 | if(WIN32 OR WIN64)
20 | add_subdirectory(libuiohook ${CMAKE_CURRENT_SOURCE_DIR}/libuiohook)
21 | elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
22 |
23 | #bootstrap and configure
24 | set(_config_headers "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/config.h")
25 | add_custom_target( "prepare_iuhook"
26 | COMMAND "./bootstrap.sh"
27 | COMMAND "./configure"
28 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook")
29 |
30 | file(GLOB SOURCE_UIHOOK_FILES "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/logger.c"
31 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/logger.h"
32 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/x11/*.c"
33 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/x11/*.h"
34 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/config.h" )
35 |
36 | add_library( "uiohook" STATIC ${SOURCE_UIHOOK_FILES} )
37 | set_target_properties("uiohook" PROPERTIES COMPILE_FLAGS "-DHAVE_CONFIG_H=1 -fPIC")
38 | add_dependencies( "uiohook" "prepare_iuhook")
39 | target_include_directories("uiohook" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/" "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src" ${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/x11 )
40 | else()
41 | #bootstrap and configure
42 | set(_config_headers "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/config.h")
43 | add_custom_target( "prepare_iuhook"
44 | COMMAND "./bootstrap.sh"
45 | COMMAND "./configure"
46 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook")
47 |
48 | file(GLOB SOURCE_UIHOOK_FILES "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/logger.c"
49 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/logger.h"
50 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/darwin/*.c"
51 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/darwin/*.h"
52 | "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/config.h" )
53 |
54 | add_library( "uiohook" STATIC ${SOURCE_UIHOOK_FILES} )
55 | set_target_properties("uiohook" PROPERTIES COMPILE_FLAGS "-DHAVE_CONFIG_H=1")
56 | add_dependencies( "uiohook" "prepare_iuhook")
57 | target_include_directories("uiohook" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/include/" "${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src" ${CMAKE_CURRENT_SOURCE_DIR}/libuiohook/src/darwin )
58 |
59 | endif()
60 |
61 | # Build a shared library named after the project from the files in `src/`
62 | file(GLOB SOURCE_FILES "src/*.cc" "src/*.h")
63 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
64 |
65 | # Gives our library file a .node extension without any "lib" prefix
66 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
67 |
68 | # Essential include files to build a node addon,
69 | # You should add this line in every CMake.js based project
70 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC})
71 |
72 | # Essential library files to link to a node addon
73 | # You should add this line in every CMake.js based project
74 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB} "uiohook")
75 |
76 | if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
77 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB} "uiohook" "xkbfile" "xkbcommon-x11" "xkbcommon" "X11-xcb" "xcb" "Xinerama" "Xt" "Xtst" "X11")
78 | endif()
79 |
80 | if(CMAKE_SYSTEM_NAME MATCHES "(Darwin)")
81 | find_library(FRAMEWORK_IOKIT IOKit)
82 | find_library(FRAMEWORK_Carbon Carbon)
83 | target_link_libraries(${PROJECT_NAME} ${FRAMEWORK_IOKIT} ${FRAMEWORK_Carbon})
84 | endif()
85 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Evan / Fero
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | iohook is a global native keyboard and mouse listener for Node.js. This is a fork of https://github.com/wilix-team/iohook, which is abandoned and unmainntained.
6 |
7 | We provide a modern CI pipeline for easy, fast, reliable, builds of iohook for modern versions of Node and Electron. At Robolab, we use this library in [MechaKeys](https://v2.robolab.io), the integrated typing environment.
8 |
9 | ## Supported Versions
10 | - Versions >= 0.9.4 support Electron 25 and Node 18
11 | - For older version support, use the wilix-team library
12 |
13 | ## Installation
14 | ```sh
15 | # Install MechaKeys iohook
16 | npm install --save @mechakeys/iohook
17 | ```
18 |
19 | ```sh
20 | # Install prebuilt binaries for your platform, architecture, and ABIs.
21 | cd ./node_modules/@mechakeys/iohook
22 | npm i
23 | node install.js
24 | ```
25 |
26 | By default, prebuilds will be downloaded for your own platform and architecture, but you can download specific ones through your package.json:
27 | ```json
28 | "iohook": {
29 | "targets": [
30 | "node-108",
31 | "electron-116"
32 | ],
33 | "platforms": [
34 | "win32",
35 | "darwin",
36 | "linux"
37 | ],
38 | "arches": [
39 | "x64",
40 | ]
41 | }
42 | ```
43 |
44 | As of right now, we do not build 32-bit versions.
45 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-midnight
2 |
--------------------------------------------------------------------------------
/binding.gyp:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [{
3 | "target_name": "iohook",
4 | "win_delay_load_hook": "true",
5 | "type": "loadable_module",
6 | "sources": [
7 | "src/iohook.cc",
8 | "src/iohook.h"
9 | ],
10 | "dependencies": [
11 | "./uiohook.gyp:uiohook"
12 | ],
13 | "include_dirs": [
14 | " targetStr.split('-'));
45 | if (process.env.npm_config_targets === 'all') {
46 | options.targets = supportedTargets.map((arr) => [arr[0], arr[2]]);
47 | options.platforms = ['win32', 'darwin', 'linux'];
48 | options.arches = ['x64', 'ia32'];
49 | }
50 | if (process.env.npm_config_platforms) {
51 | options.platforms = options.platforms.concat(
52 | process.env.npm_config_platforms.split(',')
53 | );
54 | }
55 | if (process.env.npm_config_arches) {
56 | options.arches = options.arches.concat(
57 | process.env.npm_config_arches.split(',')
58 | );
59 | }
60 |
61 | if (options.targets.length > 0) {
62 | targets = options.targets.map((e) => [
63 | e[0],
64 | nodeAbi.getTarget(e[1], e[0]),
65 | e[1],
66 | ]);
67 | } else {
68 | const runtime = process.versions['electron'] ? 'electron' : 'node';
69 | const version = process.versions.node;
70 | const abi = process.versions.modules;
71 | targets = [[runtime, version, abi]];
72 | }
73 | }
74 |
75 | targets.forEach((parts) => {
76 | let runtime = parts[0];
77 | let version = parts[1];
78 | let abi = parts[2];
79 | chain = chain
80 | .then(function () {
81 | return build(runtime, version, abi);
82 | })
83 | .then(function () {
84 | return tarGz(runtime, abi);
85 | })
86 | .catch((err) => {
87 | console.error(err);
88 | process.exit(1);
89 | });
90 | });
91 |
92 | // chain = chain.then(function () {
93 | // if ('upload' in argv && argv['upload'] === 'false') {
94 | // // If no upload has been specified, don't attempt to upload
95 | // return;
96 | // }
97 |
98 | // return uploadFiles(files);
99 | // });
100 |
101 | cpGyp();
102 | }
103 |
104 | function cpGyp() {
105 | try {
106 | fs.unlinkSync(path.join(__dirname, 'binding.gyp'));
107 | fs.unlinkSync(path.join(__dirname, 'uiohook.gyp'));
108 | } catch (e) {}
109 | switch (process.platform) {
110 | case 'win32':
111 | case 'darwin':
112 | fs.copySync(
113 | path.join(__dirname, 'build_def', process.platform, 'binding.gyp'),
114 | path.join(__dirname, 'binding.gyp')
115 | );
116 | fs.copySync(
117 | path.join(__dirname, 'build_def', process.platform, 'uiohook.gyp'),
118 | path.join(__dirname, 'uiohook.gyp')
119 | );
120 | break;
121 | default:
122 | fs.copySync(
123 | path.join(__dirname, 'build_def', 'linux', 'binding.gyp'),
124 | path.join(__dirname, 'binding.gyp')
125 | );
126 | fs.copySync(
127 | path.join(__dirname, 'build_def', 'linux', 'uiohook.gyp'),
128 | path.join(__dirname, 'uiohook.gyp')
129 | );
130 | break;
131 | }
132 | }
133 |
134 | function build(runtime, version, abi) {
135 | return new Promise(function (resolve, reject) {
136 | let args = [
137 | 'configure',
138 | 'rebuild',
139 | '--target=' + version,
140 | '--arch=' + arch,
141 | ];
142 |
143 | if (/^electron/i.test(runtime)) {
144 | args.push('--dist-url=https://artifacts.electronjs.org/headers/dist');
145 | }
146 |
147 | if (parseInt(abi) >= 80) {
148 | if (arch === 'x64') {
149 | args.push('--v8_enable_pointer_compression=1');
150 | } else {
151 | args.push('--v8_enable_pointer_compression=0');
152 | args.push('--v8_enable_31bit_smis_on_64bit_arch=1');
153 | }
154 | }
155 | if (process.platform !== 'win32') {
156 | if (parseInt(abi) >= 64) {
157 | args.push('--build_v8_with_gn=false');
158 | }
159 | if (parseInt(abi) >= 67) {
160 | args.push('--enable_lto=false');
161 | }
162 | }
163 |
164 | console.log('Building iohook for ' + runtime + ' v' + version + '>>>>');
165 | if (process.platform === 'win32') {
166 | if (version.split('.')[0] >= 4) {
167 | process.env.msvs_toolset = 15;
168 | process.env.msvs_version = 2022;
169 | } else {
170 | process.env.msvs_toolset = 12;
171 | process.env.msvs_version = 2022;
172 | }
173 | args.push('--msvs_version=' + process.env.msvs_version);
174 | } else {
175 | process.env.gyp_iohook_runtime = runtime;
176 | process.env.gyp_iohook_abi = abi;
177 | process.env.gyp_iohook_platform = process.platform;
178 | process.env.gyp_iohook_arch = arch;
179 | }
180 |
181 | let proc = spawn(gypJsPath, args, {
182 | env: process.env,
183 | });
184 | proc.stdout.pipe(process.stdout);
185 | proc.stderr.pipe(process.stderr);
186 | proc.on('exit', function (code, sig) {
187 | if (code === 1) {
188 | return reject(new Error('Failed to build...'));
189 | }
190 | resolve();
191 | });
192 | });
193 | }
194 |
195 | function tarGz(runtime, abi) {
196 | const FILES_TO_ARCHIVE = {
197 | win32: ['build/Release/iohook.node', 'build/Release/uiohook.dll'],
198 | linux: ['build/Release/iohook.node', 'build/Release/uiohook.so'],
199 | darwin: ['build/Release/iohook.node', 'build/Release/uiohook.dylib'],
200 | };
201 | const tarPath =
202 | 'prebuilds/iohook-v' +
203 | pkg.version +
204 | '-' +
205 | runtime +
206 | '-v' +
207 | abi +
208 | '-' +
209 | process.platform +
210 | '-' +
211 | arch +
212 | '.tar.gz';
213 |
214 | files.push(tarPath);
215 |
216 | if (!fs.existsSync(path.dirname(tarPath))) {
217 | fs.mkdirSync(path.dirname(tarPath));
218 | }
219 |
220 | tar.c(
221 | {
222 | gzip: true,
223 | file: tarPath,
224 | sync: true,
225 | },
226 | FILES_TO_ARCHIVE[process.platform]
227 | );
228 | }
229 |
230 | // function uploadFiles(files) {
231 | // const upload = require('prebuild/upload');
232 | // return new Promise(function (resolve, reject) {
233 | // console.log(
234 | // 'Uploading ' + files.length + ' prebuilds(s) to Github releases'
235 | // );
236 | // let opts = {
237 | // pkg: pkg,
238 | // files: files,
239 | // 'tag-prefix': 'v',
240 | // upload: process.env.GITHUB_ACCESS_TOKEN,
241 | // };
242 | // upload(opts, function (err, result) {
243 | // if (err) {
244 | // return reject(err);
245 | // }
246 | // console.log('Found ' + result.old.length + ' prebuild(s) on Github');
247 | // if (result.old.length) {
248 | // result.old.forEach(function (build) {
249 | // console.log('-> ' + build);
250 | // });
251 | // }
252 | // console.log(
253 | // 'Uploaded ' + result.new.length + ' new prebuild(s) to Github'
254 | // );
255 | // if (result.new.length) {
256 | // result.new.forEach(function (build) {
257 | // console.log('-> ' + build);
258 | // });
259 | // }
260 | // resolve();
261 | // });
262 | // });
263 | // }
264 |
--------------------------------------------------------------------------------
/build_def/darwin/binding.gyp:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [{
3 | "target_name": "iohook",
4 | "win_delay_load_hook": "true",
5 | "type": "loadable_module",
6 | "sources": [
7 | "src/iohook.cc",
8 | "src/iohook.h"
9 | ],
10 | "dependencies": [
11 | "./uiohook.gyp:uiohook"
12 | ],
13 | "cflags": [
14 | "-std=c99"
15 | ],
16 | "link_settings": {
17 | "libraries": [
18 | "-Wl,-rpath,@executable_path/.",
19 | "-Wl,-rpath,@loader_path/.",
20 | "-Wl,-rpath, 5) {
11 | console.log("Can't resolve main package.json file");
12 | return {
13 | targets: [],
14 | platforms: [process.platform],
15 | arches: [process.arch],
16 | };
17 | }
18 | let mainPath = Array(attempts).join('../');
19 | try {
20 | const content = fs.readFileSync(
21 | path.join(__dirname, mainPath, 'package.json'),
22 | 'utf-8'
23 | );
24 | const packageJson = JSON.parse(content);
25 | const opts = packageJson.iohook || {};
26 | if (!opts.targets) {
27 | opts.targets = [];
28 | }
29 | if (!opts.platforms) opts.platforms = [process.platform];
30 | if (!opts.arches) opts.arches = [process.arch];
31 | return opts;
32 | } catch (e) {
33 | return optionsFromPackage(attempts + 1);
34 | }
35 | }
36 |
37 | function printManualBuildParams() {
38 | const runtime = process.versions['electron'] ? 'electron' : 'node';
39 | const essential =
40 | runtime +
41 | '-v' +
42 | process.versions.modules +
43 | '-' +
44 | process.platform +
45 | '-' +
46 | process.arch;
47 | const modulePath = path.join(
48 | __dirname,
49 | 'builds',
50 | essential,
51 | 'build',
52 | 'Release',
53 | 'iohook.node'
54 | );
55 | console.info(
56 | `Runtime: ${runtime} ABI: ${process.versions.modules} Platform: ${process.platform} ARCH: ${process.arch}`
57 | );
58 | console.info('The path is:', modulePath);
59 | }
60 |
61 | module.exports = { optionsFromPackage, printManualBuildParams };
62 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 |
3 | /**
4 | * Native module for hooking keyboard and mouse events
5 | */
6 | declare class IOHook extends EventEmitter {
7 | /**
8 | * Start hooking engine. Call it when you ready to receive events
9 | * @param {boolean} [enableLogger] If true, module will publish debug information to stdout
10 | */
11 | start(enableLogger?: boolean): void;
12 |
13 | /**
14 | * Stop rising keyboard/mouse events
15 | */
16 | stop(): void;
17 |
18 | /**
19 | * Manual native code load. Call this function only if unload called before
20 | */
21 | load(): void;
22 |
23 | /**
24 | * Unload native code and free memory and system hooks
25 | */
26 | unload(): void;
27 |
28 | /**
29 | * Enable/Disable stdout debug
30 | * @param {boolean} mode
31 | */
32 | setDebug(mode: boolean): void;
33 |
34 | /**
35 | * Specify that key event's `rawcode` property should be used instead of
36 | * `keycode` when listening for key presses.
37 | *
38 | * This allows iohook to be used in conjunction with other programs that may
39 | * only provide a keycode.
40 | * @param {Boolean} using
41 | */
42 | useRawcode(using: boolean): void;
43 |
44 | /**
45 | * Enable mouse click propagation (enabled by default).
46 | * The click event are emitted and propagated.
47 | */
48 | enableClickPropagation(): void;
49 |
50 | /**
51 | * Disable mouse click propagation.
52 | * The click event are captured and the event emitted but not propagated to the window.
53 | */
54 | disableClickPropagation(): void;
55 |
56 | /**
57 | * Register global shortcut. When all keys in keys array pressed, callback will be called
58 | * @param {Array} keys Array of keycodes
59 | * @param {Function} callback Callback for when shortcut pressed
60 | * @param {Function} [releaseCallback] Callback for when shortcut released
61 | * @return {number} ShortcutId for unregister
62 | */
63 | registerShortcut(
64 | keys: Array,
65 | callback: Function,
66 | releaseCallback?: Function
67 | ): number;
68 |
69 | /**
70 | * Unregister shortcut by ShortcutId
71 | * @param {number} shortcutId
72 | */
73 | unregisterShortcut(shortcutId: number): void;
74 |
75 | /**
76 | * Unregister shortcut via its key codes
77 | * @param {Array} keys
78 | */
79 | unregisterShortcut(keys: Array): void;
80 |
81 | /**
82 | * Unregister all shortcuts
83 | */
84 | unregisterAllShortcuts(): void;
85 | }
86 |
87 | declare interface IOHookEvent {
88 | type: string;
89 | keychar?: number;
90 | keycode?: number;
91 | rawcode?: number;
92 | button?: number;
93 | clicks?: number;
94 | x?: number;
95 | y?: number;
96 | }
97 |
98 | declare const iohook: IOHook;
99 |
100 | export = iohook;
101 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('events');
2 | const path = require('path');
3 |
4 | const runtime = process.versions['electron'] ? 'electron' : 'node';
5 | const essential =
6 | runtime +
7 | '-v' +
8 | process.versions.modules +
9 | '-' +
10 | process.platform +
11 | '-' +
12 | process.arch;
13 | const modulePath = path.join(
14 | __dirname,
15 | 'builds',
16 | essential,
17 | 'build',
18 | 'Release',
19 | 'iohook.node'
20 | );
21 | if (process.env.DEBUG) {
22 | console.info('Loading native binary:', modulePath);
23 | }
24 | let NodeHookAddon = require(modulePath);
25 |
26 | const events = {
27 | 3: 'keypress',
28 | 4: 'keydown',
29 | 5: 'keyup',
30 | 6: 'mouseclick',
31 | 7: 'mousedown',
32 | 8: 'mouseup',
33 | 9: 'mousemove',
34 | 10: 'mousedrag',
35 | 11: 'mousewheel',
36 | };
37 |
38 | class IOHook extends EventEmitter {
39 | constructor() {
40 | super();
41 | this.active = false;
42 | this.shortcuts = [];
43 | this.eventProperty = 'keycode';
44 | this.activatedShortcuts = [];
45 |
46 | this.lastKeydownShift = false;
47 | this.lastKeydownAlt = false;
48 | this.lastKeydownCtrl = false;
49 | this.lastKeydownMeta = false;
50 |
51 | this.load();
52 | this.setDebug(false);
53 | }
54 |
55 | /**
56 | * Start hook process
57 | * @param {boolean} [enableLogger] Turn on debug logging
58 | */
59 | start(enableLogger) {
60 | if (!this.active) {
61 | this.active = true;
62 | this.setDebug(enableLogger);
63 | }
64 | }
65 |
66 | /**
67 | * Shutdown event hook
68 | */
69 | stop() {
70 | if (this.active) {
71 | this.active = false;
72 | }
73 | }
74 |
75 | /**
76 | * Register global shortcut. When all keys in keys array pressed, callback will be called
77 | * @param {Array} keys Array of keycodes
78 | * @param {Function} callback Callback for when shortcut pressed
79 | * @param {Function} [releaseCallback] Callback for when shortcut has been released
80 | * @return {number} ShortcutId for unregister
81 | */
82 | registerShortcut(keys, callback, releaseCallback) {
83 | let shortcut = {};
84 | let shortcutId = Date.now() + Math.random();
85 | keys.forEach((keyCode) => {
86 | shortcut[keyCode] = false;
87 | });
88 | shortcut.id = shortcutId;
89 | shortcut.callback = callback;
90 | shortcut.releaseCallback = releaseCallback;
91 | this.shortcuts.push(shortcut);
92 | return shortcutId;
93 | }
94 |
95 | /**
96 | * Unregister shortcut by ShortcutId
97 | * @param shortcutId
98 | */
99 | unregisterShortcut(shortcutId) {
100 | this.shortcuts.forEach((shortcut, i) => {
101 | if (shortcut.id === shortcutId) {
102 | this.shortcuts.splice(i, 1);
103 | }
104 | });
105 | }
106 |
107 | /**
108 | * Unregister shortcut via its key codes
109 | * @param {string} keyCodes Keyboard keys matching the shortcut that should be unregistered
110 | */
111 | unregisterShortcutByKeys(keyCodes) {
112 | // A traditional loop is used in order to access `this` from inside
113 | for (let i = 0; i < this.shortcuts.length; i++) {
114 | let shortcut = this.shortcuts[i];
115 |
116 | // Convert any keycode numbers to strings
117 | keyCodes.forEach((key, index) => {
118 | if (typeof key !== 'string' && !(key instanceof String)) {
119 | // Convert to string
120 | keyCodes[index] = key.toString();
121 | }
122 | });
123 |
124 | // Check if this is our shortcut
125 | Object.keys(shortcut).every((key) => {
126 | if (key === 'callback' || key === 'id') return;
127 |
128 | // Remove all given keys from keyCodes
129 | // If any are not in this shortcut, then this shortcut does not match
130 | // If at the end we have eliminated all codes in keyCodes, then we have succeeded
131 | let index = keyCodes.indexOf(key);
132 | if (index === -1) return false; // break
133 |
134 | // Remove this key from the given keyCodes array
135 | keyCodes.splice(index, 1);
136 | return true;
137 | });
138 |
139 | // Is this the shortcut we want to remove?
140 | if (keyCodes.length === 0) {
141 | // Unregister this shortcut
142 | this.shortcuts.splice(i, 1);
143 | return;
144 | }
145 | }
146 | }
147 |
148 | /**
149 | * Unregister all shortcuts
150 | */
151 | unregisterAllShortcuts() {
152 | this.shortcuts.splice(0, this.shortcuts.length);
153 | }
154 |
155 | /**
156 | * Load native module
157 | */
158 | load() {
159 | NodeHookAddon.startHook(this._handler.bind(this), this.debug || false);
160 | }
161 |
162 | /**
163 | * Unload native module and stop hook
164 | */
165 | unload() {
166 | this.stop();
167 | NodeHookAddon.stopHook();
168 | }
169 |
170 | /**
171 | * Enable or disable native debug output
172 | * @param {Boolean} mode
173 | */
174 | setDebug(mode) {
175 | NodeHookAddon.debugEnable(mode);
176 | }
177 |
178 | /**
179 | * Specify that key event's `rawcode` property should be used instead of
180 | * `keycode` when listening for key presses.
181 | *
182 | * This allows iohook to be used in conjunction with other programs that may
183 | * only provide a keycode.
184 | * @param {Boolean} using
185 | */
186 | useRawcode(using) {
187 | // If true, use rawcode, otherwise use keycode
188 | this.eventProperty = using ? 'rawcode' : 'keycode';
189 | }
190 |
191 | /**
192 | * Disable mouse click propagation.
193 | * The click event are captured and the event emitted but not propagated to the window.
194 | */
195 | disableClickPropagation() {
196 | NodeHookAddon.grabMouseClick(true);
197 | }
198 |
199 | /**
200 | * Enable mouse click propagation (enabled by default).
201 | * The click event are emitted and propagated.
202 | */
203 | enableClickPropagation() {
204 | NodeHookAddon.grabMouseClick(false);
205 | }
206 |
207 | /**
208 | * Local event handler. Don't use it in your code!
209 | * @param msg Raw event message
210 | * @private
211 | */
212 | _handler(msg) {
213 | if (this.active === false || !msg) return;
214 |
215 | if (events[msg.type]) {
216 | const event = msg.mouse || msg.keyboard || msg.wheel;
217 |
218 | event.type = events[msg.type];
219 |
220 | this._handleShift(event);
221 | this._handleAlt(event);
222 | this._handleCtrl(event);
223 | this._handleMeta(event);
224 |
225 | this.emit(events[msg.type], event);
226 |
227 | // If there is any registered shortcuts then handle them.
228 | if (
229 | (event.type === 'keydown' || event.type === 'keyup') &&
230 | iohook.shortcuts.length > 0
231 | ) {
232 | this._handleShortcut(event);
233 | }
234 | }
235 | }
236 |
237 | /**
238 | * Handles the shift key. Whenever shift is pressed, all future events would
239 | * contain { shiftKey: true } in its object, until the shift key is released.
240 | * @param event Event object
241 | * @private
242 | */
243 | _handleShift(event) {
244 | if (event.type === 'keyup' && event.shiftKey) {
245 | this.lastKeydownShift = false;
246 | }
247 |
248 | if (event.type === 'keydown' && event.shiftKey) {
249 | this.lastKeydownShift = true;
250 | }
251 |
252 | if (this.lastKeydownShift) {
253 | event.shiftKey = true;
254 | }
255 | }
256 |
257 | /**
258 | * Handles the alt key. Whenever alt is pressed, all future events would
259 | * contain { altKey: true } in its object, until the alt key is released.
260 | * @param event Event object
261 | * @private
262 | */
263 | _handleAlt(event) {
264 | if (event.type === 'keyup' && event.altKey) {
265 | this.lastKeydownAlt = false;
266 | }
267 |
268 | if (event.type === 'keydown' && event.altKey) {
269 | this.lastKeydownAlt = true;
270 | }
271 |
272 | if (this.lastKeydownAlt) {
273 | event.altKey = true;
274 | }
275 | }
276 |
277 | /**
278 | * Handles the ctrl key. Whenever ctrl is pressed, all future events would
279 | * contain { ctrlKey: true } in its object, until the ctrl key is released.
280 | * @param event Event object
281 | * @private
282 | */
283 | _handleCtrl(event) {
284 | if (event.type === 'keyup' && event.ctrlKey) {
285 | this.lastKeydownCtrl = false;
286 | }
287 |
288 | if (event.type === 'keydown' && event.ctrlKey) {
289 | this.lastKeydownCtrl = true;
290 | }
291 |
292 | if (this.lastKeydownCtrl) {
293 | event.ctrlKey = true;
294 | }
295 | }
296 |
297 | /**
298 | * Handles the meta key. Whenever meta is pressed, all future events would
299 | * contain { metaKey: true } in its object, until the meta key is released.
300 | * @param event Event object
301 | * @private
302 | */
303 | _handleMeta(event) {
304 | if (event.type === 'keyup' && event.metaKey) {
305 | this.lastKeydownMeta = false;
306 | }
307 |
308 | if (event.type === 'keydown' && event.metaKey) {
309 | this.lastKeydownMeta = true;
310 | }
311 |
312 | if (this.lastKeydownMeta) {
313 | event.metaKey = true;
314 | }
315 | }
316 |
317 | /**
318 | * Local shortcut event handler
319 | * @param event Event object
320 | * @private
321 | */
322 | _handleShortcut(event) {
323 | if (this.active === false) {
324 | return;
325 | }
326 |
327 | // Keep track of shortcuts that are currently active
328 | let activatedShortcuts = this.activatedShortcuts;
329 |
330 | if (event.type === 'keydown') {
331 | this.shortcuts.forEach((shortcut) => {
332 | if (shortcut[event[this.eventProperty]] !== undefined) {
333 | // Mark this key as currently being pressed
334 | shortcut[event[this.eventProperty]] = true;
335 |
336 | let keysTmpArray = [];
337 | let callme = true;
338 |
339 | // Iterate through each keyboard key in this shortcut
340 | Object.keys(shortcut).forEach((key) => {
341 | if (key === 'callback' || key === 'releaseCallback' || key === 'id')
342 | return;
343 |
344 | // If one of the keys aren't pressed...
345 | if (shortcut[key] === false) {
346 | // Don't call the callback and empty our temp tracking array
347 | callme = false;
348 | keysTmpArray.splice(0, keysTmpArray.length);
349 |
350 | return;
351 | }
352 |
353 | // Otherwise, this key is being pressed.
354 | // Add it to the array of keyboard keys we will send as an argument
355 | // to our callback
356 | keysTmpArray.push(key);
357 | });
358 | if (callme) {
359 | shortcut.callback(keysTmpArray);
360 |
361 | // Add this shortcut from our activate shortcuts array if not
362 | // already activated
363 | if (activatedShortcuts.indexOf(shortcut) === -1) {
364 | activatedShortcuts.push(shortcut);
365 | }
366 | }
367 | }
368 | });
369 | } else if (event.type === 'keyup') {
370 | // Mark this key as currently not being pressed in all of our shortcuts
371 | this.shortcuts.forEach((shortcut) => {
372 | if (shortcut[event[this.eventProperty]] !== undefined) {
373 | shortcut[event[this.eventProperty]] = false;
374 | }
375 | });
376 |
377 | // Check if any of our currently pressed shortcuts have been released
378 | // "released" means that all of the keys that the shortcut defines are no
379 | // longer being pressed
380 | this.activatedShortcuts.forEach((shortcut) => {
381 | if (shortcut[event[this.eventProperty]] === undefined) return;
382 |
383 | let shortcutReleased = true;
384 | let keysTmpArray = [];
385 | Object.keys(shortcut).forEach((key) => {
386 | if (key === 'callback' || key === 'releaseCallback' || key === 'id')
387 | return;
388 | keysTmpArray.push(key);
389 |
390 | // If any key is true, and thus still pressed, the shortcut is still
391 | // being held
392 | if (shortcut[key]) {
393 | shortcutReleased = false;
394 | }
395 | });
396 |
397 | if (shortcutReleased) {
398 | // Call the released function handler
399 | if (shortcut.releaseCallback) {
400 | shortcut.releaseCallback(keysTmpArray);
401 | }
402 |
403 | // Remove this shortcut from our activate shortcuts array
404 | const index = this.activatedShortcuts.indexOf(shortcut);
405 | if (index !== -1) this.activatedShortcuts.splice(index, 1);
406 | }
407 | });
408 | }
409 | }
410 | }
411 |
412 | const iohook = new IOHook();
413 |
414 | module.exports = iohook;
415 |
--------------------------------------------------------------------------------
/install.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const os = require('os');
6 | const nugget = require('nugget');
7 | const rc = require('rc');
8 | const pump = require('pump');
9 | const tfs = require('tar-fs');
10 | const zlib = require('zlib');
11 | const pkg = require('./package.json');
12 | const supportedTargets = require('./package.json').supportedTargets;
13 | const { optionsFromPackage } = require('./helpers');
14 |
15 | function onerror(err) {
16 | throw err;
17 | }
18 |
19 | /**
20 | * Download and Install prebuild
21 | * @param runtime
22 | * @param abi
23 | * @param platform
24 | * @param arch
25 | * @param cb Callback
26 | */
27 | function install(runtime, abi, platform, arch, cb) {
28 | const essential = runtime + '-v' + abi + '-' + platform + '-' + arch;
29 | const pkgVersion = pkg.version;
30 | const currentPlatform = 'iohook-v' + pkgVersion + '-' + essential;
31 |
32 | console.log('Downloading prebuild for platform:', currentPlatform);
33 | let downloadUrl =
34 | 'https://github.com/robolab-io/iohook/releases/download/v' +
35 | pkgVersion +
36 | '/' +
37 | currentPlatform +
38 | '.tar.gz';
39 |
40 | let nuggetOpts = {
41 | dir: os.tmpdir(),
42 | target: 'prebuild.tar.gz',
43 | strictSSL: true,
44 | };
45 |
46 | let npmrc = {};
47 |
48 | try {
49 | rc('npm', npmrc);
50 | } catch (error) {
51 | console.warn('Error reading npm configuration: ' + error.message);
52 | }
53 |
54 | if (npmrc && npmrc.proxy) {
55 | nuggetOpts.proxy = npmrc.proxy;
56 | }
57 |
58 | if (npmrc && npmrc['https-proxy']) {
59 | nuggetOpts.proxy = npmrc['https-proxy'];
60 | }
61 |
62 | if (npmrc && npmrc['strict-ssl'] === false) {
63 | nuggetOpts.strictSSL = false;
64 | }
65 |
66 | nugget(downloadUrl, nuggetOpts, function (errors) {
67 | if (errors) {
68 | const error = errors[0];
69 |
70 | if (error.message.indexOf('404') === -1) {
71 | onerror(error);
72 | } else {
73 | console.error(
74 | 'Prebuild for current platform (' + currentPlatform + ') not found!'
75 | );
76 | console.error('Try to build for your platform manually:');
77 | console.error('# cd node_modules/iohook;');
78 | console.error('# npm run build');
79 | console.error('');
80 | }
81 | }
82 |
83 | let options = {
84 | readable: true,
85 | writable: true,
86 | hardlinkAsFilesFallback: true,
87 | };
88 |
89 | let binaryName;
90 | let updateName = function (entry) {
91 | if (/\.node$/i.test(entry.name)) binaryName = entry.name;
92 | };
93 | let targetFile = path.join(__dirname, 'builds', essential);
94 | let extract = tfs.extract(targetFile, options).on('entry', updateName);
95 | pump(
96 | fs.createReadStream(path.join(nuggetOpts.dir, nuggetOpts.target)),
97 | zlib.createGunzip(),
98 | extract,
99 | function (err) {
100 | if (err) {
101 | return onerror(err);
102 | }
103 | cb();
104 | }
105 | );
106 | });
107 | }
108 |
109 | const options = optionsFromPackage();
110 |
111 | if (process.env.npm_config_targets) {
112 | options.targets = options.targets.concat(
113 | process.env.npm_config_targets.split(',')
114 | );
115 | }
116 | if (process.env.npm_config_targets === 'all') {
117 | options.targets = supportedTargets.map((arr) => [arr[0], arr[2]]);
118 | options.platforms = ['win32', 'darwin', 'linux'];
119 | options.arches = ['x64', 'ia32', 'arm64'];
120 | }
121 | if (process.env.npm_config_platforms) {
122 | options.platforms = options.platforms.concat(
123 | process.env.npm_config_platforms.split(',')
124 | );
125 | }
126 | if (process.env.npm_config_arches) {
127 | options.arches = options.arches.concat(
128 | process.env.npm_config_arches.split(',')
129 | );
130 | }
131 |
132 | if (!options.arches.includes('arm64')) {
133 | options.arches.push('arm64');
134 | }
135 |
136 | // Choice prebuilds for install
137 | if (options.targets.length > 0) {
138 | let chain = Promise.resolve();
139 | options.targets.forEach(function (target) {
140 | if (typeof target === 'object') {
141 | chain = chain.then(function () {
142 | return new Promise(function (resolve) {
143 | console.log(target.runtime, target.abi, target.platform, target.arch);
144 | install(
145 | target.runtime,
146 | target.abi,
147 | target.platform,
148 | target.arch,
149 | resolve
150 | );
151 | });
152 | });
153 | return;
154 | }
155 | let parts = target.split('-');
156 | let runtime = parts[0];
157 | let abi = parts[1];
158 | options.platforms.forEach(function (platform) {
159 | options.arches.forEach(function (arch) {
160 | if (platform === 'darwin' && arch === 'ia32') {
161 | return;
162 | }
163 | if (platform !== 'darwin' && arch === 'arm64') {
164 | return;
165 | }
166 | chain = chain.then(function () {
167 | return new Promise(function (resolve) {
168 | console.log(runtime, abi, platform, arch);
169 | install(runtime, abi, platform, arch, resolve);
170 | });
171 | });
172 | });
173 | });
174 | });
175 | } else {
176 | const runtime = process.versions['electron'] ? 'electron' : 'node';
177 | const abi = process.versions.modules;
178 | const platform = process.platform;
179 | const arch = process.arch;
180 | install(runtime, abi, platform, arch, function () {});
181 | }
182 |
--------------------------------------------------------------------------------
/libuiohook/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/
2 | /build/
3 | /autom4te.cache/
4 | /config/
5 | /cmake/
6 | /include/config.h.in*
7 | /m4/ltoptions.m4
8 | /m4/ltversion.m4
9 | /m4/libtool.m4
10 | /m4/ltsugar.m4
11 | /m4/lt~obsolete.m4
12 | /Makefile.in
13 | /configure
14 | /aclocal.m4
15 |
--------------------------------------------------------------------------------
/libuiohook/AUTHORS:
--------------------------------------------------------------------------------
1 | Alexander Barker
2 |
3 | Anthony Liguori
4 | original version of the keycode_to_scancode function in x11/input_helper.c
5 |
6 | Iván Munsuri Ibáñez
7 | contributed patches to include support for media keys and objective-c callbacks in x11/input_hook.c
8 | contributed hook_create_screen_info property for Windows and Darwin
9 |
10 | Fabrice Bellard
11 | xfree86_keycode_to_scancode_table lookup in x11/input_helper.c
12 |
13 | Marc-André Moreau
14 | original version of windows/input_helper.c
15 | original version of windows/input_helper.h
16 |
17 | Markus G. Kuhn
18 | keysym_unicode_table lookup table in x11/input_helper.c
19 | original version of the unicode_to_keysym function in x11/input_helper.c
20 | original version of the keysym_to_unicode function in x11/input_helper.c
21 |
--------------------------------------------------------------------------------
/libuiohook/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | project(libuiohook)
4 |
5 | # libuihook sources.
6 | if(WIN32 OR WIN64)
7 | set(UIOHOOK_SRC
8 | "${CMAKE_CURRENT_SOURCE_DIR}/include/uiohook.h"
9 | "${CMAKE_CURRENT_SOURCE_DIR}/src/logger.c"
10 | "${CMAKE_CURRENT_SOURCE_DIR}/src/logger.h"
11 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_helper.h"
12 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_helper.c"
13 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_hook.c"
14 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/post_event.c"
15 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/system_properties.c"
16 | )
17 | elseif(LINUX)
18 | set(UIOHOOK_SRC
19 | "include/uiohook.h"
20 | "src/logger.c"
21 | "src/logger.h"
22 | "src/x11/*.h"
23 | "src/x11/*.c"
24 | )
25 | elseif(APPLE)
26 | set(UIOHOOK_SRC
27 | "include/uiohook.h"
28 | "src/logger.c"
29 | "src/logger.h"
30 | "src/darwin/*.h"
31 | "src/darwin/*.c"
32 | )
33 | else()
34 | error("unknown OS")
35 | return()
36 |
37 | endif()
38 |
39 |
40 | #library
41 | add_library("uiohook" SHARED ${UIOHOOK_SRC})
42 | if(WIN32 OR WIN64)
43 | target_include_directories("uiohook" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/windows")
44 |
45 | if(MSVC_VERSION LESS 1900)
46 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf")
47 | else()
48 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS")
49 | endif()
50 | endif()
51 |
52 | #demo_hook
53 | add_executable("demo_hook" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_hook.c")
54 | add_dependencies("demo_hook" "uiohook")
55 | target_link_libraries("demo_hook" "uiohook")
56 | if(WIN32 OR WIN64)
57 | if(MSVC_VERSION LESS 1900)
58 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf")
59 | else()
60 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS")
61 | endif()
62 | endif()
63 |
64 | #demo_hook_async
65 | add_executable("demo_hook_async" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_hook_async.c")
66 | add_dependencies("demo_hook_async" "uiohook")
67 | target_link_libraries("demo_hook_async" "uiohook")
68 | if(WIN32 OR WIN64)
69 | if(MSVC_VERSION LESS 1900)
70 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf")
71 | else()
72 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS")
73 | endif()
74 | endif()
75 |
76 | #demo_post
77 | add_executable("demo_post" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_post.c")
78 | add_dependencies("demo_post" "uiohook")
79 | target_link_libraries("demo_post" "uiohook")
80 | if(WIN32 OR WIN64)
81 | if(MSVC_VERSION LESS 1900)
82 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf")
83 | else()
84 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS")
85 | endif()
86 | endif()
87 |
88 | #demo_properties
89 | add_executable("demo_properties" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_properties.c")
90 | add_dependencies("demo_properties" "uiohook")
91 | target_link_libraries("demo_properties" "uiohook")
92 | if(WIN32 OR WIN64)
93 | if(MSVC_VERSION LESS 1900)
94 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf")
95 | else()
96 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS")
97 | endif()
98 | endif()
99 |
100 |
101 | #all demo
102 | add_custom_target("all_demo"
103 | DEPENDS
104 | "demo_hook"
105 | "demo_hook_async"
106 | "demo_post"
107 | "demo_properties"
108 | )
109 |
--------------------------------------------------------------------------------
/libuiohook/COPYING.LESSER.md:
--------------------------------------------------------------------------------
1 | GNU Lesser General Public License
2 | =================================
3 |
4 | Version 3, 29 June 2007
5 |
6 | Copyright © 2007 Free Software Foundation, Inc. <>
7 |
8 | Everyone is permitted to copy and distribute verbatim copies
9 | of this license document, but changing it is not allowed.
10 |
11 |
12 | This version of the GNU Lesser General Public License incorporates
13 | the terms and conditions of version 3 of the GNU General Public
14 | License, supplemented by the additional permissions listed below.
15 |
16 | ### 0. Additional Definitions.
17 |
18 | As used herein, “this License” refers to version 3 of the GNU Lesser
19 | General Public License, and the “GNU GPL” refers to version 3 of the GNU
20 | General Public License.
21 |
22 | “The Library” refers to a covered work governed by this License,
23 | other than an Application or a Combined Work as defined below.
24 |
25 | An “Application” is any work that makes use of an interface provided
26 | by the Library, but which is not otherwise based on the Library.
27 | Defining a subclass of a class defined by the Library is deemed a mode
28 | of using an interface provided by the Library.
29 |
30 | A “Combined Work” is a work produced by combining or linking an
31 | Application with the Library. The particular version of the Library
32 | with which the Combined Work was made is also called the “Linked
33 | Version”.
34 |
35 | The “Minimal Corresponding Source” for a Combined Work means the
36 | Corresponding Source for the Combined Work, excluding any source code
37 | for portions of the Combined Work that, considered in isolation, are
38 | based on the Application, and not on the Linked Version.
39 |
40 | The “Corresponding Application Code” for a Combined Work means the
41 | object code and/or source code for the Application, including any data
42 | and utility programs needed for reproducing the Combined Work from the
43 | Application, but excluding the System Libraries of the Combined Work.
44 |
45 | ### 1. Exception to Section 3 of the GNU GPL.
46 |
47 | You may convey a covered work under sections 3 and 4 of this License
48 | without being bound by section 3 of the GNU GPL.
49 |
50 | ### 2. Conveying Modified Versions.
51 |
52 | If you modify a copy of the Library, and, in your modifications, a
53 | facility refers to a function or data to be supplied by an Application
54 | that uses the facility (other than as an argument passed when the
55 | facility is invoked), then you may convey a copy of the modified
56 | version:
57 |
58 | * a) under this License, provided that you make a good faith effort to
59 | ensure that, in the event an Application does not supply the
60 | function or data, the facility still operates, and performs
61 | whatever part of its purpose remains meaningful, or
62 |
63 | * b) under the GNU GPL, with none of the additional permissions of
64 | this License applicable to that copy.
65 |
66 | ### 3. Object Code Incorporating Material from Library Header Files.
67 |
68 | The object code form of an Application may incorporate material from
69 | a header file that is part of the Library. You may convey such object
70 | code under terms of your choice, provided that, if the incorporated
71 | material is not limited to numerical parameters, data structure
72 | layouts and accessors, or small macros, inline functions and templates
73 | (ten or fewer lines in length), you do both of the following:
74 |
75 | * a) Give prominent notice with each copy of the object code that the
76 | Library is used in it and that the Library and its use are
77 | covered by this License.
78 | * b) Accompany the object code with a copy of the GNU GPL and this license
79 | document.
80 |
81 | ### 4. Combined Works.
82 |
83 | You may convey a Combined Work under terms of your choice that,
84 | taken together, effectively do not restrict modification of the
85 | portions of the Library contained in the Combined Work and reverse
86 | engineering for debugging such modifications, if you also do each of
87 | the following:
88 |
89 | * a) Give prominent notice with each copy of the Combined Work that
90 | the Library is used in it and that the Library and its use are
91 | covered by this License.
92 |
93 | * b) Accompany the Combined Work with a copy of the GNU GPL and this license
94 | document.
95 |
96 | * c) For a Combined Work that displays copyright notices during
97 | execution, include the copyright notice for the Library among
98 | these notices, as well as a reference directing the user to the
99 | copies of the GNU GPL and this license document.
100 |
101 | * d) Do one of the following:
102 | - 0) Convey the Minimal Corresponding Source under the terms of this
103 | License, and the Corresponding Application Code in a form
104 | suitable for, and under terms that permit, the user to
105 | recombine or relink the Application with a modified version of
106 | the Linked Version to produce a modified Combined Work, in the
107 | manner specified by section 6 of the GNU GPL for conveying
108 | Corresponding Source.
109 | - 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | * e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | ### 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | * a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 | * b) Give prominent notice with the combined library that part of it
140 | is a work based on the Library, and explaining where to find the
141 | accompanying uncombined form of the same work.
142 |
143 | ### 6. Revised Versions of the GNU Lesser General Public License.
144 |
145 | The Free Software Foundation may publish revised and/or new versions
146 | of the GNU Lesser General Public License from time to time. Such new
147 | versions will be similar in spirit to the present version, but may
148 | differ in detail to address new problems or concerns.
149 |
150 | Each version is given a distinguishing version number. If the
151 | Library as you received it specifies that a certain numbered version
152 | of the GNU Lesser General Public License “or any later version”
153 | applies to it, you have the option of following the terms and
154 | conditions either of that published version or of any later version
155 | published by the Free Software Foundation. If the Library as you
156 | received it does not specify a version number of the GNU Lesser
157 | General Public License, you may choose any version of the GNU Lesser
158 | General Public License ever published by the Free Software Foundation.
159 |
160 | If the Library as you received it specifies that a proxy can decide
161 | whether future versions of the GNU Lesser General Public License shall
162 | apply, that proxy's public statement of acceptance of any version is
163 | permanent authorization for you to choose that version for the
164 | Library.
165 |
--------------------------------------------------------------------------------
/libuiohook/Makefile.am:
--------------------------------------------------------------------------------
1 | # libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | # Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | # https://github.com/kwhat/libuiohook/
4 | #
5 | # libUIOHook is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Lesser General Public License as published
7 | # by the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # libUIOHook is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Lesser General Public License
16 | # along with this program. If not, see .
17 | #
18 |
19 | ACLOCAL_AMFLAGS = -I m4
20 |
21 | HOOK_SRC = src/logger.c
22 | TEST_SRC = test/input_helper_test.c test/system_properties_test.c test/uiohook_test.c
23 | MAN3_SRC = man/hook_run.man \
24 | man/hook_stop.man \
25 | man/hook_get_auto_repeat_delay.man \
26 | man/hook_get_multi_click_time.man \
27 | man/hook_get_pointer_acceleration_threshold.man \
28 | man/hook_set_dispatch_proc.man \
29 | man/hook_get_auto_repeat_rate.man \
30 | man/hook_get_pointer_acceleration_multiplier.man \
31 | man/hook_get_pointer_sensitivity.man \
32 | man/hook_set_logger_proc.man
33 |
34 | if BUILD_DARWIN
35 | HOOK_SRC += src/darwin/input_helper.c \
36 | src/darwin/input_hook.c \
37 | src/darwin/post_event.c \
38 | src/darwin/system_properties.c
39 | endif
40 |
41 | if BUILD_X11
42 | HOOK_SRC += src/x11/input_helper.c \
43 | src/x11/input_hook.c \
44 | src/x11/post_event.c \
45 | src/x11/system_properties.c
46 | endif
47 |
48 | if BUILD_WINDOWS
49 | HOOK_SRC += src/windows/input_helper.c \
50 | src/windows/input_hook.c \
51 | src/windows/post_event.c \
52 | src/windows/system_properties.c
53 | endif
54 |
55 | pkgconfigdir = $(libdir)/pkgconfig
56 | pkgconfig_DATA = $(top_builddir)/pc/uiohook.pc
57 |
58 | include_HEADERS = include/uiohook.h
59 |
60 | bin_PROGRAMS =
61 | lib_LTLIBRARIES = libuiohook.la
62 |
63 |
64 | libuiohook_la_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic -Wno-unused-parameter -I$(top_srcdir)/include -I$(top_srcdir)/src/$(backend) -I$(top_srcdir)/src
65 | libuiohook_la_LDFLAGS = $(LTLDFLAGS) $(LDFLAGS)
66 | libuiohook_la_SOURCES = $(HOOK_SRC)
67 |
68 |
69 | if BUILD_DEMO
70 | bin_PROGRAMS += demohook demohookasync demopost demoprops
71 |
72 | demohook_SOURCES = src/demo_hook.c
73 | demohook_LDADD = $(top_builddir)/libuiohook.la
74 | demohook_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include
75 | demohook_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS)
76 |
77 | demohookasync_SOURCES = src/demo_hook_async.c
78 | demohookasync_LDADD = $(top_builddir)/libuiohook.la
79 | demohookasync_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include
80 | demohookasync_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS)
81 |
82 | demopost_SOURCES = src/demo_post.c
83 | demopost_LDADD = $(top_builddir)/libuiohook.la
84 | demopost_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include
85 | demopost_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS)
86 |
87 | demoprops_SOURCES = src/demo_properties.c
88 | demoprops_LDADD = $(top_builddir)/libuiohook.la
89 | demoprops_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include
90 | demoprops_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS)
91 | endif
92 |
93 |
94 | if BUILD_TEST
95 | bin_PROGRAMS += testhook
96 |
97 | testhook_SOURCES = $(TEST_SRC)
98 | testhook_LDADD = $(top_builddir)/libuiohook.la
99 | testhook_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(TEST_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir)/test -I$(top_srcdir)/src/$(backend) -I$(top_srcdir)/src
100 | testhook_LDFLAGS = $(LTLDFLAGS) $(TEST_LIBS)
101 | endif
102 |
103 | man3_MANS = $(MAN3_SRC)
104 |
105 | EXTRA_DIST = COPYING.md COPYING.LESSER.md README.md
106 |
--------------------------------------------------------------------------------
/libuiohook/README.md:
--------------------------------------------------------------------------------
1 | libuiohook
2 | ==========
3 |
4 | A multi-platform C library to provide global keyboard and mouse hooks from userland.
5 |
6 | ## Compiling
7 | Prerequisites: autotools, pkg-config, libtool, gcc, clang or msys2/mingw32
8 |
9 | ./bootstrap.sh
10 | ./configure
11 | make && make install
12 |
13 | ## Usage
14 | * [Hook Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_hook.c)
15 | * [Async Hook Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_hook_async.c)
16 | * [Event Post Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_post.c)
17 | * [Properties Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_properties.c)
18 | * [Public Interface](https://github.com/kwhat/libuiohook/blob/master/include/uiohook.h)
19 | * Please see the man pages for function documentation.
20 |
--------------------------------------------------------------------------------
/libuiohook/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ "$(uname)" = "Darwin" ]; then
4 | include=" -I/opt/local/share/aclocal"
5 | fi
6 |
7 | autoreconf --install --verbose --force $include
8 |
--------------------------------------------------------------------------------
/libuiohook/configure.ac:
--------------------------------------------------------------------------------
1 | # libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | # Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | # https://github.com/kwhat/libuiohook/
4 | #
5 | # libUIOHook is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU Lesser General Public License as published
7 | # by the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # libUIOHook is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU Lesser General Public License
16 | # along with this program. If not, see .
17 | #
18 |
19 | # Define a few constants using m4 macros to prevent multiple definitions.
20 | m4_define([UIOHOOK_MAJOR], [1])
21 | m4_define([UIOHOOK_MINOR], [1])
22 | m4_define([UIOHOOK_PATCH], [0])
23 | m4_define([UIOHOOK_RC], [])
24 | m4_define([UIOHOOK_BUGS], [alex@1stleg.com])
25 | m4_define([UIOHOOK_VERSION_STRING], UIOHOOK_MAJOR[.]UIOHOOK_MINOR[.]UIOHOOK_PATCH[]UIOHOOK_RC)
26 |
27 | m4_pattern_allow([AM_PROG_AR])
28 |
29 | # Library versioning
30 | # These numbers should be tweaked on every release. Read carefully:
31 | # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
32 | # http://sourceware.org/autobook/autobook/autobook_91.html
33 | lt_current="1"
34 | lt_revision="0"
35 | lt_age="0"
36 | LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}"
37 |
38 |
39 | # Initialize autoconf.
40 | AC_INIT([uiohook], [UIOHOOK_VERSION_STRING], [UIOHOOK_BUGS])
41 |
42 | AC_PREREQ(2.65)
43 |
44 | AC_CONFIG_SRCDIR([include/uiohook.h])
45 | AC_CONFIG_FILES([
46 | pc/uiohook.pc \
47 | Makefile
48 | ])
49 | AC_CONFIG_AUX_DIR([config])
50 | AC_CONFIG_HEADERS([include/config.h:include/config.h.in])
51 | AC_CONFIG_MACRO_DIR([m4])
52 |
53 | #AC_CANONICAL_SYSTEM
54 | AC_CANONICAL_HOST
55 | AC_CANONICAL_BUILD
56 | AC_CANONICAL_TARGET
57 |
58 | # Initialize automake.
59 | AM_INIT_AUTOMAKE([foreign subdir-objects])
60 |
61 | # Checks for programs.
62 | AC_PROG_CC
63 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
64 |
65 | # Checks for language.
66 | AC_LANG([C])
67 | AC_PROG_CC_C99
68 |
69 | # Checks for header files
70 | AC_C_CONST
71 | AC_HEADER_STDBOOL
72 | AC_CHECK_HEADERS([stdint.h])
73 | AC_TYPE_UINT64_T
74 |
75 | # Initialize libtool.
76 | AC_PROG_LIBTOOL
77 |
78 | # Check for operating system.
79 | AC_MSG_CHECKING(operating system)
80 | AS_CASE([$host],
81 | [*-darwin*], [
82 | AC_MSG_RESULT([$host (Mac OS X Compatible)])
83 | backend="darwin"
84 | ],
85 | [*-linux*], [
86 | AC_MSG_RESULT([$host (Linux Compatible)])
87 | backend="x11"
88 | has_evdev="1"
89 | ],
90 | [*-freebsd* | *-openbsd* | *-linux* | *-solaris*], [
91 | AC_MSG_RESULT([$host (Unix Compatible)])
92 | backend="x11"
93 | has_evdev="0"
94 | ],
95 | [*-mingw* | *-cygwin* | *-msys* ], [
96 | AC_MSG_RESULT([$host (Windows Compatible)])
97 | backend="windows"
98 |
99 | AC_LIBTOOL_WIN32_DLL
100 | ],
101 | [AC_MSG_ERROR([Unsupported operating system ${host}])])
102 |
103 |
104 | # Common Options
105 | AC_ARG_ENABLE([debug],
106 | AS_HELP_STRING([--enable-debug], [Enable debug output (default: disabled)]),
107 | [enable_debug="$enableval"],
108 | [enable_debug="no"])
109 |
110 | AC_ARG_ENABLE([demo],
111 | AS_HELP_STRING([--enable-demo], [Enable demo application (default: disabled)]),
112 | [enable_demo=$enableval],
113 | [enable_demo="no"])
114 |
115 | AC_ARG_ENABLE([test],
116 | AS_HELP_STRING([--enable-test], [Enable unit testing (default: disabled)]),
117 | [enable_test=$enableval],
118 | [enable_test="no"])
119 |
120 | AC_ARG_ENABLE([quiet],
121 | AS_HELP_STRING([--enable-quiet], [Enable copyright suppression (default: disabled)]),
122 | [enable_quiet=$enableval],
123 | [enable_quiet="no"])
124 |
125 | # Linux Options
126 | AC_ARG_ENABLE([evdev],
127 | AS_HELP_STRING([--without-evdev], [Disable generic Linux input driver (default: enabled)]),
128 | [enable_evdev="$enableval"],
129 | [enable_evdev="yes"])
130 |
131 | # Linux & Unix Options
132 | AC_ARG_ENABLE([xkbcommon],
133 | AS_HELP_STRING([--without-xkbcommon], [Disable X Keyboard Common Extension (default: enabled)]),
134 | [enable_xkbcommon="$enableval"],
135 | [enable_xkbcommon="yes"])
136 |
137 | AC_ARG_ENABLE([xkbfile],
138 | AS_HELP_STRING([--without-xkbfile], [Disable X Keyboard File Extension (default: enabled)]),
139 | [enable_xkbfile="$enableval"],
140 | [enable_xkbfile="yes"])
141 |
142 | AC_ARG_ENABLE([xcb],
143 | AS_HELP_STRING([--without-xcb], [Disable X protocol C-language Binding (default: enabled)]),
144 | [enable_xcb="$enableval"],
145 | [enable_xcb="yes"])
146 |
147 | AC_ARG_ENABLE([xkb],
148 | AS_HELP_STRING([--without-xkb], [Disable X Keyboard Extension (default: enabled)]),
149 | [enable_xkb="$enableval"],
150 | [enable_xkb="yes"])
151 |
152 | AC_ARG_ENABLE([xt],
153 | AS_HELP_STRING([--without-xt], [Disable X Toolkit Intrinsics (default: enabled)]),
154 | [enable_xt="$enableval"],
155 | [enable_xt="yes"])
156 |
157 | AC_ARG_ENABLE([xf86misc],
158 | AS_HELP_STRING([--with-xf86misc], [Enable XFree86-Misc X Extension (default: disabled)]),
159 | [enable_xf86misc="$enableval"],
160 | [enable_xf86misc="no"])
161 |
162 | AC_ARG_ENABLE([xrecord-async],
163 | AS_HELP_STRING([--enable-xrecord-async], [Enable XRecord Asynchronous API (default: disabled)]),
164 | [enable_xrecord_async="$enableval"],
165 | [enable_xrecord_async="no"])
166 |
167 | AC_ARG_ENABLE([xtest],
168 | AS_HELP_STRING([--without-xtest], [Disable XTest Extension (default: enabled)]),
169 | [enable_xtest="$enableval"],
170 | [enable_xtest="yes"])
171 |
172 | AC_ARG_ENABLE([xinerama],
173 | AS_HELP_STRING([--without-xinerama], [Disable Xinerama Extension (default: enabled)]),
174 | [enable_xinerama="$enableval"],
175 | [enable_xinerama="yes"])
176 |
177 | AC_ARG_ENABLE([xrandr],
178 | AS_HELP_STRING([--with-xrandr], [Enable XRandR Extension (default: disabled)]),
179 | [enable_xrandr="$enableval"],
180 | [enable_xrandr="no"])
181 |
182 | # Darwin Options
183 | AC_ARG_ENABLE([corefoundation],
184 | AS_HELP_STRING([--without-corefoundation], [Disable CoreFoundation framework (default: enabled)]),
185 | [enable_corefoundation="$enableval"],
186 | [enable_corefoundation="yes"])
187 |
188 | AC_ARG_ENABLE([iokit],
189 | AS_HELP_STRING([--without-iokit], [Disable IOKit framework (default: enabled)]),
190 | [enable_iokit="$enableval"],
191 | [enable_iokit="yes"])
192 |
193 | AC_ARG_ENABLE([objc],
194 | AS_HELP_STRING([--without-objc], [Disable objective-c (default: enabled)]),
195 | [enable_objc="$enableval"],
196 | [enable_objc="yes"])
197 |
198 | AC_ARG_ENABLE([weak-import],
199 | AS_HELP_STRING([--enable-weak-import], [Enable support for weakly-linked symbols (default: disabled)]),
200 | [enable_weak_import="$enableval"],
201 | [enable_weak_import="no"])
202 |
203 | AC_ARG_ENABLE([carbon-legacy],
204 | AS_HELP_STRING([--with-carbon-legacy], [Enable legacy Carbon framework functionality (default: disabled)]),
205 | [enable_carbon_legacy="$enableval"],
206 | [enable_carbon_legacy="no"])
207 |
208 | AS_IF([test "x$enable_debug" = "xyes"], [
209 | AC_DEFINE([USE_DEBUG], 1, [Enable Debug Output])
210 | ])
211 |
212 | AS_IF([test "x$enable_quiet" = "xyes"], [
213 | AC_DEFINE([USE_QUIET], 1, [Enable Copyright Suppression])
214 | ])
215 |
216 | AS_IF(
217 | [test "x$backend" = "xdarwin"], [
218 | # Force CLANG for OS X because it is required as of OS X 10.10.
219 | AC_PROG_CC([clang])
220 |
221 | AS_IF([test "x$enable_corefoundation" = "xyes"], [
222 | AC_DEFINE([USE_COREFOUNDATION], 1, [Enable CoreFoundation framework])
223 | LIBS="${LIBS} -framework ApplicationServices"
224 | ])
225 |
226 | AS_IF([test "x$enable_iokit" = "xyes"], [
227 | AC_DEFINE([USE_IOKIT], 1, [Enable IOKit framework])
228 | LIBS="${LIBS} -framework IOKit"
229 | ])
230 |
231 | AS_IF([test "x$enable_objc" = "xyes"], [
232 | AC_DEFINE([USE_OBJC], 1, [Enable objective-c])
233 | LIBS="${LIBS} -lobjc"
234 | ])
235 |
236 | AS_IF([test "x$enable_weak_import" = "xyes"], [
237 | AC_DEFINE([USE_WEAK_IMPORT], 1, [Enable support for weakly-linked symbols])
238 | LDFLAGS="$LDFLAGS -Wl,-flat_namespace,-undefined,dynamic_lookup"
239 | ])
240 |
241 | AS_IF([test "x$enable_carbon_legacy" = "xyes"], [
242 | # FIXME AC_MSG_ERROR if this is a 64 bit target.
243 | AC_MSG_WARN([Carbon legacy functionality is required for OS X version 10.4 and should only be used on 32-bit targets.])
244 | AC_DEFINE([USE_CARBON_LEGACY], 1, [Enable Carbon framework legacy functionality])
245 | ])
246 |
247 | AC_DEFINE([MAC_OS_X_VERSION_MIN_REQUIRED], 1050, [Set minimum OS X version to 10.5])
248 |
249 | # We only need pthreads if we are using the Demo.
250 | AS_IF([test "x$enable_demo" = "xyes"], [
251 | AX_PTHREAD([found_pthreads=yes], [found_pthreads=no])
252 |
253 | AS_IF([test "x$found_pthread" = "xno" ], [
254 | AC_MSG_ERROR([Pthreads are required for Async XRecord, XRandR and the demo!])
255 | ])
256 |
257 | # FIXME For some reason, llvm will not use the pthread flag.
258 | #THREAD_LIBS="$PTHREAD_LIBS $LIBS"
259 | #THREAD_CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
260 | ])
261 |
262 | # Always add Carbon due to non-deprecated #define's
263 | LIBS="${LIBS} -framework Carbon"
264 |
265 | #CFLAGS="$CFLAGS -I/usr/local/include"
266 | #LDFLAGS="$LDFLAGS -Wl,-undefined,error"
267 | ],
268 | [test "x$backend" = "xx11"], [
269 | # Checks for libraries.
270 | PKG_PROG_PKG_CONFIG
271 |
272 | PKG_CHECK_MODULES([X11], [x11])
273 | LIBS="$X11_LIBS $LIBS"
274 | #CFLAGS="$X11_CFLAGS $CFLAGS"
275 | REQUIRE="$REQUIRE x11"
276 |
277 | PKG_CHECK_MODULES([XTST], [xtst])
278 | LIBS="$XTST_LIBS $LIBS"
279 | #CFLAGS="$XTST_CFLAGS $CFLAGS"
280 | REQUIRE="$REQUIRE xtst"
281 |
282 | # Check for XRecord.
283 | AC_CHECK_LIB([Xtst], [XRecordQueryVersion], [],
284 | [AC_MSG_ERROR([libXtst does not include XRecord extention!])],
285 | $LIBS)
286 |
287 | AC_CHECK_HEADERS([X11/extensions/record.h], [],
288 | [AC_MSG_ERROR([XRecord extention header could not be found!])],
289 | [#include ])
290 |
291 | # Evdev is only available on Linux.
292 | AS_IF([test "x$has_evdev" = "x1"], [
293 | AS_IF([test "x$enable_evdev" = "xyes"], [
294 | AC_DEFINE([USE_EVDEV], 1, [Enable generic Linux input driver])
295 | ])
296 | ])
297 |
298 | AS_IF([test "x$enable_xkb" = "xyes"], [
299 | AC_DEFINE([USE_XKB], 1, [Enable X Keyboard Extension])
300 | ])
301 |
302 | AS_IF([test "x$enable_xt" = "xyes"], [
303 | AC_DEFINE([USE_XT], 1, [Enable X Toolkit Intrinsics])
304 | PKG_CHECK_MODULES([XT], [xt])
305 | #CFLAGS="$XT_CFLAGS $CFLAGS"
306 | LIBS="$XT_LIBS $LIBS"
307 | REQUIRE="$REQUIRE xt"
308 | ])
309 |
310 | AS_IF([test "x$enable_xf86misc" = "xyes"], [
311 | AC_DEFINE([USE_XF86MISC], 1, [Enable XFree86-Misc X Extension])
312 | PKG_CHECK_MODULES([XF86MISC], [xxf86misc])
313 | #CFLAGS="$XF86MISC_CFLAGS $CFLAGS"
314 | LIBS="$XF86MISC_LIBS $LIBS"
315 | REQUIRE="$REQUIRE xxf86misc"
316 | ])
317 |
318 | AS_IF([test "x$enable_xrecord_async" = "xyes"], [
319 | AC_DEFINE([USE_XRECORD_ASYNC], 1, [Enable XRecord Asynchronous API])
320 | CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199309L"
321 | ])
322 |
323 | AS_IF([test "x$enable_xtest" = "xyes"], [
324 | AC_CHECK_LIB([Xtst], [XTestQueryExtension],
325 | [AC_CHECK_HEADERS([X11/extensions/XTest.h],
326 | [AC_DEFINE([USE_XTEST], 1, [Enable XTest Extension])],
327 | [AC_MSG_WARN([XTest extention header could not be found!])],
328 | [#include ])],
329 | [AC_MSG_WARN([libXtst does not include XTest extention!])],
330 | $LIBS)
331 | ])
332 |
333 | AS_IF([test "x$enable_xrandr" = "xyes"], [
334 | AS_IF([test "x$enable_xinerama" = "xyes" ], [
335 | AC_MSG_WARN([Both Xinerama and XRandR were enabled, ignoring Xinerama!])
336 | ])
337 |
338 | AC_DEFINE([USE_XRANDR], 1, [Enable XRandR Extension])
339 | PKG_CHECK_MODULES([XRANDR], [xrandr])
340 | LIBS="$XRANDR_LIBS $LIBS"
341 | #CFLAGS="$XRANDR_CFLAGS $CFLAGS"
342 | REQUIRE="$REQUIRE xrandr"
343 | ],[
344 | AS_IF([test "x$enable_xinerama" = "xyes"], [
345 | AC_DEFINE([USE_XINERAMA], 1, [Enable Xinerama Extension])
346 | PKG_CHECK_MODULES([XINERAMA], [xinerama])
347 | LIBS="$XINERAMA_LIBS $LIBS"
348 | #CFLAGS="$XINERAMA_CFLAGS $CFLAGS"
349 | REQUIRE="$REQUIRE xinerama"
350 | ])
351 | ])
352 |
353 | # We only need pthreads if we are using XRandR, Async XRecord or the Demo.
354 | AS_IF([test "x$enable_xrecord_async" = "xyes" || test "x$enable_xrandr" = "xyes" || test "x$enable_demo" = "xyes"], [
355 | AX_PTHREAD([found_pthreads=yes], [found_pthreads=no])
356 |
357 | AS_IF([test "x$found_pthread" = "xno" ], [
358 | AC_MSG_ERROR([Pthreads are required for Async XRecord, XRandR and the demo!])
359 | ])
360 |
361 | AS_IF([test "x$enable_xrecord_async" = "xyes" || test "x$enable_xrandr" = "xyes"], [
362 | LIBS="$PTHREAD_LIBS $LIBS"
363 | CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
364 | ], [test "x$enable_demo" = "xyes"], [
365 | DEMO_LIBS="$PTHREAD_LIBS $LIBS"
366 | DEMO_CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
367 | ])
368 | ])
369 |
370 |
371 | AS_IF([test "x$enable_xkbcommon" = "xyes"], [
372 | PKG_CHECK_MODULES([X11_XCB], [x11-xcb])
373 | LIBS="$X11_XCB_LIBS $LIBS"
374 | CFLAGS="$X11_XCB_CFLAGS $CFLAGS"
375 | REQUIRE="$REQUIRE x11-xcb"
376 |
377 | PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon])
378 | LIBS="$XKBCOMMON_LIBS $LIBS"
379 | CFLAGS="$XKBCOMMON_CFLAGS $CFLAGS"
380 | REQUIRE="$REQUIRE xkbcommon"
381 |
382 | PKG_CHECK_MODULES([XKBCOMMON_X11], [xkbcommon-x11])
383 | LIBS="$XKBCOMMON_X11_LIBS $LIBS"
384 | #CFLAGS="$XKBCOMMON_X11_CFLAGS $CFLAGS"
385 | REQUIRE="$REQUIRE xkbcommon-x11"
386 |
387 | AC_DEFINE([USE_XKBCOMMON], 1, [Enable XKB Common Extension])
388 | ])
389 |
390 | AS_IF([test "x$enable_xkbfile" = "xyes"], [
391 | AS_IF([test "x$enable_kxbcommon" = "xno"], [
392 | AC_MSG_ERROR([XKB Common is required for XKB File support!])
393 | ])
394 |
395 | PKG_CHECK_MODULES([XKBFILE], [xkbfile])
396 | LIBS="$XKBFILE_LIBS $LIBS"
397 | #CFLAGS="$XKBFILE_CFLAGS $CFLAGS"
398 | REQUIRE="$REQUIRE xkbfile"
399 |
400 | AC_DEFINE([USE_XKBFILE], 1, [Enable XKB File Extension])
401 | ])
402 |
403 | LDFLAGS="$LDFLAGS -Wl,--no-undefined"
404 | ],
405 | [test "x$backend" = "xwindows"], [
406 | AC_DEFINE([_WIN32_WINNT], 0x0500, [Set minimum Windows version to Windows 2000])
407 |
408 | AS_IF([test "x$enable_debug" = "xyes"], [
409 | CFLAGS="$CFLAGS -Wno-format"
410 | ])
411 |
412 | LDFLAGS="$LDFLAGS -Wl,--no-undefined -no-undefined"
413 | ])
414 |
415 | # OS info for Automake
416 | AC_SUBST([backend])
417 |
418 | # Requires for pkg-config
419 | AC_SUBST([REQUIRE])
420 | AC_SUBST([LIBS])
421 | AC_SUBST([DEMO_CFLAGS])
422 | AC_SUBST([DEMO_LIBS])
423 | AC_SUBST([TEST_LIBS])
424 |
425 | # Should the demo application be built?
426 | AM_CONDITIONAL([BUILD_DEMO], [test "x$enable_demo" = "xyes"])
427 |
428 | # Should the unit tests be built?
429 | AM_CONDITIONAL([BUILD_TEST], [test "x$enable_test" = "xyes"])
430 |
431 | # Department of redundancy department. We have already checked the platform 3
432 | # times! I am sure there is a technical reason for this...
433 | AM_CONDITIONAL(BUILD_DARWIN, test "x$backend" = "xdarwin")
434 | AM_CONDITIONAL(BUILD_X11, test "x$backend" = "xx11")
435 | AM_CONDITIONAL(BUILD_WINDOWS, test "x$backend" = "xwindows")
436 |
437 | # Generate the "configure" script
438 | AC_OUTPUT
439 |
--------------------------------------------------------------------------------
/libuiohook/include/uiohook.h:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef __UIOHOOK_H
20 | #define __UIOHOOK_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | /* Begin Error Codes */
27 | #define UIOHOOK_SUCCESS 0x00
28 | #define UIOHOOK_FAILURE 0x01
29 |
30 | // System level errors.
31 | #define UIOHOOK_ERROR_OUT_OF_MEMORY 0x02
32 |
33 | // Unix specific errors.
34 | #define UIOHOOK_ERROR_X_OPEN_DISPLAY 0x20
35 | #define UIOHOOK_ERROR_X_RECORD_NOT_FOUND 0x21
36 | #define UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE 0x22
37 | #define UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT 0x23
38 | #define UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT 0x24
39 | #define UIOHOOK_ERROR_X_RECORD_GET_CONTEXT 0x25
40 |
41 | // Windows specific errors.
42 | #define UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30
43 | #define UIOHOOK_ERROR_GET_MODULE_HANDLE 0x31
44 |
45 | // Darwin specific errors.
46 | #define UIOHOOK_ERROR_AXAPI_DISABLED 0x40
47 | #define UIOHOOK_ERROR_CREATE_EVENT_PORT 0x41
48 | #define UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE 0x42
49 | #define UIOHOOK_ERROR_GET_RUNLOOP 0x43
50 | #define UIOHOOK_ERROR_CREATE_OBSERVER 0x44
51 | /* End Error Codes */
52 |
53 | /* Begin Log Levels and Function Prototype */
54 | typedef enum _log_level {
55 | LOG_LEVEL_DEBUG = 1,
56 | LOG_LEVEL_INFO,
57 | LOG_LEVEL_WARN,
58 | LOG_LEVEL_ERROR
59 | } log_level;
60 |
61 | // Logger callback function prototype.
62 | typedef bool (*logger_t)(unsigned int, const char *, ...);
63 | /* End Log Levels and Function Prototype */
64 |
65 | /* Begin Virtual Event Types and Data Structures */
66 | typedef enum _event_type {
67 | EVENT_HOOK_ENABLED = 1,
68 | EVENT_HOOK_DISABLED,
69 | EVENT_KEY_TYPED,
70 | EVENT_KEY_PRESSED,
71 | EVENT_KEY_RELEASED,
72 | EVENT_MOUSE_CLICKED,
73 | EVENT_MOUSE_PRESSED,
74 | EVENT_MOUSE_RELEASED,
75 | EVENT_MOUSE_MOVED,
76 | EVENT_MOUSE_DRAGGED,
77 | EVENT_MOUSE_WHEEL
78 | } event_type;
79 |
80 | typedef struct _screen_data {
81 | uint8_t number;
82 | int16_t x;
83 | int16_t y;
84 | uint16_t width;
85 | uint16_t height;
86 | } screen_data;
87 |
88 | typedef struct _keyboard_event_data {
89 | uint16_t keycode;
90 | uint16_t rawcode;
91 | uint16_t keychar;
92 | } keyboard_event_data,
93 | key_pressed_event_data,
94 | key_released_event_data,
95 | key_typed_event_data;
96 |
97 | typedef struct _mouse_event_data {
98 | uint16_t button;
99 | uint16_t clicks;
100 | int16_t x;
101 | int16_t y;
102 | } mouse_event_data,
103 | mouse_pressed_event_data,
104 | mouse_released_event_data,
105 | mouse_clicked_event_data;
106 |
107 | typedef struct _mouse_wheel_event_data {
108 | uint16_t clicks;
109 | int16_t x;
110 | int16_t y;
111 | uint8_t type;
112 | uint16_t amount;
113 | int16_t rotation;
114 | uint8_t direction;
115 | } mouse_wheel_event_data;
116 |
117 | typedef struct _uiohook_event {
118 | event_type type;
119 | uint64_t time;
120 | uint16_t mask;
121 | uint16_t reserved;
122 | union {
123 | keyboard_event_data keyboard;
124 | mouse_event_data mouse;
125 | mouse_wheel_event_data wheel;
126 | } data;
127 | } uiohook_event;
128 |
129 | typedef void (*dispatcher_t)(uiohook_event *const);
130 | /* End Virtual Event Types and Data Structures */
131 |
132 |
133 | /* Begin Virtual Key Codes */
134 | #define VC_ESCAPE 0x0001
135 |
136 | // Begin Function Keys
137 | #define VC_F1 0x003B
138 | #define VC_F2 0x003C
139 | #define VC_F3 0x003D
140 | #define VC_F4 0x003E
141 | #define VC_F5 0x003F
142 | #define VC_F6 0x0040
143 | #define VC_F7 0x0041
144 | #define VC_F8 0x0042
145 | #define VC_F9 0x0043
146 | #define VC_F10 0x0044
147 | #define VC_F11 0x0057
148 | #define VC_F12 0x0058
149 |
150 | #define VC_F13 0x005B
151 | #define VC_F14 0x005C
152 | #define VC_F15 0x005D
153 | #define VC_F16 0x0063
154 | #define VC_F17 0x0064
155 | #define VC_F18 0x0065
156 | #define VC_F19 0x0066
157 | #define VC_F20 0x0067
158 | #define VC_F21 0x0068
159 | #define VC_F22 0x0069
160 | #define VC_F23 0x006A
161 | #define VC_F24 0x006B
162 | // End Function Keys
163 |
164 |
165 | // Begin Alphanumeric Zone
166 | #define VC_BACKQUOTE 0x0029
167 |
168 | #define VC_1 0x0002
169 | #define VC_2 0x0003
170 | #define VC_3 0x0004
171 | #define VC_4 0x0005
172 | #define VC_5 0x0006
173 | #define VC_6 0x0007
174 | #define VC_7 0x0008
175 | #define VC_8 0x0009
176 | #define VC_9 0x000A
177 | #define VC_0 0x000B
178 |
179 | #define VC_MINUS 0x000C // '-'
180 | #define VC_EQUALS 0x000D // '='
181 | #define VC_BACKSPACE 0x000E
182 |
183 | #define VC_TAB 0x000F
184 | #define VC_CAPS_LOCK 0x003A
185 |
186 | #define VC_A 0x001E
187 | #define VC_B 0x0030
188 | #define VC_C 0x002E
189 | #define VC_D 0x0020
190 | #define VC_E 0x0012
191 | #define VC_F 0x0021
192 | #define VC_G 0x0022
193 | #define VC_H 0x0023
194 | #define VC_I 0x0017
195 | #define VC_J 0x0024
196 | #define VC_K 0x0025
197 | #define VC_L 0x0026
198 | #define VC_M 0x0032
199 | #define VC_N 0x0031
200 | #define VC_O 0x0018
201 | #define VC_P 0x0019
202 | #define VC_Q 0x0010
203 | #define VC_R 0x0013
204 | #define VC_S 0x001F
205 | #define VC_T 0x0014
206 | #define VC_U 0x0016
207 | #define VC_V 0x002F
208 | #define VC_W 0x0011
209 | #define VC_X 0x002D
210 | #define VC_Y 0x0015
211 | #define VC_Z 0x002C
212 |
213 | #define VC_OPEN_BRACKET 0x001A // '['
214 | #define VC_CLOSE_BRACKET 0x001B // ']'
215 | #define VC_BACK_SLASH 0x002B // '\'
216 |
217 | #define VC_SEMICOLON 0x0027 // ';'
218 | #define VC_QUOTE 0x0028
219 | #define VC_ENTER 0x001C
220 |
221 | #define VC_COMMA 0x0033 // ','
222 | #define VC_PERIOD 0x0034 // '.'
223 | #define VC_SLASH 0x0035 // '/'
224 |
225 | #define VC_SPACE 0x0039
226 | // End Alphanumeric Zone
227 |
228 |
229 | #define VC_PRINTSCREEN 0x0E37
230 | #define VC_SCROLL_LOCK 0x0046
231 | #define VC_PAUSE 0x0E45
232 |
233 |
234 | // Begin Edit Key Zone
235 | #define VC_INSERT 0x0E52
236 | #define VC_DELETE 0x0E53
237 | #define VC_HOME 0x0E47
238 | #define VC_END 0x0E4F
239 | #define VC_PAGE_UP 0x0E49
240 | #define VC_PAGE_DOWN 0x0E51
241 | // End Edit Key Zone
242 |
243 |
244 | // Begin Cursor Key Zone
245 | #define VC_UP 0xE048
246 | #define VC_LEFT 0xE04B
247 | #define VC_CLEAR 0xE04C
248 | #define VC_RIGHT 0xE04D
249 | #define VC_DOWN 0xE050
250 | // End Cursor Key Zone
251 |
252 |
253 | // Begin Numeric Zone
254 | #define VC_NUM_LOCK 0x0045
255 | #define VC_KP_DIVIDE 0x0E35
256 | #define VC_KP_MULTIPLY 0x0037
257 | #define VC_KP_SUBTRACT 0x004A
258 | #define VC_KP_EQUALS 0x0E0D
259 | #define VC_KP_ADD 0x004E
260 | #define VC_KP_ENTER 0x0E1C
261 | #define VC_KP_SEPARATOR 0x0053
262 |
263 | #define VC_KP_1 0x004F
264 | #define VC_KP_2 0x0050
265 | #define VC_KP_3 0x0051
266 | #define VC_KP_4 0x004B
267 | #define VC_KP_5 0x004C
268 | #define VC_KP_6 0x004D
269 | #define VC_KP_7 0x0047
270 | #define VC_KP_8 0x0048
271 | #define VC_KP_9 0x0049
272 | #define VC_KP_0 0x0052
273 |
274 | #define VC_KP_END 0xEE00 | VC_KP_1
275 | #define VC_KP_DOWN 0xEE00 | VC_KP_2
276 | #define VC_KP_PAGE_DOWN 0xEE00 | VC_KP_3
277 | #define VC_KP_LEFT 0xEE00 | VC_KP_4
278 | #define VC_KP_CLEAR 0xEE00 | VC_KP_5
279 | #define VC_KP_RIGHT 0xEE00 | VC_KP_6
280 | #define VC_KP_HOME 0xEE00 | VC_KP_7
281 | #define VC_KP_UP 0xEE00 | VC_KP_8
282 | #define VC_KP_PAGE_UP 0xEE00 | VC_KP_9
283 | #define VC_KP_INSERT 0xEE00 | VC_KP_0
284 | #define VC_KP_DELETE 0xEE00 | VC_KP_SEPARATOR
285 | // End Numeric Zone
286 |
287 |
288 | // Begin Modifier and Control Keys
289 | #define VC_SHIFT_L 0x002A
290 | #define VC_SHIFT_R 0x0036
291 | #define VC_CONTROL_L 0x001D
292 | #define VC_CONTROL_R 0x0E1D
293 | #define VC_ALT_L 0x0038 // Option or Alt Key
294 | #define VC_ALT_R 0x0E38 // Option or Alt Key
295 | #define VC_META_L 0x0E5B // Windows or Command Key
296 | #define VC_META_R 0x0E5C // Windows or Command Key
297 | #define VC_CONTEXT_MENU 0x0E5D
298 | // End Modifier and Control Keys
299 |
300 |
301 | // Begin Media Control Keys
302 | #define VC_POWER 0xE05E
303 | #define VC_SLEEP 0xE05F
304 | #define VC_WAKE 0xE063
305 |
306 | #define VC_MEDIA_PLAY 0xE022
307 | #define VC_MEDIA_STOP 0xE024
308 | #define VC_MEDIA_PREVIOUS 0xE010
309 | #define VC_MEDIA_NEXT 0xE019
310 | #define VC_MEDIA_SELECT 0xE06D
311 | #define VC_MEDIA_EJECT 0xE02C
312 |
313 | #define VC_VOLUME_MUTE 0xE020
314 | #define VC_VOLUME_UP 0xE030
315 | #define VC_VOLUME_DOWN 0xE02E
316 |
317 | #define VC_APP_MAIL 0xE06C
318 | #define VC_APP_CALCULATOR 0xE021
319 | #define VC_APP_MUSIC 0xE03C
320 | #define VC_APP_PICTURES 0xE064
321 |
322 | #define VC_BROWSER_SEARCH 0xE065
323 | #define VC_BROWSER_HOME 0xE032
324 | #define VC_BROWSER_BACK 0xE06A
325 | #define VC_BROWSER_FORWARD 0xE069
326 | #define VC_BROWSER_STOP 0xE068
327 | #define VC_BROWSER_REFRESH 0xE067
328 | #define VC_BROWSER_FAVORITES 0xE066
329 | // End Media Control Keys
330 |
331 | // Begin Japanese Language Keys
332 | #define VC_KATAKANA 0x0070
333 | #define VC_UNDERSCORE 0x0073
334 | #define VC_FURIGANA 0x0077
335 | #define VC_KANJI 0x0079
336 | #define VC_HIRAGANA 0x007B
337 | #define VC_YEN 0x007D
338 | #define VC_KP_COMMA 0x007E
339 | // End Japanese Language Keys
340 |
341 | // Begin Sun keyboards
342 | #define VC_SUN_HELP 0xFF75
343 |
344 | #define VC_SUN_STOP 0xFF78
345 | #define VC_SUN_PROPS 0xFF76
346 | #define VC_SUN_FRONT 0xFF77
347 | #define VC_SUN_OPEN 0xFF74
348 | #define VC_SUN_FIND 0xFF7E
349 | #define VC_SUN_AGAIN 0xFF79
350 | #define VC_SUN_UNDO 0xFF7A
351 | #define VC_SUN_COPY 0xFF7C
352 | #define VC_SUN_INSERT 0xFF7D
353 | #define VC_SUN_CUT 0xFF7B
354 | // End Sun keyboards
355 |
356 | #define VC_UNDEFINED 0x0000 // KeyCode Unknown
357 |
358 | #define CHAR_UNDEFINED 0xFFFF // CharCode Unknown
359 | /* End Virtual Key Codes */
360 |
361 |
362 | /* Begin Virtual Modifier Masks */
363 | #define MASK_SHIFT_L 1 << 0
364 | #define MASK_CTRL_L 1 << 1
365 | #define MASK_META_L 1 << 2
366 | #define MASK_ALT_L 1 << 3
367 |
368 | #define MASK_SHIFT_R 1 << 4
369 | #define MASK_CTRL_R 1 << 5
370 | #define MASK_META_R 1 << 6
371 | #define MASK_ALT_R 1 << 7
372 |
373 | #define MASK_SHIFT MASK_SHIFT_L | MASK_SHIFT_R
374 | #define MASK_CTRL MASK_CTRL_L | MASK_CTRL_R
375 | #define MASK_META MASK_META_L | MASK_META_R
376 | #define MASK_ALT MASK_ALT_L | MASK_ALT_R
377 |
378 | #define MASK_BUTTON1 1 << 8
379 | #define MASK_BUTTON2 1 << 9
380 | #define MASK_BUTTON3 1 << 10
381 | #define MASK_BUTTON4 1 << 11
382 | #define MASK_BUTTON5 1 << 12
383 |
384 | #define MASK_NUM_LOCK 1 << 13
385 | #define MASK_CAPS_LOCK 1 << 14
386 | #define MASK_SCROLL_LOCK 1 << 15
387 | /* End Virtual Modifier Masks */
388 |
389 |
390 | /* Begin Virtual Mouse Buttons */
391 | #define MOUSE_NOBUTTON 0 // Any Button
392 | #define MOUSE_BUTTON1 1 // Left Button
393 | #define MOUSE_BUTTON2 2 // Right Button
394 | #define MOUSE_BUTTON3 3 // Middle Button
395 | #define MOUSE_BUTTON4 4 // Extra Mouse Button
396 | #define MOUSE_BUTTON5 5 // Extra Mouse Button
397 |
398 | #define WHEEL_UNIT_SCROLL 1
399 | #define WHEEL_BLOCK_SCROLL 2
400 |
401 | #define WHEEL_VERTICAL_DIRECTION 3
402 | #define WHEEL_HORIZONTAL_DIRECTION 4
403 | /* End Virtual Mouse Buttons */
404 |
405 |
406 | #ifdef _WIN32
407 | #define UIOHOOK_API __declspec(dllexport)
408 | #else
409 | #define UIOHOOK_API
410 | #endif
411 |
412 | #ifdef __cplusplus
413 | extern "C" {
414 | #endif
415 |
416 | // Set the logger callback functions.
417 | UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc);
418 |
419 | // Send a virtual event back to the system.
420 | UIOHOOK_API void hook_post_event(uiohook_event * const event);
421 |
422 | // Set the event callback function.
423 | UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc);
424 |
425 | // Insert the event hook.
426 | UIOHOOK_API int hook_run();
427 |
428 | // Withdraw the event hook.
429 | UIOHOOK_API int hook_stop();
430 |
431 | UIOHOOK_API void grab_mouse_click(bool enable);
432 |
433 | // Retrieves an array of screen data for each available monitor.
434 | UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count);
435 |
436 | // Retrieves the keyboard auto repeat rate.
437 | UIOHOOK_API long int hook_get_auto_repeat_rate();
438 |
439 | // Retrieves the keyboard auto repeat delay.
440 | UIOHOOK_API long int hook_get_auto_repeat_delay();
441 |
442 | // Retrieves the mouse acceleration multiplier.
443 | UIOHOOK_API long int hook_get_pointer_acceleration_multiplier();
444 |
445 | // Retrieves the mouse acceleration threshold.
446 | UIOHOOK_API long int hook_get_pointer_acceleration_threshold();
447 |
448 | // Retrieves the mouse sensitivity.
449 | UIOHOOK_API long int hook_get_pointer_sensitivity();
450 |
451 | // Retrieves the double/triple click interval.
452 | UIOHOOK_API long int hook_get_multi_click_time();
453 |
454 | #ifdef __cplusplus
455 | }
456 | #endif
457 |
458 | #endif
459 |
--------------------------------------------------------------------------------
/libuiohook/m4/ax_pthread.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
8 | #
9 | # DESCRIPTION
10 | #
11 | # This macro figures out how to build C programs using POSIX threads. It
12 | # sets the PTHREAD_LIBS output variable to the threads library and linker
13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler
14 | # flags that are needed. (The user can also force certain compiler
15 | # flags/libs to be tested by setting these environment variables.)
16 | #
17 | # Also sets PTHREAD_CC to any special C compiler that is needed for
18 | # multi-threaded programs (defaults to the value of CC otherwise). (This
19 | # is necessary on AIX to use the special cc_r compiler alias.)
20 | #
21 | # NOTE: You are assumed to not only compile your program with these flags,
22 | # but also link it with them as well. e.g. you should link with
23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
24 | #
25 | # If you are only building threads programs, you may wish to use these
26 | # variables in your default LIBS, CFLAGS, and CC:
27 | #
28 | # LIBS="$PTHREAD_LIBS $LIBS"
29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
30 | # CC="$PTHREAD_CC"
31 | #
32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
35 | #
36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with
38 | # PTHREAD_CFLAGS.
39 | #
40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library
41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
42 | # is not found. If ACTION-IF-FOUND is not specified, the default action
43 | # will define HAVE_PTHREAD.
44 | #
45 | # Please let the authors know if this macro fails on any platform, or if
46 | # you have any other suggestions or comments. This macro was based on work
47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also
50 | # grateful for the helpful feedback of numerous users.
51 | #
52 | # Updated for Autoconf 2.68 by Daniel Richard G.
53 | #
54 | # LICENSE
55 | #
56 | # Copyright (c) 2008 Steven G. Johnson
57 | # Copyright (c) 2011 Daniel Richard G.
58 | #
59 | # This program is free software: you can redistribute it and/or modify it
60 | # under the terms of the GNU General Public License as published by the
61 | # Free Software Foundation, either version 3 of the License, or (at your
62 | # option) any later version.
63 | #
64 | # This program is distributed in the hope that it will be useful, but
65 | # WITHOUT ANY WARRANTY; without even the implied warranty of
66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
67 | # Public License for more details.
68 | #
69 | # You should have received a copy of the GNU General Public License along
70 | # with this program. If not, see .
71 | #
72 | # As a special exception, the respective Autoconf Macro's copyright owner
73 | # gives unlimited permission to copy, distribute and modify the configure
74 | # scripts that are the output of Autoconf when processing the Macro. You
75 | # need not follow the terms of the GNU General Public License when using
76 | # or distributing such scripts, even though portions of the text of the
77 | # Macro appear in them. The GNU General Public License (GPL) does govern
78 | # all other use of the material that constitutes the Autoconf Macro.
79 | #
80 | # This special exception to the GPL applies to versions of the Autoconf
81 | # Macro released by the Autoconf Archive. When you make and distribute a
82 | # modified version of the Autoconf Macro, you may extend this special
83 | # exception to the GPL to apply to your modified version as well.
84 |
85 | #serial 20
86 |
87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
88 | AC_DEFUN([AX_PTHREAD], [
89 | AC_REQUIRE([AC_CANONICAL_HOST])
90 | AC_LANG_PUSH([C])
91 | ax_pthread_ok=no
92 |
93 | # We used to check for pthread.h first, but this fails if pthread.h
94 | # requires special compiler flags (e.g. on True64 or Sequent).
95 | # It gets checked for in the link test anyway.
96 |
97 | # First of all, check if the user has set any of the PTHREAD_LIBS,
98 | # etcetera environment variables, and if threads linking works using
99 | # them:
100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
101 | save_CFLAGS="$CFLAGS"
102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
103 | save_LIBS="$LIBS"
104 | LIBS="$PTHREAD_LIBS $LIBS"
105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
106 | AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
107 | AC_MSG_RESULT($ax_pthread_ok)
108 | if test x"$ax_pthread_ok" = xno; then
109 | PTHREAD_LIBS=""
110 | PTHREAD_CFLAGS=""
111 | fi
112 | LIBS="$save_LIBS"
113 | CFLAGS="$save_CFLAGS"
114 | fi
115 |
116 | # We must check for the threads library under a number of different
117 | # names; the ordering is very important because some systems
118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
119 | # libraries is broken (non-POSIX).
120 |
121 | # Create a list of thread flags to try. Items starting with a "-" are
122 | # C compiler flags, and other items are library names, except for "none"
123 | # which indicates that we try without any flags at all, and "pthread-config"
124 | # which is a program returning the flags for the Pth emulation library.
125 |
126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
127 |
128 | # The ordering *is* (sometimes) important. Some notes on the
129 | # individual items follow:
130 |
131 | # pthreads: AIX (must check this before -lpthread)
132 | # none: in case threads are in libc; should be tried before -Kthread and
133 | # other compiler flags to prevent continual compiler warnings
134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
138 | # -pthreads: Solaris/gcc
139 | # -mthreads: Mingw32/gcc, Lynx/gcc
140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
141 | # doesn't hurt to check since this sometimes defines pthreads too;
142 | # also defines -D_REENTRANT)
143 | # ... -mt is also the pthreads flag for HP/aCC
144 | # pthread: Linux, etcetera
145 | # --thread-safe: KAI C++
146 | # pthread-config: use pthread-config program (for GNU Pth library)
147 |
148 | case ${host_os} in
149 | solaris*)
150 |
151 | # On Solaris (at least, for some versions), libc contains stubbed
152 | # (non-functional) versions of the pthreads routines, so link-based
153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/
154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
155 | # a function called by this macro, so we could check for that, but
156 | # who knows whether they'll stub that too in a future libc.) So,
157 | # we'll just look for -pthreads and -lpthread first:
158 |
159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
160 | ;;
161 |
162 | darwin*)
163 | ax_pthread_flags="-pthread $ax_pthread_flags"
164 | ;;
165 | esac
166 |
167 | if test x"$ax_pthread_ok" = xno; then
168 | for flag in $ax_pthread_flags; do
169 |
170 | case $flag in
171 | none)
172 | AC_MSG_CHECKING([whether pthreads work without any flags])
173 | ;;
174 |
175 | -*)
176 | AC_MSG_CHECKING([whether pthreads work with $flag])
177 | PTHREAD_CFLAGS="$flag"
178 | ;;
179 |
180 | pthread-config)
181 | AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
182 | if test x"$ax_pthread_config" = xno; then continue; fi
183 | PTHREAD_CFLAGS="`pthread-config --cflags`"
184 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
185 | ;;
186 |
187 | *)
188 | AC_MSG_CHECKING([for the pthreads library -l$flag])
189 | PTHREAD_LIBS="-l$flag"
190 | ;;
191 | esac
192 |
193 | save_LIBS="$LIBS"
194 | save_CFLAGS="$CFLAGS"
195 | LIBS="$PTHREAD_LIBS $LIBS"
196 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
197 |
198 | # Check for various functions. We must include pthread.h,
199 | # since some functions may be macros. (On the Sequent, we
200 | # need a special flag -Kthread to make this header compile.)
201 | # We check for pthread_join because it is in -lpthread on IRIX
202 | # while pthread_create is in libc. We check for pthread_attr_init
203 | # due to DEC craziness with -lpthreads. We check for
204 | # pthread_cleanup_push because it is one of the few pthread
205 | # functions on Solaris that doesn't have a non-functional libc stub.
206 | # We try pthread_create on general principles.
207 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include
208 | static void routine(void *a) { a = 0; }
209 | static void *start_routine(void *a) { return a; }],
210 | [pthread_t th; pthread_attr_t attr;
211 | pthread_create(&th, 0, start_routine, 0);
212 | pthread_join(th, 0);
213 | pthread_attr_init(&attr);
214 | pthread_cleanup_push(routine, 0);
215 | pthread_cleanup_pop(0) /* ; */])],
216 | [ax_pthread_ok=yes],
217 | [])
218 |
219 | LIBS="$save_LIBS"
220 | CFLAGS="$save_CFLAGS"
221 |
222 | AC_MSG_RESULT($ax_pthread_ok)
223 | if test "x$ax_pthread_ok" = xyes; then
224 | break;
225 | fi
226 |
227 | PTHREAD_LIBS=""
228 | PTHREAD_CFLAGS=""
229 | done
230 | fi
231 |
232 | # Various other checks:
233 | if test "x$ax_pthread_ok" = xyes; then
234 | save_LIBS="$LIBS"
235 | LIBS="$PTHREAD_LIBS $LIBS"
236 | save_CFLAGS="$CFLAGS"
237 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
238 |
239 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
240 | AC_MSG_CHECKING([for joinable pthread attribute])
241 | attr_name=unknown
242 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
243 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ],
244 | [int attr = $attr; return attr /* ; */])],
245 | [attr_name=$attr; break],
246 | [])
247 | done
248 | AC_MSG_RESULT($attr_name)
249 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
250 | AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
251 | [Define to necessary symbol if this constant
252 | uses a non-standard name on your system.])
253 | fi
254 |
255 | AC_MSG_CHECKING([if more special flags are required for pthreads])
256 | flag=no
257 | case ${host_os} in
258 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
259 | osf* | hpux*) flag="-D_REENTRANT";;
260 | solaris*)
261 | if test "$GCC" = "yes"; then
262 | flag="-D_REENTRANT"
263 | else
264 | flag="-mt -D_REENTRANT"
265 | fi
266 | ;;
267 | esac
268 | AC_MSG_RESULT(${flag})
269 | if test "x$flag" != xno; then
270 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
271 | fi
272 |
273 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
274 | ax_cv_PTHREAD_PRIO_INHERIT, [
275 | AC_LINK_IFELSE([
276 | AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])],
277 | [ax_cv_PTHREAD_PRIO_INHERIT=yes],
278 | [ax_cv_PTHREAD_PRIO_INHERIT=no])
279 | ])
280 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
281 | AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
282 |
283 | LIBS="$save_LIBS"
284 | CFLAGS="$save_CFLAGS"
285 |
286 | # More AIX lossage: compile with *_r variant
287 | if test "x$GCC" != xyes; then
288 | case $host_os in
289 | aix*)
290 | AS_CASE(["x/$CC"],
291 | [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
292 | [#handle absolute path differently from PATH based program lookup
293 | AS_CASE(["x$CC"],
294 | [x/*],
295 | [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
296 | [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
297 | ;;
298 | esac
299 | fi
300 | fi
301 |
302 | test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
303 |
304 | AC_SUBST(PTHREAD_LIBS)
305 | AC_SUBST(PTHREAD_CFLAGS)
306 | AC_SUBST(PTHREAD_CC)
307 |
308 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
309 | if test x"$ax_pthread_ok" = xyes; then
310 | ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
311 | :
312 | else
313 | ax_pthread_ok=no
314 | $2
315 | fi
316 | AC_LANG_POP
317 | ])dnl AX_PTHREAD
318 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_auto_repeat_delay.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_get_auto_repeat_rate.3
2 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_auto_repeat_rate.man:
--------------------------------------------------------------------------------
1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com)
2 | .\"
3 | .\" %%%LICENSE_START(VERBATIM)
4 | .\" libUIOHook is free software: you can redistribute it and/or modify
5 | .\" it under the terms of the GNU Lesser General Public License as published
6 | .\" by the Free Software Foundation, either version 3 of the License, or
7 | .\" (at your option) any later version.
8 | .\"
9 | .\" libUIOHook is distributed in the hope that it will be useful,
10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | .\" GNU General Public License for more details.
13 | .\"
14 | .\" You should have received a copy of the GNU Lesser General Public License
15 | .\" along with this program. If not, see .
16 | .\" %%%LICENSE_END
17 | .\"
18 | .TH hook_get_auto_repeat_rate 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual"
19 | .SH NAME
20 | hook_get_auto_repeat_rate, hook_get_auto_repeat_delay \- Keyboard auto\-repeat rate / delay
21 | .HP
22 | hook_get_pointer_acceleration_multiplier, hook_get_pointer_acceleration_threshold \- Pointer acceleration values
23 | .HP
24 | hook_get_pointer_sensitivity \- Pointer sensitivity
25 | .HP
26 | hook_get_multi_click_time \- Button multi-click timeout
27 | .SH SYNTAX
28 | #include
29 | .HP
30 | UIOHOOK_API long int hook_get_auto_repeat_rate\^(\fIvoid\fP\^);
31 | .HP
32 | UIOHOOK_API long int hook_get_auto_repeat_delay\^(\fIvoid\fP\^);
33 | .HP
34 | UIOHOOK_API long int hook_get_pointer_acceleration_multiplier\^(\fIvoid\fP\^);
35 | .HP
36 | UIOHOOK_API long int hook_get_pointer_acceleration_threshold\^(\fIvoid\fP\^);
37 | .HP
38 | UIOHOOK_API long int hook_get_pointer_sensitivity\^(\fIvoid\fP\^);
39 | .HP
40 | UIOHOOK_API long int hook_get_multi_click_time\^(\fIvoid\fP\^);
41 | .SH ARGUMENTS
42 | .IP \fIvoid\fP 1i
43 | .SH RETURN VALUE
44 | All functions return a value between zero and LONG_MAX inclusive for the
45 | requested property. If a system property could not be determined, due to error
46 | or omission, a value of -1 will be returned.
47 | .SH DESCRIPTION
48 | There is no guarantee that any of the above values will be available at any
49 | point during runtime. The return value should be checked for error after each
50 | call.
51 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_multi_click_time.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_get_auto_repeat_rate.3
2 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_pointer_acceleration_multiplier.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_get_auto_repeat_rate.3
2 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_pointer_acceleration_threshold.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_get_auto_repeat_rate.3
2 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_get_pointer_sensitivity.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_get_auto_repeat_rate.3
2 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_run.man:
--------------------------------------------------------------------------------
1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com)
2 | .\"
3 | .\" %%%LICENSE_START(VERBATIM)
4 | .\" libUIOHook is free software: you can redistribute it and/or modify
5 | .\" it under the terms of the GNU Lesser General Public License as published
6 | .\" by the Free Software Foundation, either version 3 of the License, or
7 | .\" (at your option) any later version.
8 | .\"
9 | .\" libUIOHook is distributed in the hope that it will be useful,
10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | .\" GNU General Public License for more details.
13 | .\"
14 | .\" You should have received a copy of the GNU Lesser General Public License
15 | .\" along with this program. If not, see .
16 | .\" %%%LICENSE_END
17 | .\"
18 | .TH hook_run 3 "12 Dec 2014" "Version 1.0" "libUIOHook Programmer's Manual"
19 | .SH NAME
20 | hook_run, hook_disable \- Insert / Withdraw the native event hook
21 | .SH SYNTAX
22 | #include
23 | .HP
24 | UIOHOOK_API int hook_run\^(\fIvoid\fP\^);
25 | .HP
26 | UIOHOOK_API int hook_stop\^(\fIvoid\fP\^);
27 | .SH ARGUMENTS
28 | .IP \fIvoid\fP 1i
29 |
30 | .SH RETURN VALUE
31 | .IP \fIUIOHOOK_SUCCESS\fP li
32 | Returned on success.
33 | .IP \fIUIOHOOK_FAILURE\fP li
34 | General failure status.
35 | .IP \fIUIOHOOK_ERROR_OUT_OF_MEMORY\fP li
36 | Out of system memory.
37 |
38 | .IP \fIUIOHOOK_ERROR_X_OPEN_DISPLAY\fP li
39 | X11 specific error for XOpenDisplay\^(\^) failures.
40 | .IP \fIUIOHOOK_ERROR_X_RECORD_NOT_FOUND\fP li
41 | X11 specific error if XRecord is unavailable.
42 | .IP \fIUIOHOOK_ERROR_X_RECORD_ALLOC_RANGE\fP li
43 | X11 specific error for XRecordAllocRange\^(\^) failures.
44 | .IP \fIUIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT\fP li
45 | X11 specific error for XRecordCreateContext\^(...\^) failures.
46 | .IP \fIUIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT\fP li
47 | X11 specific error for XRecordEnableContext\^(...\^) failures.
48 |
49 | .IP \fIUIOHOOK_ERROR_SET_WINDOWS_HOOK_EX\fP li
50 | Windows specific error for SetWindowsHookEx\^(...\^) failures.
51 |
52 | .IP \fIUIOHOOK_ERROR_AXAPI_DISABLED\fP li
53 | Darwin specific error if the Accessibility API for the calling application has not been enabled.
54 | .IP \fIUIOHOOK_ERROR_EVENT_PORT\fP li
55 | Darwin specfici error for CGEventTapCreate\^(...\^) failures.
56 | .IP \fIUIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE\fP li
57 | Darwin specfici error for CFMachPortCreateRunLoopSource\^(...\^) failures.
58 | .IP \fIUIOHOOK_ERROR_GET_RUNLOOP\fP li
59 | Darwin specfici error for CFRunLoopGetCurrent\^(\^) failures.
60 | .IP \fIUIOHOOK_ERROR_OBSERVER_CREATE\fP li
61 | Darwin specfici error for CFRunLoopObserverCreate\^(...\^) failures.
62 |
63 | .SH DESCRIPTION
64 | hook_run\^(\^) will block until either the hook has completed or an error
65 | occurred during the hook registration process. You may assume successful
66 | completion of hook_run() after receiving an event of EVENT_HOOK_ENABLED.
67 |
68 | The hook_stop\^(\^) function is asynchronous, and will only signal the running
69 | hook to stop. This function will return an error if signaling was not possible.
70 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_set_dispatch_proc.man:
--------------------------------------------------------------------------------
1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com)
2 | .\"
3 | .\" %%%LICENSE_START(VERBATIM)
4 | .\" libUIOHook is free software: you can redistribute it and/or modify
5 | .\" it under the terms of the GNU Lesser General Public License as published
6 | .\" by the Free Software Foundation, either version 3 of the License, or
7 | .\" (at your option) any later version.
8 | .\"
9 | .\" libUIOHook is distributed in the hope that it will be useful,
10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | .\" GNU General Public License for more details.
13 | .\"
14 | .\" You should have received a copy of the GNU Lesser General Public License
15 | .\" along with this program. If not, see .
16 | .\" %%%LICENSE_END
17 | .\"
18 | .TH hook_set_dispatch_proc 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual"
19 | .SH NAME
20 | hook_set_dispatch_proc \- Set the event callback function
21 | .SH SYNTAX
22 | #include
23 | .HP
24 | void dispatch_proc\^(\fIuiohook_event * const event\fP\^) {
25 | ...
26 | }
27 | .HP
28 | hook_set_dispatch_proc(&dispatch_proc);
29 |
30 | .SH ARGUMENTS
31 | .IP \fIdispatcher_t\fP 1i
32 | A function pointer to a matching dispatcher_t function.
33 | .SH RETURN VALUE
34 | .IP \fIvoid\fP li
35 |
36 | .SH DESCRIPTION
37 | Passing NULL to void dispatch_proc\^(\^) will remove the currently set callback.
38 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_set_logger_proc.man:
--------------------------------------------------------------------------------
1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com)
2 | .\"
3 | .\" %%%LICENSE_START(VERBATIM)
4 | .\" libUIOHook is free software: you can redistribute it and/or modify
5 | .\" it under the terms of the GNU Lesser General Public License as published
6 | .\" by the Free Software Foundation, either version 3 of the License, or
7 | .\" (at your option) any later version.
8 | .\"
9 | .\" libUIOHook is distributed in the hope that it will be useful,
10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | .\" GNU General Public License for more details.
13 | .\"
14 | .\" You should have received a copy of the GNU Lesser General Public License
15 | .\" along with this program. If not, see .
16 | .\" %%%LICENSE_END
17 | .\"
18 | .TH hook_set_logger_proc 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual"
19 | .SH NAME
20 | hook_set_logger_proc \- Set the event callback function
21 | .SH SYNTAX
22 | #include
23 | .HP
24 | void logger_proc\^(\fIunsigned int level, const char *format, ...\fP\^) {
25 | ...
26 | }
27 | .HP
28 | hook_set_logger_proc(&logger_proc);
29 |
30 | .SH ARGUMENTS
31 | .IP \fIlogger_t\fP 1i
32 | A function pointer to a matching logger_t function.
33 | .SH RETURN VALUE
34 | .IP \fIvoid\fP li
35 |
36 | .SH DESCRIPTION
37 | Passing NULL to void dispatch_proc\^(\^) will remove the currently set callback.
38 |
--------------------------------------------------------------------------------
/libuiohook/man/hook_stop.man:
--------------------------------------------------------------------------------
1 | .so man3/hook_run.3
2 |
--------------------------------------------------------------------------------
/libuiohook/pc/uiohook.pc.in:
--------------------------------------------------------------------------------
1 | prefix=@prefix@
2 | libdir=${prefix}/lib
3 | includedir=${prefix}/include
4 |
5 | Name: uiohook
6 | Description: uiohook library
7 | Version: @VERSION@
8 | Libs: @LIBS@ -L${libdir} -luiohook
9 | Cflags: -I${includedir}
10 | Requires: @REQUIRE@
11 |
--------------------------------------------------------------------------------
/libuiohook/src/darwin/input_helper.h:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | // Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png
20 | // Reference: https://svn.blender.org/svnroot/bf-blender/branches/render25/intern/ghost/intern/GHOST_SystemCocoa.mm
21 | // Reference: http://www.mactech.com/macintosh-c/chap02-1.html
22 |
23 | #ifndef _included_input_helper
24 | #define _included_input_helper
25 |
26 | #include
27 | #include // For HIToolbox kVK_ keycodes and TIS funcitons.
28 | #ifdef USE_IOKIT
29 | #include
30 | #endif
31 | #include
32 |
33 |
34 | #ifndef USE_IOKIT
35 | // Some of the system key codes that are needed from IOKit.
36 | #define NX_KEYTYPE_SOUND_UP 0x00
37 | #define NX_KEYTYPE_SOUND_DOWN 0x01
38 | #define NX_KEYTYPE_MUTE 0x07
39 |
40 | /* Display controls...
41 | #define NX_KEYTYPE_BRIGHTNESS_UP 0x02
42 | #define NX_KEYTYPE_BRIGHTNESS_DOWN 0x03
43 | #define NX_KEYTYPE_CONTRAST_UP 0x0B
44 | #define NX_KEYTYPE_CONTRAST_DOWN 0x0C
45 | #define NX_KEYTYPE_ILLUMINATION_UP 0x15
46 | #define NX_KEYTYPE_ILLUMINATION_DOWN 0x16
47 | #define NX_KEYTYPE_ILLUMINATION_TOGGLE 0x17
48 | */
49 |
50 | #define NX_KEYTYPE_CAPS_LOCK 0x04
51 | //#define NX_KEYTYPE_HELP 0x05
52 | #define NX_POWER_KEY 0x06
53 |
54 | #define NX_KEYTYPE_EJECT 0x0E
55 | #define NX_KEYTYPE_PLAY 0x10
56 | #define NX_KEYTYPE_NEXT 0x12
57 | #define NX_KEYTYPE_PREVIOUS 0x13
58 |
59 | /* There is no official fast-forward or rewind scan code support.
60 | #define NX_KEYTYPE_FAST 0x14
61 | #define NX_KEYTYPE_REWIND 0x15
62 | */
63 | #endif
64 |
65 | // These virtual key codes do not appear to be defined anywhere by Apple.
66 | #define kVK_NX_Power 0xE0 | NX_POWER_KEY /* 0xE6 */
67 | #define kVK_NX_Eject 0xE0 | NX_KEYTYPE_EJECT /* 0xEE */
68 |
69 | #define kVK_MEDIA_Play 0xE0 | NX_KEYTYPE_PLAY /* 0xF0 */
70 | #define kVK_MEDIA_Next 0xE0 | NX_KEYTYPE_NEXT /* 0xF1 */
71 | #define kVK_MEDIA_Previous 0xE0 | NX_KEYTYPE_PREVIOUS /* 0xF2 */
72 |
73 | #define kVK_RightCommand 0x36
74 | #define kVK_ContextMenu 0x6E // AKA kMenuPowerGlyph
75 | #define kVK_Undefined 0xFF
76 |
77 | // These button codes do not appear to be defined anywhere by Apple.
78 | #define kVK_LBUTTON kCGMouseButtonLeft
79 | #define kVK_RBUTTON kCGMouseButtonRight
80 | #define kVK_MBUTTON kCGMouseButtonCenter
81 | #define kVK_XBUTTON1 3
82 | #define kVK_XBUTTON2 4
83 |
84 | // These button masks do not appear to be defined anywhere by Apple.
85 | #define kCGEventFlagMaskButtonLeft 1 << 0
86 | #define kCGEventFlagMaskButtonRight 1 << 1
87 | #define kCGEventFlagMaskButtonCenter 1 << 2
88 | #define kCGEventFlagMaskXButton1 1 << 3
89 | #define kCGEventFlagMaskXButton2 1 << 4
90 |
91 |
92 | /* Check for access to Apples accessibility API.
93 | */
94 | extern bool is_accessibility_enabled();
95 |
96 | /* Converts an OSX key code and event mask to the appropriate Unicode character
97 | * representation.
98 | */
99 | extern UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size);
100 |
101 | /* Converts an OSX keycode to the appropriate UIOHook scancode constant.
102 | */
103 | extern uint16_t keycode_to_scancode(UInt64 keycode);
104 |
105 | /* Converts a UIOHook scancode constant to the appropriate OSX keycode.
106 | */
107 | extern UInt64 scancode_to_keycode(uint16_t keycode);
108 |
109 |
110 | /* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
111 | * functionality. This method is called by OnLibraryLoad() and may need to be
112 | * called in combination with UnloadInputHelper() if the native keyboard layout
113 | * is changed.
114 | */
115 | extern void load_input_helper();
116 |
117 | /* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
118 | * functionality. This method is called by OnLibraryUnload() and may need to be
119 | * called in combination with LoadInputHelper() if the native keyboard layout
120 | * is changed.
121 | */
122 | extern void unload_input_helper();
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/libuiohook/src/darwin/post_event.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "input_helper.h"
30 | #include "logger.h"
31 |
32 | // TODO Possibly relocate to input helper.
33 | static inline CGEventFlags get_key_event_mask(uiohook_event * const event) {
34 | CGEventFlags native_mask = 0x00;
35 |
36 | if (event->mask & (MASK_SHIFT)) { native_mask |= kCGEventFlagMaskShift; }
37 | if (event->mask & (MASK_CTRL)) { native_mask |= kCGEventFlagMaskControl; }
38 | if (event->mask & (MASK_META)) { native_mask |= kCGEventFlagMaskControl; }
39 | if (event->mask & (MASK_ALT)) { native_mask |= kCGEventFlagMaskAlternate; }
40 |
41 | if (event->type == EVENT_KEY_PRESSED || event->type == EVENT_KEY_RELEASED || event->type == EVENT_KEY_TYPED) {
42 | switch (event->data.keyboard.keycode) {
43 | case VC_KP_0:
44 | case VC_KP_1:
45 | case VC_KP_2:
46 | case VC_KP_3:
47 | case VC_KP_4:
48 | case VC_KP_5:
49 | case VC_KP_6:
50 | case VC_KP_7:
51 | case VC_KP_8:
52 | case VC_KP_9:
53 |
54 | case VC_NUM_LOCK:
55 | case VC_KP_ENTER:
56 | case VC_KP_MULTIPLY:
57 | case VC_KP_ADD:
58 | case VC_KP_SEPARATOR:
59 | case VC_KP_SUBTRACT:
60 | case VC_KP_DIVIDE:
61 | case VC_KP_COMMA:
62 | native_mask |= kCGEventFlagMaskNumericPad;
63 | break;
64 | }
65 | }
66 |
67 | return native_mask;
68 | }
69 |
70 | static inline void post_key_event(uiohook_event * const event) {
71 | bool is_pressed = event->type == EVENT_KEY_PRESSED;
72 |
73 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
74 | CGEventRef cg_event = CGEventCreateKeyboardEvent(src,
75 | (CGKeyCode) scancode_to_keycode(event->data.keyboard.keycode),
76 | is_pressed);
77 |
78 | CGEventSetFlags(cg_event, get_key_event_mask(event));
79 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
80 | CFRelease(cg_event);
81 | CFRelease(src);
82 | }
83 |
84 | static inline void post_mouse_button_event(uiohook_event * const event, bool is_pressed) {
85 | CGMouseButton mouse_button;
86 | CGEventType mouse_type;
87 | if (event->data.mouse.button == MOUSE_BUTTON1) {
88 | if (is_pressed) {
89 | mouse_type = kCGEventLeftMouseDown;
90 | }
91 | else {
92 | mouse_type = kCGEventLeftMouseUp;
93 | }
94 | mouse_button = kCGMouseButtonLeft;
95 | }
96 | else if (event->data.mouse.button == MOUSE_BUTTON2) {
97 | if (is_pressed) {
98 | mouse_type = kCGEventRightMouseDown;
99 | }
100 | else {
101 | mouse_type = kCGEventRightMouseUp;
102 | }
103 | mouse_button = kCGMouseButtonRight;
104 | }
105 | else {
106 | if (is_pressed) {
107 | mouse_type = kCGEventOtherMouseDown;
108 | }
109 | else {
110 | mouse_type = kCGEventOtherMouseUp;
111 | }
112 | mouse_button = event->data.mouse.button - 1;
113 | }
114 |
115 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
116 | CGEventRef cg_event = CGEventCreateMouseEvent(src,
117 | mouse_type,
118 | CGPointMake(
119 | (CGFloat) event->data.mouse.x,
120 | (CGFloat) event->data.mouse.y
121 | ),
122 | mouse_button
123 | );
124 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
125 | CFRelease(cg_event);
126 | CFRelease(src);
127 | }
128 |
129 | static inline void post_mouse_wheel_event(uiohook_event * const event) {
130 | // FIXME Should I create a source event with the coords?
131 | // It seems to automagically use the current location of the cursor.
132 | // Two options: Query the mouse, move it to x/y, scroll, then move back
133 | // OR disable x/y for scroll events on Windows & X11.
134 | CGScrollEventUnit scroll_unit;
135 | if (event->data.wheel.type == WHEEL_BLOCK_SCROLL) {
136 | // Scrolling data is line-based.
137 | scroll_unit = kCGScrollEventUnitLine;
138 | }
139 | else {
140 | // Scrolling data is pixel-based.
141 | scroll_unit = kCGScrollEventUnitPixel;
142 | }
143 |
144 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
145 | CGEventRef cg_event = CGEventCreateScrollWheelEvent(src,
146 | kCGScrollEventUnitLine,
147 | // TODO Currently only support 1 wheel axis.
148 | (CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
149 | event->data.wheel.amount * event->data.wheel.rotation);
150 |
151 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
152 | CFRelease(cg_event);
153 | CFRelease(src);
154 | }
155 |
156 | static inline void post_mouse_motion_event(uiohook_event * const event) {
157 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
158 | CGEventRef cg_event;
159 | if (event->mask >> 8 == 0x00) {
160 | // No mouse flags.
161 | cg_event = CGEventCreateMouseEvent(src,
162 | kCGEventMouseMoved,
163 | CGPointMake(
164 | (CGFloat) event->data.mouse.x,
165 | (CGFloat) event->data.mouse.y
166 | ),
167 | 0
168 | );
169 | }
170 | else if (event->mask & MASK_BUTTON1) {
171 | cg_event = CGEventCreateMouseEvent(src,
172 | kCGEventLeftMouseDragged,
173 | CGPointMake(
174 | (CGFloat) event->data.mouse.x,
175 | (CGFloat) event->data.mouse.y
176 | ),
177 | kCGMouseButtonLeft
178 | );
179 | }
180 | else if (event->mask & MASK_BUTTON2) {
181 | cg_event = CGEventCreateMouseEvent(src,
182 | kCGEventRightMouseDragged,
183 | CGPointMake(
184 | (CGFloat) event->data.mouse.x,
185 | (CGFloat) event->data.mouse.y
186 | ),
187 | kCGMouseButtonRight
188 | );
189 | }
190 | else {
191 | cg_event = CGEventCreateMouseEvent(src,
192 | kCGEventOtherMouseDragged,
193 | CGPointMake(
194 | (CGFloat) event->data.mouse.x,
195 | (CGFloat) event->data.mouse.y
196 | ),
197 | (event->mask >> 8) - 1
198 | );
199 | }
200 |
201 | // kCGSessionEventTap also works.
202 | CGEventPost(kCGHIDEventTap, cg_event);
203 | CFRelease(cg_event);
204 | CFRelease(src);
205 | }
206 |
207 | UIOHOOK_API void hook_post_event(uiohook_event * const event) {
208 | switch (event->type) {
209 | case EVENT_KEY_PRESSED:
210 | case EVENT_KEY_RELEASED:
211 | post_key_event(event);
212 | break;
213 |
214 |
215 | case EVENT_MOUSE_PRESSED:
216 | post_mouse_button_event(event, true);
217 | break;
218 |
219 | case EVENT_MOUSE_RELEASED:
220 | post_mouse_button_event(event, false);
221 | break;
222 |
223 | case EVENT_MOUSE_CLICKED:
224 | post_mouse_button_event(event, true);
225 | post_mouse_button_event(event, false);
226 | break;
227 |
228 | case EVENT_MOUSE_WHEEL:
229 | post_mouse_wheel_event(event);
230 | break;
231 |
232 |
233 | case EVENT_MOUSE_MOVED:
234 | case EVENT_MOUSE_DRAGGED:
235 | post_mouse_motion_event(event);
236 | break;
237 |
238 |
239 | case EVENT_KEY_TYPED:
240 | // FIXME Ignoreing EVENT_KEY_TYPED events.
241 |
242 | case EVENT_HOOK_ENABLED:
243 | case EVENT_HOOK_DISABLED:
244 | // Ignore hook enabled / disabled events.
245 |
246 | default:
247 | // Ignore any other garbage.
248 | logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
249 | __FUNCTION__, __LINE__, event->type);
250 | break;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/libuiohook/src/demo_hook.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | bool logger_proc(unsigned int level, const char *format, ...) {
31 | bool status = false;
32 |
33 | va_list args;
34 | switch (level) {
35 | #ifdef USE_DEBUG
36 | case LOG_LEVEL_DEBUG:
37 | case LOG_LEVEL_INFO:
38 | va_start(args, format);
39 | status = vfprintf(stdout, format, args) >= 0;
40 | va_end(args);
41 | break;
42 | #endif
43 |
44 | case LOG_LEVEL_WARN:
45 | case LOG_LEVEL_ERROR:
46 | va_start(args, format);
47 | status = vfprintf(stderr, format, args) >= 0;
48 | va_end(args);
49 | break;
50 | }
51 |
52 | return status;
53 | }
54 |
55 | // NOTE: The following callback executes on the same thread that hook_run() is called
56 | // from. This is important because hook_run() attaches to the operating systems
57 | // event dispatcher and may delay event delivery to the target application.
58 | // Furthermore, some operating systems may choose to disable your hook if it
59 | // takes to long to process. If you need to do any extended processing, please
60 | // do so by copying the event to your own queued dispatch thread.
61 | void dispatch_proc(uiohook_event * const event) {
62 | char buffer[256] = { 0 };
63 | size_t length = snprintf(buffer, sizeof(buffer),
64 | "id=%i,when=%" PRIu64 ",mask=0x%X",
65 | event->type, event->time, event->mask);
66 |
67 | switch (event->type) {
68 | case EVENT_KEY_PRESSED:
69 | // If the escape key is pressed, naturally terminate the program.
70 | if (event->data.keyboard.keycode == VC_ESCAPE) {
71 | int status = hook_stop();
72 | switch (status) {
73 | // System level errors.
74 | case UIOHOOK_ERROR_OUT_OF_MEMORY:
75 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
76 | break;
77 |
78 | case UIOHOOK_ERROR_X_RECORD_GET_CONTEXT:
79 | // NOTE This is the only platform specific error that occurs on hook_stop().
80 | logger_proc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
81 | break;
82 |
83 | // Default error.
84 | case UIOHOOK_FAILURE:
85 | default:
86 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
87 | break;
88 | }
89 | }
90 | case EVENT_KEY_RELEASED:
91 | snprintf(buffer + length, sizeof(buffer) - length,
92 | ",keycode=%u,rawcode=0x%X",
93 | event->data.keyboard.keycode, event->data.keyboard.rawcode);
94 | break;
95 |
96 | case EVENT_KEY_TYPED:
97 | snprintf(buffer + length, sizeof(buffer) - length,
98 | ",keychar=%lc,rawcode=%u",
99 | (wint_t) event->data.keyboard.keychar,
100 | event->data.keyboard.rawcode);
101 | break;
102 |
103 | case EVENT_MOUSE_PRESSED:
104 | case EVENT_MOUSE_RELEASED:
105 | case EVENT_MOUSE_CLICKED:
106 | case EVENT_MOUSE_MOVED:
107 | case EVENT_MOUSE_DRAGGED:
108 | snprintf(buffer + length, sizeof(buffer) - length,
109 | ",x=%i,y=%i,button=%i,clicks=%i",
110 | event->data.mouse.x, event->data.mouse.y,
111 | event->data.mouse.button, event->data.mouse.clicks);
112 | break;
113 |
114 | case EVENT_MOUSE_WHEEL:
115 | snprintf(buffer + length, sizeof(buffer) - length,
116 | ",type=%i,amount=%i,rotation=%i",
117 | event->data.wheel.type, event->data.wheel.amount,
118 | event->data.wheel.rotation);
119 | break;
120 |
121 | default:
122 | break;
123 | }
124 |
125 | fprintf(stdout, "%s\n", buffer);
126 | }
127 |
128 | int main() {
129 | // Set the logger callback for library output.
130 | hook_set_logger_proc(&logger_proc);
131 |
132 | // Set the event callback for uiohook events.
133 | hook_set_dispatch_proc(&dispatch_proc);
134 |
135 | // Start the hook and block.
136 | // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed.
137 | int status = hook_run();
138 | switch (status) {
139 | case UIOHOOK_SUCCESS:
140 | // Everything is ok.
141 | break;
142 |
143 | // System level errors.
144 | case UIOHOOK_ERROR_OUT_OF_MEMORY:
145 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
146 | break;
147 |
148 |
149 | // X11 specific errors.
150 | case UIOHOOK_ERROR_X_OPEN_DISPLAY:
151 | logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status);
152 | break;
153 |
154 | case UIOHOOK_ERROR_X_RECORD_NOT_FOUND:
155 | logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status);
156 | break;
157 |
158 | case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE:
159 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status);
160 | break;
161 |
162 | case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT:
163 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status);
164 | break;
165 |
166 | case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT:
167 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status);
168 | break;
169 |
170 |
171 | // Windows specific errors.
172 | case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX:
173 | logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status);
174 | break;
175 |
176 |
177 | // Darwin specific errors.
178 | case UIOHOOK_ERROR_AXAPI_DISABLED:
179 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status);
180 | break;
181 |
182 | case UIOHOOK_ERROR_CREATE_EVENT_PORT:
183 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status);
184 | break;
185 |
186 | case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE:
187 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status);
188 | break;
189 |
190 | case UIOHOOK_ERROR_GET_RUNLOOP:
191 | logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status);
192 | break;
193 |
194 | case UIOHOOK_ERROR_CREATE_OBSERVER:
195 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status);
196 | break;
197 |
198 | // Default error.
199 | case UIOHOOK_FAILURE:
200 | default:
201 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
202 | break;
203 | }
204 |
205 | return status;
206 | }
207 |
--------------------------------------------------------------------------------
/libuiohook/src/demo_hook_async.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #ifdef _WIN32
32 | #include
33 | #else
34 | #if defined(__APPLE__) && defined(__MACH__)
35 | #include
36 | #endif
37 |
38 | #include
39 | #endif
40 |
41 | // Native thread errors.
42 | #define UIOHOOK_ERROR_THREAD_CREATE 0x10
43 |
44 | // Thread and mutex variables.
45 | #ifdef _WIN32
46 | static HANDLE hook_thread;
47 |
48 | static HANDLE hook_running_mutex;
49 | static HANDLE hook_control_mutex;
50 | static HANDLE hook_control_cond;
51 | #else
52 | static pthread_t hook_thread;
53 |
54 | static pthread_mutex_t hook_running_mutex;
55 | static pthread_mutex_t hook_control_mutex;
56 | static pthread_cond_t hook_control_cond;
57 | #endif
58 |
59 |
60 | bool logger_proc(unsigned int level, const char *format, ...) {
61 | bool status = false;
62 |
63 | va_list args;
64 | switch (level) {
65 | #ifdef USE_DEBUG
66 | case LOG_LEVEL_DEBUG:
67 | case LOG_LEVEL_INFO:
68 | va_start(args, format);
69 | status = vfprintf(stdout, format, args) >= 0;
70 | va_end(args);
71 | break;
72 | #endif
73 |
74 | case LOG_LEVEL_WARN:
75 | case LOG_LEVEL_ERROR:
76 | va_start(args, format);
77 | status = vfprintf(stderr, format, args) >= 0;
78 | va_end(args);
79 | break;
80 | }
81 |
82 | return status;
83 | }
84 |
85 | // NOTE: The following callback executes on the same thread that hook_run() is called
86 | // from. This is important because hook_run() attaches to the operating systems
87 | // event dispatcher and may delay event delivery to the target application.
88 | // Furthermore, some operating systems may choose to disable your hook if it
89 | // takes to long to process. If you need to do any extended processing, please
90 | // do so by copying the event to your own queued dispatch thread.
91 | void dispatch_proc(uiohook_event * const event) {
92 | char buffer[256] = { 0 };
93 | size_t length = snprintf(buffer, sizeof(buffer),
94 | "id=%i,when=%" PRIu64 ",mask=0x%X",
95 | event->type, event->time, event->mask);
96 |
97 | switch (event->type) {
98 | case EVENT_HOOK_ENABLED:
99 | // Lock the running mutex so we know if the hook is enabled.
100 | #ifdef _WIN32
101 | WaitForSingleObject(hook_running_mutex, INFINITE);
102 | #else
103 | pthread_mutex_lock(&hook_running_mutex);
104 | #endif
105 |
106 |
107 | #ifdef _WIN32
108 | // Signal the control event.
109 | SetEvent(hook_control_cond);
110 | #else
111 | // Unlock the control mutex so hook_enable() can continue.
112 | pthread_cond_signal(&hook_control_cond);
113 | pthread_mutex_unlock(&hook_control_mutex);
114 | #endif
115 | break;
116 |
117 | case EVENT_HOOK_DISABLED:
118 | // Lock the control mutex until we exit.
119 | #ifdef _WIN32
120 | WaitForSingleObject(hook_control_mutex, INFINITE);
121 | #else
122 | pthread_mutex_lock(&hook_control_mutex);
123 | #endif
124 |
125 | // Unlock the running mutex so we know if the hook is disabled.
126 | #ifdef _WIN32
127 | ReleaseMutex(hook_running_mutex);
128 | ResetEvent(hook_control_cond);
129 | #else
130 | #if defined(__APPLE__) && defined(__MACH__)
131 | // Stop the main runloop so that this program ends.
132 | CFRunLoopStop(CFRunLoopGetMain());
133 | #endif
134 |
135 | pthread_mutex_unlock(&hook_running_mutex);
136 | #endif
137 | break;
138 |
139 | case EVENT_KEY_PRESSED:
140 | // If the escape key is pressed, naturally terminate the program.
141 | if (event->data.keyboard.keycode == VC_ESCAPE) {
142 | int status = hook_stop();
143 | switch (status) {
144 | // System level errors.
145 | case UIOHOOK_ERROR_OUT_OF_MEMORY:
146 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
147 | break;
148 |
149 | case UIOHOOK_ERROR_X_RECORD_GET_CONTEXT:
150 | // NOTE This is the only platform specific error that occurs on hook_stop().
151 | logger_proc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
152 | break;
153 |
154 | // Default error.
155 | case UIOHOOK_FAILURE:
156 | default:
157 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
158 | break;
159 | }
160 | }
161 | case EVENT_KEY_RELEASED:
162 | snprintf(buffer + length, sizeof(buffer) - length,
163 | ",keycode=%u,rawcode=0x%X",
164 | event->data.keyboard.keycode, event->data.keyboard.rawcode);
165 | break;
166 |
167 | case EVENT_KEY_TYPED:
168 | snprintf(buffer + length, sizeof(buffer) - length,
169 | ",keychar=%lc,rawcode=%u",
170 | (wint_t) event->data.keyboard.keychar,
171 | event->data.keyboard.rawcode);
172 | break;
173 |
174 | case EVENT_MOUSE_PRESSED:
175 | case EVENT_MOUSE_RELEASED:
176 | case EVENT_MOUSE_CLICKED:
177 | case EVENT_MOUSE_MOVED:
178 | case EVENT_MOUSE_DRAGGED:
179 | snprintf(buffer + length, sizeof(buffer) - length,
180 | ",x=%i,y=%i,button=%i,clicks=%i",
181 | event->data.mouse.x, event->data.mouse.y,
182 | event->data.mouse.button, event->data.mouse.clicks);
183 | break;
184 |
185 | case EVENT_MOUSE_WHEEL:
186 | snprintf(buffer + length, sizeof(buffer) - length,
187 | ",type=%i,amount=%i,rotation=%i",
188 | event->data.wheel.type, event->data.wheel.amount,
189 | event->data.wheel.rotation);
190 | break;
191 |
192 | default:
193 | break;
194 | }
195 |
196 | fprintf(stdout, "%s\n", buffer);
197 | }
198 |
199 | #ifdef _WIN32
200 | DWORD WINAPI hook_thread_proc(LPVOID arg) {
201 | #else
202 | void *hook_thread_proc(void *arg) {
203 | #endif
204 | // Set the hook status.
205 | int status = hook_run();
206 | if (status != UIOHOOK_SUCCESS) {
207 | #ifdef _WIN32
208 | *(DWORD *) arg = status;
209 | #else
210 | *(int *) arg = status;
211 | #endif
212 | }
213 |
214 | // Make sure we signal that we have passed any exception throwing code for
215 | // the waiting hook_enable().
216 | #ifdef _WIN32
217 | SetEvent(hook_control_cond);
218 |
219 | return status;
220 | #else
221 | // Make sure we signal that we have passed any exception throwing code for
222 | // the waiting hook_enable().
223 | pthread_cond_signal(&hook_control_cond);
224 | pthread_mutex_unlock(&hook_control_mutex);
225 |
226 | return arg;
227 | #endif
228 | }
229 |
230 | int hook_enable() {
231 | // Lock the thread control mutex. This will be unlocked when the
232 | // thread has finished starting, or when it has fully stopped.
233 | #ifdef _WIN32
234 | WaitForSingleObject(hook_control_mutex, INFINITE);
235 | #else
236 | pthread_mutex_lock(&hook_control_mutex);
237 | #endif
238 |
239 | // Set the initial status.
240 | int status = UIOHOOK_FAILURE;
241 |
242 | #ifndef _WIN32
243 | // Create the thread attribute.
244 | pthread_attr_t hook_thread_attr;
245 | pthread_attr_init(&hook_thread_attr);
246 |
247 | // Get the policy and priority for the thread attr.
248 | int policy;
249 | pthread_attr_getschedpolicy(&hook_thread_attr, &policy);
250 | int priority = sched_get_priority_max(policy);
251 | #endif
252 |
253 | #if defined(_WIN32)
254 | DWORD hook_thread_id;
255 | DWORD *hook_thread_status = malloc(sizeof(DWORD));
256 | hook_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hook_thread_proc, hook_thread_status, 0, &hook_thread_id);
257 | if (hook_thread != INVALID_HANDLE_VALUE) {
258 | #else
259 | int *hook_thread_status = malloc(sizeof(int));
260 | if (pthread_create(&hook_thread, &hook_thread_attr, hook_thread_proc, hook_thread_status) == 0) {
261 | #endif
262 | #if defined(_WIN32)
263 | // Attempt to set the thread priority to time critical.
264 | if (SetThreadPriority(hook_thread, THREAD_PRIORITY_TIME_CRITICAL) == 0) {
265 | logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %li for thread %#p! (%#lX)\n",
266 | __FUNCTION__, __LINE__, (long) THREAD_PRIORITY_TIME_CRITICAL,
267 | hook_thread , (unsigned long) GetLastError());
268 | }
269 | #elif (defined(__APPLE__) && defined(__MACH__)) || _POSIX_C_SOURCE >= 200112L
270 | // Some POSIX revisions do not support pthread_setschedprio so we will
271 | // use pthread_setschedparam instead.
272 | struct sched_param param = { .sched_priority = priority };
273 | if (pthread_setschedparam(hook_thread, SCHED_OTHER, ¶m) != 0) {
274 | logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n",
275 | __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread);
276 | }
277 | #else
278 | // Raise the thread priority using glibc pthread_setschedprio.
279 | if (pthread_setschedprio(hook_thread, priority) != 0) {
280 | logger_proc(LOG_LEVEL_WARN, "%s [%u]: Could not set thread priority %i for thread 0x%lX!\n",
281 | __FUNCTION__, __LINE__, priority, (unsigned long) hook_thread);
282 | }
283 | #endif
284 |
285 |
286 | // Wait for the thread to indicate that it has passed the
287 | // initialization portion by blocking until either a EVENT_HOOK_ENABLED
288 | // event is received or the thread terminates.
289 | // NOTE This unlocks the hook_control_mutex while we wait.
290 | #ifdef _WIN32
291 | WaitForSingleObject(hook_control_cond, INFINITE);
292 | #else
293 | pthread_cond_wait(&hook_control_cond, &hook_control_mutex);
294 | #endif
295 |
296 | #ifdef _WIN32
297 | if (WaitForSingleObject(hook_running_mutex, 0) != WAIT_TIMEOUT) {
298 | #else
299 | if (pthread_mutex_trylock(&hook_running_mutex) == 0) {
300 | #endif
301 | // Lock Successful; The hook is not running but the hook_control_cond
302 | // was signaled! This indicates that there was a startup problem!
303 |
304 | // Get the status back from the thread.
305 | #ifdef _WIN32
306 | WaitForSingleObject(hook_thread, INFINITE);
307 | GetExitCodeThread(hook_thread, hook_thread_status);
308 | #else
309 | pthread_join(hook_thread, (void **) &hook_thread_status);
310 | status = *hook_thread_status;
311 | #endif
312 | }
313 | else {
314 | // Lock Failure; The hook is currently running and wait was signaled
315 | // indicating that we have passed all possible start checks. We can
316 | // always assume a successful startup at this point.
317 | status = UIOHOOK_SUCCESS;
318 | }
319 |
320 | free(hook_thread_status);
321 |
322 | logger_proc(LOG_LEVEL_DEBUG, "%s [%u]: Thread Result: (%#X).\n",
323 | __FUNCTION__, __LINE__, status);
324 | }
325 | else {
326 | status = UIOHOOK_ERROR_THREAD_CREATE;
327 | }
328 |
329 | // Make sure the control mutex is unlocked.
330 | #ifdef _WIN32
331 | ReleaseMutex(hook_control_mutex);
332 | #else
333 | pthread_mutex_unlock(&hook_control_mutex);
334 | #endif
335 |
336 | return status;
337 | }
338 |
339 |
340 | int main() {
341 | // Lock the thread control mutex. This will be unlocked when the
342 | // thread has finished starting, or when it has fully stopped.
343 | #ifdef _WIN32
344 | // Create event handles for the thread hook.
345 | hook_running_mutex = CreateMutex(NULL, FALSE, TEXT("hook_running_mutex"));
346 | hook_control_mutex = CreateMutex(NULL, FALSE, TEXT("hook_control_mutex"));
347 | hook_control_cond = CreateEvent(NULL, TRUE, FALSE, TEXT("hook_control_cond"));
348 | #else
349 | pthread_mutex_init(&hook_running_mutex, NULL);
350 | pthread_mutex_init(&hook_control_mutex, NULL);
351 | pthread_cond_init(&hook_control_cond, NULL);
352 | #endif
353 |
354 | // Set the logger callback for library output.
355 | hook_set_logger_proc(&logger_proc);
356 |
357 | // Set the event callback for uiohook events.
358 | hook_set_dispatch_proc(&dispatch_proc);
359 |
360 | // Start the hook and block.
361 | // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed.
362 | int status = hook_enable();
363 | switch (status) {
364 | case UIOHOOK_SUCCESS:
365 | // We no longer block, so we need to explicitly wait for the thread to die.
366 | #ifdef _WIN32
367 | WaitForSingleObject(hook_thread, INFINITE);
368 | #else
369 | #if defined(__APPLE__) && defined(__MACH__)
370 | // NOTE Darwin requires that you start your own runloop from main.
371 | CFRunLoopRun();
372 | #endif
373 |
374 | pthread_join(hook_thread, NULL);
375 | #endif
376 | break;
377 |
378 | // System level errors.
379 | case UIOHOOK_ERROR_OUT_OF_MEMORY:
380 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)\n", status);
381 | break;
382 |
383 |
384 | // X11 specific errors.
385 | case UIOHOOK_ERROR_X_OPEN_DISPLAY:
386 | logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)\n", status);
387 | break;
388 |
389 | case UIOHOOK_ERROR_X_RECORD_NOT_FOUND:
390 | logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)\n", status);
391 | break;
392 |
393 | case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE:
394 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)\n", status);
395 | break;
396 |
397 | case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT:
398 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)\n", status);
399 | break;
400 |
401 | case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT:
402 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)\n", status);
403 | break;
404 |
405 |
406 | // Windows specific errors.
407 | case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX:
408 | logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)\n", status);
409 | break;
410 |
411 |
412 | // Darwin specific errors.
413 | case UIOHOOK_ERROR_AXAPI_DISABLED:
414 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)\n", status);
415 | break;
416 |
417 | case UIOHOOK_ERROR_CREATE_EVENT_PORT:
418 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)\n", status);
419 | break;
420 |
421 | case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE:
422 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)\n", status);
423 | break;
424 |
425 | case UIOHOOK_ERROR_GET_RUNLOOP:
426 | logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)\n", status);
427 | break;
428 |
429 | case UIOHOOK_ERROR_CREATE_OBSERVER:
430 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)\n", status);
431 | break;
432 |
433 | // Default error.
434 | case UIOHOOK_FAILURE:
435 | default:
436 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)\n", status);
437 | break;
438 | }
439 |
440 | #ifdef _WIN32
441 | // Create event handles for the thread hook.
442 | CloseHandle(hook_thread);
443 | CloseHandle(hook_running_mutex);
444 | CloseHandle(hook_control_mutex);
445 | CloseHandle(hook_control_cond);
446 | #else
447 | pthread_mutex_destroy(&hook_running_mutex);
448 | pthread_mutex_destroy(&hook_control_mutex);
449 | pthread_cond_destroy(&hook_control_cond);
450 | #endif
451 |
452 | return status;
453 | }
454 |
--------------------------------------------------------------------------------
/libuiohook/src/demo_post.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | #ifdef _WIN32
28 | #include
29 | #endif
30 |
31 | // Virtual event pointers
32 | static uiohook_event *event = NULL;
33 |
34 | // TODO Implement CLI options.
35 | //int main(int argc, char *argv[]) {
36 | int main() {
37 | // Allocate memory for the virtual events only once.
38 | event = (uiohook_event *) malloc(sizeof(uiohook_event));
39 |
40 | event->type = EVENT_KEY_PRESSED;
41 | event->mask = 0x00;
42 |
43 | event->data.keyboard.keycode = VC_A;
44 | event->data.keyboard.keychar = CHAR_UNDEFINED;
45 |
46 | hook_post_event(event);
47 |
48 | event->type = EVENT_KEY_RELEASED;
49 |
50 | hook_post_event(event);
51 |
52 | free(event);
53 |
54 | return 0;
55 | }
56 |
--------------------------------------------------------------------------------
/libuiohook/src/demo_properties.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | bool logger_proc(unsigned int level, const char *format, ...) {
29 | bool status = false;
30 |
31 | va_list args;
32 | switch (level) {
33 | case LOG_LEVEL_WARN:
34 | case LOG_LEVEL_ERROR:
35 | va_start(args, format);
36 | status = vfprintf(stderr, format, args) >= 0;
37 | va_end(args);
38 | break;
39 | }
40 |
41 | return status;
42 | }
43 |
44 | int main() {
45 | // Disable the logger.
46 | hook_set_logger_proc(&logger_proc);
47 |
48 | // Retrieves the keyboard auto repeat rate.
49 | long int repeat_rate = hook_get_auto_repeat_rate();
50 | if (repeat_rate >= 0) {
51 | fprintf(stdout, "Auto Repeat Rate:\t%ld\n", repeat_rate);
52 | }
53 | else {
54 | fprintf(stderr, "Failed to aquire keyboard auto repeat rate!\n");
55 | }
56 |
57 | // Retrieves the keyboard auto repeat delay.
58 | long int repeat_delay = hook_get_auto_repeat_delay();
59 | if (repeat_delay >= 0) {
60 | fprintf(stdout, "Auto Repeat Delay:\t%ld\n", repeat_delay);
61 | }
62 | else {
63 | fprintf(stderr, "Failed to acquire keyboard auto repeat delay!\n");
64 | }
65 |
66 | // Retrieves the mouse acceleration multiplier.
67 | long int acceleration_multiplier = hook_get_pointer_acceleration_multiplier();
68 | if (acceleration_multiplier >= 0) {
69 | fprintf(stdout, "Mouse Acceleration Multiplier:\t%ld\n", acceleration_multiplier);
70 | }
71 | else {
72 | fprintf(stderr, "Failed to acquire mouse acceleration multiplier!\n");
73 | }
74 |
75 | // Retrieves the mouse acceleration threshold.
76 | long int acceleration_threshold = hook_get_pointer_acceleration_threshold();
77 | if (acceleration_threshold >= 0) {
78 | fprintf(stdout, "Mouse Acceleration Threshold:\t%ld\n", acceleration_threshold);
79 | }
80 | else {
81 | fprintf(stderr, "Failed to acquire mouse acceleration threshold!\n");
82 | }
83 |
84 | // Retrieves the mouse sensitivity.
85 | long int sensitivity = hook_get_pointer_sensitivity();
86 | if (sensitivity >= 0) {
87 | fprintf(stdout, "Mouse Sensitivity:\t%ld\n", sensitivity);
88 | }
89 | else {
90 | fprintf(stderr, "Failed to acquire keyboard auto repeat rate!\n");
91 | }
92 |
93 | // Retrieves the double/triple click interval.
94 | long int click_time = hook_get_multi_click_time();
95 | if (click_time >= 0) {
96 | fprintf(stdout, "Multi-Click Time:\t%ld\n", click_time);
97 | }
98 | else {
99 | fprintf(stderr, "Failed to acquire keyboard auto repeat rate!\n");
100 | }
101 |
102 | return EXIT_SUCCESS;
103 | }
104 |
--------------------------------------------------------------------------------
/libuiohook/src/logger.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "logger.h"
30 |
31 | static bool default_logger(unsigned int level, const char *format, ...) {
32 | bool status = false;
33 |
34 | #ifndef USE_QUIET
35 | va_list args;
36 | switch (level) {
37 | #ifdef USE_DEBUG
38 | case LOG_LEVEL_DEBUG:
39 | #endif
40 | case LOG_LEVEL_INFO:
41 | va_start(args, format);
42 | status = vfprintf(stdout, format, args) >= 0;
43 | va_end(args);
44 | break;
45 |
46 | case LOG_LEVEL_WARN:
47 | case LOG_LEVEL_ERROR:
48 | va_start(args, format);
49 | status = vfprintf(stderr, format, args) >= 0;
50 | va_end(args);
51 | break;
52 | }
53 | #endif
54 |
55 | return status;
56 | }
57 |
58 | // Current logger function pointer, this should never be null.
59 | // FIXME This should be static and wrapped with a public facing function.
60 | logger_t logger = &default_logger;
61 |
62 |
63 | UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc) {
64 | if (logger_proc == NULL) {
65 | logger = &default_logger;
66 | }
67 | else {
68 | logger = logger_proc;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/libuiohook/src/logger.h:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _included_logger
20 | #define _included_logger
21 |
22 | #include
23 | #include
24 |
25 | #ifndef __FUNCTION__
26 | #define __FUNCTION__ __func__
27 | #endif
28 |
29 | // logger(level, message)
30 | extern logger_t logger;
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/libuiohook/src/windows/input_helper.h:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | /***********************************************************************
20 | * The following code is based on code provided by Marc-André Moreau
21 | * to work around a failure to support dead keys in the ToUnicode() API.
22 | * According to the author some parts were taken directly from
23 | * Microsoft's kbd.h header file that is shipped with the Windows Driver
24 | * Development Kit.
25 | *
26 | * The original code was substantially modified to provide the following:
27 | * 1) More dynamic code structure.
28 | * 2) Support for compilers that do not implement _ptr64 (GCC / LLVM).
29 | * 3) Support for Wow64 at runtime via 32-bit binary.
30 | * 4) Support for contextual language switching.
31 | *
32 | * I have contacted Marc-André Moreau who has granted permission for
33 | * his original source code to be used under the Public Domain. Although
34 | * the libUIOHook library as a whole is currently covered under the LGPLv3,
35 | * please feel free to use and learn from the source code contained in the
36 | * following functions under the terms of the Public Domain.
37 | *
38 | * For further reading and the original code, please visit:
39 | * http://legacy.docdroppers.org/wiki/index.php?title=Writing_Keyloggers
40 | * http://www.techmantras.com/content/writing-keyloggers-full-length-tutorial
41 | *
42 | ***********************************************************************/
43 |
44 | #ifndef _included_input_helper
45 | #define _included_input_helper
46 |
47 | #include
48 | #include
49 |
50 | #ifndef LPFN_ISWOW64PROCESS
51 | typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
52 | #endif
53 |
54 | typedef void* (CALLBACK *KbdLayerDescriptor) (VOID);
55 |
56 | #define CAPLOK 0x01
57 | #define WCH_NONE 0xF000
58 | #define WCH_DEAD 0xF001
59 |
60 | #ifndef WM_MOUSEHWHEEL
61 | #define WM_MOUSEHWHEEL 0x020E
62 | #endif
63 |
64 | typedef struct _VK_TO_WCHARS {
65 | BYTE VirtualKey;
66 | BYTE Attributes;
67 | WCHAR wch[];
68 | } VK_TO_WCHARS, *PVK_TO_WCHARS;
69 |
70 | typedef struct _LIGATURE {
71 | BYTE VirtualKey;
72 | WORD ModificationNumber;
73 | WCHAR wch[];
74 | } LIGATURE, *PLIGATURE;
75 |
76 | typedef struct _VK_TO_BIT {
77 | BYTE Vk;
78 | BYTE ModBits;
79 | } VK_TO_BIT, *PVK_TO_BIT;
80 |
81 | typedef struct _MODIFIERS {
82 | PVK_TO_BIT pVkToBit; // __ptr64
83 | WORD wMaxModBits;
84 | BYTE ModNumber[];
85 | } MODIFIERS, *PMODIFIERS;
86 |
87 | typedef struct _VSC_VK {
88 | BYTE Vsc;
89 | USHORT Vk;
90 | } VSC_VK, *PVSC_VK;
91 |
92 | typedef struct _VK_TO_WCHAR_TABLE {
93 | PVK_TO_WCHARS pVkToWchars; // __ptr64
94 | BYTE nModifications;
95 | BYTE cbSize;
96 | } VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
97 |
98 | typedef struct _DEADKEY {
99 | DWORD dwBoth;
100 | WCHAR wchComposed;
101 | USHORT uFlags;
102 | } DEADKEY, *PDEADKEY;
103 |
104 | typedef struct _VSC_LPWSTR {
105 | BYTE vsc;
106 | WCHAR *pwsz; // __ptr64
107 | } VSC_LPWSTR, *PVSC_LPWSTR;
108 |
109 | typedef struct tagKbdLayer {
110 | PMODIFIERS pCharModifiers; // __ptr64
111 | PVK_TO_WCHAR_TABLE pVkToWcharTable; // __ptr64
112 | PDEADKEY pDeadKey; // __ptr64
113 | PVSC_LPWSTR pKeyNames; // __ptr64
114 | PVSC_LPWSTR pKeyNamesExt; // __ptr64
115 | WCHAR **pKeyNamesDead; // __ptr64
116 | USHORT *pusVSCtoVK; // __ptr64
117 | BYTE bMaxVSCtoVK;
118 | PVSC_VK pVSCtoVK_E0; // __ptr64
119 | PVSC_VK pVSCtoVK_E1; // __ptr64
120 | DWORD fLocaleFlags;
121 | BYTE nLgMax;
122 | BYTE cbLgEntry;
123 | PLIGATURE pLigature; // __ptr64
124 | DWORD dwType;
125 | DWORD dwSubType;
126 | } KBDTABLES, *PKBDTABLES; // __ptr64
127 |
128 |
129 | extern SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size);
130 |
131 | //extern DWORD unicode_to_keycode(wchar_t unicode);
132 |
133 | extern unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags);
134 |
135 | extern DWORD scancode_to_keycode(unsigned short scancode);
136 |
137 | // Initialize the locale list and wow64 pointer size.
138 | extern int load_input_helper();
139 |
140 | // Cleanup the initialized locales.
141 | extern int unload_input_helper();
142 |
143 | #endif
144 |
--------------------------------------------------------------------------------
/libuiohook/src/windows/post_event.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | #include "input_helper.h"
28 | #include "logger.h"
29 |
30 | // Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
31 | #ifndef MAPVK_VK_TO_VSC
32 | #define MAPVK_VK_TO_VSC 0
33 | #define MAPVK_VSC_TO_VK 1
34 | #define MAPVK_VK_TO_CHAR 2
35 | #define MAPVK_VSC_TO_VK_EX 3
36 | #endif
37 | // Some buggy versions of MinGW and MSys only define this value for Windows
38 | // versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
39 | #ifndef MAPVK_VK_TO_VSC_EX
40 | #define MAPVK_VK_TO_VSC_EX 4
41 | #endif
42 |
43 | #ifndef KEYEVENTF_SCANCODE
44 | #define KEYEVENTF_EXTENDEDKEY 0x0001
45 | #define KEYEVENTF_KEYUP 0x0002
46 | #define KEYEVENTF_UNICODE 0x0004
47 | #define KEYEVENTF_SCANCODE 0x0008
48 | #endif
49 |
50 | #ifndef KEYEVENTF_KEYDOWN
51 | #define KEYEVENTF_KEYDOWN 0x0000
52 | #endif
53 |
54 | #define MAX_WINDOWS_COORD_VALUE 65535
55 |
56 | static UINT keymask_lookup[8] = {
57 | VK_LSHIFT,
58 | VK_LCONTROL,
59 | VK_LWIN,
60 | VK_LMENU,
61 |
62 | VK_RSHIFT,
63 | VK_RCONTROL,
64 | VK_RWIN,
65 | VK_RMENU
66 | };
67 |
68 | UIOHOOK_API void hook_post_event(uiohook_event * const event) {
69 | //FIXME implement multiple monitor support
70 | uint16_t screen_width = GetSystemMetrics( SM_CXSCREEN );
71 | uint16_t screen_height = GetSystemMetrics( SM_CYSCREEN );
72 |
73 | unsigned char events_size = 0, events_max = 28;
74 | INPUT *events = malloc(sizeof(INPUT) * events_max);
75 |
76 | if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
77 | for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
78 | if (event->mask & 1 << i) {
79 | events[events_size].type = INPUT_KEYBOARD;
80 | events[events_size].ki.wVk = keymask_lookup[i];
81 | events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN;
82 | events[events_size].ki.time = 0; // Use current system time.
83 | events_size++;
84 | }
85 | }
86 | }
87 |
88 | if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
89 | events[events_size].type = INPUT_MOUSE;
90 | events[events_size].mi.dx = 0; // Relative mouse movement due to
91 | events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
92 | events[events_size].mi.mouseData = 0x00;
93 | events[events_size].mi.time = 0; // Use current system time.
94 |
95 | if (event->mask & MASK_BUTTON1) {
96 | events[events_size].mi.mouseData |= MOUSEEVENTF_LEFTDOWN;
97 | }
98 |
99 | if (event->mask & MASK_BUTTON2) {
100 | events[events_size].mi.mouseData |= MOUSEEVENTF_RIGHTDOWN;
101 | }
102 |
103 | if (event->mask & MASK_BUTTON3) {
104 | events[events_size].mi.mouseData |= MOUSEEVENTF_MIDDLEDOWN;
105 | }
106 |
107 | if (event->mask & MASK_BUTTON4) {
108 | events[events_size].mi.mouseData = XBUTTON1;
109 | events[events_size].mi.mouseData |= MOUSEEVENTF_XDOWN;
110 | }
111 |
112 | if (event->mask & MASK_BUTTON5) {
113 | events[events_size].mi.mouseData = XBUTTON2;
114 | events[events_size].mi.dwFlags |= MOUSEEVENTF_XDOWN;
115 | }
116 |
117 | events_size++;
118 | }
119 |
120 |
121 | switch (event->type) {
122 | case EVENT_KEY_PRESSED:
123 | events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
124 | if (events[events_size].ki.wVk != 0x0000) {
125 | events[events_size].type = INPUT_KEYBOARD;
126 | events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN; // |= KEYEVENTF_SCANCODE;
127 | events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
128 | events[events_size].ki.time = 0; // GetSystemTime()
129 | events_size++;
130 | }
131 | else {
132 | logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
133 | __FUNCTION__, __LINE__,
134 | event->data.keyboard.keycode);
135 | }
136 | break;
137 |
138 | case EVENT_KEY_RELEASED:
139 | events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
140 | if (events[events_size].ki.wVk != 0x0000) {
141 | events[events_size].type = INPUT_KEYBOARD;
142 | events[events_size].ki.dwFlags = KEYEVENTF_KEYUP; // |= KEYEVENTF_SCANCODE;
143 | events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
144 | events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
145 | events[events_size].ki.time = 0; // GetSystemTime()
146 | events_size++;
147 | }
148 | else {
149 | logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
150 | __FUNCTION__, __LINE__,
151 | event->data.keyboard.keycode);
152 | }
153 | break;
154 |
155 |
156 | case EVENT_MOUSE_PRESSED:
157 | events[events_size].type = INPUT_MOUSE;
158 | events[events_size].mi.dwFlags = MOUSEEVENTF_XDOWN;
159 |
160 | switch (event->data.mouse.button) {
161 | case MOUSE_BUTTON1:
162 | events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
163 | break;
164 |
165 | case MOUSE_BUTTON2:
166 | events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
167 | break;
168 |
169 | case MOUSE_BUTTON3:
170 | events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
171 | break;
172 |
173 | case MOUSE_BUTTON4:
174 | events[events_size].mi.mouseData = XBUTTON1;
175 | break;
176 |
177 | case MOUSE_BUTTON5:
178 | events[events_size].mi.mouseData = XBUTTON2;
179 | break;
180 |
181 | default:
182 | // Extra buttons.
183 | if (event->data.mouse.button > 3) {
184 | events[events_size].mi.mouseData = event->data.mouse.button - 3;
185 | }
186 | }
187 |
188 | events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
189 | events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
190 |
191 | events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
192 | events[events_size].mi.time = 0; // GetSystemTime()
193 |
194 | events_size++;
195 | break;
196 |
197 | case EVENT_MOUSE_RELEASED:
198 | events[events_size].type = INPUT_MOUSE;
199 | events[events_size].mi.dwFlags = MOUSEEVENTF_XUP;
200 |
201 | switch (event->data.mouse.button) {
202 | case MOUSE_BUTTON1:
203 | events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTUP;
204 | break;
205 |
206 | case MOUSE_BUTTON2:
207 | events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
208 | break;
209 |
210 | case MOUSE_BUTTON3:
211 | events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
212 | break;
213 |
214 | case MOUSE_BUTTON4:
215 | events[events_size].mi.mouseData = XBUTTON1;
216 | break;
217 |
218 | case MOUSE_BUTTON5:
219 | events[events_size].mi.mouseData = XBUTTON2;
220 | break;
221 |
222 | default:
223 | // Extra buttons.
224 | if (event->data.mouse.button > 3) {
225 | events[events_size].mi.mouseData = event->data.mouse.button - 3;
226 | }
227 | }
228 |
229 | events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
230 | events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
231 |
232 | events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
233 | events[events_size].mi.time = 0; // GetSystemTime()
234 | events_size++;
235 | break;
236 |
237 |
238 | case EVENT_MOUSE_WHEEL:
239 | events[events_size].type = INPUT_MOUSE;
240 | events[events_size].mi.dwFlags = MOUSEEVENTF_WHEEL;
241 |
242 | // type, amount and rotation?
243 | events[events_size].mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
244 |
245 | events[events_size].mi.dx = event->data.wheel.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
246 | events[events_size].mi.dy = event->data.wheel.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
247 |
248 | events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
249 | events[events_size].mi.time = 0; // GetSystemTime()
250 | events_size++;
251 | break;
252 |
253 |
254 | case EVENT_MOUSE_DRAGGED:
255 | // The button masks are all applied with the modifier masks.
256 |
257 | case EVENT_MOUSE_MOVED:
258 | events[events_size].type = INPUT_MOUSE;
259 | events[events_size].mi.dwFlags = MOUSEEVENTF_MOVE;
260 |
261 | events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
262 | events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
263 |
264 | events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
265 | events[events_size].mi.time = 0; // GetSystemTime()
266 | events_size++;
267 | break;
268 |
269 |
270 | case EVENT_MOUSE_CLICKED:
271 | case EVENT_KEY_TYPED:
272 | // Ignore clicked and typed events.
273 |
274 | case EVENT_HOOK_ENABLED:
275 | case EVENT_HOOK_DISABLED:
276 | // Ignore hook enabled / disabled events.
277 |
278 | default:
279 | // Ignore any other garbage.
280 | logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
281 | __FUNCTION__, __LINE__, event->type);
282 | break;
283 | }
284 |
285 | // Release the previously held modifier keys used to fake the event mask.
286 | if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
287 | for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
288 | if (event->mask & 1 << i) {
289 | events[events_size].type = INPUT_KEYBOARD;
290 | events[events_size].ki.wVk = keymask_lookup[i];
291 | events[events_size].ki.dwFlags = KEYEVENTF_KEYUP;
292 | events[events_size].ki.time = 0; // Use current system time.
293 | events_size++;
294 | }
295 | }
296 | }
297 |
298 | if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
299 | events[events_size].type = INPUT_MOUSE;
300 | events[events_size].mi.dx = 0; // Relative mouse movement due to
301 | events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
302 | events[events_size].mi.mouseData = 0x00;
303 | events[events_size].mi.time = 0; // Use current system time.
304 |
305 | // If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or MOUSEEVENTF_XUP,
306 | // then mouseData should be zero.
307 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx
308 | if (event->mask & MASK_BUTTON1) {
309 | events[events_size].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
310 | }
311 |
312 | if (event->mask & MASK_BUTTON2) {
313 | events[events_size].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
314 | }
315 |
316 | if (event->mask & MASK_BUTTON3) {
317 | events[events_size].mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
318 | }
319 |
320 | if (event->mask & MASK_BUTTON4) {
321 | events[events_size].mi.mouseData = XBUTTON1;
322 | events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
323 | }
324 |
325 | if (event->mask & MASK_BUTTON5) {
326 | events[events_size].mi.mouseData = XBUTTON2;
327 | events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
328 | }
329 |
330 | events_size++;
331 | }
332 |
333 | // Create the key release input
334 | // memcpy(key_events + 1, key_events, sizeof(INPUT));
335 | // key_events[1].ki.dwFlags |= KEYEVENTF_KEYUP;
336 |
337 | if (! SendInput(events_size, events, sizeof(INPUT)) ) {
338 | logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
339 | __FUNCTION__, __LINE__, (unsigned long) GetLastError());
340 | }
341 |
342 | free(events);
343 | }
344 |
--------------------------------------------------------------------------------
/libuiohook/src/windows/system_properties.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 |
26 | #include "logger.h"
27 | #include "input_helper.h"
28 |
29 | // The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
30 | HINSTANCE hInst;
31 |
32 | // input_hook.c
33 | extern void unregister_running_hooks();
34 |
35 |
36 | // Structure for the monitor_enum_proc() callback so we can track the count.
37 | typedef struct _screen_info {
38 | uint8_t count;
39 | screen_data *data;
40 | } screen_info;
41 |
42 | /* The following function was contributed by Anthony Liguori Jan 14, 2015.
43 | * https://github.com/kwhat/libuiohook/pull/17
44 | *
45 | * callback function called by EnumDisplayMonitors for each enabled monitor
46 | * http://msdn.microsoft.com/en-us/library/windows/desktop/dd162610(v=vs.85).aspx
47 | * http://msdn.microsoft.com/en-us/library/dd162610%28VS.85%29.aspx
48 | * http://msdn.microsoft.com/en-us/library/dd145061%28VS.85%29.aspx
49 | * http://msdn.microsoft.com/en-us/library/dd144901(v=vs.85).aspx
50 | */
51 | static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
52 | int width = lprcMonitor->right - lprcMonitor->left;
53 | int height = lprcMonitor->bottom - lprcMonitor->top;
54 | int origin_x = lprcMonitor->left;
55 | int origin_y = lprcMonitor->top;
56 |
57 | if (width > 0 && height > 0) {
58 | screen_info *screens = (screen_info *) dwData;
59 |
60 | if (screens->data == NULL) {
61 | screens->data = (screen_data *) malloc(sizeof(screen_data));
62 | }
63 | else {
64 | screens->data = (screen_data *) realloc(screens, sizeof(screen_data) * screens->count);
65 | }
66 |
67 | screens->data[screens->count++] = (screen_data) {
68 | // Should monitor count start @ zero? Currently it starts at 1.
69 | .number = screens->count,
70 | .x = origin_x,
71 | .y = origin_y,
72 | .width = width,
73 | .height = height
74 | };
75 |
76 | logger(LOG_LEVEL_INFO, "%s [%u]: Monitor %d: %ldx%ld (%ld, %ld)\n",
77 | __FUNCTION__, __LINE__, screens->count, width, height, origin_x, origin_y);
78 | }
79 |
80 | return TRUE;
81 | }
82 |
83 | /* The following function was contributed by Anthony Liguori Jan 14, 2015.
84 | * https://github.com/kwhat/libuiohook/pull/17
85 | */
86 | UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
87 | // Initialize count to zero.
88 | *count = 0;
89 |
90 | // Create a simple structure to make working with monitor_enum_proc easier.
91 | screen_info screens = {
92 | .count = 0,
93 | .data = NULL
94 | };
95 |
96 | BOOL status = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM) &screens);
97 |
98 | if (!status || screens.count == 0) {
99 | // Fallback in case EnumDisplayMonitors fails.
100 | logger(LOG_LEVEL_INFO, "%s [%u]: EnumDisplayMonitors failed. Fallback.\n",
101 | __FUNCTION__, __LINE__);
102 |
103 | int width = GetSystemMetrics(SM_CXSCREEN);
104 | int height = GetSystemMetrics(SM_CYSCREEN);
105 |
106 | if (width > 0 && height > 0) {
107 | screens.data = (screen_data *) malloc(sizeof(screen_data));
108 |
109 | if (screens.data != NULL) {
110 | *count = 1;
111 | screens.data[0] = (screen_data) {
112 | .number = 1,
113 | .x = 0,
114 | .y = 0,
115 | .width = width,
116 | .height = height
117 | };
118 | }
119 | }
120 | }
121 | else {
122 | // Populate the count.
123 | *count = screens.count;
124 | }
125 |
126 | return screens.data;
127 | }
128 |
129 | UIOHOOK_API long int hook_get_auto_repeat_rate() {
130 | long int value = -1;
131 | long int rate;
132 |
133 | if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) {
134 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n",
135 | __FUNCTION__, __LINE__, rate);
136 |
137 | value = rate;
138 | }
139 |
140 | return value;
141 | }
142 |
143 | UIOHOOK_API long int hook_get_auto_repeat_delay() {
144 | long int value = -1;
145 | long int delay;
146 |
147 | if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) {
148 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n",
149 | __FUNCTION__, __LINE__, delay);
150 |
151 | value = delay;
152 | }
153 |
154 | return value;
155 | }
156 |
157 | UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
158 | long int value = -1;
159 | int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
160 |
161 | if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
162 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[2]: %i.\n",
163 | __FUNCTION__, __LINE__, mouse[2]);
164 |
165 | value = mouse[2];
166 | }
167 |
168 | return value;
169 | }
170 |
171 | UIOHOOK_API long int hook_get_pointer_acceleration_threshold() {
172 | long int value = -1;
173 | int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
174 |
175 | if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
176 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[0]: %i.\n",
177 | __FUNCTION__, __LINE__, mouse[0]);
178 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[1]: %i.\n",
179 | __FUNCTION__, __LINE__, mouse[1]);
180 |
181 | // Average the x and y thresholds.
182 | value = (mouse[0] + mouse[1]) / 2;
183 | }
184 |
185 | return value;
186 | }
187 |
188 | UIOHOOK_API long int hook_get_pointer_sensitivity() {
189 | long int value = -1;
190 | int sensitivity;
191 |
192 | if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) {
193 | logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSESPEED: %i.\n",
194 | __FUNCTION__, __LINE__, sensitivity);
195 |
196 | value = sensitivity;
197 | }
198 |
199 | return value;
200 | }
201 |
202 | UIOHOOK_API long int hook_get_multi_click_time() {
203 | long int value = -1;
204 | UINT clicktime;
205 |
206 | clicktime = GetDoubleClickTime();
207 | logger(LOG_LEVEL_INFO, "%s [%u]: GetDoubleClickTime: %u.\n",
208 | __FUNCTION__, __LINE__, (unsigned int) clicktime);
209 |
210 | value = (long int) clicktime;
211 |
212 | return value;
213 | }
214 |
215 | // DLL Entry point.
216 | BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
217 | switch (fdwReason) {
218 | case DLL_PROCESS_ATTACH:
219 | // Save the DLL address.
220 | hInst = hInstDLL;
221 |
222 | // Initialize native input helper functions.
223 | load_input_helper();
224 | break;
225 |
226 | case DLL_PROCESS_DETACH:
227 | // Unregister any hooks that may still be installed.
228 | unregister_running_hooks();
229 |
230 | // Deinitialize native input helper functions.
231 | unload_input_helper();
232 | break;
233 |
234 | case DLL_THREAD_ATTACH:
235 | case DLL_THREAD_DETACH:
236 | // Do Nothing.
237 | break;
238 | }
239 |
240 | return TRUE;
241 | }
242 |
--------------------------------------------------------------------------------
/libuiohook/src/x11/input_helper.h:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifndef _included_input_helper
20 | #define _included_input_helper
21 |
22 | #include
23 | #include
24 |
25 | #ifdef USE_XKBCOMMON
26 | #include
27 | #include
28 | #include
29 | #endif
30 |
31 |
32 | // Virtual button codes that are not defined by X11.
33 | #define Button1 1
34 | #define Button2 2
35 | #define Button3 3
36 | #define WheelUp 4
37 | #define WheelDown 5
38 | #define WheelLeft 6
39 | #define WheelRight 7
40 | #define XButton1 8
41 | #define XButton2 9
42 |
43 | /* Converts an X11 key symbol to a single Unicode character. No direct X11
44 | * functionality exists to provide this information.
45 | */
46 | extern size_t keysym_to_unicode(KeySym keysym, uint16_t *buffer, size_t size);
47 |
48 | /* Convert a single Unicode character to an X11 key symbol. This function
49 | * provides a better translation than XStringToKeysym() for Unicode characters.
50 | */
51 | extern KeySym unicode_to_keysym(uint16_t unicode);
52 |
53 | /* Converts an X11 key code to the appropriate keyboard scan code.
54 | */
55 | extern uint16_t keycode_to_scancode(KeyCode keycode);
56 |
57 | /* Converts a keyboard scan code to the appropriate X11 key code.
58 | */
59 | extern KeyCode scancode_to_keycode(uint16_t scancode);
60 |
61 |
62 | #ifdef USE_XKBCOMMON
63 |
64 | /* Converts an X11 key code to a Unicode character sequence. libXKBCommon support
65 | * is required for this method.
66 | */
67 | extern size_t keycode_to_unicode(struct xkb_state* state, KeyCode keycode, uint16_t *buffer, size_t size);
68 |
69 | /* Create a xkb_state structure and return a pointer to it.
70 | */
71 | extern struct xkb_state * create_xkb_state(struct xkb_context *context, xcb_connection_t *connection);
72 |
73 | /* Release xkb_state structure created by create_xkb_state().
74 | */
75 | extern void destroy_xkb_state(struct xkb_state* state);
76 |
77 | #else
78 |
79 | /* Converts an X11 key code and event mask to the appropriate X11 key symbol.
80 | * This functions in much the same way as XKeycodeToKeysym() but allows for a
81 | * faster and more flexible lookup.
82 | */
83 | extern KeySym keycode_to_keysym(KeyCode keycode, unsigned int modifier_mask);
84 |
85 | #endif
86 |
87 | /* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
88 | * functionality. This method is called by OnLibraryLoad() and may need to be
89 | * called in combination with UnloadInputHelper() if the native keyboard layout
90 | * is changed.
91 | */
92 | extern void load_input_helper();
93 |
94 | /* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
95 | * functionality. This method is called by OnLibraryUnload() and may need to be
96 | * called in combination with LoadInputHelper() if the native keyboard layout
97 | * is changed.
98 | */
99 | extern void unload_input_helper();
100 |
101 | #endif
102 |
--------------------------------------------------------------------------------
/libuiohook/src/x11/post_event.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #ifdef HAVE_CONFIG_H
20 | #include
21 | #endif
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #ifdef USE_XTEST
30 | #include
31 | #endif
32 |
33 | #include "input_helper.h"
34 | #include "logger.h"
35 |
36 | extern Display *properties_disp;
37 |
38 | // This lookup table must be in the same order the masks are defined.
39 | #ifdef USE_XTEST
40 | static KeySym keymask_lookup[8] = {
41 | XK_Shift_L,
42 | XK_Control_L,
43 | XK_Meta_L,
44 | XK_Alt_L,
45 |
46 | XK_Shift_R,
47 | XK_Control_R,
48 | XK_Meta_R,
49 | XK_Alt_R
50 | };
51 |
52 | static unsigned int btnmask_lookup[5] = {
53 | MASK_BUTTON1,
54 | MASK_BUTTON2,
55 | MASK_BUTTON3,
56 | MASK_BUTTON4,
57 | MASK_BUTTON5
58 | };
59 | #else
60 | // TODO Possibly relocate to input helper.
61 | static unsigned int convert_to_native_mask(unsigned int mask) {
62 | unsigned int native_mask = 0x00;
63 |
64 | if (mask & (MASK_SHIFT)) { native_mask |= ShiftMask; }
65 | if (mask & (MASK_CTRL)) { native_mask |= ControlMask; }
66 | if (mask & (MASK_META)) { native_mask |= Mod4Mask; }
67 | if (mask & (MASK_ALT)) { native_mask |= Mod1Mask; }
68 |
69 | if (mask & MASK_BUTTON1) { native_mask |= Button1Mask; }
70 | if (mask & MASK_BUTTON2) { native_mask |= Button2Mask; }
71 | if (mask & MASK_BUTTON3) { native_mask |= Button3Mask; }
72 | if (mask & MASK_BUTTON4) { native_mask |= Button4Mask; }
73 | if (mask & MASK_BUTTON5) { native_mask |= Button5Mask; }
74 |
75 | return native_mask;
76 | }
77 | #endif
78 |
79 | static inline void post_key_event(uiohook_event * const event) {
80 | #ifdef USE_XTEST
81 | // FIXME Currently ignoring EVENT_KEY_TYPED.
82 | if (event->type == EVENT_KEY_PRESSED) {
83 | XTestFakeKeyEvent(
84 | properties_disp,
85 | scancode_to_keycode(event->data.keyboard.keycode),
86 | True,
87 | 0);
88 | }
89 | else if (event->type == EVENT_KEY_RELEASED) {
90 | XTestFakeKeyEvent(
91 | properties_disp,
92 | scancode_to_keycode(event->data.keyboard.keycode),
93 | False,
94 | 0);
95 | }
96 | #else
97 | XKeyEvent key_event;
98 |
99 | key_event.serial = 0x00;
100 | key_event.send_event = False;
101 | key_event.display = properties_disp;
102 | key_event.time = CurrentTime;
103 | key_event.same_screen = True;
104 |
105 | unsigned int mask;
106 | if (!XQueryPointer(properties_disp, DefaultRootWindow(properties_disp), &(key_event.root), &(key_event.subwindow), &(key_event.x_root), &(key_event.y_root), &(key_event.x), &(key_event.y), &mask)) {
107 | key_event.root = DefaultRootWindow(properties_disp);
108 | key_event.window = key_event.root;
109 | key_event.subwindow = None;
110 |
111 | key_event.x_root = 0;
112 | key_event.y_root = 0;
113 | key_event.x = 0;
114 | key_event.y = 0;
115 | }
116 |
117 | key_event.state = convert_to_native_mask(event->mask);
118 | key_event.keycode = XKeysymToKeycode(properties_disp, scancode_to_keycode(event->data.keyboard.keycode));
119 |
120 | // FIXME Currently ignoring typed events.
121 | if (event->type == EVENT_KEY_PRESSED) {
122 | key_event.type = KeyPress;
123 | XSendEvent(properties_disp, InputFocus, False, KeyPressMask, (XEvent *) &key_event);
124 | }
125 | else if (event->type == EVENT_KEY_RELEASED) {
126 | key_event.type = KeyRelease;
127 | XSendEvent(properties_disp, InputFocus, False, KeyReleaseMask, (XEvent *) &key_event);
128 | }
129 | #endif
130 | }
131 |
132 | static inline void post_mouse_button_event(uiohook_event * const event) {
133 | #ifdef USE_XTEST
134 | Window ret_root;
135 | Window ret_child;
136 | int root_x;
137 | int root_y;
138 | int win_x;
139 | int win_y;
140 | unsigned int mask;
141 |
142 | Window win_root = XDefaultRootWindow(properties_disp);
143 | Bool query_status = XQueryPointer(properties_disp, win_root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
144 | if (query_status) {
145 | if (event->data.mouse.x != root_x || event->data.mouse.y != root_y) {
146 | // Move the pointer to the specified position.
147 | XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
148 | }
149 | else {
150 | query_status = False;
151 | }
152 | }
153 |
154 | if (event->type == EVENT_MOUSE_WHEEL) {
155 | // Wheel events should be the same as click events on X11.
156 | // type, amount and rotation
157 | if (event->data.wheel.rotation < 0) {
158 | XTestFakeButtonEvent(properties_disp, WheelUp, True, 0);
159 | XTestFakeButtonEvent(properties_disp, WheelUp, False, 0);
160 | }
161 | else {
162 | XTestFakeButtonEvent(properties_disp, WheelDown, True, 0);
163 | XTestFakeButtonEvent(properties_disp, WheelDown, False, 0);
164 | }
165 | }
166 | else if (event->type == EVENT_MOUSE_PRESSED) {
167 | XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
168 | }
169 | else if (event->type == EVENT_MOUSE_RELEASED) {
170 | XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
171 | }
172 | else if (event->type == EVENT_MOUSE_CLICKED) {
173 | XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
174 | XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
175 | }
176 |
177 | if (query_status) {
178 | // Move the pointer back to the original position.
179 | XTestFakeMotionEvent(properties_disp, -1, root_x, root_y, 0);
180 | }
181 | #else
182 | XButtonEvent btn_event;
183 |
184 | btn_event.serial = 0x00;
185 | btn_event.send_event = False;
186 | btn_event.display = properties_disp;
187 | btn_event.time = CurrentTime;
188 | btn_event.same_screen = True;
189 |
190 | btn_event.root = DefaultRootWindow(properties_disp);
191 | btn_event.window = btn_event.root;
192 | btn_event.subwindow = None;
193 |
194 | btn_event.type = 0x00;
195 | btn_event.state = 0x00;
196 | btn_event.x_root = 0;
197 | btn_event.y_root = 0;
198 | btn_event.x = 0;
199 | btn_event.y = 0;
200 | btn_event.button = 0x00;
201 |
202 | btn_event.state = convert_to_native_mask(event->mask);
203 |
204 | btn_event.x = event->data.mouse.x;
205 | btn_event.y = event->data.mouse.y;
206 |
207 | #if defined(USE_XINERAMA) || defined(USE_XRANDR)
208 | uint8_t screen_count;
209 | screen_data *screens = hook_create_screen_info(&screen_count);
210 | if (screen_count > 1) {
211 | btn_event.x += screens[0].x;
212 | btn_event.y += screens[0].y;
213 | }
214 |
215 | if (screens != NULL) {
216 | free(screens);
217 | }
218 | #endif
219 |
220 | // These are the same because Window == Root Window.
221 | btn_event.x_root = btn_event.x;
222 | btn_event.y_root = btn_event.y;
223 |
224 | if (event->type == EVENT_MOUSE_WHEEL) {
225 | // type, amount and rotation
226 | if (event->data.wheel.rotation < 0) {
227 | btn_event.button = WheelUp;
228 | }
229 | else {
230 | btn_event.button = WheelDown;
231 | }
232 | }
233 |
234 | if (event->type != EVENT_MOUSE_RELEASED) {
235 | // FIXME Where do we set event->button?
236 | btn_event.type = ButtonPress;
237 | XSendEvent(properties_disp, InputFocus, False, ButtonPressMask, (XEvent *) &btn_event);
238 | }
239 |
240 | if (event->type != EVENT_MOUSE_PRESSED) {
241 | btn_event.type = ButtonRelease;
242 | XSendEvent(properties_disp, InputFocus, False, ButtonReleaseMask, (XEvent *) &btn_event);
243 | }
244 | #endif
245 | }
246 |
247 | static inline void post_mouse_motion_event(uiohook_event * const event) {
248 | #ifdef USE_XTEST
249 | XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
250 | #else
251 | XMotionEvent mov_event;
252 |
253 | mov_event.serial = MotionNotify;
254 | mov_event.send_event = False;
255 | mov_event.display = properties_disp;
256 | mov_event.time = CurrentTime;
257 | mov_event.same_screen = True;
258 | mov_event.is_hint = NotifyNormal,
259 | mov_event.root = DefaultRootWindow(properties_disp);
260 | mov_event.window = mov_event.root;
261 | mov_event.subwindow = None;
262 |
263 | mov_event.type = 0x00;
264 | mov_event.state = 0x00;
265 | mov_event.x_root = 0;
266 | mov_event.y_root = 0;
267 | mov_event.x = 0;
268 | mov_event.y = 0;
269 |
270 | mov_event.state = convert_to_native_mask(event->mask);
271 |
272 | mov_event.x = event->data.mouse.x;
273 | mov_event.y = event->data.mouse.y;
274 |
275 | #if defined(USE_XINERAMA) || defined(USE_XRANDR)
276 | uint8_t screen_count;
277 | screen_data *screens = hook_create_screen_info(&screen_count);
278 | if (screen_count > 1) {
279 | mov_event.x += screens[0].x;
280 | mov_event.y += screens[0].y;
281 | }
282 |
283 | if (screens != NULL) {
284 | free(screens);
285 | }
286 | #endif
287 |
288 | // These are the same because Window == Root Window.
289 | mov_event.x_root = mov_event.x;
290 | mov_event.y_root = mov_event.y;
291 |
292 | long int event_mask = NoEventMask;
293 | if (event->type == EVENT_MOUSE_DRAGGED) {
294 | #if Button1Mask == Button1MotionMask && \
295 | Button2Mask == Button2MotionMask && \
296 | Button3Mask == Button3MotionMask && \
297 | Button4Mask == Button4MotionMask && \
298 | Button5Mask == Button5MotionMask
299 | // This little trick only works if Button#MotionMasks align with
300 | // the Button#Masks.
301 | event_mask = mov_event.state &
302 | (Button1MotionMask | Button2MotionMask |
303 | Button2MotionMask | Button3MotionMask | Button5MotionMask);
304 | #else
305 | // Fallback to some slightly larger...
306 | if (event->state & Button1Mask) {
307 | event_mask |= Button1MotionMask;
308 | }
309 |
310 | if (event->state & Button2Mask) {
311 | event_mask |= Button2MotionMask;
312 | }
313 |
314 | if (event->state & Button3Mask) {
315 | event_mask |= Button3MotionMask;
316 | }
317 |
318 | if (event->state & Button4Mask) {
319 | event_mask |= Button4MotionMask;
320 | }
321 |
322 | if (event->state & Button5Mask) {
323 | event_mask |= Button5MotionMask;
324 | }
325 | #endif
326 | }
327 |
328 | // NOTE x_mask = NoEventMask.
329 | XSendEvent(properties_disp, InputFocus, False, event_mask, (XEvent *) &mov_event);
330 | #endif
331 | }
332 |
333 | UIOHOOK_API void hook_post_event(uiohook_event * const event) {
334 | XLockDisplay(properties_disp);
335 |
336 | #ifdef USE_XTEST
337 | // XTest does not have modifier support, so we fake it by depressing the
338 | // appropriate modifier keys.
339 | unsigned int i = 0;
340 | for (i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
341 | if (event->mask & 1 << i) {
342 | XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), True, 0);
343 | }
344 | }
345 |
346 | for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
347 | if (event->mask & btnmask_lookup[i]) {
348 | XTestFakeButtonEvent(properties_disp, i + 1, True, 0);
349 | }
350 | }
351 | #endif
352 |
353 | switch (event->type) {
354 | case EVENT_KEY_PRESSED:
355 | case EVENT_KEY_RELEASED:
356 | case EVENT_KEY_TYPED:
357 | post_key_event(event);
358 | break;
359 |
360 | case EVENT_MOUSE_PRESSED:
361 | case EVENT_MOUSE_RELEASED:
362 | case EVENT_MOUSE_WHEEL:
363 | case EVENT_MOUSE_CLICKED:
364 | post_mouse_button_event(event);
365 | break;
366 |
367 | case EVENT_MOUSE_DRAGGED:
368 | case EVENT_MOUSE_MOVED:
369 | post_mouse_motion_event(event);
370 | break;
371 |
372 | case EVENT_HOOK_ENABLED:
373 | case EVENT_HOOK_DISABLED:
374 | // Ignore hook enabled / disabled events.
375 |
376 | default:
377 | // Ignore any other garbage.
378 | logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
379 | __FUNCTION__, __LINE__, event->type);
380 | break;
381 | }
382 |
383 | #ifdef USE_XTEST
384 | // Release the previously held modifier keys used to fake the event mask.
385 | for (i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
386 | if (event->mask & 1 << i) {
387 | XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), False, 0);
388 | }
389 | }
390 |
391 | for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
392 | if (event->mask & btnmask_lookup[i]) {
393 | XTestFakeButtonEvent(properties_disp, i + 1, False, 0);
394 | }
395 | }
396 | #endif
397 |
398 | // Don't forget to flush!
399 | XSync(properties_disp, True);
400 | XUnlockDisplay(properties_disp);
401 | }
402 |
--------------------------------------------------------------------------------
/libuiohook/test/input_helper_test.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 |
22 | #include "input_helper.h"
23 | #include "minunit.h"
24 | #include "uiohook.h"
25 |
26 | /* Make sure all native keycodes map to virtual scancodes */
27 | static char * test_bidirectional_keycode() {
28 | for (unsigned short i = 0; i < 256; i++) {
29 | printf("Testing keycode\t\t\t%3u\t[0x%04X]\n", i, i);
30 |
31 | #ifdef _WIN32
32 | if ((i > 6 && i < 16) || i > 18) {
33 | #endif
34 | // Lookup the virtual scancode...
35 | uint16_t scancode = keycode_to_scancode(i);
36 | printf("\tproduced scancode\t%3u\t[0x%04X]\n", scancode, scancode);
37 |
38 | // Lookup the native keycode...
39 | uint16_t keycode = (uint16_t) scancode_to_keycode(scancode);
40 | printf("\treproduced keycode\t%3u\t[0x%04X]\n", keycode, keycode);
41 |
42 | // If the returned virtual scancode > 127, we used an offset to
43 | // calculate the keycode index used above.
44 | if (scancode > 127) {
45 | printf("\t\tusing offset\t%3u\t[0x%04X]\n", (scancode & 0x7F) | 0x80, (scancode & 0x7F) | 0x80);
46 | }
47 |
48 | printf("\n");
49 |
50 | if (scancode != VC_UNDEFINED) {
51 | mu_assert("error, scancode to keycode failed to convert back", i == keycode);
52 | }
53 | #ifdef _WIN32
54 | }
55 | #endif
56 | }
57 |
58 | return NULL;
59 | }
60 |
61 | /* Make sure all virtual scancodes map to native keycodes */
62 | static char * test_bidirectional_scancode() {
63 | for (unsigned short i = 0; i < 256; i++) {
64 | printf("Testing scancode\t\t%3u\t[0x%04X]\n", i, i);
65 |
66 | // Lookup the native keycode...
67 | uint16_t keycode = (uint16_t) scancode_to_keycode(i);
68 | printf("\treproduced keycode\t%3u\t[0x%04X]\n", keycode, keycode);
69 |
70 | // Lookup the virtual scancode...
71 | uint16_t scancode = keycode_to_scancode(keycode);
72 | printf("\tproduced scancode\t%3u\t[0x%04X]\n", scancode, scancode);
73 |
74 | // If the returned virtual scancode > 127, we used an offset to
75 | // calculate the keycode index used above.
76 | if (scancode > 127) {
77 | // Fix the scancode for upper offsets.
78 | scancode = (scancode & 0x7F) | 0x80;
79 | printf("\t\tusing offset\t%3u\t[0x%04X]\n", scancode, scancode);
80 | }
81 |
82 | printf("\n");
83 |
84 | #if defined(__APPLE__) && defined(__MACH__)
85 | if (keycode != 255) {
86 | #else
87 | if (keycode != 0x0000) {
88 | #endif
89 | mu_assert("error, scancode to keycode failed to convert back", i == scancode);
90 | }
91 | }
92 |
93 | return NULL;
94 | }
95 |
96 | char * input_helper_tests() {
97 | mu_run_test(test_bidirectional_keycode);
98 | mu_run_test(test_bidirectional_scancode);
99 |
100 | return NULL;
101 | }
102 |
--------------------------------------------------------------------------------
/libuiohook/test/minunit.h:
--------------------------------------------------------------------------------
1 | // MinUnit -- A minimal unit testing framework for C
2 | #define mu_assert(message, test) do { if (!(test)) return message; } while (0)
3 | #define mu_run_test(test) do { char *message = test(); tests_run++; if (message) return message; } while (0)
4 |
5 | extern int tests_run;
6 |
--------------------------------------------------------------------------------
/libuiohook/test/system_properties_test.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | #include "minunit.h"
24 |
25 | static char * test_auto_repeat_rate() {
26 | long int i = hook_get_auto_repeat_rate();
27 |
28 | fprintf(stdout, "Auto repeat rate: %li\n", i);
29 | mu_assert("error, could not determine auto repeat rate", i >= 0);
30 |
31 | return NULL;
32 | }
33 |
34 | static char * test_auto_repeat_delay() {
35 | long int i = hook_get_auto_repeat_delay();
36 |
37 | fprintf(stdout, "Auto repeat delay: %li\n", i);
38 | mu_assert("error, could not determine auto repeat delay", i >= 0);
39 |
40 | return NULL;
41 | }
42 |
43 | static char * test_pointer_acceleration_multiplier() {
44 | long int i = hook_get_pointer_acceleration_multiplier();
45 |
46 | fprintf(stdout, "Pointer acceleration multiplier: %li\n", i);
47 | mu_assert("error, could not determine pointer acceleration multiplier", i >= 0);
48 |
49 | return NULL;
50 | }
51 |
52 | static char * test_pointer_acceleration_threshold() {
53 | long int i = hook_get_pointer_acceleration_threshold();
54 |
55 | fprintf(stdout, "Pointer acceleration threshold: %li\n", i);
56 | mu_assert("error, could not determine pointer acceleration threshold", i >= 0);
57 |
58 | return NULL;
59 | }
60 |
61 | static char * test_pointer_sensitivity() {
62 | long int i = hook_get_pointer_sensitivity();
63 |
64 | fprintf(stdout, "Pointer sensitivity: %li\n", i);
65 | mu_assert("error, could not determine pointer sensitivity", i >= 0);
66 |
67 | return NULL;
68 | }
69 |
70 | static char * test_multi_click_time() {
71 | long int i = hook_get_multi_click_time();
72 |
73 | fprintf(stdout, "Multi click time: %li\n", i);
74 | mu_assert("error, could not determine multi click time", i >= 0);
75 |
76 | return NULL;
77 | }
78 |
79 | char * system_properties_tests() {
80 | mu_run_test(test_auto_repeat_rate);
81 | mu_run_test(test_auto_repeat_delay);
82 |
83 | mu_run_test(test_pointer_acceleration_multiplier);
84 | mu_run_test(test_pointer_acceleration_threshold);
85 | mu_run_test(test_pointer_sensitivity);
86 |
87 | mu_run_test(test_multi_click_time);
88 |
89 | return NULL;
90 | }
91 |
--------------------------------------------------------------------------------
/libuiohook/test/uiohook_test.c:
--------------------------------------------------------------------------------
1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
3 | * https://github.com/kwhat/libuiohook/
4 | *
5 | * libUIOHook is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published
7 | * by the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * libUIOHook is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU Lesser General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include
20 |
21 | #if !defined(__APPLE__) && !defined(__MACH__) && !defined(_WIN32)
22 | #include
23 | #endif
24 |
25 | #include "input_helper.h"
26 | #include "minunit.h"
27 |
28 | extern char * system_properties_tests();
29 | extern char * input_helper_tests();
30 |
31 | #if !defined(__APPLE__) && !defined(__MACH__) && !defined(_WIN32)
32 | static Display *disp;
33 | #endif
34 |
35 | int tests_run = 0;
36 |
37 | static char * init_tests() {
38 | #if !defined(__APPLE__) && !defined(__MACH__) && !defined(_WIN32)
39 | // TODO Create our own AC_DEFINE for this value. Currently defaults to X11 platforms.
40 | Display *disp = XOpenDisplay(XDisplayName(NULL));
41 | mu_assert("error, could not open X display", disp != NULL);
42 |
43 | load_input_helper(disp);
44 | #else
45 | load_input_helper();
46 | #endif
47 |
48 | return NULL;
49 | }
50 |
51 | static char * cleanup_tests() {
52 | #if !defined(__APPLE__) && !defined(__MACH__) && !defined(_WIN32)
53 | if (disp != NULL) {
54 | XCloseDisplay(disp);
55 | disp = NULL;
56 | }
57 | #else
58 | unload_input_helper();
59 | #endif
60 |
61 | return NULL;
62 | }
63 |
64 | static char * all_tests() {
65 | mu_run_test(init_tests);
66 |
67 | mu_run_test(system_properties_tests);
68 | mu_run_test(input_helper_tests);
69 |
70 | mu_run_test(cleanup_tests);
71 |
72 | return NULL;
73 | }
74 |
75 | int main() {
76 | int status = 1;
77 |
78 | char *result = all_tests();
79 | if (result != NULL) {
80 | status = 0;
81 | printf("%s\n", result);
82 | }
83 | else {
84 | printf("ALL TESTS PASSED\n");
85 | }
86 | printf("Tests run: %d\n", tests_run);
87 |
88 | return status;
89 | }
90 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mechakeys/iohook",
3 | "version": "1.1.1",
4 | "description": "Node.js global keyboard and mouse hook",
5 | "author": "Robolab",
6 | "license": "MIT",
7 | "main": "index.js",
8 | "types": "index.d.ts",
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/robolab-io/iohook.git"
12 | },
13 | "scripts": {
14 | "build": "node build.js --upload=false",
15 | "build:ci": "node build.js --all --openssl_fips=''",
16 | "build:print": "node -e 'require(\"./helpers\").printManualBuildParams()'"
17 | },
18 | "keywords": [
19 | "hook",
20 | "electron",
21 | "nw.js",
22 | "listener",
23 | "mousemove",
24 | "keypress",
25 | "keyup",
26 | "global keypress",
27 | "shortcut"
28 | ],
29 | "lint-staged": {
30 | "examples/**/*.{js,jsx,ts,tsx}": [
31 | "eslint --fix",
32 | "prettier --write"
33 | ],
34 | "docs/**/*.{js,jsx,ts,tsx}": [
35 | "eslint --fix",
36 | "prettier --write"
37 | ],
38 | "test/**/*.{js,jsx,ts,tsx}": [
39 | "eslint --fix",
40 | "prettier --write"
41 | ],
42 | "*.{js,jsx,ts,tsx}": [
43 | "eslint --fix",
44 | "prettier --write"
45 | ]
46 | },
47 | "dependencies": {
48 | "nan": "github:robolab-io/nan#remove_accessor_signature",
49 | "nugget": "^2.0.1",
50 | "pump": "^1.0.3",
51 | "rc": "^1.2.8",
52 | "tar-fs": "^1.16.3"
53 | },
54 | "devDependencies": {
55 | "@electron/rebuild": "^3.2.13",
56 | "@types/node": "^7.0.62",
57 | "electron": "^25.3.0",
58 | "eslint": "^7.28.0",
59 | "eslint-config-prettier": "^8.3.0",
60 | "eslint-plugin-only-warn": "^1.0.2",
61 | "eslint-plugin-prettier": "^3.4.0",
62 | "fs-extra": "^11.1.1",
63 | "minimist": "^1.2.5",
64 | "node-abi": "^3.45.0",
65 | "node-gyp": "^9.4.0",
66 | "prebuild": "^11.0.4",
67 | "prettier": "^3.0.0",
68 | "tar": "^6.1.15"
69 | },
70 | "supportedTargets": [
71 | [
72 | "electron",
73 | "25.0.0",
74 | "116"
75 | ],
76 | [
77 | "electron",
78 | "24.0.0",
79 | "114"
80 | ],
81 | [
82 | "electron",
83 | "23.0.0",
84 | "113"
85 | ],
86 | [
87 | "electron",
88 | "22.0.0",
89 | "110"
90 | ],
91 | [
92 | "node",
93 | "18.0.0",
94 | "108"
95 | ]
96 | ]
97 | }
98 |
--------------------------------------------------------------------------------
/src/iohook.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 |
7 | #include "uiohook.h"
8 |
9 | class HookProcessWorker : public Nan::AsyncProgressWorkerBase
10 | {
11 | public:
12 |
13 | typedef Nan::AsyncProgressWorkerBase::ExecutionProgress HookExecution;
14 |
15 | HookProcessWorker(Nan::Callback * callback);
16 |
17 | void Execute(const ExecutionProgress& progress);
18 |
19 | void HandleProgressCallback(const uiohook_event *event, size_t size);
20 |
21 | void Stop();
22 |
23 | const HookExecution* fHookExecution;
24 | };
--------------------------------------------------------------------------------
/uiohook.gyp:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [{
3 | "target_name": "uiohook",
4 | "type": "shared_library",
5 | "sources": [
6 | "libuiohook/include/uiohook.h",
7 | "libuiohook/src/logger.c",
8 | "libuiohook/src/logger.h",
9 | "libuiohook/src/windows/input_helper.h",
10 | "libuiohook/src/windows/input_helper.c",
11 | "libuiohook/src/windows/input_hook.c",
12 | "libuiohook/src/windows/post_event.c",
13 | "libuiohook/src/windows/system_properties.c"
14 | ],
15 | "include_dirs": [
16 | 'node_modules/nan',
17 | 'libuiohook/include',
18 | 'libuiohook/src'
19 | ]
20 | }]
21 | }
22 |
--------------------------------------------------------------------------------