├── .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 | --------------------------------------------------------------------------------