├── .eslintrc.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── ci.yml
│ └── lint.yml
├── .gitignore
├── .lintignore
├── .npmignore
├── .nvmrc
├── .prettierrc.json
├── CHANGELOG.md
├── CMakeLists.txt
├── LICENSE
├── README.md
├── _config.yml
├── build.js
├── build_def
├── darwin
│ ├── binding.gyp
│ └── uiohook.gyp
├── linux
│ ├── binding.gyp
│ └── uiohook.gyp
└── win32
│ ├── binding.gyp
│ └── uiohook.gyp
├── docs
├── .vuepress
│ ├── config.js
│ └── override.styl
├── README.md
├── faq.md
├── installation.md
├── manual-build.md
├── os-support.md
└── usage.md
├── examples
├── electron-main
│ ├── main.js
│ ├── package-lock.json
│ └── package.json
├── electron-renderer
│ ├── index.html
│ ├── main.js
│ ├── package-lock.json
│ └── package.json
└── node
│ ├── example.js
│ ├── mouse-click-propagation.js
│ └── package.json
├── 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
└── test
└── specs
└── keyboard.spec.js
/.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 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # [djiit]
4 | patreon: djiit
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Expected Behavior
4 |
5 |
6 |
7 |
8 | ## Current Behavior
9 |
10 |
11 |
12 |
13 | ## Possible Solution
14 |
15 |
16 |
17 |
18 | ## Steps to Reproduce (for bugs)
19 |
20 |
21 |
22 |
23 | 1.
24 | 2.
25 | 3.
26 | 4.
27 |
28 | ## Context
29 |
30 |
31 |
32 |
33 | ## Your Environment
34 |
35 |
36 |
37 | - IOHook Version:
38 | - Environment name and version (e.g. Chrome 39, node.js 5.4, electron 11.2):
39 | - Operating System and version (desktop or mobile):
40 | - Optional Link to your project:
41 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Motivation and Context
8 |
9 |
10 |
11 |
12 | ## How Has This Been Tested?
13 |
14 |
15 |
16 |
17 |
18 | ## Screenshots (if appropriate):
19 |
20 | ## Types of changes
21 |
22 |
23 |
24 | - [ ] Bug fix (non-breaking change which fixes an issue)
25 | - [ ] New feature (non-breaking change which adds functionality)
26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
27 |
28 | ## Checklist:
29 |
30 |
31 |
32 |
33 | - [ ] My code follows the code style of this project.
34 | - [ ] My change requires a change to the documentation.
35 | - [ ] I have updated the documentation accordingly.
36 | - [ ] I have added tests to cover my changes.
37 | - [ ] All new and existing tests passed.
38 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | push:
7 | pull_request:
8 | branches: [master]
9 |
10 | jobs:
11 | ci:
12 | strategy:
13 | matrix:
14 | os: [ubuntu-latest, macos-latest, windows-latest]
15 | arch: [x86_64]
16 | include:
17 | - os: windows-latest
18 | arch: i686
19 | runs-on: ${{ matrix.os }}
20 | name: ${{ matrix.os }} (${{matrix.arch}})
21 | steps:
22 | - name: Checkout Code
23 | uses: actions/checkout@v2
24 |
25 | - name: Setup Node
26 | uses: actions/setup-node@v2
27 | with:
28 | # Set registry to NPM instead of github packages for publication
29 | registry-url: 'https://registry.npmjs.org'
30 | node-version: 14
31 |
32 | - name: Setup Linux Dependencies
33 | if: startsWith(matrix.os, 'ubuntu')
34 | run: |
35 | sudo apt-get install -y software-properties-common libx11-dev libxtst-dev libxt-dev libx11-xcb-dev libxkbcommon-dev libxkbcommon-x11-dev xorg-dev libxcb-xkb-dev libxkbfile-dev libxinerama-dev
36 |
37 | - name: Install Packages
38 | run: npm i --ignore-scripts
39 |
40 | # Build only if version tag is not present
41 | - name: Build
42 | run: npm run build -- --all --msvs_version=2019
43 | if: startsWith(github.ref, 'refs/tags/v') != true
44 | env:
45 | ARCH: ${{ matrix.arch }}
46 |
47 | # Build and publish if version tag is present
48 | - name: Build & Publish Prebuilds
49 | run: npm run build:ci -- --msvs_version=2019
50 | if: startsWith(github.ref, 'refs/tags/v')
51 | env:
52 | ARCH: ${{ matrix.arch }}
53 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 |
55 | - name: Publish NPM Package
56 | if: startsWith(github.ref, 'refs/tags/v') && startsWith(matrix.os, 'ubuntu')
57 | run: npm publish --access public
58 | env:
59 | NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
60 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint Code Base
2 |
3 | on: [workflow_dispatch]
4 |
5 | jobs:
6 | super-lint:
7 | name: Lint code base
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@v2
13 | with:
14 | # Full git history is needed to get a proper list of changed files within `super-linter`
15 | fetch-depth: 0
16 |
17 | - name: Lint Code Base
18 | uses: github/super-linter@v3
19 | env:
20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 | JAVASCRIPT_DEFAULT_STYLE: prettier
22 | VALIDATE_ALL_CODEBASE: true
23 | VALIDATE_JAVASCRIPT_ES: true
24 | VALIDATE_HTML: true
25 | VALIDATE_MARKDOWN: true
26 | LINTER_RULES_PATH: /
27 | JAVASCRIPT_ES_CONFIG_FILE: .eslintrc.json
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | .scratch
4 | build
5 | builds
6 | docs/.vuepress/dist
7 | logs
8 | node_modules
9 | prebuilds
10 | .env
11 | *.DS_Store
12 | *.iml
13 | *.log
14 | examples/**/.DS_Store
15 | npm-debug.log*
16 | stacktrace*
17 | binding.gyp
18 | uiohook.gyp
19 |
20 | libuiohook/nbproject/
21 | libuiohook/build/
22 | libuiohook/autom4te.cache/
23 | libuiohook/config/
24 | libuiohook/cmake/
25 | libuiohook/include/config.h.in*
26 | libuiohook/m4/ltoptions.m4
27 | libuiohook/m4/ltversion.m4
28 | libuiohook/m4/libtool.m4
29 | libuiohook/m4/ltsugar.m4
30 | libuiohook/m4/lt~obsolete.m4
31 | libuiohook/Makefile.in
32 | libuiohook/configure
33 | libuiohook/aclocal.m4
34 |
35 | libuiohook/CMakeFiles/
36 | libuiohook/demo_hook_async.dir/
37 | libuiohook/demo_hook.dir/
38 | libuiohook/demo_post.dir/
39 | libuiohook/demo_properties.dir/
40 | libuiohook/Release/
41 | libuiohook/uiohook.dir/
42 | libuiohook/*.vcxproj
43 | libuiohook/*.vcxproj.filters
44 | libuiohook/*.cmake
45 | libuiohook/*.sln
46 |
--------------------------------------------------------------------------------
/.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 | .github/
11 | CMakeFiles/
12 | docs/
13 | examples/
14 | test/
15 | .env
16 | *.DS_Store
17 | *.iml
18 | *.log
19 | npm-debug.log*
20 | stacktrace*
21 | /binding.gyp
22 | /uiohook.gyp
23 | .lintignore
24 | .eslintrc.json
25 | .prettierrc.json
26 | _config.yml
27 |
--------------------------------------------------------------------------------
/.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) 2016 wilix
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 | # iohook
2 |
3 | [](https://www.npmjs.com/package/iohook)
4 | [](https://github.com/wilix-team/iohook/releases/latest)
5 | [](https://github.com/marketplace/actions/super-linter)
6 | [](https://github.com/wilix-team/iohook/actions/workflows/ci.yml)
7 | [](https://github.com/prettier/prettier)
8 | [](https://gitter.im/iohookjs/Lobby)
9 | [](https://github.com/wilix-team/iohook/issues)
10 |
11 | ## About
12 |
13 | Node.js global native keyboard and mouse listener.
14 |
15 | This module can handle keyboard and mouse events via native hooks inside and outside your JavaScript/TypeScript application.
16 |
17 | Found a bug? Have an idea? Feel free to post an [issue](https://github.com/wilix-team/iohook/issues) or submit a [PR](https://github.com/wilix-team/iohook/pulls).
18 |
19 | **Check out the [documentation](https://wilix-team.github.io/iohook).**
20 |
21 | ## Platform support
22 |
23 | - Versions >= 0.6.0 support only officially supported platforms versions.
24 | - Versions 0.5.X are the last to support Electron < 4.0.0
25 | - Versions 0.4.X are the last to support for Node < 8.0 and Electron < 2.0.0
26 |
27 | ## Installation
28 |
29 | iohook provides prebuilt version for a bunch of OSes and platforms.
30 |
31 | ### Linux (including WSL)
32 |
33 | ```bash
34 | # On Linux (including WSL) platform, you will need libxkbcommon-x11 installed
35 | sudo apt-get install -y libxkbcommon-x11-0
36 | ```
37 |
38 | ### All platforms
39 |
40 | ```bash
41 | npm install iohook --save # or yarn add iohook
42 | ```
43 |
44 | ## FAQ
45 |
46 | Q. _Does this module require Java ?_
47 |
48 | A. No, this module doesn't require Java (like jnativehook) or any other runtimes.
49 |
50 | Q. _Is iohook compatible with Node/Electron version X.Y.Z ?_
51 |
52 | A. We try to match the currently supported version of both [Node](https://nodejs.org/en/about/releases/) and [Electron](https://electronjs.org/docs/tutorial/support#currently-supported-versions).
53 |
54 | ## Apps
55 |
56 | Are you using iohook in your project ? Please tell us in a [PR](https://github.com/wilix-team/iohook/pulls) so we can add it to the list !
57 |
58 | - [Cortex](https://crtx.gg/)
59 | - [Tracklify](https://tracklify.com/)
60 | - [CrewLink](https://github.com/ottomated/CrewLink)
61 | - [Runtime](https://github.com/yikuansun/desktopspeedruntools#runtime-speedrun-tools)
62 |
63 | ## Contributors
64 |
65 | Thanks to _kwhat_ for the [libuiohook](https://github.com/kwhat/libuiohook) project and [ayoubserti](https://github.com/ayoubserti) for the first iohook prototype.
66 |
67 | - [vespakoen](https://github.com/vespakoen) (prebuild system implementation)
68 | - [matthewshirley](https://github.com/matthewshirley) (Windows prebuild fix)
69 | - [djiit](https://github.com/djiit) (project & community help)
70 | - [ezain](https://github.com/eboukamza) (add feature enable/disable mouse click propagation)
71 | - [anoadragon453](https://github.com/anoadragon453) (electron 4+ support)
72 | - [ykhwong](https://github.com/ykhwong) (node-gyp usage, electron 9+ support)
73 | - All the other contributors. Feel free to extend this list !
74 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-midnight
2 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | const spawn = require('child_process').spawn;
2 | const fs = require('fs-extra');
3 | const path = require('path');
4 | const tar = require('tar');
5 | const argv = require('minimist')(process.argv.slice(2), {
6 | // Specify that these arguments should be a string
7 | string: ['version', 'runtime', 'abi'],
8 | });
9 | const pkg = require('./package.json');
10 | const nodeAbi = require('node-abi');
11 | const { optionsFromPackage } = require('./helpers');
12 |
13 | let arch = process.env.ARCH
14 | ? process.env.ARCH.replace('i686', 'ia32').replace('x86_64', 'x64')
15 | : process.arch;
16 |
17 | let gypJsPath = path.join(
18 | __dirname,
19 | 'node_modules',
20 | '.bin',
21 | process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp'
22 | );
23 |
24 | let files = [];
25 | let targets;
26 | let chain = Promise.resolve();
27 |
28 | initBuild();
29 |
30 | function initBuild() {
31 | // Check if a specific runtime has been specified from the command line
32 | if ('runtime' in argv && 'version' in argv && 'abi' in argv) {
33 | targets = [[argv['runtime'], argv['version'], argv['abi']]];
34 | } else if ('all' in argv) {
35 | // If "--all", use those defined in package.json
36 | targets = require('./package.json').supportedTargets;
37 | } else {
38 | const options = optionsFromPackage();
39 | if (process.env.npm_config_targets) {
40 | options.targets = options.targets.concat(
41 | process.env.npm_config_targets.split(',')
42 | );
43 | }
44 | options.targets = options.targets.map((targetStr) => 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://atom.io/download/electron');
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 = argv.msvs_version || 2017;
169 | } else {
170 | process.env.msvs_toolset = 12;
171 | process.env.msvs_version = 2013;
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, {
65 | console.log(event); // { type: 'mousemove', x: 700, y: 400 }
66 | });
67 |
68 | // Register and start hook
69 | ioHook.start();
70 |
71 | // Alternatively, pass true to start in DEBUG mode.
72 | ioHook.start(true);
73 |
74 | // False to disable DEBUG. Cleaner terminal output.
75 | ioHook.start(false);
76 | ```
77 |
78 | ## Available events
79 |
80 | ### keydown
81 |
82 | Triggered when user presses a key.
83 |
84 | ```js
85 | {
86 | keycode: 46,
87 | rawcode: 8,
88 | type: 'keydown',
89 | altKey: true,
90 | shiftKey: true,
91 | ctrlKey: false,
92 | metaKey: false
93 | }
94 | ```
95 |
96 | ### keyup
97 |
98 | Triggered when user releases a key.
99 |
100 | ```js
101 | {
102 | keycode: 19,
103 | rawcode: 15,
104 | type: 'keyup',
105 | altKey: true,
106 | shiftKey: true,
107 | ctrlKey: false,
108 | metaKey: false
109 | }
110 | ```
111 |
112 | ### mouseclick
113 |
114 | Triggered when user clicks a mouse button.
115 |
116 | ```js
117 | { button: 1, clicks: 1, x: 545, y: 696, type: 'mouseclick' }
118 | ```
119 |
120 | ### mousedown
121 |
122 | Triggered when user clicks a mouse button.
123 |
124 | ```js
125 | { button: 1, clicks: 1, x: 545, y: 696, type: 'mousedown' }
126 | ```
127 |
128 | ### mouseup
129 |
130 | Triggered when user releases a mouse button.
131 |
132 | ```js
133 | { button: 1, clicks: 1, x: 545, y: 696, type: 'mouseup' }
134 | ```
135 |
136 | ### mousemove
137 |
138 | Triggered when user moves the mouse.
139 |
140 | ```js
141 | { button: 0, clicks: 0, x: 521, y: 737, type: 'mousemove' }
142 | ```
143 |
144 | ### mousedrag
145 |
146 | Triggered when user clicks and drags something.
147 |
148 | ```js
149 | { button: 0, clicks: 0, x: 373, y: 683, type: 'mousedrag' }
150 | ```
151 |
152 | ### mousewheel
153 |
154 | Triggered when user uses the mouse wheel.
155 |
156 | ```js
157 | { amount: 3, clicks: 1, direction: 3, rotation: 1, type: 'mousewheel', x: 466, y: 683 }
158 | ```
159 |
160 | ## Shortcuts
161 |
162 | You can register global shortcuts.
163 |
164 | ::: tip NOTE
165 | When a shortcut is caught, keyup/keydown events still emit events. It means, that if you register a keyup AND shortcut for `ALT+T`, both events will be emited.
166 | :::
167 |
168 | ### registerShortcut(keys, callback, releaseCallback?)
169 |
170 | In the next example we register CTRL+F7 shortcut (in MacOS. For other OSes, the keycodes could be different).
171 |
172 | ```js
173 | const id = ioHook.registerShortcut([29, 65], (keys) => {
174 | console.log('Shortcut called with keys:', keys);
175 | });
176 | ```
177 |
178 | We can also specify a callback to run when our shortcut has been released by specifying a third function argument.
179 |
180 | ```js
181 | const id = ioHook.registerShortcut(
182 | [29, 65],
183 | (keys) => {
184 | console.log('Shortcut called with keys:', keys);
185 | },
186 | (keys) => {
187 | console.log('Shortcut has been released!');
188 | }
189 | );
190 | ```
191 |
192 | ### unregisterShortcut(shortcutId)
193 |
194 | You can unregister shortcut by using shortcutId returned by `registerShortcut()`.
195 |
196 | ```js
197 | ioHook.unregisterShortcut(id);
198 | ```
199 |
200 | ### unregisterShortcutByKeys(keys)
201 |
202 | You can unregister shortcut by using the keys codes passed to `registerShortcut()`. Passing codes in the same order as during registration is not required.
203 |
204 | ```js
205 | ioHook.unregisterShortcutByKeys(keys);
206 | ```
207 |
208 | ### unregisterAllShortcuts()
209 |
210 | You can also unregister all shortcuts.
211 |
212 | ```js
213 | ioHook.unregisterAllShortcuts();
214 | ```
215 |
216 | ### useRawcode(using)
217 |
218 | Some libraries, such as [Mousetrap]() will emit keyboard events that contain
219 | a `rawcode` value. This is a separate, but equally valid, representation of
220 | the key that was pressed. However by default iohook instead uses an event's
221 | `keycode` field to determine which key was pressed. If these key codes do not
222 | line up, your shortcut will not be detected as pressed.
223 |
224 | To tell iohook to use the `rawcode` value instead, simply do so before
225 | starting iohook.
226 |
227 | ```js
228 | iohook.useRawcode(true);
229 | iohook.start();
230 | ```
231 |
232 | ### disableClickPropagation()
233 |
234 | You can disable mouse click event propagation. Click events are captured and emitted but not propagated to the window.
235 |
236 | ```js
237 | ioHook.disableClickPropagation();
238 | ```
239 |
240 | ### enableClickPropagation()
241 |
242 | You can enable mouse click event propagation if it's disabled. Click event are propagated by default.
243 |
244 | ```js
245 | ioHook.enableClickPropagation();
246 | ```
247 |
--------------------------------------------------------------------------------
/examples/electron-main/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { app } = require('electron');
4 | const ioHook = require('iohook');
5 |
6 | function eventHandler(event) {
7 | console.log(event);
8 | }
9 |
10 | app.on('ready', () => {
11 | console.log(
12 | 'node: ' +
13 | process.versions.node +
14 | ', chromium: ' +
15 | process.versions.chrome +
16 | ', electron: ' +
17 | process.versions.electron
18 | );
19 | ioHook.start(true);
20 | ioHook.on('mouseclick', eventHandler);
21 | ioHook.on('keydown', eventHandler);
22 | ioHook.on('mousewheel', eventHandler);
23 | ioHook.on('mousemove', eventHandler);
24 | console.log('Try move your mouse or press any key');
25 | });
26 |
27 | app.on('before-quit', () => {
28 | ioHook.unload();
29 | ioHook.stop();
30 | });
31 |
--------------------------------------------------------------------------------
/examples/electron-main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "iohook-electron-example",
3 | "version": "1.0.0",
4 | "description": "Show how to use this module with electron",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron ."
8 | },
9 | "author": "Aloyan Dmitry",
10 | "license": "MIT",
11 | "dependencies": {
12 | "electron": ">=11.1.0",
13 | "iohook": "^0.9.2"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/electron-renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello World!
6 |
7 |
28 |
29 |
30 |
31 | We are using Node.js , Chromium
32 | , and Electron
33 | .
34 | Try to move your mouse or press any key!
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/examples/electron-renderer/main.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const { app, BrowserWindow } = require('electron');
3 | app.allowRendererProcessReuse = false;
4 |
5 | function createWindow() {
6 | // Create the browser window.
7 | const mainWindow = new BrowserWindow({
8 | width: 1000,
9 | height: 600,
10 | webPreferences: {
11 | nodeIntegration: true,
12 | },
13 | });
14 |
15 | // and load the index.html of the app.
16 | mainWindow.loadFile('index.html');
17 |
18 | // Open the DevTools.
19 | mainWindow.webContents.openDevTools();
20 | }
21 |
22 | // This method will be called when Electron has finished
23 | // initialization and is ready to create browser windows.
24 | // Some APIs can only be used after this event occurs.
25 | app.whenReady().then(() => {
26 | createWindow();
27 | });
28 |
29 | // Quit when all windows are closed
30 | app.on('window-all-closed', function () {
31 | app.quit();
32 | });
33 |
--------------------------------------------------------------------------------
/examples/electron-renderer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-quick-start",
3 | "version": "1.0.0",
4 | "description": "A minimal Electron application",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron ."
8 | },
9 | "devDependencies": {
10 | "electron": ">=11.1.0",
11 | "iohook": "^0.9.2"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/node/example.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ioHook = require('iohook');
4 |
5 | ioHook.on('mousedown', function (msg) {
6 | console.log(msg);
7 | });
8 |
9 | ioHook.on('keypress', function (msg) {
10 | console.log(msg);
11 | });
12 |
13 | ioHook.on('keydown', function (msg) {
14 | console.log(msg);
15 | });
16 |
17 | ioHook.on('keyup', function (msg) {
18 | console.log(msg);
19 | });
20 |
21 | ioHook.on('mouseclick', function (msg) {
22 | console.log(msg);
23 | });
24 |
25 | ioHook.on('mousewheel', function (msg) {
26 | console.log(msg);
27 | });
28 |
29 | ioHook.on('mousemove', function (msg) {
30 | console.log(msg);
31 | });
32 |
33 | ioHook.on('mousedrag', function (msg) {
34 | console.log(msg);
35 | });
36 |
37 | //start ioHook
38 | ioHook.start();
39 | // ioHook.setDebug(true); // Uncomment this line for see all debug information from iohook
40 |
41 | const CTRL = 29;
42 | const ALT = 56;
43 | const F7 = 65;
44 |
45 | ioHook.registerShortcut([CTRL, F7], (keys) => {
46 | console.log('Shortcut pressed with keys:', keys);
47 | });
48 |
49 | let shId = ioHook.registerShortcut([ALT, F7], (keys) => {
50 | console.log('This shortcut will be called once. Keys:', keys);
51 | ioHook.unregisterShortcut(shId);
52 | });
53 |
54 | console.log('Hook started. Try type something or move mouse');
55 |
--------------------------------------------------------------------------------
/examples/node/mouse-click-propagation.js:
--------------------------------------------------------------------------------
1 | const iohook = require('iohook');
2 |
3 | iohook.on('mouseup', (event) => console.log(event));
4 |
5 | let clickpropagation = true;
6 |
7 | iohook.on('keyup', (event) => {
8 | if (event.keycode !== 57) return; // space key
9 |
10 | clickpropagation
11 | ? iohook.disableClickPropagation()
12 | : iohook.enableClickPropagation();
13 | clickpropagation = !clickpropagation;
14 | });
15 |
16 | iohook.start(true);
17 |
18 | console.log(
19 | 'Hook started. Use space key to enable or disable click propagation'
20 | );
21 |
--------------------------------------------------------------------------------
/examples/node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-quick-start",
3 | "version": "1.0.0",
4 | "description": "A minimal Electron application",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "node example.js",
8 | "start:propagation": "node mouse-click-propagation.js"
9 | },
10 | "devDependencies": {
11 | "iohook": "^0.9.2"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/helpers.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 |
4 | /**
5 | * Return options for iohook from package.json
6 | * @return {Object}
7 | */
8 | function optionsFromPackage(attempts) {
9 | attempts = attempts || 2;
10 | if (attempts > 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/wilix-team/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 install');
79 | console.error('# npm run build');
80 | console.error('');
81 | }
82 | }
83 |
84 | let options = {
85 | readable: true,
86 | writable: true,
87 | hardlinkAsFilesFallback: true,
88 | };
89 |
90 | let binaryName;
91 | let updateName = function (entry) {
92 | if (/\.node$/i.test(entry.name)) binaryName = entry.name;
93 | };
94 | let targetFile = path.join(__dirname, 'builds', essential);
95 | let extract = tfs.extract(targetFile, options).on('entry', updateName);
96 | pump(
97 | fs.createReadStream(path.join(nuggetOpts.dir, nuggetOpts.target)),
98 | zlib.createGunzip(),
99 | extract,
100 | function (err) {
101 | if (err) {
102 | return onerror(err);
103 | }
104 | cb();
105 | }
106 | );
107 | });
108 | }
109 |
110 | const options = optionsFromPackage();
111 |
112 | if (process.env.npm_config_targets) {
113 | options.targets = options.targets.concat(
114 | process.env.npm_config_targets.split(',')
115 | );
116 | }
117 | if (process.env.npm_config_targets === 'all') {
118 | options.targets = supportedTargets.map((arr) => [arr[0], arr[2]]);
119 | options.platforms = ['win32', 'darwin', 'linux'];
120 | options.arches = ['x64', 'ia32'];
121 | }
122 | if (process.env.npm_config_platforms) {
123 | options.platforms = options.platforms.concat(
124 | process.env.npm_config_platforms.split(',')
125 | );
126 | }
127 | if (process.env.npm_config_arches) {
128 | options.arches = options.arches.concat(
129 | process.env.npm_config_arches.split(',')
130 | );
131 | }
132 |
133 | // Choice prebuilds for install
134 | if (options.targets.length > 0) {
135 | let chain = Promise.resolve();
136 | options.targets.forEach(function (target) {
137 | if (typeof target === 'object') {
138 | chain = chain.then(function () {
139 | return new Promise(function (resolve) {
140 | console.log(target.runtime, target.abi, target.platform, target.arch);
141 | install(
142 | target.runtime,
143 | target.abi,
144 | target.platform,
145 | target.arch,
146 | resolve
147 | );
148 | });
149 | });
150 | return;
151 | }
152 | let parts = target.split('-');
153 | let runtime = parts[0];
154 | let abi = parts[1];
155 | options.platforms.forEach(function (platform) {
156 | options.arches.forEach(function (arch) {
157 | if (platform === 'darwin' && arch === 'ia32') {
158 | return;
159 | }
160 | chain = chain.then(function () {
161 | return new Promise(function (resolve) {
162 | console.log(runtime, abi, platform, arch);
163 | install(runtime, abi, platform, arch, resolve);
164 | });
165 | });
166 | });
167 | });
168 | });
169 | } else {
170 | const runtime = process.versions['electron'] ? 'electron' : 'node';
171 | const abi = process.versions.modules;
172 | const platform = process.platform;
173 | const arch = process.arch;
174 | install(runtime, abi, platform, arch, function () {});
175 | }
176 |
--------------------------------------------------------------------------------
/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/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 |= kCGEventFlagMaskCommand; }
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_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": "iohook",
3 | "version": "0.9.3",
4 | "description": "Node.js global keyboard and mouse hook",
5 | "author": "Aloyan Dmitry",
6 | "license": "MIT",
7 | "homepage": "https://github.com/wilix-team/iohook",
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/wilix-team/iohook.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/wilix-team/iohook/issues"
14 | },
15 | "main": "index.js",
16 | "types": "index.d.ts",
17 | "scripts": {
18 | "install": "node install.js",
19 | "build": "node build.js --upload=false",
20 | "build:ci": "node build.js --all",
21 | "build:print": "node -e 'require(\"./helpers\").printManualBuildParams()'",
22 | "test": "jest",
23 | "lint:dry": "eslint --ignore-path .lintignore .",
24 | "lint:fix": "eslint --ignore-path .lintignore --fix . && prettier --ignore-path .lintignore --write .",
25 | "docs:dev": "vuepress dev docs",
26 | "docs:build": "vuepress build docs",
27 | "docs:deploy": "npm run docs:build && gh-pages -d docs/.vuepress/dist/"
28 | },
29 | "keywords": [
30 | "hook",
31 | "electron",
32 | "nw.js",
33 | "listener",
34 | "mousemove",
35 | "keypress",
36 | "keyup",
37 | "global keypress",
38 | "shortcut"
39 | ],
40 | "lint-staged": {
41 | "examples/**/*.{js,jsx,ts,tsx}": [
42 | "eslint --fix",
43 | "prettier --write"
44 | ],
45 | "docs/**/*.{js,jsx,ts,tsx}": [
46 | "eslint --fix",
47 | "prettier --write"
48 | ],
49 | "test/**/*.{js,jsx,ts,tsx}": [
50 | "eslint --fix",
51 | "prettier --write"
52 | ],
53 | "*.{js,jsx,ts,tsx}": [
54 | "eslint --fix",
55 | "prettier --write"
56 | ]
57 | },
58 | "husky": {
59 | "hooks": {
60 | "pre-commit": "lint-staged"
61 | }
62 | },
63 | "dependencies": {
64 | "nugget": "^2.0.2",
65 | "pump": "^1.0.3",
66 | "rc": "^1.2.8",
67 | "tar-fs": "^1.16.3"
68 | },
69 | "devDependencies": {
70 | "@types/node": "^7.0.62",
71 | "eslint": "^7.28.0",
72 | "eslint-config-prettier": "^8.3.0",
73 | "eslint-plugin-only-warn": "^1.0.2",
74 | "eslint-plugin-prettier": "^3.4.0",
75 | "fs-extra": "^9.0.1",
76 | "gh-pages": "^3.2.0",
77 | "htmlhint": "^0.15.1",
78 | "husky": "^6.0.0",
79 | "jest": "^26.6.3",
80 | "lint-staged": "^11.0.0",
81 | "minimist": "^1.2.5",
82 | "nan": "^2.15.0",
83 | "node-abi": "^2.19.3",
84 | "node-gyp": "^7.1.2",
85 | "prebuild": "^10.0.1",
86 | "prettier": "^2.3.1",
87 | "robotjs": "^0.6.0",
88 | "tar": "^6.0.5",
89 | "vuepress": "^1.7.1"
90 | },
91 | "supportedTargets": [
92 | [
93 | "electron",
94 | "4.0.4",
95 | "69"
96 | ],
97 | [
98 | "electron",
99 | "5.0.0",
100 | "70"
101 | ],
102 | [
103 | "electron",
104 | "6.0.0",
105 | "73"
106 | ],
107 | [
108 | "electron",
109 | "7.0.0",
110 | "75"
111 | ],
112 | [
113 | "electron",
114 | "8.0.0",
115 | "76"
116 | ],
117 | [
118 | "electron",
119 | "9.0.0",
120 | "80"
121 | ],
122 | [
123 | "electron",
124 | "10.0.0",
125 | "82"
126 | ],
127 | [
128 | "electron",
129 | "11.0.0",
130 | "85"
131 | ],
132 | [
133 | "electron",
134 | "12.0.0",
135 | "87"
136 | ],
137 | [
138 | "electron",
139 | "13.0.0",
140 | "89"
141 | ],
142 | [
143 | "electron",
144 | "14.0.0",
145 | "89"
146 | ],
147 | [
148 | "node",
149 | "10.0.0",
150 | "64"
151 | ],
152 | [
153 | "node",
154 | "11.4.0",
155 | "67"
156 | ],
157 | [
158 | "node",
159 | "12.0.0",
160 | "72"
161 | ],
162 | [
163 | "node",
164 | "13.0.0",
165 | "79"
166 | ],
167 | [
168 | "node",
169 | "14.0.0",
170 | "83"
171 | ],
172 | [
173 | "node",
174 | "15.0.0",
175 | "88"
176 | ],
177 | [
178 | "node",
179 | "16.0.0",
180 | "93"
181 | ]
182 | ]
183 | }
184 |
--------------------------------------------------------------------------------
/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 | };
--------------------------------------------------------------------------------
/test/specs/keyboard.spec.js:
--------------------------------------------------------------------------------
1 | const ioHook = require('../../index');
2 | const robot = require('robotjs');
3 |
4 | describe('Keyboard events', () => {
5 | afterEach(() => {
6 | ioHook.stop();
7 | });
8 |
9 | it('receives the text "hello world" on keyup event', (done) => {
10 | expect.assertions(22);
11 |
12 | const chars = [
13 | { keycode: 35, value: 'h' },
14 | { keycode: 18, value: 'e' },
15 | { keycode: 38, value: 'l' },
16 | { keycode: 38, value: 'l' },
17 | { keycode: 24, value: 'o' },
18 | { keycode: 57, value: ' ' },
19 | { keycode: 17, value: 'w' },
20 | { keycode: 24, value: 'o' },
21 | { keycode: 19, value: 'r' },
22 | { keycode: 38, value: 'l' },
23 | { keycode: 32, value: 'd' },
24 | ];
25 | let i = 0;
26 |
27 | ioHook.on('keydown', (event) => {
28 | expect(event).toEqual({
29 | keycode: chars[i].keycode,
30 | type: 'keydown',
31 | shiftKey: false,
32 | altKey: false,
33 | ctrlKey: false,
34 | metaKey: false,
35 | });
36 | });
37 | ioHook.on('keyup', (event) => {
38 | expect(event).toEqual({
39 | keycode: chars[i].keycode,
40 | type: 'keydown',
41 | shiftKey: false,
42 | altKey: false,
43 | ctrlKey: false,
44 | metaKey: false,
45 | });
46 |
47 | if (i === chars.length - 1) {
48 | done();
49 | }
50 |
51 | i += 1;
52 | });
53 | ioHook.start();
54 |
55 | setTimeout(() => {
56 | // Make sure ioHook starts before anything gets typed
57 | for (const char of chars) {
58 | robot.keyTap(char.value);
59 | }
60 | }, 50);
61 | });
62 |
63 | it('recognizes shift key being pressed', (done) => {
64 | expect.assertions(8);
65 |
66 | ioHook.on('keydown', (event) => {
67 | expect(event).toEqual({
68 | type: 'keydown',
69 | shiftKey: true,
70 | altKey: false,
71 | ctrlKey: false,
72 | metaKey: false,
73 | });
74 | });
75 | ioHook.on('keyup', (event) => {
76 | expect(event).toEqual({
77 | type: 'keydown',
78 | shiftKey: true,
79 | altKey: false,
80 | ctrlKey: false,
81 | metaKey: false,
82 | });
83 | });
84 | ioHook.start();
85 |
86 | setTimeout(() => {
87 | // Make sure ioHook starts before anything gets typed
88 | robot.keyToggle('shift', 'down');
89 | robot.keyTap('1');
90 | robot.keyToggle('shift', 'up');
91 | }, 50);
92 | });
93 |
94 | it('recognizes alt key being pressed', (done) => {
95 | expect.assertions(8);
96 |
97 | ioHook.on('keydown', (event) => {
98 | expect(event).toEqual({
99 | type: 'keydown',
100 | shiftKey: false,
101 | altKey: true,
102 | ctrlKey: false,
103 | metaKey: false,
104 | });
105 | });
106 | ioHook.on('keyup', (event) => {
107 | expect(event).toEqual({
108 | type: 'keydown',
109 | shiftKey: false,
110 | altKey: true,
111 | ctrlKey: false,
112 | metaKey: false,
113 | });
114 | });
115 | ioHook.start();
116 |
117 | setTimeout(() => {
118 | // Make sure ioHook starts before anything gets typed
119 | robot.keyToggle('alt', 'down');
120 | robot.keyTap('1');
121 | robot.keyToggle('alt', 'up');
122 | }, 50);
123 | });
124 |
125 | it('recognizes ctrl key being pressed', (done) => {
126 | expect.assertions(8);
127 |
128 | ioHook.on('keydown', (event) => {
129 | expect(event).toEqual({
130 | type: 'keydown',
131 | shiftKey: false,
132 | altKey: false,
133 | ctrlKey: true,
134 | metaKey: false,
135 | });
136 | });
137 | ioHook.on('keyup', (event) => {
138 | expect(event).toEqual({
139 | type: 'keydown',
140 | shiftKey: false,
141 | altKey: false,
142 | ctrlKey: true,
143 | metaKey: false,
144 | });
145 | });
146 | ioHook.start();
147 |
148 | setTimeout(() => {
149 | // Make sure ioHook starts before anything gets typed
150 | robot.keyToggle('control', 'down');
151 | robot.keyTap('1');
152 | robot.keyToggle('control', 'up');
153 | }, 50);
154 | });
155 |
156 | it('recognizes meta key being pressed', (done) => {
157 | expect.assertions(8);
158 |
159 | ioHook.on('keydown', (event) => {
160 | expect(event).toEqual({
161 | type: 'keydown',
162 | shiftKey: false,
163 | altKey: false,
164 | ctrlKey: false,
165 | metaKey: true,
166 | });
167 | });
168 | ioHook.on('keyup', (event) => {
169 | expect(event).toEqual({
170 | type: 'keydown',
171 | shiftKey: false,
172 | altKey: false,
173 | ctrlKey: false,
174 | metaKey: true,
175 | });
176 | });
177 | ioHook.start();
178 |
179 | setTimeout(() => {
180 | // Make sure ioHook starts before anything gets typed
181 | robot.keyToggle('command', 'down');
182 | robot.keyTap('1');
183 | robot.keyToggle('command', 'up');
184 | }, 50);
185 | });
186 |
187 | it('runs a callback when a shortcut has been released', (done) => {
188 | expect.assertions(2);
189 |
190 | let shortcut = [42, 30];
191 |
192 | ioHook.registerShortcut(
193 | shortcut,
194 | (keys) => {
195 | expect(shortcut.sort()).toEqual(keys.sort());
196 | },
197 | (keys) => {
198 | expect(shortcut.sort()).toEqual(keys.sort());
199 | }
200 | );
201 |
202 | setTimeout(() => {
203 | // Make sure ioHook starts before anything gets typed
204 | robot.keyToggle('shift', 'down');
205 | robot.keyTap('a');
206 | robot.keyToggle('shift', 'up');
207 | }, 50);
208 | });
209 |
210 | it('can unregister a shortcut via its keycodes', (done) => {
211 | expect.assertions(0);
212 |
213 | let shortcut = [42, 30];
214 |
215 | // Register the shortcut
216 | ioHook.registerShortcut(shortcut, (event) => {
217 | // We're unregistering this shortcut. It should not have been called
218 | expect.not.toHaveBeenCalled();
219 | });
220 |
221 | // Unregister the shortcut
222 | ioHook.unregisterShortcutByKeys(shortcut);
223 |
224 | ioHook.start();
225 |
226 | setTimeout(() => {
227 | // Make sure ioHook starts before anything gets typed
228 | robot.keyToggle('shift', 'down');
229 | robot.keyTap('a');
230 | robot.keyToggle('shift', 'up');
231 | }, 50);
232 | });
233 |
234 | it('can use rawcode instead of keycode when detecting events', (done) => {
235 | expect.assertions(1);
236 |
237 | let rawCodeShortcut = [65505, 65]; // Shift + A in rawcode
238 |
239 | ioHook.registerShortcut(rawCodeShortcut, (event) => {
240 | expect.toHaveBeenCalled();
241 | });
242 |
243 | // Check rawcode detection works
244 | ioHook.useRawcode(true);
245 | ioHook.start();
246 |
247 | setTimeout(() => {
248 | // Make sure ioHook starts before anything gets typed
249 | robot.keyToggle('shift', 'down');
250 | robot.keyTap('a');
251 | robot.keyToggle('shift', 'up');
252 | }, 50);
253 | });
254 | });
255 |
--------------------------------------------------------------------------------