├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── appveyor.yml ├── assets └── switch.ico ├── bin └── addon.node ├── build ├── Interprocess.js ├── Interprocess.js.map ├── enums.js ├── enums.js.map ├── interfaces.js ├── interfaces.js.map ├── switch.js ├── switch.js.map ├── test.js ├── test.js.map ├── text.js ├── text.js.map ├── utils.js └── utils.js.map ├── 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 ├── post-build.js ├── src ├── Interprocess.ts ├── enums.ts ├── interfaces.ts ├── switch.ts ├── test.ts ├── text.ts └── utils.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | bin 4 | /*.zip -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: true 3 | os: osx 4 | 5 | node_js: 6 | - 10 7 | 8 | install: 9 | - npm i --ignore-scripts 10 | - npm install -g cmake-js 11 | - pwd 12 | 13 | before_script: 14 | # MacOS 64 bits version 15 | #- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi 16 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew list cmake &>/dev/null || brew install cmake; fi 17 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew list automake &>/dev/null || brew install automake; fi 18 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew list libtool &>/dev/null || brew install libtool; fi 19 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew list pkg-config &>/dev/null || brew install pkg-config; fi 20 | 21 | script: 22 | # Compiles switch service 23 | - mv -vn ./libuiohook ./node_modules/iohook/ 24 | - cd ./node_modules/node-window-manager && npm run rebuild 25 | - cd ../iohook && chmod +x ./libuiohook/bootstrap.sh && cmake-js compile 26 | - cd ../../ 27 | - npm run build-darwin 28 | 29 | branches: 30 | only: 31 | - master 32 | 33 | deploy: 34 | provider: releases 35 | api_key: $GH_TOKEN 36 | file_glob: true 37 | file: 38 | - ./*_release.zip 39 | skip_cleanup: true 40 | tag_name: switch-v0.0.4 41 | name: switch-v0.0.4 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Switch Service 2 | 3 | [![Build Status](https://travis-ci.org/ahkohd/switch.svg?branch=master)](https://travis-ci.org/ahkohd/switch) [![Build status](https://ci.appveyor.com/api/projects/status/gbm5k5qc2l32s8iv?svg=true)](https://ci.appveyor.com/project/ahkohd/switch) 4 | 5 | Switch's cross-platform stand-alone service for switching apps. Uses IPC (Inter Process Communication) to communicate (two-way) with [Switch Desktop.](https://github.com/ahkohd/switch-desktop) 6 | 7 | # Running 8 | 9 | > Before running Switch Service in development mode, please ensure you have Node.JS installed on your machine. 10 | 11 | Clone this repo, and cd into it, Then: 12 | 13 | ```bash 14 | $npm i 15 | $npm run dev 16 | ``` 17 | 18 | # Building 19 | 20 | Switch uses`pkg` to ship executables for Windows, MacOS (Under development) that will be spawned at [Switch Desktop](http://ahkohd.github.com/switch-desktop) start up. 21 | 22 | > Before building Switch Service, please ensure you have Node.JS installed on your machine. 23 | 24 | Clone this repo, and cd into it, Then: 25 | 26 | ```bash 27 | $npm i 28 | # for windows build 29 | $npm run build-win32 30 | # for mac build 31 | $npm run build-darwin 32 | 33 | ``` 34 | 35 | # License 36 | 37 | [Read LICENSE.md](./LICENSE.md) 38 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | platform: 4 | - x64 5 | 6 | cache: 7 | - node_modules 8 | 9 | init: 10 | - git config --global core.autocrlf input 11 | 12 | install: 13 | - ps: Install-Product node 10 x64 14 | - npm i 15 | 16 | build_script: 17 | - npm run build-win32 18 | 19 | test: off 20 | 21 | branches: 22 | only: 23 | - master 24 | 25 | artifacts: 26 | - path: /*_release.zip 27 | name: Releases 28 | 29 | deploy: 30 | release: switch-v0.0.4 31 | description: "Update" 32 | provider: GitHub 33 | auth_token: 34 | secure: CT5g4t5aF301xzMk6NfS5ieiYT/PB77ua4s6Z7T2glcYG9dd1A7skEoQy1oNoCv2 35 | artifact: Releases 36 | prerelease: false 37 | on: 38 | branch: master 39 | -------------------------------------------------------------------------------- /assets/switch.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahkohd/switch/08c629f988e7e3bccbd8ab7f8ae972fab59d2fce/assets/switch.ico -------------------------------------------------------------------------------- /bin/addon.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahkohd/switch/08c629f988e7e3bccbd8ab7f8ae972fab59d2fce/bin/addon.node -------------------------------------------------------------------------------- /build/Interprocess.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const events_1 = require("events"); 4 | const enums_1 = require("./enums"); 5 | const utils_1 = require("./utils"); 6 | const log = utils_1.switchLog.bind({ isDevMode: utils_1.checkDevMode() }); 7 | const ipc = require('node-ipc'); 8 | let socket; 9 | class InterProcessChannel { 10 | constructor() { 11 | this.emitter = new events_1.EventEmitter(); 12 | this.kickstart(); 13 | log(enums_1.Switch.LOG_INFO, 'Started IPC channel'); 14 | ipc.server.start(); 15 | } 16 | kickstart() { 17 | ipc.config.id = 'switch-service-channel'; 18 | ipc.config.retry = 1500; 19 | ipc.config.silent = true; 20 | ipc.serve(() => ipc.server.on('switch-service-incoming', (message, _socket) => { 21 | socket = _socket; 22 | const msg = JSON.parse(message); 23 | switch (msg.type) { 24 | case 'update-hot-apps': 25 | this.emitter.emit('update-hot-apps', msg.data); 26 | break; 27 | case 'client-pid': 28 | this.emitter.emit('client-pid', msg.data); 29 | break; 30 | case 'config-update': 31 | this.emitter.emit('config-update', msg.data); 32 | this.sendConfigUpdateToDockClient(msg.data); 33 | break; 34 | case 'show-dock': 35 | console.log('[info] Show dock'); 36 | this.sendShowClient(); 37 | break; 38 | } 39 | })); 40 | } 41 | sendShowClient() { 42 | try { 43 | ipc.server.emit(socket, 'client-show', { show: true }); 44 | } 45 | catch (e) { } 46 | } 47 | sendConfigUpdateToDockClient(update) { 48 | try { 49 | ipc.server.emit(socket, 'config-update', update); 50 | } 51 | catch (e) { } 52 | } 53 | sendlastSwitched(app) { 54 | try { 55 | ipc.server.emit(socket, 'last-switched-app', { hotApp: app }); 56 | } 57 | catch (e) { } 58 | } 59 | } 60 | exports.InterProcessChannel = InterProcessChannel; 61 | //# sourceMappingURL=Interprocess.js.map -------------------------------------------------------------------------------- /build/Interprocess.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Interprocess.js","sourceRoot":"","sources":["../src/Interprocess.ts"],"names":[],"mappings":";;AAAA,mCAAsC;AAEtC,mCAAiC;AACjC,mCAAkD;AAElD,MAAM,GAAG,GAAG,iBAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,oBAAY,EAAE,EAAE,CAAC,CAAC;AAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAChC,IAAI,MAAM,CAAC;AAEX,MAAa,mBAAmB;IAG5B;QACI,IAAI,CAAC,OAAO,GAAG,IAAI,qBAAY,EAAE,CAAC;QAElC,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,GAAG,CAAC,cAAM,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC;QAC5C,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAKD,SAAS;QAEL,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,wBAAwB,CAAC;QACzC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;QACxB,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QAGzB,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;YAC1E,MAAM,GAAG,OAAO,CAAC;YACjB,MAAM,GAAG,GAAmB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAChD,QAAQ,GAAG,CAAC,IAAI,EAAE;gBACd,KAAK,iBAAiB;oBAClB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;oBAC9C,MAAM;gBACV,KAAK,YAAY;oBACb,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC1C,MAAM;gBACV,KAAK,eAAe;oBAChB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;oBAE7C,IAAI,CAAC,4BAA4B,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAC5C,MAAM;gBACV,KAAK,WAAW;oBACZ,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;oBAChC,IAAI,CAAC,cAAc,EAAE,CAAC;oBACtB,MAAM;aACb;QACL,CAAC,CAAC,CAAC,CAAC;IACR,CAAC;IAKD,cAAc;QACV,IAAI;YACA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;SAC1D;QAAC,OAAO,CAAC,EAAE,GAAG;IACnB,CAAC;IAKD,4BAA4B,CAAC,MAAM;QAC/B,IAAI;YACA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;SACpD;QAAC,OAAO,CAAC,EAAE,GAAG;IACnB,CAAC;IAKD,gBAAgB,CAAC,GAAiB;QAC9B,IAAI;YACA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;SACjE;QAAC,OAAO,CAAC,EAAE,GAAG;IACnB,CAAC;CACJ;AAvED,kDAuEC"} -------------------------------------------------------------------------------- /build/enums.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var Switch; 4 | (function (Switch) { 5 | Switch["VERSION"] = "0.0.1"; 6 | Switch[Switch["ERROR_NOTI"] = 0] = "ERROR_NOTI"; 7 | Switch[Switch["INFO_NOTI"] = 1] = "INFO_NOTI"; 8 | Switch["NOTI_ICON"] = ""; 9 | Switch["APP_PATH"] = ""; 10 | Switch["LOG_INFO"] = "Info"; 11 | Switch["LOG_ERROR"] = "Error"; 12 | Switch["WINDOWS"] = "Windows_NT"; 13 | })(Switch = exports.Switch || (exports.Switch = {})); 14 | //# sourceMappingURL=enums.js.map -------------------------------------------------------------------------------- /build/enums.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":";;AAAA,IAAY,MAUX;AAVD,WAAY,MAAM;IAEd,2BAAiB,CAAA;IACjB,+CAAc,CAAA;IACd,6CAAa,CAAA;IACb,wBAAc,CAAA;IACd,uBAAa,CAAA;IACb,2BAAiB,CAAA;IACjB,6BAAmB,CAAA;IACnB,gCAAsB,CAAA;AAC1B,CAAC,EAVW,MAAM,GAAN,cAAM,KAAN,cAAM,QAUjB"} -------------------------------------------------------------------------------- /build/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=interfaces.js.map -------------------------------------------------------------------------------- /build/interfaces.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /build/switch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const utils_1 = require("./utils"); 4 | const text_1 = require("./text"); 5 | const enums_1 = require("./enums"); 6 | const interprocess_1 = require("./interprocess"); 7 | const Sentry = require('@sentry/node'); 8 | Sentry.init({ dsn: 'https://57f0f5bf1422411ca17906d52bedb2ee@sentry.io/1541801' }); 9 | const interChannel = new interprocess_1.InterProcessChannel(); 10 | const ioHook = require('iohook'); 11 | let clientPID = null; 12 | let hotapps = utils_1.getHotApps(); 13 | let config = utils_1.getConfig(); 14 | const log = utils_1.switchLog.bind({ isDevMode: utils_1.checkDevMode() }); 15 | let disableKeyUpListen = false; 16 | let disableKeyUpListenTimeout; 17 | function react(event) { 18 | let hotApp = utils_1.whichHotApp((process.platform == "darwin" ? event.keycode : event.rawcode), hotapps); 19 | if (hotApp != null) { 20 | utils_1.minimizeCurrentWindow(); 21 | let processes = utils_1.getAllProcessThatMatchAppName(hotApp.name, hotApp.path); 22 | if (processes) { 23 | utils_1.MakeHotAppActive(processes, config.maximize); 24 | interChannel.sendlastSwitched(hotApp); 25 | } 26 | else { 27 | utils_1.switchMessage(enums_1.Switch.ERROR_NOTI, { title: text_1.default.errorTitle, message: text_1.default.processNotFound(hotApp.name), hotApp: hotApp }); 28 | } 29 | } 30 | else { 31 | if (process.platform == 'darwin') { 32 | if (event.keycode >= 0 && event.keycode <= 9) { 33 | interChannel.sendShowClient(); 34 | utils_1.switchMessage(enums_1.Switch.ERROR_NOTI, { title: text_1.default.errorTitle, message: text_1.default.noHotApp(event.keycode - 1), hotApp: hotApp }); 35 | } 36 | } 37 | else { 38 | if (event.rawcode >= 48 && event.rawcode <= 58) { 39 | interChannel.sendShowClient(); 40 | utils_1.switchMessage(enums_1.Switch.ERROR_NOTI, { title: text_1.default.errorTitle, message: text_1.default.noHotApp(event.rawcode - 48), hotApp: hotApp }); 41 | } 42 | } 43 | } 44 | } 45 | function fnMethod(event) { 46 | if (event.altKey) { 47 | react(event); 48 | } 49 | } 50 | if (process.platform == 'win32') { 51 | ioHook.on('keyup', event => { 52 | if (disableKeyUpListen && event.rawcode != 164) { 53 | if (disableKeyUpListenTimeout) 54 | clearTimeout(disableKeyUpListenTimeout); 55 | disableKeyUpListenTimeout = setTimeout(() => { 56 | clearTimeout(disableKeyUpListenTimeout); 57 | disableKeyUpListen = false; 58 | }, 1000); 59 | return; 60 | } 61 | ; 62 | fnMethod(event); 63 | }); 64 | ioHook.on('keydown', event => { 65 | if (event.altKey) { 66 | if (config.disableAltGr && event.rawcode == 165) { 67 | disableKeyUpListen = true; 68 | return; 69 | } 70 | interChannel.sendShowClient(); 71 | } 72 | }); 73 | } 74 | if (process.platform == "darwin") { 75 | const numKeys = [ 76 | { keycode: 2, rawcode: 18 }, 77 | { keycode: 3, rawcode: 19 }, 78 | { keycode: 4, rawcode: 20 }, 79 | { keycode: 5, rawcode: 21 }, 80 | { keycode: 6, rawcode: 23 }, 81 | { keycode: 7, rawcode: 22 }, 82 | { keycode: 8, rawcode: 26 }, 83 | { keycode: 9, rawcode: 28 }, 84 | { keycode: 10, rawcode: 25 }, 85 | { keycode: 11, rawcode: 29 } 86 | ]; 87 | for (const current of numKeys) { 88 | ioHook.registerShortcut([3676, 3640, current.keycode], (keys) => { 89 | interChannel.sendShowClient(); 90 | react(current); 91 | }); 92 | ioHook.registerShortcut([3675, 56, current.keycode], (keys) => { 93 | interChannel.sendShowClient(); 94 | react(current); 95 | }); 96 | } 97 | ioHook.registerShortcut([3676, 3640], () => { 98 | interChannel.sendShowClient(); 99 | }); 100 | ioHook.registerShortcut([3675, 56], () => { 101 | interChannel.sendShowClient(); 102 | }); 103 | } 104 | (utils_1.checkDevMode()) ? ioHook.start(true) : ioHook.start(); 105 | utils_1.registerNotifierOnClick(); 106 | interChannel.emitter.on('update-hot-apps', (happs) => { 107 | hotapps = happs; 108 | log(enums_1.Switch.LOG_INFO, 'Hot apps update received', hotapps); 109 | utils_1.saveHotApps(happs); 110 | }); 111 | interChannel.emitter.on('config-update', (settings) => { 112 | log(enums_1.Switch.LOG_INFO, 'Config update update received', settings); 113 | config = settings; 114 | utils_1.saveConfig(settings); 115 | }); 116 | interChannel.emitter.on('client-pid', (pid) => { 117 | clientPID = pid; 118 | log(enums_1.Switch.LOG_INFO, 'Hot client pid ::', pid); 119 | }); 120 | //# sourceMappingURL=switch.js.map -------------------------------------------------------------------------------- /build/switch.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"switch.js","sourceRoot":"","sources":["../src/switch.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /build/test.js: -------------------------------------------------------------------------------- 1 | const { windowManager } = require("node-window-manager"); 2 | const current = windowManager.getActiveWindow(); 3 | console.log(current); 4 | current.minimize(); 5 | console.log('worked!'); 6 | //# sourceMappingURL=test.js.map -------------------------------------------------------------------------------- /build/test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;AACzD,MAAM,OAAO,GAAG,aAAa,CAAC,eAAe,EAAE,CAAC;AAChD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACrB,OAAO,CAAC,QAAQ,EAAE,CAAC;AACnB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC"} -------------------------------------------------------------------------------- /build/text.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const TextTemplate = { 4 | errorTitle: "Yikes 🤔", 5 | processNotFound: (name) => `It seems like ${name.split('.exe')[0]} is yet to be opened. Click to launch app! 🚀`, 6 | noHotApp: (key) => `No hot app as been assigned to NUM ${key}, add one at the Switch dock! 👆 ` 7 | }; 8 | exports.default = TextTemplate; 9 | //# sourceMappingURL=text.js.map -------------------------------------------------------------------------------- /build/text.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":";;AAAA,MAAM,YAAY,GAAG;IACjB,UAAU,EAAE,UAAU;IACtB,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,+CAA+C;IAChH,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,sCAAsC,GAAG,mCAAmC;CAClG,CAAA;AAED,kBAAe,YAAY,CAAC"} -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const enums_1 = require("./enums"); 4 | const ostype = require("os").type(); 5 | const { windowManager } = require("node-window-manager"); 6 | const open = require('open'); 7 | const notifier = require('node-notifier'); 8 | const path = require('path'); 9 | const blackList = ['explorer.exe']; 10 | const Conf = require('conf'); 11 | const config = new Conf({ 12 | projectName: 'SwitchService', 13 | encryptionKey: '..kta#md!@a-k2j', 14 | }); 15 | const log = switchLog.bind({ isDevMode: checkDevMode() }); 16 | const icoPath = (process.pkg) ? path.join(path.dirname(process.execPath), './switch.ico') : path.join(__dirname, '../assets/switch.ico'); 17 | const stringSimilarity = require('string-similarity'); 18 | log(enums_1.Switch.LOG_INFO, 'ENV', ostype); 19 | function switchMessage(type, data) { 20 | notifier.notify({ 21 | title: 'Switch - ' + data.title, 22 | message: data.message, 23 | icon: icoPath, 24 | sound: false, 25 | wait: true, 26 | hotApp: (data.hotApp) ? data.hotApp : null 27 | }); 28 | } 29 | exports.switchMessage = switchMessage; 30 | function registerNotifierOnClick() { 31 | const onclick = debounce((notifierObject, options, event) => { 32 | if (options.hotApp) 33 | openHotApp(options.hotApp.path); 34 | }, 3000, false); 35 | notifier.on('click', onclick); 36 | } 37 | exports.registerNotifierOnClick = registerNotifierOnClick; 38 | function getHotApps() { 39 | const hotApps = config.get('hotApps'); 40 | if (hotApps == null) { 41 | config.set('hotApps', []); 42 | return []; 43 | } 44 | else { 45 | return hotApps; 46 | } 47 | } 48 | exports.getHotApps = getHotApps; 49 | function getConfig() { 50 | let settings = config.get('config'); 51 | if (settings == null) { 52 | const initial = { 53 | autoHide: true, 54 | maximize: true, 55 | disableAltGr: false 56 | }; 57 | config.set('config', initial); 58 | return initial; 59 | } 60 | else { 61 | return settings; 62 | } 63 | } 64 | exports.getConfig = getConfig; 65 | function saveConfig(settings) { 66 | config.set('config', settings); 67 | } 68 | exports.saveConfig = saveConfig; 69 | function saveHotApps(hotApps) { 70 | config.set('hotApps', hotApps); 71 | } 72 | exports.saveHotApps = saveHotApps; 73 | function whichHotApp(rawcode, hotApps) { 74 | let whichHotWindowToOpen = hotApps.filter(app => app.rawcode == rawcode); 75 | if (whichHotWindowToOpen.length == 0) 76 | return null; 77 | return whichHotWindowToOpen[0]; 78 | } 79 | exports.whichHotApp = whichHotApp; 80 | function getAllProcessThatMatchPath(_path) { 81 | let processes = windowManager.getWindows().filter(window => path.basename(window.path) == path.basename(_path)); 82 | if (processes == null || processes.length == 0) 83 | return null; 84 | return processes; 85 | } 86 | exports.getAllProcessThatMatchPath = getAllProcessThatMatchPath; 87 | function getProcessWithPID(pid) { 88 | let process = windowManager.getWindows().filter(window => window.processId == pid); 89 | if (process.length == 0) 90 | return null; 91 | return process[0]; 92 | } 93 | exports.getProcessWithPID = getProcessWithPID; 94 | function getAllProcessThatMatchAppName(name, path) { 95 | let filterProcessByname = []; 96 | if (ostype == enums_1.Switch.WINDOWS) { 97 | filterProcessByname = windowManager.getWindows().filter(window => window.isVisible() && window.getTitle().toLowerCase().includes(name.split('.exe')[0].toLowerCase().replace(/[^a-zA-Z ]/, ' '))); 98 | } 99 | else { 100 | filterProcessByname = windowManager.getWindows().filter(window => window.path == path); 101 | } 102 | if (filterProcessByname == null || filterProcessByname.length == 0) { 103 | if (process.platform == 'darwin') { 104 | return null; 105 | } 106 | else { 107 | return processPathSimilarityMatch(windowManager.getWindows().filter(window => window.isVisible()), path, .65); 108 | } 109 | } 110 | else { 111 | const filterProcessByPath = filterProcessByname.filter(window => window.path.toLowerCase() == path.toLowerCase()); 112 | if (filterProcessByPath == null || filterProcessByPath.length == 0) { 113 | return filterProcessByname; 114 | } 115 | else { 116 | return filterProcessByPath; 117 | } 118 | } 119 | } 120 | exports.getAllProcessThatMatchAppName = getAllProcessThatMatchAppName; 121 | function processPathSimilarityMatch(processes, path, treshold) { 122 | let simScores = []; 123 | processes.forEach(process => { 124 | simScores.push(stringSimilarity.compareTwoStrings(path, process.path)); 125 | }); 126 | const highest = Math.max(...simScores); 127 | if (highest < treshold) 128 | return null; 129 | const finalProcesses = []; 130 | simScores.forEach((score, index) => { 131 | if (score == highest) 132 | finalProcesses.push(processes[index]); 133 | }); 134 | return finalProcesses; 135 | } 136 | exports.processPathSimilarityMatch = processPathSimilarityMatch; 137 | function clearCurrentWidow() { 138 | const currentWindow = windowManager.getActiveWindow(); 139 | if (currentWindow.isWindow()) { 140 | try { 141 | currentWindow.minimize(); 142 | } 143 | catch (e) { } 144 | } 145 | } 146 | exports.clearCurrentWidow = clearCurrentWidow; 147 | function MakeHotAppActive(hotProcesses, maximize = true) { 148 | hotProcesses.sort(function (a, b) { 149 | return b.processId - a.processId; 150 | }); 151 | log(enums_1.Switch.LOG_INFO, 'Sorted processes', hotProcesses); 152 | let least = hotProcesses[0]; 153 | if (least.isWindow()) { 154 | least.bringToTop(); 155 | if (!maximize) { 156 | least.restore(); 157 | } 158 | else { 159 | least.maximize(); 160 | } 161 | } 162 | else { 163 | least = hotProcesses; 164 | least.shift(); 165 | for (let i = 0; i < least.length; i++) { 166 | if (least[i].isWindow()) { 167 | const hot = least[i]; 168 | hot.bringToTop(); 169 | if (!maximize) { 170 | hot.restore(); 171 | } 172 | else { 173 | hot.maximize(); 174 | } 175 | break; 176 | } 177 | } 178 | } 179 | } 180 | exports.MakeHotAppActive = MakeHotAppActive; 181 | function openHotApp(path) { 182 | open(path); 183 | } 184 | exports.openHotApp = openHotApp; 185 | function debounce(callback, wait, immediate = false) { 186 | let timeout = null; 187 | return function () { 188 | const callNow = immediate && !timeout; 189 | const next = () => callback.apply(this, arguments); 190 | clearTimeout(timeout); 191 | timeout = setTimeout(next, wait); 192 | if (callNow) { 193 | next(); 194 | } 195 | }; 196 | } 197 | exports.debounce = debounce; 198 | function minimizeCurrentWindow() { 199 | if (process.platform == "darwin") 200 | return; 201 | const current = windowManager.getActiveWindow(); 202 | const info = current.getInfo(); 203 | if (blackList.filter(item => info.path.includes(item)).length > 0) { 204 | log(enums_1.Switch.LOG_INFO, 'Cannot minize'); 205 | return; 206 | } 207 | ; 208 | if (current.isWindow() && current.getTitle().toLowerCase() != 'switch') { 209 | current.minimize(); 210 | } 211 | } 212 | exports.minimizeCurrentWindow = minimizeCurrentWindow; 213 | function checkDevMode() { 214 | if (process.argv[2]) { 215 | return (process.argv[2].toLowerCase() == '--dev') ? true : false; 216 | } 217 | else { 218 | return false; 219 | } 220 | } 221 | exports.checkDevMode = checkDevMode; 222 | function switchLog(type, ...args) { 223 | if (this.isDevMode) { 224 | console.log('[' + type + ']:', ...args); 225 | } 226 | } 227 | exports.switchLog = switchLog; 228 | function bringSwitchToFocus(pid) { 229 | if (process.platform == 'darwin') { 230 | try { 231 | const switchWindow = windowManager.getWindows().filter(window => window.processId == pid); 232 | console.log("FOCUSED SWITCH:", switchWindow.length > 0 ? true : false); 233 | switchWindow[0].bringToTop(); 234 | } 235 | catch (e) { } 236 | } 237 | } 238 | exports.bringSwitchToFocus = bringSwitchToFocus; 239 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /build/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /libuiohook/.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /build/ 3 | /autom4te.cache/ 4 | /config/ 5 | /cmake/ 6 | /include/config.h.in* 7 | /m4/ltoptions.m4 8 | /m4/ltversion.m4 9 | /m4/libtool.m4 10 | /m4/ltsugar.m4 11 | /m4/lt~obsolete.m4 12 | /Makefile.in 13 | /configure 14 | /aclocal.m4 15 | -------------------------------------------------------------------------------- /libuiohook/AUTHORS: -------------------------------------------------------------------------------- 1 | Alexander Barker 2 | 3 | Anthony Liguori 4 | original version of the keycode_to_scancode function in x11/input_helper.c 5 | 6 | Iván Munsuri Ibáñez 7 | contributed patches to include support for media keys and objective-c callbacks in x11/input_hook.c 8 | contributed hook_create_screen_info property for Windows and Darwin 9 | 10 | Fabrice Bellard 11 | xfree86_keycode_to_scancode_table lookup in x11/input_helper.c 12 | 13 | Marc-André Moreau 14 | original version of windows/input_helper.c 15 | original version of windows/input_helper.h 16 | 17 | Markus G. Kuhn 18 | keysym_unicode_table lookup table in x11/input_helper.c 19 | original version of the unicode_to_keysym function in x11/input_helper.c 20 | original version of the keysym_to_unicode function in x11/input_helper.c 21 | -------------------------------------------------------------------------------- /libuiohook/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(libuiohook) 4 | 5 | # libuihook sources. 6 | if(WIN32 OR WIN64) 7 | set(UIOHOOK_SRC 8 | "${CMAKE_CURRENT_SOURCE_DIR}/include/uiohook.h" 9 | "${CMAKE_CURRENT_SOURCE_DIR}/src/logger.c" 10 | "${CMAKE_CURRENT_SOURCE_DIR}/src/logger.h" 11 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_helper.h" 12 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_helper.c" 13 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/input_hook.c" 14 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/post_event.c" 15 | "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/system_properties.c" 16 | ) 17 | elseif(LINUX) 18 | set(UIOHOOK_SRC 19 | "include/uiohook.h" 20 | "src/logger.c" 21 | "src/logger.h" 22 | "src/x11/*.h" 23 | "src/x11/*.c" 24 | ) 25 | elseif(APPLE) 26 | set(UIOHOOK_SRC 27 | "include/uiohook.h" 28 | "src/logger.c" 29 | "src/logger.h" 30 | "src/darwin/*.h" 31 | "src/darwin/*.c" 32 | ) 33 | else() 34 | error("unknown OS") 35 | return() 36 | 37 | endif() 38 | 39 | 40 | #library 41 | add_library("uiohook" SHARED ${UIOHOOK_SRC}) 42 | if(WIN32 OR WIN64) 43 | target_include_directories("uiohook" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/windows") 44 | 45 | if(MSVC_VERSION LESS 1900) 46 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf") 47 | else() 48 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS") 49 | endif() 50 | endif() 51 | 52 | #demo_hook 53 | add_executable("demo_hook" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_hook.c") 54 | add_dependencies("demo_hook" "uiohook") 55 | target_link_libraries("demo_hook" "uiohook") 56 | if(WIN32 OR WIN64) 57 | if(MSVC_VERSION LESS 1900) 58 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf") 59 | else() 60 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS") 61 | endif() 62 | endif() 63 | 64 | #demo_hook_async 65 | add_executable("demo_hook_async" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_hook_async.c") 66 | add_dependencies("demo_hook_async" "uiohook") 67 | target_link_libraries("demo_hook_async" "uiohook") 68 | if(WIN32 OR WIN64) 69 | if(MSVC_VERSION LESS 1900) 70 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf") 71 | else() 72 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS") 73 | endif() 74 | endif() 75 | 76 | #demo_post 77 | add_executable("demo_post" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_post.c") 78 | add_dependencies("demo_post" "uiohook") 79 | target_link_libraries("demo_post" "uiohook") 80 | if(WIN32 OR WIN64) 81 | if(MSVC_VERSION LESS 1900) 82 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf") 83 | else() 84 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS") 85 | endif() 86 | endif() 87 | 88 | #demo_properties 89 | add_executable("demo_properties" "${CMAKE_CURRENT_SOURCE_DIR}/src/demo_properties.c") 90 | add_dependencies("demo_properties" "uiohook") 91 | target_link_libraries("demo_properties" "uiohook") 92 | if(WIN32 OR WIN64) 93 | if(MSVC_VERSION LESS 1900) 94 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS -Dsnprintf=_snprintf") 95 | else() 96 | SET_TARGET_PROPERTIES("uiohook" PROPERTIES COMPILE_FLAGS "-Dinline=__inline -D_CRT_SECURE_NO_WARNINGS") 97 | endif() 98 | endif() 99 | 100 | 101 | #all demo 102 | add_custom_target("all_demo" 103 | DEPENDS 104 | "demo_hook" 105 | "demo_hook_async" 106 | "demo_post" 107 | "demo_properties" 108 | ) 109 | -------------------------------------------------------------------------------- /libuiohook/COPYING.LESSER.md: -------------------------------------------------------------------------------- 1 | GNU Lesser General Public License 2 | ================================= 3 | 4 | Version 3, 29 June 2007 5 | 6 | Copyright © 2007 Free Software Foundation, Inc. <> 7 | 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | 12 | This version of the GNU Lesser General Public License incorporates 13 | the terms and conditions of version 3 of the GNU General Public 14 | License, supplemented by the additional permissions listed below. 15 | 16 | ### 0. Additional Definitions. 17 | 18 | As used herein, “this License” refers to version 3 of the GNU Lesser 19 | General Public License, and the “GNU GPL” refers to version 3 of the GNU 20 | General Public License. 21 | 22 | “The Library” refers to a covered work governed by this License, 23 | other than an Application or a Combined Work as defined below. 24 | 25 | An “Application” is any work that makes use of an interface provided 26 | by the Library, but which is not otherwise based on the Library. 27 | Defining a subclass of a class defined by the Library is deemed a mode 28 | of using an interface provided by the Library. 29 | 30 | A “Combined Work” is a work produced by combining or linking an 31 | Application with the Library. The particular version of the Library 32 | with which the Combined Work was made is also called the “Linked 33 | Version”. 34 | 35 | The “Minimal Corresponding Source” for a Combined Work means the 36 | Corresponding Source for the Combined Work, excluding any source code 37 | for portions of the Combined Work that, considered in isolation, are 38 | based on the Application, and not on the Linked Version. 39 | 40 | The “Corresponding Application Code” for a Combined Work means the 41 | object code and/or source code for the Application, including any data 42 | and utility programs needed for reproducing the Combined Work from the 43 | Application, but excluding the System Libraries of the Combined Work. 44 | 45 | ### 1. Exception to Section 3 of the GNU GPL. 46 | 47 | You may convey a covered work under sections 3 and 4 of this License 48 | without being bound by section 3 of the GNU GPL. 49 | 50 | ### 2. Conveying Modified Versions. 51 | 52 | If you modify a copy of the Library, and, in your modifications, a 53 | facility refers to a function or data to be supplied by an Application 54 | that uses the facility (other than as an argument passed when the 55 | facility is invoked), then you may convey a copy of the modified 56 | version: 57 | 58 | * a) under this License, provided that you make a good faith effort to 59 | ensure that, in the event an Application does not supply the 60 | function or data, the facility still operates, and performs 61 | whatever part of its purpose remains meaningful, or 62 | 63 | * b) under the GNU GPL, with none of the additional permissions of 64 | this License applicable to that copy. 65 | 66 | ### 3. Object Code Incorporating Material from Library Header Files. 67 | 68 | The object code form of an Application may incorporate material from 69 | a header file that is part of the Library. You may convey such object 70 | code under terms of your choice, provided that, if the incorporated 71 | material is not limited to numerical parameters, data structure 72 | layouts and accessors, or small macros, inline functions and templates 73 | (ten or fewer lines in length), you do both of the following: 74 | 75 | * a) Give prominent notice with each copy of the object code that the 76 | Library is used in it and that the Library and its use are 77 | covered by this License. 78 | * b) Accompany the object code with a copy of the GNU GPL and this license 79 | document. 80 | 81 | ### 4. Combined Works. 82 | 83 | You may convey a Combined Work under terms of your choice that, 84 | taken together, effectively do not restrict modification of the 85 | portions of the Library contained in the Combined Work and reverse 86 | engineering for debugging such modifications, if you also do each of 87 | the following: 88 | 89 | * a) Give prominent notice with each copy of the Combined Work that 90 | the Library is used in it and that the Library and its use are 91 | covered by this License. 92 | 93 | * b) Accompany the Combined Work with a copy of the GNU GPL and this license 94 | document. 95 | 96 | * c) For a Combined Work that displays copyright notices during 97 | execution, include the copyright notice for the Library among 98 | these notices, as well as a reference directing the user to the 99 | copies of the GNU GPL and this license document. 100 | 101 | * d) Do one of the following: 102 | - 0) Convey the Minimal Corresponding Source under the terms of this 103 | License, and the Corresponding Application Code in a form 104 | suitable for, and under terms that permit, the user to 105 | recombine or relink the Application with a modified version of 106 | the Linked Version to produce a modified Combined Work, in the 107 | manner specified by section 6 of the GNU GPL for conveying 108 | Corresponding Source. 109 | - 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | * e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | ### 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | * a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | * b) Give prominent notice with the combined library that part of it 140 | is a work based on the Library, and explaining where to find the 141 | accompanying uncombined form of the same work. 142 | 143 | ### 6. Revised Versions of the GNU Lesser General Public License. 144 | 145 | The Free Software Foundation may publish revised and/or new versions 146 | of the GNU Lesser General Public License from time to time. Such new 147 | versions will be similar in spirit to the present version, but may 148 | differ in detail to address new problems or concerns. 149 | 150 | Each version is given a distinguishing version number. If the 151 | Library as you received it specifies that a certain numbered version 152 | of the GNU Lesser General Public License “or any later version” 153 | applies to it, you have the option of following the terms and 154 | conditions either of that published version or of any later version 155 | published by the Free Software Foundation. If the Library as you 156 | received it does not specify a version number of the GNU Lesser 157 | General Public License, you may choose any version of the GNU Lesser 158 | General Public License ever published by the Free Software Foundation. 159 | 160 | If the Library as you received it specifies that a proxy can decide 161 | whether future versions of the GNU Lesser General Public License shall 162 | apply, that proxy's public statement of acceptance of any version is 163 | permanent authorization for you to choose that version for the 164 | Library. 165 | -------------------------------------------------------------------------------- /libuiohook/Makefile.am: -------------------------------------------------------------------------------- 1 | # libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | # Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | # https://github.com/kwhat/libuiohook/ 4 | # 5 | # libUIOHook is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published 7 | # by the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # libUIOHook is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | ACLOCAL_AMFLAGS = -I m4 20 | 21 | HOOK_SRC = src/logger.c 22 | TEST_SRC = test/input_helper_test.c test/system_properties_test.c test/uiohook_test.c 23 | MAN3_SRC = man/hook_run.man \ 24 | man/hook_stop.man \ 25 | man/hook_get_auto_repeat_delay.man \ 26 | man/hook_get_multi_click_time.man \ 27 | man/hook_get_pointer_acceleration_threshold.man \ 28 | man/hook_set_dispatch_proc.man \ 29 | man/hook_get_auto_repeat_rate.man \ 30 | man/hook_get_pointer_acceleration_multiplier.man \ 31 | man/hook_get_pointer_sensitivity.man \ 32 | man/hook_set_logger_proc.man 33 | 34 | if BUILD_DARWIN 35 | HOOK_SRC += src/darwin/input_helper.c \ 36 | src/darwin/input_hook.c \ 37 | src/darwin/post_event.c \ 38 | src/darwin/system_properties.c 39 | endif 40 | 41 | if BUILD_X11 42 | HOOK_SRC += src/x11/input_helper.c \ 43 | src/x11/input_hook.c \ 44 | src/x11/post_event.c \ 45 | src/x11/system_properties.c 46 | endif 47 | 48 | if BUILD_WINDOWS 49 | HOOK_SRC += src/windows/input_helper.c \ 50 | src/windows/input_hook.c \ 51 | src/windows/post_event.c \ 52 | src/windows/system_properties.c 53 | endif 54 | 55 | pkgconfigdir = $(libdir)/pkgconfig 56 | pkgconfig_DATA = $(top_builddir)/pc/uiohook.pc 57 | 58 | include_HEADERS = include/uiohook.h 59 | 60 | bin_PROGRAMS = 61 | lib_LTLIBRARIES = libuiohook.la 62 | 63 | 64 | libuiohook_la_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic -Wno-unused-parameter -I$(top_srcdir)/include -I$(top_srcdir)/src/$(backend) -I$(top_srcdir)/src 65 | libuiohook_la_LDFLAGS = $(LTLDFLAGS) $(LDFLAGS) 66 | libuiohook_la_SOURCES = $(HOOK_SRC) 67 | 68 | 69 | if BUILD_DEMO 70 | bin_PROGRAMS += demohook demohookasync demopost demoprops 71 | 72 | demohook_SOURCES = src/demo_hook.c 73 | demohook_LDADD = $(top_builddir)/libuiohook.la 74 | demohook_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include 75 | demohook_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS) 76 | 77 | demohookasync_SOURCES = src/demo_hook_async.c 78 | demohookasync_LDADD = $(top_builddir)/libuiohook.la 79 | demohookasync_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include 80 | demohookasync_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS) 81 | 82 | demopost_SOURCES = src/demo_post.c 83 | demopost_LDADD = $(top_builddir)/libuiohook.la 84 | demopost_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include 85 | demopost_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS) 86 | 87 | demoprops_SOURCES = src/demo_properties.c 88 | demoprops_LDADD = $(top_builddir)/libuiohook.la 89 | demoprops_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(DEMO_CFLAGS) -I$(top_srcdir)/include 90 | demoprops_LDFLAGS = $(LTLDFLAGS) $(DEMO_LIBS) 91 | endif 92 | 93 | 94 | if BUILD_TEST 95 | bin_PROGRAMS += testhook 96 | 97 | testhook_SOURCES = $(TEST_SRC) 98 | testhook_LDADD = $(top_builddir)/libuiohook.la 99 | testhook_CFLAGS = $(AM_CFLAGS) -Wall -Wextra -pedantic $(TEST_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir)/test -I$(top_srcdir)/src/$(backend) -I$(top_srcdir)/src 100 | testhook_LDFLAGS = $(LTLDFLAGS) $(TEST_LIBS) 101 | endif 102 | 103 | man3_MANS = $(MAN3_SRC) 104 | 105 | EXTRA_DIST = COPYING.md COPYING.LESSER.md README.md 106 | -------------------------------------------------------------------------------- /libuiohook/README.md: -------------------------------------------------------------------------------- 1 | libuiohook 2 | ========== 3 | 4 | A multi-platform C library to provide global keyboard and mouse hooks from userland. 5 | 6 | ## Compiling 7 | Prerequisites: autotools, pkg-config, libtool, gcc, clang or msys2/mingw32 8 | 9 | ./bootstrap.sh 10 | ./configure 11 | make && make install 12 | 13 | ## Usage 14 | * [Hook Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_hook.c) 15 | * [Async Hook Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_hook_async.c) 16 | * [Event Post Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_post.c) 17 | * [Properties Demo](https://github.com/kwhat/libuiohook/blob/master/src/demo_properties.c) 18 | * [Public Interface](https://github.com/kwhat/libuiohook/blob/master/include/uiohook.h) 19 | * Please see the man pages for function documentation. 20 | -------------------------------------------------------------------------------- /libuiohook/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$(uname)" = "Darwin" ]; then 4 | include=" -I/opt/local/share/aclocal" 5 | fi 6 | 7 | autoreconf --install --verbose --force $include 8 | -------------------------------------------------------------------------------- /libuiohook/configure.ac: -------------------------------------------------------------------------------- 1 | # libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | # Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | # https://github.com/kwhat/libuiohook/ 4 | # 5 | # libUIOHook is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published 7 | # by the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # libUIOHook is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | # Define a few constants using m4 macros to prevent multiple definitions. 20 | m4_define([UIOHOOK_MAJOR], [1]) 21 | m4_define([UIOHOOK_MINOR], [1]) 22 | m4_define([UIOHOOK_PATCH], [0]) 23 | m4_define([UIOHOOK_RC], []) 24 | m4_define([UIOHOOK_BUGS], [alex@1stleg.com]) 25 | m4_define([UIOHOOK_VERSION_STRING], UIOHOOK_MAJOR[.]UIOHOOK_MINOR[.]UIOHOOK_PATCH[]UIOHOOK_RC) 26 | 27 | m4_pattern_allow([AM_PROG_AR]) 28 | 29 | # Library versioning 30 | # These numbers should be tweaked on every release. Read carefully: 31 | # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 32 | # http://sourceware.org/autobook/autobook/autobook_91.html 33 | lt_current="1" 34 | lt_revision="0" 35 | lt_age="0" 36 | LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" 37 | 38 | 39 | # Initialize autoconf. 40 | AC_INIT([uiohook], [UIOHOOK_VERSION_STRING], [UIOHOOK_BUGS]) 41 | 42 | AC_PREREQ(2.65) 43 | 44 | AC_CONFIG_SRCDIR([include/uiohook.h]) 45 | AC_CONFIG_FILES([ 46 | pc/uiohook.pc \ 47 | Makefile 48 | ]) 49 | AC_CONFIG_AUX_DIR([config]) 50 | AC_CONFIG_HEADERS([include/config.h:include/config.h.in]) 51 | AC_CONFIG_MACRO_DIR([m4]) 52 | 53 | #AC_CANONICAL_SYSTEM 54 | AC_CANONICAL_HOST 55 | AC_CANONICAL_BUILD 56 | AC_CANONICAL_TARGET 57 | 58 | # Initialize automake. 59 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 60 | 61 | # Checks for programs. 62 | AC_PROG_CC 63 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 64 | 65 | # Checks for language. 66 | AC_LANG([C]) 67 | AC_PROG_CC_C99 68 | 69 | # Checks for header files 70 | AC_C_CONST 71 | AC_HEADER_STDBOOL 72 | AC_CHECK_HEADERS([stdint.h]) 73 | AC_TYPE_UINT64_T 74 | 75 | # Initialize libtool. 76 | AC_PROG_LIBTOOL 77 | 78 | # Check for operating system. 79 | AC_MSG_CHECKING(operating system) 80 | AS_CASE([$host], 81 | [*-darwin*], [ 82 | AC_MSG_RESULT([$host (Mac OS X Compatible)]) 83 | backend="darwin" 84 | ], 85 | [*-linux*], [ 86 | AC_MSG_RESULT([$host (Linux Compatible)]) 87 | backend="x11" 88 | has_evdev="1" 89 | ], 90 | [*-freebsd* | *-openbsd* | *-linux* | *-solaris*], [ 91 | AC_MSG_RESULT([$host (Unix Compatible)]) 92 | backend="x11" 93 | has_evdev="0" 94 | ], 95 | [*-mingw* | *-cygwin* | *-msys* ], [ 96 | AC_MSG_RESULT([$host (Windows Compatible)]) 97 | backend="windows" 98 | 99 | AC_LIBTOOL_WIN32_DLL 100 | ], 101 | [AC_MSG_ERROR([Unsupported operating system ${host}])]) 102 | 103 | 104 | # Common Options 105 | AC_ARG_ENABLE([debug], 106 | AS_HELP_STRING([--enable-debug], [Enable debug output (default: disabled)]), 107 | [enable_debug="$enableval"], 108 | [enable_debug="no"]) 109 | 110 | AC_ARG_ENABLE([demo], 111 | AS_HELP_STRING([--enable-demo], [Enable demo application (default: disabled)]), 112 | [enable_demo=$enableval], 113 | [enable_demo="no"]) 114 | 115 | AC_ARG_ENABLE([test], 116 | AS_HELP_STRING([--enable-test], [Enable unit testing (default: disabled)]), 117 | [enable_test=$enableval], 118 | [enable_test="no"]) 119 | 120 | AC_ARG_ENABLE([quiet], 121 | AS_HELP_STRING([--enable-quiet], [Enable copyright suppression (default: disabled)]), 122 | [enable_quiet=$enableval], 123 | [enable_quiet="no"]) 124 | 125 | # Linux Options 126 | AC_ARG_ENABLE([evdev], 127 | AS_HELP_STRING([--without-evdev], [Disable generic Linux input driver (default: enabled)]), 128 | [enable_evdev="$enableval"], 129 | [enable_evdev="yes"]) 130 | 131 | # Linux & Unix Options 132 | AC_ARG_ENABLE([xkbcommon], 133 | AS_HELP_STRING([--without-xkbcommon], [Disable X Keyboard Common Extension (default: enabled)]), 134 | [enable_xkbcommon="$enableval"], 135 | [enable_xkbcommon="yes"]) 136 | 137 | AC_ARG_ENABLE([xkbfile], 138 | AS_HELP_STRING([--without-xkbfile], [Disable X Keyboard File Extension (default: enabled)]), 139 | [enable_xkbfile="$enableval"], 140 | [enable_xkbfile="yes"]) 141 | 142 | AC_ARG_ENABLE([xcb], 143 | AS_HELP_STRING([--without-xcb], [Disable X protocol C-language Binding (default: enabled)]), 144 | [enable_xcb="$enableval"], 145 | [enable_xcb="yes"]) 146 | 147 | AC_ARG_ENABLE([xkb], 148 | AS_HELP_STRING([--without-xkb], [Disable X Keyboard Extension (default: enabled)]), 149 | [enable_xkb="$enableval"], 150 | [enable_xkb="yes"]) 151 | 152 | AC_ARG_ENABLE([xt], 153 | AS_HELP_STRING([--without-xt], [Disable X Toolkit Intrinsics (default: enabled)]), 154 | [enable_xt="$enableval"], 155 | [enable_xt="yes"]) 156 | 157 | AC_ARG_ENABLE([xf86misc], 158 | AS_HELP_STRING([--with-xf86misc], [Enable XFree86-Misc X Extension (default: disabled)]), 159 | [enable_xf86misc="$enableval"], 160 | [enable_xf86misc="no"]) 161 | 162 | AC_ARG_ENABLE([xrecord-async], 163 | AS_HELP_STRING([--enable-xrecord-async], [Enable XRecord Asynchronous API (default: disabled)]), 164 | [enable_xrecord_async="$enableval"], 165 | [enable_xrecord_async="no"]) 166 | 167 | AC_ARG_ENABLE([xtest], 168 | AS_HELP_STRING([--without-xtest], [Disable XTest Extension (default: enabled)]), 169 | [enable_xtest="$enableval"], 170 | [enable_xtest="yes"]) 171 | 172 | AC_ARG_ENABLE([xinerama], 173 | AS_HELP_STRING([--without-xinerama], [Disable Xinerama Extension (default: enabled)]), 174 | [enable_xinerama="$enableval"], 175 | [enable_xinerama="yes"]) 176 | 177 | AC_ARG_ENABLE([xrandr], 178 | AS_HELP_STRING([--with-xrandr], [Enable XRandR Extension (default: disabled)]), 179 | [enable_xrandr="$enableval"], 180 | [enable_xrandr="no"]) 181 | 182 | # Darwin Options 183 | AC_ARG_ENABLE([corefoundation], 184 | AS_HELP_STRING([--without-corefoundation], [Disable CoreFoundation framework (default: enabled)]), 185 | [enable_corefoundation="$enableval"], 186 | [enable_corefoundation="yes"]) 187 | 188 | AC_ARG_ENABLE([iokit], 189 | AS_HELP_STRING([--without-iokit], [Disable IOKit framework (default: enabled)]), 190 | [enable_iokit="$enableval"], 191 | [enable_iokit="yes"]) 192 | 193 | AC_ARG_ENABLE([objc], 194 | AS_HELP_STRING([--without-objc], [Disable objective-c (default: enabled)]), 195 | [enable_objc="$enableval"], 196 | [enable_objc="yes"]) 197 | 198 | AC_ARG_ENABLE([weak-import], 199 | AS_HELP_STRING([--enable-weak-import], [Enable support for weakly-linked symbols (default: disabled)]), 200 | [enable_weak_import="$enableval"], 201 | [enable_weak_import="no"]) 202 | 203 | AC_ARG_ENABLE([carbon-legacy], 204 | AS_HELP_STRING([--with-carbon-legacy], [Enable legacy Carbon framework functionality (default: disabled)]), 205 | [enable_carbon_legacy="$enableval"], 206 | [enable_carbon_legacy="no"]) 207 | 208 | AS_IF([test "x$enable_debug" = "xyes"], [ 209 | AC_DEFINE([USE_DEBUG], 1, [Enable Debug Output]) 210 | ]) 211 | 212 | AS_IF([test "x$enable_quiet" = "xyes"], [ 213 | AC_DEFINE([USE_QUIET], 1, [Enable Copyright Suppression]) 214 | ]) 215 | 216 | AS_IF( 217 | [test "x$backend" = "xdarwin"], [ 218 | # Force CLANG for OS X because it is required as of OS X 10.10. 219 | AC_PROG_CC([clang]) 220 | 221 | AS_IF([test "x$enable_corefoundation" = "xyes"], [ 222 | AC_DEFINE([USE_COREFOUNDATION], 1, [Enable CoreFoundation framework]) 223 | LIBS="${LIBS} -framework ApplicationServices" 224 | ]) 225 | 226 | AS_IF([test "x$enable_iokit" = "xyes"], [ 227 | AC_DEFINE([USE_IOKIT], 1, [Enable IOKit framework]) 228 | LIBS="${LIBS} -framework IOKit" 229 | ]) 230 | 231 | AS_IF([test "x$enable_objc" = "xyes"], [ 232 | AC_DEFINE([USE_OBJC], 1, [Enable objective-c]) 233 | LIBS="${LIBS} -lobjc" 234 | ]) 235 | 236 | AS_IF([test "x$enable_weak_import" = "xyes"], [ 237 | AC_DEFINE([USE_WEAK_IMPORT], 1, [Enable support for weakly-linked symbols]) 238 | LDFLAGS="$LDFLAGS -Wl,-flat_namespace,-undefined,dynamic_lookup" 239 | ]) 240 | 241 | AS_IF([test "x$enable_carbon_legacy" = "xyes"], [ 242 | # FIXME AC_MSG_ERROR if this is a 64 bit target. 243 | AC_MSG_WARN([Carbon legacy functionality is required for OS X version 10.4 and should only be used on 32-bit targets.]) 244 | AC_DEFINE([USE_CARBON_LEGACY], 1, [Enable Carbon framework legacy functionality]) 245 | ]) 246 | 247 | AC_DEFINE([MAC_OS_X_VERSION_MIN_REQUIRED], 1050, [Set minimum OS X version to 10.5]) 248 | 249 | # We only need pthreads if we are using the Demo. 250 | AS_IF([test "x$enable_demo" = "xyes"], [ 251 | AX_PTHREAD([found_pthreads=yes], [found_pthreads=no]) 252 | 253 | AS_IF([test "x$found_pthread" = "xno" ], [ 254 | AC_MSG_ERROR([Pthreads are required for Async XRecord, XRandR and the demo!]) 255 | ]) 256 | 257 | # FIXME For some reason, llvm will not use the pthread flag. 258 | #THREAD_LIBS="$PTHREAD_LIBS $LIBS" 259 | #THREAD_CFLAGS="$PTHREAD_CFLAGS $CFLAGS" 260 | ]) 261 | 262 | # Always add Carbon due to non-deprecated #define's 263 | LIBS="${LIBS} -framework Carbon" 264 | 265 | #CFLAGS="$CFLAGS -I/usr/local/include" 266 | #LDFLAGS="$LDFLAGS -Wl,-undefined,error" 267 | ], 268 | [test "x$backend" = "xx11"], [ 269 | # Checks for libraries. 270 | PKG_PROG_PKG_CONFIG 271 | 272 | PKG_CHECK_MODULES([X11], [x11]) 273 | LIBS="$X11_LIBS $LIBS" 274 | #CFLAGS="$X11_CFLAGS $CFLAGS" 275 | REQUIRE="$REQUIRE x11" 276 | 277 | PKG_CHECK_MODULES([XTST], [xtst]) 278 | LIBS="$XTST_LIBS $LIBS" 279 | #CFLAGS="$XTST_CFLAGS $CFLAGS" 280 | REQUIRE="$REQUIRE xtst" 281 | 282 | # Check for XRecord. 283 | AC_CHECK_LIB([Xtst], [XRecordQueryVersion], [], 284 | [AC_MSG_ERROR([libXtst does not include XRecord extention!])], 285 | $LIBS) 286 | 287 | AC_CHECK_HEADERS([X11/extensions/record.h], [], 288 | [AC_MSG_ERROR([XRecord extention header could not be found!])], 289 | [#include ]) 290 | 291 | # Evdev is only available on Linux. 292 | AS_IF([test "x$has_evdev" = "x1"], [ 293 | AS_IF([test "x$enable_evdev" = "xyes"], [ 294 | AC_DEFINE([USE_EVDEV], 1, [Enable generic Linux input driver]) 295 | ]) 296 | ]) 297 | 298 | AS_IF([test "x$enable_xkb" = "xyes"], [ 299 | AC_DEFINE([USE_XKB], 1, [Enable X Keyboard Extension]) 300 | ]) 301 | 302 | AS_IF([test "x$enable_xt" = "xyes"], [ 303 | AC_DEFINE([USE_XT], 1, [Enable X Toolkit Intrinsics]) 304 | PKG_CHECK_MODULES([XT], [xt]) 305 | #CFLAGS="$XT_CFLAGS $CFLAGS" 306 | LIBS="$XT_LIBS $LIBS" 307 | REQUIRE="$REQUIRE xt" 308 | ]) 309 | 310 | AS_IF([test "x$enable_xf86misc" = "xyes"], [ 311 | AC_DEFINE([USE_XF86MISC], 1, [Enable XFree86-Misc X Extension]) 312 | PKG_CHECK_MODULES([XF86MISC], [xxf86misc]) 313 | #CFLAGS="$XF86MISC_CFLAGS $CFLAGS" 314 | LIBS="$XF86MISC_LIBS $LIBS" 315 | REQUIRE="$REQUIRE xxf86misc" 316 | ]) 317 | 318 | AS_IF([test "x$enable_xrecord_async" = "xyes"], [ 319 | AC_DEFINE([USE_XRECORD_ASYNC], 1, [Enable XRecord Asynchronous API]) 320 | CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199309L" 321 | ]) 322 | 323 | AS_IF([test "x$enable_xtest" = "xyes"], [ 324 | AC_CHECK_LIB([Xtst], [XTestQueryExtension], 325 | [AC_CHECK_HEADERS([X11/extensions/XTest.h], 326 | [AC_DEFINE([USE_XTEST], 1, [Enable XTest Extension])], 327 | [AC_MSG_WARN([XTest extention header could not be found!])], 328 | [#include ])], 329 | [AC_MSG_WARN([libXtst does not include XTest extention!])], 330 | $LIBS) 331 | ]) 332 | 333 | AS_IF([test "x$enable_xrandr" = "xyes"], [ 334 | AS_IF([test "x$enable_xinerama" = "xyes" ], [ 335 | AC_MSG_WARN([Both Xinerama and XRandR were enabled, ignoring Xinerama!]) 336 | ]) 337 | 338 | AC_DEFINE([USE_XRANDR], 1, [Enable XRandR Extension]) 339 | PKG_CHECK_MODULES([XRANDR], [xrandr]) 340 | LIBS="$XRANDR_LIBS $LIBS" 341 | #CFLAGS="$XRANDR_CFLAGS $CFLAGS" 342 | REQUIRE="$REQUIRE xrandr" 343 | ],[ 344 | AS_IF([test "x$enable_xinerama" = "xyes"], [ 345 | AC_DEFINE([USE_XINERAMA], 1, [Enable Xinerama Extension]) 346 | PKG_CHECK_MODULES([XINERAMA], [xinerama]) 347 | LIBS="$XINERAMA_LIBS $LIBS" 348 | #CFLAGS="$XINERAMA_CFLAGS $CFLAGS" 349 | REQUIRE="$REQUIRE xinerama" 350 | ]) 351 | ]) 352 | 353 | # We only need pthreads if we are using XRandR, Async XRecord or the Demo. 354 | AS_IF([test "x$enable_xrecord_async" = "xyes" || test "x$enable_xrandr" = "xyes" || test "x$enable_demo" = "xyes"], [ 355 | AX_PTHREAD([found_pthreads=yes], [found_pthreads=no]) 356 | 357 | AS_IF([test "x$found_pthread" = "xno" ], [ 358 | AC_MSG_ERROR([Pthreads are required for Async XRecord, XRandR and the demo!]) 359 | ]) 360 | 361 | AS_IF([test "x$enable_xrecord_async" = "xyes" || test "x$enable_xrandr" = "xyes"], [ 362 | LIBS="$PTHREAD_LIBS $LIBS" 363 | CFLAGS="$PTHREAD_CFLAGS $CFLAGS" 364 | ], [test "x$enable_demo" = "xyes"], [ 365 | DEMO_LIBS="$PTHREAD_LIBS $LIBS" 366 | DEMO_CFLAGS="$PTHREAD_CFLAGS $CFLAGS" 367 | ]) 368 | ]) 369 | 370 | 371 | AS_IF([test "x$enable_xkbcommon" = "xyes"], [ 372 | PKG_CHECK_MODULES([X11_XCB], [x11-xcb]) 373 | LIBS="$X11_XCB_LIBS $LIBS" 374 | CFLAGS="$X11_XCB_CFLAGS $CFLAGS" 375 | REQUIRE="$REQUIRE x11-xcb" 376 | 377 | PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon]) 378 | LIBS="$XKBCOMMON_LIBS $LIBS" 379 | CFLAGS="$XKBCOMMON_CFLAGS $CFLAGS" 380 | REQUIRE="$REQUIRE xkbcommon" 381 | 382 | PKG_CHECK_MODULES([XKBCOMMON_X11], [xkbcommon-x11]) 383 | LIBS="$XKBCOMMON_X11_LIBS $LIBS" 384 | #CFLAGS="$XKBCOMMON_X11_CFLAGS $CFLAGS" 385 | REQUIRE="$REQUIRE xkbcommon-x11" 386 | 387 | AC_DEFINE([USE_XKBCOMMON], 1, [Enable XKB Common Extension]) 388 | ]) 389 | 390 | AS_IF([test "x$enable_xkbfile" = "xyes"], [ 391 | AS_IF([test "x$enable_kxbcommon" = "xno"], [ 392 | AC_MSG_ERROR([XKB Common is required for XKB File support!]) 393 | ]) 394 | 395 | PKG_CHECK_MODULES([XKBFILE], [xkbfile]) 396 | LIBS="$XKBFILE_LIBS $LIBS" 397 | #CFLAGS="$XKBFILE_CFLAGS $CFLAGS" 398 | REQUIRE="$REQUIRE xkbfile" 399 | 400 | AC_DEFINE([USE_XKBFILE], 1, [Enable XKB File Extension]) 401 | ]) 402 | 403 | LDFLAGS="$LDFLAGS -Wl,--no-undefined" 404 | ], 405 | [test "x$backend" = "xwindows"], [ 406 | AC_DEFINE([_WIN32_WINNT], 0x0500, [Set minimum Windows version to Windows 2000]) 407 | 408 | AS_IF([test "x$enable_debug" = "xyes"], [ 409 | CFLAGS="$CFLAGS -Wno-format" 410 | ]) 411 | 412 | LDFLAGS="$LDFLAGS -Wl,--no-undefined -no-undefined" 413 | ]) 414 | 415 | # OS info for Automake 416 | AC_SUBST([backend]) 417 | 418 | # Requires for pkg-config 419 | AC_SUBST([REQUIRE]) 420 | AC_SUBST([LIBS]) 421 | AC_SUBST([DEMO_CFLAGS]) 422 | AC_SUBST([DEMO_LIBS]) 423 | AC_SUBST([TEST_LIBS]) 424 | 425 | # Should the demo application be built? 426 | AM_CONDITIONAL([BUILD_DEMO], [test "x$enable_demo" = "xyes"]) 427 | 428 | # Should the unit tests be built? 429 | AM_CONDITIONAL([BUILD_TEST], [test "x$enable_test" = "xyes"]) 430 | 431 | # Department of redundancy department. We have already checked the platform 3 432 | # times! I am sure there is a technical reason for this... 433 | AM_CONDITIONAL(BUILD_DARWIN, test "x$backend" = "xdarwin") 434 | AM_CONDITIONAL(BUILD_X11, test "x$backend" = "xx11") 435 | AM_CONDITIONAL(BUILD_WINDOWS, test "x$backend" = "xwindows") 436 | 437 | # Generate the "configure" script 438 | AC_OUTPUT 439 | -------------------------------------------------------------------------------- /libuiohook/include/uiohook.h: -------------------------------------------------------------------------------- 1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | * https://github.com/kwhat/libuiohook/ 4 | * 5 | * libUIOHook is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * libUIOHook is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef __UIOHOOK_H 20 | #define __UIOHOOK_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | /* Begin Error Codes */ 27 | #define UIOHOOK_SUCCESS 0x00 28 | #define UIOHOOK_FAILURE 0x01 29 | 30 | // System level errors. 31 | #define UIOHOOK_ERROR_OUT_OF_MEMORY 0x02 32 | 33 | // Unix specific errors. 34 | #define UIOHOOK_ERROR_X_OPEN_DISPLAY 0x20 35 | #define UIOHOOK_ERROR_X_RECORD_NOT_FOUND 0x21 36 | #define UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE 0x22 37 | #define UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT 0x23 38 | #define UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT 0x24 39 | #define UIOHOOK_ERROR_X_RECORD_GET_CONTEXT 0x25 40 | 41 | // Windows specific errors. 42 | #define UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30 43 | #define UIOHOOK_ERROR_GET_MODULE_HANDLE 0x31 44 | 45 | // Darwin specific errors. 46 | #define UIOHOOK_ERROR_AXAPI_DISABLED 0x40 47 | #define UIOHOOK_ERROR_CREATE_EVENT_PORT 0x41 48 | #define UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE 0x42 49 | #define UIOHOOK_ERROR_GET_RUNLOOP 0x43 50 | #define UIOHOOK_ERROR_CREATE_OBSERVER 0x44 51 | /* End Error Codes */ 52 | 53 | /* Begin Log Levels and Function Prototype */ 54 | typedef enum _log_level { 55 | LOG_LEVEL_DEBUG = 1, 56 | LOG_LEVEL_INFO, 57 | LOG_LEVEL_WARN, 58 | LOG_LEVEL_ERROR 59 | } log_level; 60 | 61 | // Logger callback function prototype. 62 | typedef bool (*logger_t)(unsigned int, const char *, ...); 63 | /* End Log Levels and Function Prototype */ 64 | 65 | /* Begin Virtual Event Types and Data Structures */ 66 | typedef enum _event_type { 67 | EVENT_HOOK_ENABLED = 1, 68 | EVENT_HOOK_DISABLED, 69 | EVENT_KEY_TYPED, 70 | EVENT_KEY_PRESSED, 71 | EVENT_KEY_RELEASED, 72 | EVENT_MOUSE_CLICKED, 73 | EVENT_MOUSE_PRESSED, 74 | EVENT_MOUSE_RELEASED, 75 | EVENT_MOUSE_MOVED, 76 | EVENT_MOUSE_DRAGGED, 77 | EVENT_MOUSE_WHEEL 78 | } event_type; 79 | 80 | typedef struct _screen_data { 81 | uint8_t number; 82 | int16_t x; 83 | int16_t y; 84 | uint16_t width; 85 | uint16_t height; 86 | } screen_data; 87 | 88 | typedef struct _keyboard_event_data { 89 | uint16_t keycode; 90 | uint16_t rawcode; 91 | uint16_t keychar; 92 | } keyboard_event_data, 93 | key_pressed_event_data, 94 | key_released_event_data, 95 | key_typed_event_data; 96 | 97 | typedef struct _mouse_event_data { 98 | uint16_t button; 99 | uint16_t clicks; 100 | int16_t x; 101 | int16_t y; 102 | } mouse_event_data, 103 | mouse_pressed_event_data, 104 | mouse_released_event_data, 105 | mouse_clicked_event_data; 106 | 107 | typedef struct _mouse_wheel_event_data { 108 | uint16_t clicks; 109 | int16_t x; 110 | int16_t y; 111 | uint8_t type; 112 | uint16_t amount; 113 | int16_t rotation; 114 | uint8_t direction; 115 | } mouse_wheel_event_data; 116 | 117 | typedef struct _uiohook_event { 118 | event_type type; 119 | uint64_t time; 120 | uint16_t mask; 121 | uint16_t reserved; 122 | union { 123 | keyboard_event_data keyboard; 124 | mouse_event_data mouse; 125 | mouse_wheel_event_data wheel; 126 | } data; 127 | } uiohook_event; 128 | 129 | typedef void (*dispatcher_t)(uiohook_event *const); 130 | /* End Virtual Event Types and Data Structures */ 131 | 132 | 133 | /* Begin Virtual Key Codes */ 134 | #define VC_ESCAPE 0x0001 135 | 136 | // Begin Function Keys 137 | #define VC_F1 0x003B 138 | #define VC_F2 0x003C 139 | #define VC_F3 0x003D 140 | #define VC_F4 0x003E 141 | #define VC_F5 0x003F 142 | #define VC_F6 0x0040 143 | #define VC_F7 0x0041 144 | #define VC_F8 0x0042 145 | #define VC_F9 0x0043 146 | #define VC_F10 0x0044 147 | #define VC_F11 0x0057 148 | #define VC_F12 0x0058 149 | 150 | #define VC_F13 0x005B 151 | #define VC_F14 0x005C 152 | #define VC_F15 0x005D 153 | #define VC_F16 0x0063 154 | #define VC_F17 0x0064 155 | #define VC_F18 0x0065 156 | #define VC_F19 0x0066 157 | #define VC_F20 0x0067 158 | #define VC_F21 0x0068 159 | #define VC_F22 0x0069 160 | #define VC_F23 0x006A 161 | #define VC_F24 0x006B 162 | // End Function Keys 163 | 164 | 165 | // Begin Alphanumeric Zone 166 | #define VC_BACKQUOTE 0x0029 167 | 168 | #define VC_1 0x0002 169 | #define VC_2 0x0003 170 | #define VC_3 0x0004 171 | #define VC_4 0x0005 172 | #define VC_5 0x0006 173 | #define VC_6 0x0007 174 | #define VC_7 0x0008 175 | #define VC_8 0x0009 176 | #define VC_9 0x000A 177 | #define VC_0 0x000B 178 | 179 | #define VC_MINUS 0x000C // '-' 180 | #define VC_EQUALS 0x000D // '=' 181 | #define VC_BACKSPACE 0x000E 182 | 183 | #define VC_TAB 0x000F 184 | #define VC_CAPS_LOCK 0x003A 185 | 186 | #define VC_A 0x001E 187 | #define VC_B 0x0030 188 | #define VC_C 0x002E 189 | #define VC_D 0x0020 190 | #define VC_E 0x0012 191 | #define VC_F 0x0021 192 | #define VC_G 0x0022 193 | #define VC_H 0x0023 194 | #define VC_I 0x0017 195 | #define VC_J 0x0024 196 | #define VC_K 0x0025 197 | #define VC_L 0x0026 198 | #define VC_M 0x0032 199 | #define VC_N 0x0031 200 | #define VC_O 0x0018 201 | #define VC_P 0x0019 202 | #define VC_Q 0x0010 203 | #define VC_R 0x0013 204 | #define VC_S 0x001F 205 | #define VC_T 0x0014 206 | #define VC_U 0x0016 207 | #define VC_V 0x002F 208 | #define VC_W 0x0011 209 | #define VC_X 0x002D 210 | #define VC_Y 0x0015 211 | #define VC_Z 0x002C 212 | 213 | #define VC_OPEN_BRACKET 0x001A // '[' 214 | #define VC_CLOSE_BRACKET 0x001B // ']' 215 | #define VC_BACK_SLASH 0x002B // '\' 216 | 217 | #define VC_SEMICOLON 0x0027 // ';' 218 | #define VC_QUOTE 0x0028 219 | #define VC_ENTER 0x001C 220 | 221 | #define VC_COMMA 0x0033 // ',' 222 | #define VC_PERIOD 0x0034 // '.' 223 | #define VC_SLASH 0x0035 // '/' 224 | 225 | #define VC_SPACE 0x0039 226 | // End Alphanumeric Zone 227 | 228 | 229 | #define VC_PRINTSCREEN 0x0E37 230 | #define VC_SCROLL_LOCK 0x0046 231 | #define VC_PAUSE 0x0E45 232 | 233 | 234 | // Begin Edit Key Zone 235 | #define VC_INSERT 0x0E52 236 | #define VC_DELETE 0x0E53 237 | #define VC_HOME 0x0E47 238 | #define VC_END 0x0E4F 239 | #define VC_PAGE_UP 0x0E49 240 | #define VC_PAGE_DOWN 0x0E51 241 | // End Edit Key Zone 242 | 243 | 244 | // Begin Cursor Key Zone 245 | #define VC_UP 0xE048 246 | #define VC_LEFT 0xE04B 247 | #define VC_CLEAR 0xE04C 248 | #define VC_RIGHT 0xE04D 249 | #define VC_DOWN 0xE050 250 | // End Cursor Key Zone 251 | 252 | 253 | // Begin Numeric Zone 254 | #define VC_NUM_LOCK 0x0045 255 | #define VC_KP_DIVIDE 0x0E35 256 | #define VC_KP_MULTIPLY 0x0037 257 | #define VC_KP_SUBTRACT 0x004A 258 | #define VC_KP_EQUALS 0x0E0D 259 | #define VC_KP_ADD 0x004E 260 | #define VC_KP_ENTER 0x0E1C 261 | #define VC_KP_SEPARATOR 0x0053 262 | 263 | #define VC_KP_1 0x004F 264 | #define VC_KP_2 0x0050 265 | #define VC_KP_3 0x0051 266 | #define VC_KP_4 0x004B 267 | #define VC_KP_5 0x004C 268 | #define VC_KP_6 0x004D 269 | #define VC_KP_7 0x0047 270 | #define VC_KP_8 0x0048 271 | #define VC_KP_9 0x0049 272 | #define VC_KP_0 0x0052 273 | 274 | #define VC_KP_END 0xEE00 | VC_KP_1 275 | #define VC_KP_DOWN 0xEE00 | VC_KP_2 276 | #define VC_KP_PAGE_DOWN 0xEE00 | VC_KP_3 277 | #define VC_KP_LEFT 0xEE00 | VC_KP_4 278 | #define VC_KP_CLEAR 0xEE00 | VC_KP_5 279 | #define VC_KP_RIGHT 0xEE00 | VC_KP_6 280 | #define VC_KP_HOME 0xEE00 | VC_KP_7 281 | #define VC_KP_UP 0xEE00 | VC_KP_8 282 | #define VC_KP_PAGE_UP 0xEE00 | VC_KP_9 283 | #define VC_KP_INSERT 0xEE00 | VC_KP_0 284 | #define VC_KP_DELETE 0xEE00 | VC_KP_SEPARATOR 285 | // End Numeric Zone 286 | 287 | 288 | // Begin Modifier and Control Keys 289 | #define VC_SHIFT_L 0x002A 290 | #define VC_SHIFT_R 0x0036 291 | #define VC_CONTROL_L 0x001D 292 | #define VC_CONTROL_R 0x0E1D 293 | #define VC_ALT_L 0x0038 // Option or Alt Key 294 | #define VC_ALT_R 0x0E38 // Option or Alt Key 295 | #define VC_META_L 0x0E5B // Windows or Command Key 296 | #define VC_META_R 0x0E5C // Windows or Command Key 297 | #define VC_CONTEXT_MENU 0x0E5D 298 | // End Modifier and Control Keys 299 | 300 | 301 | // Begin Media Control Keys 302 | #define VC_POWER 0xE05E 303 | #define VC_SLEEP 0xE05F 304 | #define VC_WAKE 0xE063 305 | 306 | #define VC_MEDIA_PLAY 0xE022 307 | #define VC_MEDIA_STOP 0xE024 308 | #define VC_MEDIA_PREVIOUS 0xE010 309 | #define VC_MEDIA_NEXT 0xE019 310 | #define VC_MEDIA_SELECT 0xE06D 311 | #define VC_MEDIA_EJECT 0xE02C 312 | 313 | #define VC_VOLUME_MUTE 0xE020 314 | #define VC_VOLUME_UP 0xE030 315 | #define VC_VOLUME_DOWN 0xE02E 316 | 317 | #define VC_APP_MAIL 0xE06C 318 | #define VC_APP_CALCULATOR 0xE021 319 | #define VC_APP_MUSIC 0xE03C 320 | #define VC_APP_PICTURES 0xE064 321 | 322 | #define VC_BROWSER_SEARCH 0xE065 323 | #define VC_BROWSER_HOME 0xE032 324 | #define VC_BROWSER_BACK 0xE06A 325 | #define VC_BROWSER_FORWARD 0xE069 326 | #define VC_BROWSER_STOP 0xE068 327 | #define VC_BROWSER_REFRESH 0xE067 328 | #define VC_BROWSER_FAVORITES 0xE066 329 | // End Media Control Keys 330 | 331 | // Begin Japanese Language Keys 332 | #define VC_KATAKANA 0x0070 333 | #define VC_UNDERSCORE 0x0073 334 | #define VC_FURIGANA 0x0077 335 | #define VC_KANJI 0x0079 336 | #define VC_HIRAGANA 0x007B 337 | #define VC_YEN 0x007D 338 | #define VC_KP_COMMA 0x007E 339 | // End Japanese Language Keys 340 | 341 | // Begin Sun keyboards 342 | #define VC_SUN_HELP 0xFF75 343 | 344 | #define VC_SUN_STOP 0xFF78 345 | #define VC_SUN_PROPS 0xFF76 346 | #define VC_SUN_FRONT 0xFF77 347 | #define VC_SUN_OPEN 0xFF74 348 | #define VC_SUN_FIND 0xFF7E 349 | #define VC_SUN_AGAIN 0xFF79 350 | #define VC_SUN_UNDO 0xFF7A 351 | #define VC_SUN_COPY 0xFF7C 352 | #define VC_SUN_INSERT 0xFF7D 353 | #define VC_SUN_CUT 0xFF7B 354 | // End Sun keyboards 355 | 356 | #define VC_UNDEFINED 0x0000 // KeyCode Unknown 357 | 358 | #define CHAR_UNDEFINED 0xFFFF // CharCode Unknown 359 | /* End Virtual Key Codes */ 360 | 361 | 362 | /* Begin Virtual Modifier Masks */ 363 | #define MASK_SHIFT_L 1 << 0 364 | #define MASK_CTRL_L 1 << 1 365 | #define MASK_META_L 1 << 2 366 | #define MASK_ALT_L 1 << 3 367 | 368 | #define MASK_SHIFT_R 1 << 4 369 | #define MASK_CTRL_R 1 << 5 370 | #define MASK_META_R 1 << 6 371 | #define MASK_ALT_R 1 << 7 372 | 373 | #define MASK_SHIFT MASK_SHIFT_L | MASK_SHIFT_R 374 | #define MASK_CTRL MASK_CTRL_L | MASK_CTRL_R 375 | #define MASK_META MASK_META_L | MASK_META_R 376 | #define MASK_ALT MASK_ALT_L | MASK_ALT_R 377 | 378 | #define MASK_BUTTON1 1 << 8 379 | #define MASK_BUTTON2 1 << 9 380 | #define MASK_BUTTON3 1 << 10 381 | #define MASK_BUTTON4 1 << 11 382 | #define MASK_BUTTON5 1 << 12 383 | 384 | #define MASK_NUM_LOCK 1 << 13 385 | #define MASK_CAPS_LOCK 1 << 14 386 | #define MASK_SCROLL_LOCK 1 << 15 387 | /* End Virtual Modifier Masks */ 388 | 389 | 390 | /* Begin Virtual Mouse Buttons */ 391 | #define MOUSE_NOBUTTON 0 // Any Button 392 | #define MOUSE_BUTTON1 1 // Left Button 393 | #define MOUSE_BUTTON2 2 // Right Button 394 | #define MOUSE_BUTTON3 3 // Middle Button 395 | #define MOUSE_BUTTON4 4 // Extra Mouse Button 396 | #define MOUSE_BUTTON5 5 // Extra Mouse Button 397 | 398 | #define WHEEL_UNIT_SCROLL 1 399 | #define WHEEL_BLOCK_SCROLL 2 400 | 401 | #define WHEEL_VERTICAL_DIRECTION 3 402 | #define WHEEL_HORIZONTAL_DIRECTION 4 403 | /* End Virtual Mouse Buttons */ 404 | 405 | 406 | #ifdef _WIN32 407 | #define UIOHOOK_API __declspec(dllexport) 408 | #else 409 | #define UIOHOOK_API 410 | #endif 411 | 412 | #ifdef __cplusplus 413 | extern "C" { 414 | #endif 415 | 416 | // Set the logger callback functions. 417 | UIOHOOK_API void hook_set_logger_proc(logger_t logger_proc); 418 | 419 | // Send a virtual event back to the system. 420 | UIOHOOK_API void hook_post_event(uiohook_event * const event); 421 | 422 | // Set the event callback function. 423 | UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc); 424 | 425 | // Insert the event hook. 426 | UIOHOOK_API int hook_run(); 427 | 428 | // Withdraw the event hook. 429 | UIOHOOK_API int hook_stop(); 430 | 431 | UIOHOOK_API void grab_mouse_click(bool enable); 432 | 433 | // Retrieves an array of screen data for each available monitor. 434 | UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count); 435 | 436 | // Retrieves the keyboard auto repeat rate. 437 | UIOHOOK_API long int hook_get_auto_repeat_rate(); 438 | 439 | // Retrieves the keyboard auto repeat delay. 440 | UIOHOOK_API long int hook_get_auto_repeat_delay(); 441 | 442 | // Retrieves the mouse acceleration multiplier. 443 | UIOHOOK_API long int hook_get_pointer_acceleration_multiplier(); 444 | 445 | // Retrieves the mouse acceleration threshold. 446 | UIOHOOK_API long int hook_get_pointer_acceleration_threshold(); 447 | 448 | // Retrieves the mouse sensitivity. 449 | UIOHOOK_API long int hook_get_pointer_sensitivity(); 450 | 451 | // Retrieves the double/triple click interval. 452 | UIOHOOK_API long int hook_get_multi_click_time(); 453 | 454 | #ifdef __cplusplus 455 | } 456 | #endif 457 | 458 | #endif 459 | -------------------------------------------------------------------------------- /libuiohook/m4/ax_pthread.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro figures out how to build C programs using POSIX threads. It 12 | # sets the PTHREAD_LIBS output variable to the threads library and linker 13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler 14 | # flags that are needed. (The user can also force certain compiler 15 | # flags/libs to be tested by setting these environment variables.) 16 | # 17 | # Also sets PTHREAD_CC to any special C compiler that is needed for 18 | # multi-threaded programs (defaults to the value of CC otherwise). (This 19 | # is necessary on AIX to use the special cc_r compiler alias.) 20 | # 21 | # NOTE: You are assumed to not only compile your program with these flags, 22 | # but also link it with them as well. e.g. you should link with 23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS 24 | # 25 | # If you are only building threads programs, you may wish to use these 26 | # variables in your default LIBS, CFLAGS, and CC: 27 | # 28 | # LIBS="$PTHREAD_LIBS $LIBS" 29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 30 | # CC="$PTHREAD_CC" 31 | # 32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant 33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name 34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). 35 | # 36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the 37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with 38 | # PTHREAD_CFLAGS. 39 | # 40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library 41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it 42 | # is not found. If ACTION-IF-FOUND is not specified, the default action 43 | # will define HAVE_PTHREAD. 44 | # 45 | # Please let the authors know if this macro fails on any platform, or if 46 | # you have any other suggestions or comments. This macro was based on work 47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help 48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by 49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also 50 | # grateful for the helpful feedback of numerous users. 51 | # 52 | # Updated for Autoconf 2.68 by Daniel Richard G. 53 | # 54 | # LICENSE 55 | # 56 | # Copyright (c) 2008 Steven G. Johnson 57 | # Copyright (c) 2011 Daniel Richard G. 58 | # 59 | # This program is free software: you can redistribute it and/or modify it 60 | # under the terms of the GNU General Public License as published by the 61 | # Free Software Foundation, either version 3 of the License, or (at your 62 | # option) any later version. 63 | # 64 | # This program is distributed in the hope that it will be useful, but 65 | # WITHOUT ANY WARRANTY; without even the implied warranty of 66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 67 | # Public License for more details. 68 | # 69 | # You should have received a copy of the GNU General Public License along 70 | # with this program. If not, see . 71 | # 72 | # As a special exception, the respective Autoconf Macro's copyright owner 73 | # gives unlimited permission to copy, distribute and modify the configure 74 | # scripts that are the output of Autoconf when processing the Macro. You 75 | # need not follow the terms of the GNU General Public License when using 76 | # or distributing such scripts, even though portions of the text of the 77 | # Macro appear in them. The GNU General Public License (GPL) does govern 78 | # all other use of the material that constitutes the Autoconf Macro. 79 | # 80 | # This special exception to the GPL applies to versions of the Autoconf 81 | # Macro released by the Autoconf Archive. When you make and distribute a 82 | # modified version of the Autoconf Macro, you may extend this special 83 | # exception to the GPL to apply to your modified version as well. 84 | 85 | #serial 20 86 | 87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) 88 | AC_DEFUN([AX_PTHREAD], [ 89 | AC_REQUIRE([AC_CANONICAL_HOST]) 90 | AC_LANG_PUSH([C]) 91 | ax_pthread_ok=no 92 | 93 | # We used to check for pthread.h first, but this fails if pthread.h 94 | # requires special compiler flags (e.g. on True64 or Sequent). 95 | # It gets checked for in the link test anyway. 96 | 97 | # First of all, check if the user has set any of the PTHREAD_LIBS, 98 | # etcetera environment variables, and if threads linking works using 99 | # them: 100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then 101 | save_CFLAGS="$CFLAGS" 102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 103 | save_LIBS="$LIBS" 104 | LIBS="$PTHREAD_LIBS $LIBS" 105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) 106 | AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) 107 | AC_MSG_RESULT($ax_pthread_ok) 108 | if test x"$ax_pthread_ok" = xno; then 109 | PTHREAD_LIBS="" 110 | PTHREAD_CFLAGS="" 111 | fi 112 | LIBS="$save_LIBS" 113 | CFLAGS="$save_CFLAGS" 114 | fi 115 | 116 | # We must check for the threads library under a number of different 117 | # names; the ordering is very important because some systems 118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the 119 | # libraries is broken (non-POSIX). 120 | 121 | # Create a list of thread flags to try. Items starting with a "-" are 122 | # C compiler flags, and other items are library names, except for "none" 123 | # which indicates that we try without any flags at all, and "pthread-config" 124 | # which is a program returning the flags for the Pth emulation library. 125 | 126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" 127 | 128 | # The ordering *is* (sometimes) important. Some notes on the 129 | # individual items follow: 130 | 131 | # pthreads: AIX (must check this before -lpthread) 132 | # none: in case threads are in libc; should be tried before -Kthread and 133 | # other compiler flags to prevent continual compiler warnings 134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) 135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) 136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) 137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) 138 | # -pthreads: Solaris/gcc 139 | # -mthreads: Mingw32/gcc, Lynx/gcc 140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it 141 | # doesn't hurt to check since this sometimes defines pthreads too; 142 | # also defines -D_REENTRANT) 143 | # ... -mt is also the pthreads flag for HP/aCC 144 | # pthread: Linux, etcetera 145 | # --thread-safe: KAI C++ 146 | # pthread-config: use pthread-config program (for GNU Pth library) 147 | 148 | case ${host_os} in 149 | solaris*) 150 | 151 | # On Solaris (at least, for some versions), libc contains stubbed 152 | # (non-functional) versions of the pthreads routines, so link-based 153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/ 154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather 155 | # a function called by this macro, so we could check for that, but 156 | # who knows whether they'll stub that too in a future libc.) So, 157 | # we'll just look for -pthreads and -lpthread first: 158 | 159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" 160 | ;; 161 | 162 | darwin*) 163 | ax_pthread_flags="-pthread $ax_pthread_flags" 164 | ;; 165 | esac 166 | 167 | if test x"$ax_pthread_ok" = xno; then 168 | for flag in $ax_pthread_flags; do 169 | 170 | case $flag in 171 | none) 172 | AC_MSG_CHECKING([whether pthreads work without any flags]) 173 | ;; 174 | 175 | -*) 176 | AC_MSG_CHECKING([whether pthreads work with $flag]) 177 | PTHREAD_CFLAGS="$flag" 178 | ;; 179 | 180 | pthread-config) 181 | AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) 182 | if test x"$ax_pthread_config" = xno; then continue; fi 183 | PTHREAD_CFLAGS="`pthread-config --cflags`" 184 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" 185 | ;; 186 | 187 | *) 188 | AC_MSG_CHECKING([for the pthreads library -l$flag]) 189 | PTHREAD_LIBS="-l$flag" 190 | ;; 191 | esac 192 | 193 | save_LIBS="$LIBS" 194 | save_CFLAGS="$CFLAGS" 195 | LIBS="$PTHREAD_LIBS $LIBS" 196 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 197 | 198 | # Check for various functions. We must include pthread.h, 199 | # since some functions may be macros. (On the Sequent, we 200 | # need a special flag -Kthread to make this header compile.) 201 | # We check for pthread_join because it is in -lpthread on IRIX 202 | # while pthread_create is in libc. We check for pthread_attr_init 203 | # due to DEC craziness with -lpthreads. We check for 204 | # pthread_cleanup_push because it is one of the few pthread 205 | # functions on Solaris that doesn't have a non-functional libc stub. 206 | # We try pthread_create on general principles. 207 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include 208 | static void routine(void *a) { a = 0; } 209 | static void *start_routine(void *a) { return a; }], 210 | [pthread_t th; pthread_attr_t attr; 211 | pthread_create(&th, 0, start_routine, 0); 212 | pthread_join(th, 0); 213 | pthread_attr_init(&attr); 214 | pthread_cleanup_push(routine, 0); 215 | pthread_cleanup_pop(0) /* ; */])], 216 | [ax_pthread_ok=yes], 217 | []) 218 | 219 | LIBS="$save_LIBS" 220 | CFLAGS="$save_CFLAGS" 221 | 222 | AC_MSG_RESULT($ax_pthread_ok) 223 | if test "x$ax_pthread_ok" = xyes; then 224 | break; 225 | fi 226 | 227 | PTHREAD_LIBS="" 228 | PTHREAD_CFLAGS="" 229 | done 230 | fi 231 | 232 | # Various other checks: 233 | if test "x$ax_pthread_ok" = xyes; then 234 | save_LIBS="$LIBS" 235 | LIBS="$PTHREAD_LIBS $LIBS" 236 | save_CFLAGS="$CFLAGS" 237 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 238 | 239 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. 240 | AC_MSG_CHECKING([for joinable pthread attribute]) 241 | attr_name=unknown 242 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do 243 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], 244 | [int attr = $attr; return attr /* ; */])], 245 | [attr_name=$attr; break], 246 | []) 247 | done 248 | AC_MSG_RESULT($attr_name) 249 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then 250 | AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, 251 | [Define to necessary symbol if this constant 252 | uses a non-standard name on your system.]) 253 | fi 254 | 255 | AC_MSG_CHECKING([if more special flags are required for pthreads]) 256 | flag=no 257 | case ${host_os} in 258 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; 259 | osf* | hpux*) flag="-D_REENTRANT";; 260 | solaris*) 261 | if test "$GCC" = "yes"; then 262 | flag="-D_REENTRANT" 263 | else 264 | flag="-mt -D_REENTRANT" 265 | fi 266 | ;; 267 | esac 268 | AC_MSG_RESULT(${flag}) 269 | if test "x$flag" != xno; then 270 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" 271 | fi 272 | 273 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], 274 | ax_cv_PTHREAD_PRIO_INHERIT, [ 275 | AC_LINK_IFELSE([ 276 | AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], 277 | [ax_cv_PTHREAD_PRIO_INHERIT=yes], 278 | [ax_cv_PTHREAD_PRIO_INHERIT=no]) 279 | ]) 280 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], 281 | AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) 282 | 283 | LIBS="$save_LIBS" 284 | CFLAGS="$save_CFLAGS" 285 | 286 | # More AIX lossage: compile with *_r variant 287 | if test "x$GCC" != xyes; then 288 | case $host_os in 289 | aix*) 290 | AS_CASE(["x/$CC"], 291 | [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], 292 | [#handle absolute path differently from PATH based program lookup 293 | AS_CASE(["x$CC"], 294 | [x/*], 295 | [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], 296 | [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) 297 | ;; 298 | esac 299 | fi 300 | fi 301 | 302 | test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" 303 | 304 | AC_SUBST(PTHREAD_LIBS) 305 | AC_SUBST(PTHREAD_CFLAGS) 306 | AC_SUBST(PTHREAD_CC) 307 | 308 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 309 | if test x"$ax_pthread_ok" = xyes; then 310 | ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) 311 | : 312 | else 313 | ax_pthread_ok=no 314 | $2 315 | fi 316 | AC_LANG_POP 317 | ])dnl AX_PTHREAD 318 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_auto_repeat_delay.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_get_auto_repeat_rate.3 2 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_auto_repeat_rate.man: -------------------------------------------------------------------------------- 1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com) 2 | .\" 3 | .\" %%%LICENSE_START(VERBATIM) 4 | .\" libUIOHook is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU Lesser General Public License as published 6 | .\" by the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" libUIOHook is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU Lesser General Public License 15 | .\" along with this program. If not, see . 16 | .\" %%%LICENSE_END 17 | .\" 18 | .TH hook_get_auto_repeat_rate 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual" 19 | .SH NAME 20 | hook_get_auto_repeat_rate, hook_get_auto_repeat_delay \- Keyboard auto\-repeat rate / delay 21 | .HP 22 | hook_get_pointer_acceleration_multiplier, hook_get_pointer_acceleration_threshold \- Pointer acceleration values 23 | .HP 24 | hook_get_pointer_sensitivity \- Pointer sensitivity 25 | .HP 26 | hook_get_multi_click_time \- Button multi-click timeout 27 | .SH SYNTAX 28 | #include 29 | .HP 30 | UIOHOOK_API long int hook_get_auto_repeat_rate\^(\fIvoid\fP\^); 31 | .HP 32 | UIOHOOK_API long int hook_get_auto_repeat_delay\^(\fIvoid\fP\^); 33 | .HP 34 | UIOHOOK_API long int hook_get_pointer_acceleration_multiplier\^(\fIvoid\fP\^); 35 | .HP 36 | UIOHOOK_API long int hook_get_pointer_acceleration_threshold\^(\fIvoid\fP\^); 37 | .HP 38 | UIOHOOK_API long int hook_get_pointer_sensitivity\^(\fIvoid\fP\^); 39 | .HP 40 | UIOHOOK_API long int hook_get_multi_click_time\^(\fIvoid\fP\^); 41 | .SH ARGUMENTS 42 | .IP \fIvoid\fP 1i 43 | .SH RETURN VALUE 44 | All functions return a value between zero and LONG_MAX inclusive for the 45 | requested property. If a system property could not be determined, due to error 46 | or omission, a value of -1 will be returned. 47 | .SH DESCRIPTION 48 | There is no guarantee that any of the above values will be available at any 49 | point during runtime. The return value should be checked for error after each 50 | call. 51 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_multi_click_time.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_get_auto_repeat_rate.3 2 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_pointer_acceleration_multiplier.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_get_auto_repeat_rate.3 2 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_pointer_acceleration_threshold.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_get_auto_repeat_rate.3 2 | -------------------------------------------------------------------------------- /libuiohook/man/hook_get_pointer_sensitivity.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_get_auto_repeat_rate.3 2 | -------------------------------------------------------------------------------- /libuiohook/man/hook_run.man: -------------------------------------------------------------------------------- 1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com) 2 | .\" 3 | .\" %%%LICENSE_START(VERBATIM) 4 | .\" libUIOHook is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU Lesser General Public License as published 6 | .\" by the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" libUIOHook is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU Lesser General Public License 15 | .\" along with this program. If not, see . 16 | .\" %%%LICENSE_END 17 | .\" 18 | .TH hook_run 3 "12 Dec 2014" "Version 1.0" "libUIOHook Programmer's Manual" 19 | .SH NAME 20 | hook_run, hook_disable \- Insert / Withdraw the native event hook 21 | .SH SYNTAX 22 | #include 23 | .HP 24 | UIOHOOK_API int hook_run\^(\fIvoid\fP\^); 25 | .HP 26 | UIOHOOK_API int hook_stop\^(\fIvoid\fP\^); 27 | .SH ARGUMENTS 28 | .IP \fIvoid\fP 1i 29 | 30 | .SH RETURN VALUE 31 | .IP \fIUIOHOOK_SUCCESS\fP li 32 | Returned on success. 33 | .IP \fIUIOHOOK_FAILURE\fP li 34 | General failure status. 35 | .IP \fIUIOHOOK_ERROR_OUT_OF_MEMORY\fP li 36 | Out of system memory. 37 | 38 | .IP \fIUIOHOOK_ERROR_X_OPEN_DISPLAY\fP li 39 | X11 specific error for XOpenDisplay\^(\^) failures. 40 | .IP \fIUIOHOOK_ERROR_X_RECORD_NOT_FOUND\fP li 41 | X11 specific error if XRecord is unavailable. 42 | .IP \fIUIOHOOK_ERROR_X_RECORD_ALLOC_RANGE\fP li 43 | X11 specific error for XRecordAllocRange\^(\^) failures. 44 | .IP \fIUIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT\fP li 45 | X11 specific error for XRecordCreateContext\^(...\^) failures. 46 | .IP \fIUIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT\fP li 47 | X11 specific error for XRecordEnableContext\^(...\^) failures. 48 | 49 | .IP \fIUIOHOOK_ERROR_SET_WINDOWS_HOOK_EX\fP li 50 | Windows specific error for SetWindowsHookEx\^(...\^) failures. 51 | 52 | .IP \fIUIOHOOK_ERROR_AXAPI_DISABLED\fP li 53 | Darwin specific error if the Accessibility API for the calling application has not been enabled. 54 | .IP \fIUIOHOOK_ERROR_EVENT_PORT\fP li 55 | Darwin specfici error for CGEventTapCreate\^(...\^) failures. 56 | .IP \fIUIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE\fP li 57 | Darwin specfici error for CFMachPortCreateRunLoopSource\^(...\^) failures. 58 | .IP \fIUIOHOOK_ERROR_GET_RUNLOOP\fP li 59 | Darwin specfici error for CFRunLoopGetCurrent\^(\^) failures. 60 | .IP \fIUIOHOOK_ERROR_OBSERVER_CREATE\fP li 61 | Darwin specfici error for CFRunLoopObserverCreate\^(...\^) failures. 62 | 63 | .SH DESCRIPTION 64 | hook_run\^(\^) will block until either the hook has completed or an error 65 | occurred during the hook registration process. You may assume successful 66 | completion of hook_run() after receiving an event of EVENT_HOOK_ENABLED. 67 | 68 | The hook_stop\^(\^) function is asynchronous, and will only signal the running 69 | hook to stop. This function will return an error if signaling was not possible. 70 | -------------------------------------------------------------------------------- /libuiohook/man/hook_set_dispatch_proc.man: -------------------------------------------------------------------------------- 1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com) 2 | .\" 3 | .\" %%%LICENSE_START(VERBATIM) 4 | .\" libUIOHook is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU Lesser General Public License as published 6 | .\" by the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" libUIOHook is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU Lesser General Public License 15 | .\" along with this program. If not, see . 16 | .\" %%%LICENSE_END 17 | .\" 18 | .TH hook_set_dispatch_proc 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual" 19 | .SH NAME 20 | hook_set_dispatch_proc \- Set the event callback function 21 | .SH SYNTAX 22 | #include 23 | .HP 24 | void dispatch_proc\^(\fIuiohook_event * const event\fP\^) { 25 | ... 26 | } 27 | .HP 28 | hook_set_dispatch_proc(&dispatch_proc); 29 | 30 | .SH ARGUMENTS 31 | .IP \fIdispatcher_t\fP 1i 32 | A function pointer to a matching dispatcher_t function. 33 | .SH RETURN VALUE 34 | .IP \fIvoid\fP li 35 | 36 | .SH DESCRIPTION 37 | Passing NULL to void dispatch_proc\^(\^) will remove the currently set callback. 38 | -------------------------------------------------------------------------------- /libuiohook/man/hook_set_logger_proc.man: -------------------------------------------------------------------------------- 1 | .\" Copyright 2006-2017 Alexander Barker (alex@1stleg.com) 2 | .\" 3 | .\" %%%LICENSE_START(VERBATIM) 4 | .\" libUIOHook is free software: you can redistribute it and/or modify 5 | .\" it under the terms of the GNU Lesser General Public License as published 6 | .\" by the Free Software Foundation, either version 3 of the License, or 7 | .\" (at your option) any later version. 8 | .\" 9 | .\" libUIOHook is distributed in the hope that it will be useful, 10 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | .\" GNU General Public License for more details. 13 | .\" 14 | .\" You should have received a copy of the GNU Lesser General Public License 15 | .\" along with this program. If not, see . 16 | .\" %%%LICENSE_END 17 | .\" 18 | .TH hook_set_logger_proc 3 "07 July 2014" "Version 1.0" "libUIOHook Programmer's Manual" 19 | .SH NAME 20 | hook_set_logger_proc \- Set the event callback function 21 | .SH SYNTAX 22 | #include 23 | .HP 24 | void logger_proc\^(\fIunsigned int level, const char *format, ...\fP\^) { 25 | ... 26 | } 27 | .HP 28 | hook_set_logger_proc(&logger_proc); 29 | 30 | .SH ARGUMENTS 31 | .IP \fIlogger_t\fP 1i 32 | A function pointer to a matching logger_t function. 33 | .SH RETURN VALUE 34 | .IP \fIvoid\fP li 35 | 36 | .SH DESCRIPTION 37 | Passing NULL to void dispatch_proc\^(\^) will remove the currently set callback. 38 | -------------------------------------------------------------------------------- /libuiohook/man/hook_stop.man: -------------------------------------------------------------------------------- 1 | .so man3/hook_run.3 2 | -------------------------------------------------------------------------------- /libuiohook/pc/uiohook.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | libdir=${prefix}/lib 3 | includedir=${prefix}/include 4 | 5 | Name: uiohook 6 | Description: uiohook library 7 | Version: @VERSION@ 8 | Libs: @LIBS@ -L${libdir} -luiohook 9 | Cflags: -I${includedir} 10 | Requires: @REQUIRE@ 11 | -------------------------------------------------------------------------------- /libuiohook/src/darwin/input_helper.h: -------------------------------------------------------------------------------- 1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | * https://github.com/kwhat/libuiohook/ 4 | * 5 | * libUIOHook is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * libUIOHook is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | // Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png 20 | // Reference: https://svn.blender.org/svnroot/bf-blender/branches/render25/intern/ghost/intern/GHOST_SystemCocoa.mm 21 | // Reference: http://www.mactech.com/macintosh-c/chap02-1.html 22 | 23 | #ifndef _included_input_helper 24 | #define _included_input_helper 25 | 26 | #include 27 | #include // For HIToolbox kVK_ keycodes and TIS funcitons. 28 | #ifdef USE_IOKIT 29 | #include 30 | #endif 31 | #include 32 | 33 | 34 | #ifndef USE_IOKIT 35 | // Some of the system key codes that are needed from IOKit. 36 | #define NX_KEYTYPE_SOUND_UP 0x00 37 | #define NX_KEYTYPE_SOUND_DOWN 0x01 38 | #define NX_KEYTYPE_MUTE 0x07 39 | 40 | /* Display controls... 41 | #define NX_KEYTYPE_BRIGHTNESS_UP 0x02 42 | #define NX_KEYTYPE_BRIGHTNESS_DOWN 0x03 43 | #define NX_KEYTYPE_CONTRAST_UP 0x0B 44 | #define NX_KEYTYPE_CONTRAST_DOWN 0x0C 45 | #define NX_KEYTYPE_ILLUMINATION_UP 0x15 46 | #define NX_KEYTYPE_ILLUMINATION_DOWN 0x16 47 | #define NX_KEYTYPE_ILLUMINATION_TOGGLE 0x17 48 | */ 49 | 50 | #define NX_KEYTYPE_CAPS_LOCK 0x04 51 | //#define NX_KEYTYPE_HELP 0x05 52 | #define NX_POWER_KEY 0x06 53 | 54 | #define NX_KEYTYPE_EJECT 0x0E 55 | #define NX_KEYTYPE_PLAY 0x10 56 | #define NX_KEYTYPE_NEXT 0x12 57 | #define NX_KEYTYPE_PREVIOUS 0x13 58 | 59 | /* There is no official fast-forward or rewind scan code support. 60 | #define NX_KEYTYPE_FAST 0x14 61 | #define NX_KEYTYPE_REWIND 0x15 62 | */ 63 | #endif 64 | 65 | // These virtual key codes do not appear to be defined anywhere by Apple. 66 | #define kVK_NX_Power 0xE0 | NX_POWER_KEY /* 0xE6 */ 67 | #define kVK_NX_Eject 0xE0 | NX_KEYTYPE_EJECT /* 0xEE */ 68 | 69 | #define kVK_MEDIA_Play 0xE0 | NX_KEYTYPE_PLAY /* 0xF0 */ 70 | #define kVK_MEDIA_Next 0xE0 | NX_KEYTYPE_NEXT /* 0xF1 */ 71 | #define kVK_MEDIA_Previous 0xE0 | NX_KEYTYPE_PREVIOUS /* 0xF2 */ 72 | 73 | #define kVK_RightCommand 0x36 74 | #define kVK_ContextMenu 0x6E // AKA kMenuPowerGlyph 75 | #define kVK_Undefined 0xFF 76 | 77 | // These button codes do not appear to be defined anywhere by Apple. 78 | #define kVK_LBUTTON kCGMouseButtonLeft 79 | #define kVK_RBUTTON kCGMouseButtonRight 80 | #define kVK_MBUTTON kCGMouseButtonCenter 81 | #define kVK_XBUTTON1 3 82 | #define kVK_XBUTTON2 4 83 | 84 | // These button masks do not appear to be defined anywhere by Apple. 85 | #define kCGEventFlagMaskButtonLeft 1 << 0 86 | #define kCGEventFlagMaskButtonRight 1 << 1 87 | #define kCGEventFlagMaskButtonCenter 1 << 2 88 | #define kCGEventFlagMaskXButton1 1 << 3 89 | #define kCGEventFlagMaskXButton2 1 << 4 90 | 91 | 92 | /* Check for access to Apples accessibility API. 93 | */ 94 | extern bool is_accessibility_enabled(); 95 | 96 | /* Converts an OSX key code and event mask to the appropriate Unicode character 97 | * representation. 98 | */ 99 | extern UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size); 100 | 101 | /* Converts an OSX keycode to the appropriate UIOHook scancode constant. 102 | */ 103 | extern uint16_t keycode_to_scancode(UInt64 keycode); 104 | 105 | /* Converts a UIOHook scancode constant to the appropriate OSX keycode. 106 | */ 107 | extern UInt64 scancode_to_keycode(uint16_t keycode); 108 | 109 | 110 | /* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode() 111 | * functionality. This method is called by OnLibraryLoad() and may need to be 112 | * called in combination with UnloadInputHelper() if the native keyboard layout 113 | * is changed. 114 | */ 115 | extern void load_input_helper(); 116 | 117 | /* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode() 118 | * functionality. This method is called by OnLibraryUnload() and may need to be 119 | * called in combination with LoadInputHelper() if the native keyboard layout 120 | * is changed. 121 | */ 122 | extern void unload_input_helper(); 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /libuiohook/src/darwin/post_event.c: -------------------------------------------------------------------------------- 1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | * https://github.com/kwhat/libuiohook/ 4 | * 5 | * libUIOHook is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * libUIOHook is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "input_helper.h" 30 | #include "logger.h" 31 | 32 | // TODO Possibly relocate to input helper. 33 | static inline CGEventFlags get_key_event_mask(uiohook_event * const event) { 34 | CGEventFlags native_mask = 0x00; 35 | 36 | if (event->mask & (MASK_SHIFT)) { native_mask |= kCGEventFlagMaskShift; } 37 | if (event->mask & (MASK_CTRL)) { native_mask |= kCGEventFlagMaskControl; } 38 | if (event->mask & (MASK_META)) { native_mask |= kCGEventFlagMaskControl; } 39 | if (event->mask & (MASK_ALT)) { native_mask |= kCGEventFlagMaskAlternate; } 40 | 41 | if (event->type == EVENT_KEY_PRESSED || event->type == EVENT_KEY_RELEASED || event->type == EVENT_KEY_TYPED) { 42 | switch (event->data.keyboard.keycode) { 43 | case VC_KP_0: 44 | case VC_KP_1: 45 | case VC_KP_2: 46 | case VC_KP_3: 47 | case VC_KP_4: 48 | case VC_KP_5: 49 | case VC_KP_6: 50 | case VC_KP_7: 51 | case VC_KP_8: 52 | case VC_KP_9: 53 | 54 | case VC_NUM_LOCK: 55 | case VC_KP_ENTER: 56 | case VC_KP_MULTIPLY: 57 | case VC_KP_ADD: 58 | case VC_KP_SEPARATOR: 59 | case VC_KP_SUBTRACT: 60 | case VC_KP_DIVIDE: 61 | case VC_KP_COMMA: 62 | native_mask |= kCGEventFlagMaskNumericPad; 63 | break; 64 | } 65 | } 66 | 67 | return native_mask; 68 | } 69 | 70 | static inline void post_key_event(uiohook_event * const event) { 71 | bool is_pressed = event->type == EVENT_KEY_PRESSED; 72 | 73 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 74 | CGEventRef cg_event = CGEventCreateKeyboardEvent(src, 75 | (CGKeyCode) scancode_to_keycode(event->data.keyboard.keycode), 76 | is_pressed); 77 | 78 | CGEventSetFlags(cg_event, get_key_event_mask(event)); 79 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works. 80 | CFRelease(cg_event); 81 | CFRelease(src); 82 | } 83 | 84 | static inline void post_mouse_button_event(uiohook_event * const event, bool is_pressed) { 85 | CGMouseButton mouse_button; 86 | CGEventType mouse_type; 87 | if (event->data.mouse.button == MOUSE_BUTTON1) { 88 | if (is_pressed) { 89 | mouse_type = kCGEventLeftMouseDown; 90 | } 91 | else { 92 | mouse_type = kCGEventLeftMouseUp; 93 | } 94 | mouse_button = kCGMouseButtonLeft; 95 | } 96 | else if (event->data.mouse.button == MOUSE_BUTTON2) { 97 | if (is_pressed) { 98 | mouse_type = kCGEventRightMouseDown; 99 | } 100 | else { 101 | mouse_type = kCGEventRightMouseUp; 102 | } 103 | mouse_button = kCGMouseButtonRight; 104 | } 105 | else { 106 | if (is_pressed) { 107 | mouse_type = kCGEventOtherMouseDown; 108 | } 109 | else { 110 | mouse_type = kCGEventOtherMouseUp; 111 | } 112 | mouse_button = event->data.mouse.button - 1; 113 | } 114 | 115 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 116 | CGEventRef cg_event = CGEventCreateMouseEvent(src, 117 | mouse_type, 118 | CGPointMake( 119 | (CGFloat) event->data.mouse.x, 120 | (CGFloat) event->data.mouse.y 121 | ), 122 | mouse_button 123 | ); 124 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works. 125 | CFRelease(cg_event); 126 | CFRelease(src); 127 | } 128 | 129 | static inline void post_mouse_wheel_event(uiohook_event * const event) { 130 | // FIXME Should I create a source event with the coords? 131 | // It seems to automagically use the current location of the cursor. 132 | // Two options: Query the mouse, move it to x/y, scroll, then move back 133 | // OR disable x/y for scroll events on Windows & X11. 134 | CGScrollEventUnit scroll_unit; 135 | if (event->data.wheel.type == WHEEL_BLOCK_SCROLL) { 136 | // Scrolling data is line-based. 137 | scroll_unit = kCGScrollEventUnitLine; 138 | } 139 | else { 140 | // Scrolling data is pixel-based. 141 | scroll_unit = kCGScrollEventUnitPixel; 142 | } 143 | 144 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 145 | CGEventRef cg_event = CGEventCreateScrollWheelEvent(src, 146 | kCGScrollEventUnitLine, 147 | // TODO Currently only support 1 wheel axis. 148 | (CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z 149 | event->data.wheel.amount * event->data.wheel.rotation); 150 | 151 | CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works. 152 | CFRelease(cg_event); 153 | CFRelease(src); 154 | } 155 | 156 | static inline void post_mouse_motion_event(uiohook_event * const event) { 157 | CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 158 | CGEventRef cg_event; 159 | if (event->mask >> 8 == 0x00) { 160 | // No mouse flags. 161 | cg_event = CGEventCreateMouseEvent(src, 162 | kCGEventMouseMoved, 163 | CGPointMake( 164 | (CGFloat) event->data.mouse.x, 165 | (CGFloat) event->data.mouse.y 166 | ), 167 | 0 168 | ); 169 | } 170 | else if (event->mask & MASK_BUTTON1) { 171 | cg_event = CGEventCreateMouseEvent(src, 172 | kCGEventLeftMouseDragged, 173 | CGPointMake( 174 | (CGFloat) event->data.mouse.x, 175 | (CGFloat) event->data.mouse.y 176 | ), 177 | kCGMouseButtonLeft 178 | ); 179 | } 180 | else if (event->mask & MASK_BUTTON2) { 181 | cg_event = CGEventCreateMouseEvent(src, 182 | kCGEventRightMouseDragged, 183 | CGPointMake( 184 | (CGFloat) event->data.mouse.x, 185 | (CGFloat) event->data.mouse.y 186 | ), 187 | kCGMouseButtonRight 188 | ); 189 | } 190 | else { 191 | cg_event = CGEventCreateMouseEvent(src, 192 | kCGEventOtherMouseDragged, 193 | CGPointMake( 194 | (CGFloat) event->data.mouse.x, 195 | (CGFloat) event->data.mouse.y 196 | ), 197 | (event->mask >> 8) - 1 198 | ); 199 | } 200 | 201 | // kCGSessionEventTap also works. 202 | CGEventPost(kCGHIDEventTap, cg_event); 203 | CFRelease(cg_event); 204 | CFRelease(src); 205 | } 206 | 207 | UIOHOOK_API void hook_post_event(uiohook_event * const event) { 208 | switch (event->type) { 209 | case EVENT_KEY_PRESSED: 210 | case EVENT_KEY_RELEASED: 211 | post_key_event(event); 212 | break; 213 | 214 | 215 | case EVENT_MOUSE_PRESSED: 216 | post_mouse_button_event(event, true); 217 | break; 218 | 219 | case EVENT_MOUSE_RELEASED: 220 | post_mouse_button_event(event, false); 221 | break; 222 | 223 | case EVENT_MOUSE_CLICKED: 224 | post_mouse_button_event(event, true); 225 | post_mouse_button_event(event, false); 226 | break; 227 | 228 | case EVENT_MOUSE_WHEEL: 229 | post_mouse_wheel_event(event); 230 | break; 231 | 232 | 233 | case EVENT_MOUSE_MOVED: 234 | case EVENT_MOUSE_DRAGGED: 235 | post_mouse_motion_event(event); 236 | break; 237 | 238 | 239 | case EVENT_KEY_TYPED: 240 | // FIXME Ignoreing EVENT_KEY_TYPED events. 241 | 242 | case EVENT_HOOK_ENABLED: 243 | case EVENT_HOOK_DISABLED: 244 | // Ignore hook enabled / disabled events. 245 | 246 | default: 247 | // Ignore any other garbage. 248 | logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", 249 | __FUNCTION__, __LINE__, event->type); 250 | break; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /libuiohook/src/demo_hook.c: -------------------------------------------------------------------------------- 1 | /* libUIOHook: Cross-platfrom userland keyboard and mouse hooking. 2 | * Copyright (C) 2006-2017 Alexander Barker. All Rights Received. 3 | * https://github.com/kwhat/libuiohook/ 4 | * 5 | * libUIOHook is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * libUIOHook is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | bool logger_proc(unsigned int level, const char *format, ...) { 31 | bool status = false; 32 | 33 | va_list args; 34 | switch (level) { 35 | #ifdef USE_DEBUG 36 | case LOG_LEVEL_DEBUG: 37 | case LOG_LEVEL_INFO: 38 | va_start(args, format); 39 | status = vfprintf(stdout, format, args) >= 0; 40 | va_end(args); 41 | break; 42 | #endif 43 | 44 | case LOG_LEVEL_WARN: 45 | case LOG_LEVEL_ERROR: 46 | va_start(args, format); 47 | status = vfprintf(stderr, format, args) >= 0; 48 | va_end(args); 49 | break; 50 | } 51 | 52 | return status; 53 | } 54 | 55 | // NOTE: The following callback executes on the same thread that hook_run() is called 56 | // from. This is important because hook_run() attaches to the operating systems 57 | // event dispatcher and may delay event delivery to the target application. 58 | // Furthermore, some operating systems may choose to disable your hook if it 59 | // takes to long to process. If you need to do any extended processing, please 60 | // do so by copying the event to your own queued dispatch thread. 61 | void dispatch_proc(uiohook_event * const event) { 62 | char buffer[256] = { 0 }; 63 | size_t length = snprintf(buffer, sizeof(buffer), 64 | "id=%i,when=%" PRIu64 ",mask=0x%X", 65 | event->type, event->time, event->mask); 66 | 67 | switch (event->type) { 68 | case EVENT_KEY_PRESSED: 69 | // If the escape key is pressed, naturally terminate the program. 70 | if (event->data.keyboard.keycode == VC_ESCAPE) { 71 | int status = hook_stop(); 72 | switch (status) { 73 | // System level errors. 74 | case UIOHOOK_ERROR_OUT_OF_MEMORY: 75 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); 76 | break; 77 | 78 | case UIOHOOK_ERROR_X_RECORD_GET_CONTEXT: 79 | // NOTE This is the only platform specific error that occurs on hook_stop(). 80 | logger_proc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); 81 | break; 82 | 83 | // Default error. 84 | case UIOHOOK_FAILURE: 85 | default: 86 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); 87 | break; 88 | } 89 | } 90 | case EVENT_KEY_RELEASED: 91 | snprintf(buffer + length, sizeof(buffer) - length, 92 | ",keycode=%u,rawcode=0x%X", 93 | event->data.keyboard.keycode, event->data.keyboard.rawcode); 94 | break; 95 | 96 | case EVENT_KEY_TYPED: 97 | snprintf(buffer + length, sizeof(buffer) - length, 98 | ",keychar=%lc,rawcode=%u", 99 | (wint_t) event->data.keyboard.keychar, 100 | event->data.keyboard.rawcode); 101 | break; 102 | 103 | case EVENT_MOUSE_PRESSED: 104 | case EVENT_MOUSE_RELEASED: 105 | case EVENT_MOUSE_CLICKED: 106 | case EVENT_MOUSE_MOVED: 107 | case EVENT_MOUSE_DRAGGED: 108 | snprintf(buffer + length, sizeof(buffer) - length, 109 | ",x=%i,y=%i,button=%i,clicks=%i", 110 | event->data.mouse.x, event->data.mouse.y, 111 | event->data.mouse.button, event->data.mouse.clicks); 112 | break; 113 | 114 | case EVENT_MOUSE_WHEEL: 115 | snprintf(buffer + length, sizeof(buffer) - length, 116 | ",type=%i,amount=%i,rotation=%i", 117 | event->data.wheel.type, event->data.wheel.amount, 118 | event->data.wheel.rotation); 119 | break; 120 | 121 | default: 122 | break; 123 | } 124 | 125 | fprintf(stdout, "%s\n", buffer); 126 | } 127 | 128 | int main() { 129 | // Set the logger callback for library output. 130 | hook_set_logger_proc(&logger_proc); 131 | 132 | // Set the event callback for uiohook events. 133 | hook_set_dispatch_proc(&dispatch_proc); 134 | 135 | // Start the hook and block. 136 | // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed. 137 | int status = hook_run(); 138 | switch (status) { 139 | case UIOHOOK_SUCCESS: 140 | // Everything is ok. 141 | break; 142 | 143 | // System level errors. 144 | case UIOHOOK_ERROR_OUT_OF_MEMORY: 145 | logger_proc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); 146 | break; 147 | 148 | 149 | // X11 specific errors. 150 | case UIOHOOK_ERROR_X_OPEN_DISPLAY: 151 | logger_proc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status); 152 | break; 153 | 154 | case UIOHOOK_ERROR_X_RECORD_NOT_FOUND: 155 | logger_proc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status); 156 | break; 157 | 158 | case UIOHOOK_ERROR_X_RECORD_ALLOC_RANGE: 159 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status); 160 | break; 161 | 162 | case UIOHOOK_ERROR_X_RECORD_CREATE_CONTEXT: 163 | logger_proc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status); 164 | break; 165 | 166 | case UIOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT: 167 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status); 168 | break; 169 | 170 | 171 | // Windows specific errors. 172 | case UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX: 173 | logger_proc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status); 174 | break; 175 | 176 | 177 | // Darwin specific errors. 178 | case UIOHOOK_ERROR_AXAPI_DISABLED: 179 | logger_proc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status); 180 | break; 181 | 182 | case UIOHOOK_ERROR_CREATE_EVENT_PORT: 183 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status); 184 | break; 185 | 186 | case UIOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE: 187 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status); 188 | break; 189 | 190 | case UIOHOOK_ERROR_GET_RUNLOOP: 191 | logger_proc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status); 192 | break; 193 | 194 | case UIOHOOK_ERROR_CREATE_OBSERVER: 195 | logger_proc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status); 196 | break; 197 | 198 | // Default error. 199 | case UIOHOOK_FAILURE: 200 | default: 201 | logger_proc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); 202 | break; 203 | } 204 | 205 | return status; 206 | } 207 | -------------------------------------------------------------------------------- /libuiohook/src/demo_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 | for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) { 340 | if (event->mask & 1 << i) { 341 | XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), True, 0); 342 | } 343 | } 344 | 345 | for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) { 346 | if (event->mask & btnmask_lookup[i]) { 347 | XTestFakeButtonEvent(properties_disp, i + 1, True, 0); 348 | } 349 | } 350 | #endif 351 | 352 | switch (event->type) { 353 | case EVENT_KEY_PRESSED: 354 | case EVENT_KEY_RELEASED: 355 | case EVENT_KEY_TYPED: 356 | post_key_event(event); 357 | break; 358 | 359 | case EVENT_MOUSE_PRESSED: 360 | case EVENT_MOUSE_RELEASED: 361 | case EVENT_MOUSE_WHEEL: 362 | case EVENT_MOUSE_CLICKED: 363 | post_mouse_button_event(event); 364 | break; 365 | 366 | case EVENT_MOUSE_DRAGGED: 367 | case EVENT_MOUSE_MOVED: 368 | post_mouse_motion_event(event); 369 | break; 370 | 371 | case EVENT_HOOK_ENABLED: 372 | case EVENT_HOOK_DISABLED: 373 | // Ignore hook enabled / disabled events. 374 | 375 | default: 376 | // Ignore any other garbage. 377 | logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n", 378 | __FUNCTION__, __LINE__, event->type); 379 | break; 380 | } 381 | 382 | #ifdef USE_XTEST 383 | // Release the previously held modifier keys used to fake the event mask. 384 | for (unsigned int i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) { 385 | if (event->mask & 1 << i) { 386 | XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), False, 0); 387 | } 388 | } 389 | 390 | for (unsigned int i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) { 391 | if (event->mask & btnmask_lookup[i]) { 392 | XTestFakeButtonEvent(properties_disp, i + 1, False, 0); 393 | } 394 | } 395 | #endif 396 | 397 | // Don't forget to flush! 398 | XSync(properties_disp, True); 399 | XUnlockDisplay(properties_disp); 400 | } 401 | -------------------------------------------------------------------------------- /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": "switch", 3 | "version": "0.0.4", 4 | "description": "Switch between your favourite apps lightning fast (Windows, MacOS)", 5 | "main": "build/switch.js", 6 | "scripts": { 7 | "watch-ts": "tsc -w", 8 | "dev": "concurrently \"npm:watch-ts\" \"nodemon ./build/switch.js --dev\"", 9 | "app": "concurrently \"npm:watch-ts\" \"nodemon ./build/switch.js\"", 10 | "start": "concurrently \"npm:watch-ts\" \"nodemon ./build/switch.js\"", 11 | "build-scripts": "cd ./node_modules/node-window-manager && npm i && npm run build && cd ../../ && tsc", 12 | "build-win32": "npm run build-scripts && pkg . --output bin/switch --targets node10-win-x64 && node post-build.js", 13 | "build-darwin": "npm run build-scripts && pkg . --output bin/switch --targets node10-macos-x64 && node post-build.js", 14 | "build-linux": "npm run build-scripts && pkg . --output bin/switch --targets node10-linux-x64 && node post-build.js" 15 | }, 16 | "bin": "build/switch.js", 17 | "author": "Victor Aremu", 18 | "license": "", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/ahkohd/switch.git" 22 | }, 23 | "dependencies": { 24 | "@sentry/node": "^5.6.2", 25 | "conf": "^5.0.0", 26 | "fs-copy-file-sync": "^1.1.1", 27 | "iohook": "^0.6.1", 28 | "node-ipc": "^9.1.1", 29 | "node-notifier": "^5.4.1", 30 | "node-window-manager": "git+https://github.com/sentialx/node-window-manager.git", 31 | "open": "^6.4.0", 32 | "string-similarity": "^3.0.0", 33 | "zip-folder": "^1.0.0" 34 | }, 35 | "devDependencies": { 36 | "concurrently": "^4.1.1", 37 | "nodemon": "^1.19.1", 38 | "pkg": "^4.4.0", 39 | "typescript": "^3.5.3" 40 | }, 41 | "iohook": { 42 | "targets": [ 43 | "node-64" 44 | ], 45 | "platforms": [ 46 | "win32", 47 | "darwin" 48 | ], 49 | "arches": [ 50 | "x64" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /post-build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const zipFolder = require('zip-folder'); 3 | const copyFileSync = require('fs-copy-file-sync'); 4 | 5 | console.log("SWITCH SERVICE POST BUILD"); 6 | // create bin folder if does not exists. 7 | console.log('[info]: Checking ./bin'); 8 | if (!fs.existsSync('./bin')) { 9 | console.log('[info]: ./bin not found!'); 10 | console.log('[info]: Created ./bin'); 11 | fs.mkdirSync('./bin'); 12 | } 13 | 14 | // create bin/notifier folder if does not exists. 15 | console.log('[info]: Checking ./bin/notifier'); 16 | if (!fs.existsSync('./bin/notifier')) { 17 | console.log('[info]: ./bin/notifier not found!'); 18 | console.log('[info]: Created ./bin/notifier'); 19 | fs.mkdirSync('./bin/notifier'); 20 | } 21 | 22 | let list; 23 | 24 | // iohook platform specific for mac and windows. 25 | if(process.platform == 'darwin') 26 | { 27 | // 1. terminal-notifier 28 | // 2. addon.node - node window manager 29 | // 3. iohook.node 30 | list = [{ 31 | from: './node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier', 32 | to: 'bin/notifier/terminal-notifier' 33 | }, { 34 | from: './node_modules/node-window-manager/build/Release/addon.node', 35 | to: 'bin/addon.node' 36 | },{ 37 | from: `./node_modules/iohook/build/Release/iohook.node`, 38 | to: 'bin/iohook.node' 39 | }]; 40 | } else if(process.platform == 'win32') 41 | { 42 | // 1. notifu 43 | // 2. notifu64 44 | // 3. SnoreToast 45 | // 4. iohook.node 46 | // 5. uiohook.dll 47 | // 6. addon.node - node window manager 48 | 49 | list = [{ 50 | from: './node_modules/node-notifier/vendor/notifu/notifu.exe', 51 | to: 'bin/notifier/notifu.exe' 52 | }, 53 | { 54 | from: './node_modules/node-notifier/vendor/notifu/notifu64.exe', 55 | to: 'bin/notifier/notifu64.exe' 56 | }, 57 | { 58 | from: './node_modules/node-notifier/vendor/snoreToast/SnoreToast.exe', 59 | to: 'bin/notifier/SnoreToast.exe' 60 | },{ 61 | from: `./node_modules/iohook/builds/node-v64-win32-x64/build/Release/iohook.node`, 62 | to: 'bin/iohook.node' 63 | },{ 64 | from: `./node_modules/iohook/builds/node-v64-win32-x64/build/Release/uiohook.dll`, 65 | to: 'bin/uiohook.dll' 66 | }, { 67 | from: './node_modules/node-window-manager/build/Release/addon.node', 68 | to: 'bin/addon.node' 69 | }]; 70 | } 71 | 72 | list.forEach(path => { 73 | if (fs.existsSync(path.from)) { 74 | console.log(`[info]: Copy ${path.from}`); 75 | copyFileSync(path.from, path.to); 76 | console.log(`[info]: Copied ${path.to}`); 77 | } 78 | }); 79 | 80 | const getVersion = JSON.parse(fs.readFileSync('./package.json')).version; 81 | zipFolder('./bin', `./switch_deamon_v${getVersion}_${process.platform}_release.zip`, function (err) { 82 | if (err) { 83 | console.log('[info]: Packing failed', err); 84 | } else { 85 | console.log('[success]: BUILD SUCCEED!'); 86 | } 87 | }); -------------------------------------------------------------------------------- /src/Interprocess.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | import { SwitchHotApp, ProcessMessage } from "./interfaces"; 3 | import { Switch } from './enums'; 4 | import { switchLog, checkDevMode } from './utils'; 5 | 6 | const log = switchLog.bind({ isDevMode: checkDevMode() }); 7 | const ipc = require('node-ipc'); 8 | let socket; 9 | 10 | export class InterProcessChannel { 11 | emitter: EventEmitter; 12 | 13 | constructor() { 14 | this.emitter = new EventEmitter(); 15 | // kick start communication channel 16 | this.kickstart(); 17 | // start server.. 18 | log(Switch.LOG_INFO, 'Started IPC channel'); 19 | ipc.server.start(); 20 | } 21 | 22 | /** 23 | * Starts the IPC and listens to incoming events 24 | */ 25 | kickstart() { 26 | // Config IPC 27 | ipc.config.id = 'switch-service-channel'; 28 | ipc.config.retry = 1500; 29 | ipc.config.silent = true; 30 | 31 | // Serve, then listen to incoming events 32 | ipc.serve(() => ipc.server.on('switch-service-incoming', (message, _socket) => { 33 | socket = _socket; 34 | const msg: ProcessMessage = JSON.parse(message); 35 | switch (msg.type) { 36 | case 'update-hot-apps': 37 | this.emitter.emit('update-hot-apps', msg.data) 38 | break; 39 | case 'client-pid': 40 | this.emitter.emit('client-pid', msg.data); 41 | break; 42 | case 'config-update': 43 | this.emitter.emit('config-update', msg.data); 44 | // send config update to client.. 45 | this.sendConfigUpdateToDockClient(msg.data); 46 | break; 47 | case 'show-dock': 48 | console.log('[info] Show dock'); 49 | this.sendShowClient(); 50 | break; 51 | } 52 | })); 53 | } 54 | 55 | /** 56 | * Sends client-show event to the client(dock) 57 | */ 58 | sendShowClient() { 59 | try { 60 | ipc.server.emit(socket, 'client-show', { show: true }); 61 | } catch (e) { } 62 | } 63 | 64 | /** 65 | * Sends client-update event to the client(dock) 66 | */ 67 | sendConfigUpdateToDockClient(update) { 68 | try { 69 | ipc.server.emit(socket, 'config-update', update); 70 | } catch (e) { } 71 | } 72 | 73 | /** 74 | * Sends last-switched-app event to the client(dock) 75 | */ 76 | sendlastSwitched(app: SwitchHotApp) { 77 | try { 78 | ipc.server.emit(socket, 'last-switched-app', { hotApp: app }); 79 | } catch (e) { } 80 | } 81 | } -------------------------------------------------------------------------------- /src/enums.ts: -------------------------------------------------------------------------------- 1 | export enum Switch 2 | { 3 | VERSION = '0.0.1', 4 | ERROR_NOTI = 0, 5 | INFO_NOTI = 1, 6 | NOTI_ICON = '', 7 | APP_PATH = '', 8 | LOG_INFO = 'Info', 9 | LOG_ERROR = 'Error', 10 | WINDOWS = 'Windows_NT' 11 | } -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { Switch } from './enums'; 2 | 3 | export interface SwitchNotiMessage 4 | { 5 | title: string, 6 | message: string, 7 | hotApp?: SwitchHotApp 8 | } 9 | 10 | export interface ProcessMessage { 11 | type: string, 12 | data: any, 13 | } 14 | 15 | export interface SwitchHotApp 16 | { 17 | name: string, 18 | rawcode: number, 19 | path: string, 20 | pid: number 21 | } 22 | 23 | export interface Settings { 24 | autoHide: boolean, 25 | maximize: boolean, 26 | placement: string, 27 | disableAltGr: boolean 28 | } -------------------------------------------------------------------------------- /src/switch.ts: -------------------------------------------------------------------------------- 1 | import { 2 | whichHotApp, 3 | switchMessage, 4 | saveHotApps, 5 | MakeHotAppActive, 6 | getAllProcessThatMatchAppName, 7 | registerNotifierOnClick, 8 | minimizeCurrentWindow, 9 | getHotApps, 10 | saveConfig, 11 | getConfig, 12 | checkDevMode, 13 | switchLog 14 | } from './utils'; 15 | 16 | import { SwitchHotApp, Settings } from './interfaces'; 17 | import TemplateText from './text'; 18 | import { Switch } from './enums'; 19 | import { InterProcessChannel } from './interprocess'; 20 | 21 | const Sentry = require('@sentry/node'); 22 | Sentry.init({ dsn: 'https://57f0f5bf1422411ca17906d52bedb2ee@sentry.io/1541801' }); 23 | 24 | const interChannel = new InterProcessChannel(); 25 | const ioHook = require('iohook'); 26 | 27 | let clientPID = null; 28 | let hotapps: SwitchHotApp[] = getHotApps(); 29 | let config = getConfig(); 30 | const log = switchLog.bind({ isDevMode: checkDevMode() }); 31 | let disableKeyUpListen = false; 32 | let disableKeyUpListenTimeout; 33 | 34 | /** 35 | * Called to activate hot app switching 36 | * @param {} event 37 | */ 38 | function react(event) { 39 | let hotApp = whichHotApp((process.platform == "darwin" ? event.keycode : event.rawcode), hotapps); 40 | if (hotApp != null) { 41 | // Minimize current window 42 | minimizeCurrentWindow(); 43 | // If the hot app that match the rawcode is found... 44 | // get all process that match hot app's name and path 45 | let processes = getAllProcessThatMatchAppName(hotApp.name, hotApp.path); 46 | // log(Switch.LOG_INFO, 'matched windows', processes); 47 | if (processes) { 48 | // minimizeCurrentWindow(); 49 | // Make hotapp active 50 | MakeHotAppActive(processes, config.maximize); 51 | interChannel.sendlastSwitched(hotApp); 52 | } else { 53 | switchMessage(Switch.ERROR_NOTI, { title: TemplateText.errorTitle, message: TemplateText.processNotFound(hotApp.name), hotApp: hotApp }); 54 | } 55 | } else { 56 | // if not hot app found make the client active.. 57 | 58 | if (process.platform == 'darwin') { 59 | if (event.keycode >= 0 && event.keycode <= 9) { 60 | interChannel.sendShowClient(); 61 | switchMessage(Switch.ERROR_NOTI, { title: TemplateText.errorTitle, message: TemplateText.noHotApp(event.keycode - 1), hotApp: hotApp }); 62 | } 63 | } else { 64 | if (event.rawcode >= 48 && event.rawcode <= 58) { 65 | interChannel.sendShowClient(); 66 | switchMessage(Switch.ERROR_NOTI, { title: TemplateText.errorTitle, message: TemplateText.noHotApp(event.rawcode - 48), hotApp: hotApp }); 67 | } 68 | } 69 | 70 | 71 | } 72 | } 73 | 74 | /** 75 | * Activates hot app switch if user holds the alt key 76 | * @param {} event 77 | */ 78 | function fnMethod(event) { 79 | // if altgr is disabled do not switch... 80 | 81 | if (event.altKey) { 82 | react(event); 83 | } 84 | } 85 | 86 | if (process.platform == 'win32') { 87 | /** 88 | * Fires on user's keyup 89 | */ 90 | 91 | ioHook.on('keyup', event => { 92 | // if altgr is disabled do not show dock... 93 | if (disableKeyUpListen && event.rawcode != 164) { 94 | if (disableKeyUpListenTimeout) clearTimeout(disableKeyUpListenTimeout); 95 | disableKeyUpListenTimeout = setTimeout(() => { 96 | clearTimeout(disableKeyUpListenTimeout) 97 | disableKeyUpListen = false; 98 | }, 1000); 99 | return; 100 | }; 101 | fnMethod(event); 102 | }); 103 | 104 | /** 105 | * Fires on user's keydown 106 | */ 107 | ioHook.on('keydown', event => { 108 | if (event.altKey) { 109 | // If alt key is pressed, show dock 110 | // if altgr is disabled do not show... 111 | if (config.disableAltGr && event.rawcode == 165) { 112 | disableKeyUpListen = true; 113 | return; 114 | } 115 | interChannel.sendShowClient(); 116 | } 117 | }); 118 | } 119 | 120 | if (process.platform == "darwin") { 121 | 122 | // MacOS switching strategy ... 123 | 124 | const numKeys = [ 125 | // 1 126 | { keycode: 2, rawcode: 18 }, 127 | // 2 128 | { keycode: 3, rawcode: 19 }, 129 | // 3 130 | { keycode: 4, rawcode: 20 }, 131 | // 4 132 | { keycode: 5, rawcode: 21 }, 133 | // 5 134 | { keycode: 6, rawcode: 23 }, 135 | // 6 136 | { keycode: 7, rawcode: 22 }, 137 | // 7 138 | { keycode: 8, rawcode: 26 }, 139 | // 8 140 | { keycode: 9, rawcode: 28 }, 141 | // 9 142 | { keycode: 10, rawcode: 25 }, 143 | // 0 144 | { keycode: 11, rawcode: 29 } 145 | ]; 146 | 147 | // regsiter a num keys with comand+option as shortcuts 148 | for (const current of numKeys) { 149 | 150 | // right 151 | ioHook.registerShortcut([3676, 3640, current.keycode], (keys) => { 152 | // console.log('Cmd+Option+' + (current.keycode - 1), keys, current) 153 | interChannel.sendShowClient(); 154 | react(current); 155 | }); 156 | 157 | // left 158 | ioHook.registerShortcut([3675, 56, current.keycode], (keys) => { 159 | // console.log('Cmd+Option+' + (current.keycode - 1), keys, current) 160 | interChannel.sendShowClient(); 161 | react(current); 162 | }); 163 | 164 | 165 | 166 | } 167 | 168 | // When user tabs Comand+Options 169 | ioHook.registerShortcut([3676, 3640], () => { 170 | interChannel.sendShowClient(); 171 | }); 172 | 173 | ioHook.registerShortcut([3675, 56], () => { 174 | interChannel.sendShowClient(); 175 | }); 176 | 177 | 178 | } 179 | 180 | // Register and start hook. 181 | (checkDevMode()) ? ioHook.start(true) : ioHook.start(); 182 | 183 | // Registers the on toast click event handler. 184 | registerNotifierOnClick(); 185 | 186 | /** 187 | * Fires when hot apps list 188 | * update is recieved from client 189 | */ 190 | interChannel.emitter.on('update-hot-apps', (happs) => { 191 | hotapps = happs; 192 | log(Switch.LOG_INFO, 'Hot apps update received', hotapps); 193 | saveHotApps(happs); 194 | }); 195 | 196 | /** 197 | * Fires when config 198 | * update is recieved from client 199 | */ 200 | interChannel.emitter.on('config-update', (settings: Settings) => { 201 | log(Switch.LOG_INFO, 'Config update update received', settings); 202 | config = settings; 203 | saveConfig(settings); 204 | }); 205 | 206 | /** 207 | * Fires when docks PID 208 | * update is recieved from client 209 | */ 210 | interChannel.emitter.on('client-pid', (pid) => { 211 | clientPID = pid; 212 | log(Switch.LOG_INFO, 'Hot client pid ::', pid); 213 | }); -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | const { windowManager } = require("node-window-manager"); 2 | const current = windowManager.getActiveWindow(); 3 | console.log(current); 4 | current.minimize(); 5 | console.log('worked!'); -------------------------------------------------------------------------------- /src/text.ts: -------------------------------------------------------------------------------- 1 | const TextTemplate = { 2 | errorTitle: "Yikes 🤔", 3 | processNotFound: (name) => `It seems like ${name.split('.exe')[0]} is yet to be opened. Click to launch app! 🚀`, 4 | noHotApp: (key) => `No hot app as been assigned to NUM ${key}, add one at the Switch dock! 👆 ` 5 | } 6 | 7 | export default TextTemplate; -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { SwitchNotiMessage, SwitchHotApp, Settings } from './interfaces'; 2 | import { Switch } from './enums'; 3 | const ostype = require("os").type(); 4 | const { windowManager } = require("node-window-manager"); 5 | const open = require('open'); 6 | 7 | const notifier = require('node-notifier'); 8 | const path = require('path'); 9 | const blackList = ['explorer.exe']; 10 | 11 | const Conf = require('conf'); 12 | const config = new Conf({ 13 | projectName: 'SwitchService', 14 | encryptionKey: '..kta#md!@a-k2j', 15 | }); 16 | const log = switchLog.bind({ isDevMode: checkDevMode() }); 17 | const icoPath = ((process as any).pkg) ? path.join(path.dirname(process.execPath), './switch.ico') : path.join(__dirname, '../assets/switch.ico'); 18 | const stringSimilarity = require('string-similarity'); 19 | 20 | log(Switch.LOG_INFO, 'ENV', ostype); 21 | 22 | /** 23 | * Sends notification to the user 24 | * @param {Switch.ERROR_NOTI | Switch.INFO_NOTI} type Type of notification 25 | * @param {SwitchNotiMessage} data Information to be sent 26 | * @param {} callback? If present, what to do when user reponds to notifcation 27 | */ 28 | 29 | export function switchMessage(type: Switch.ERROR_NOTI | Switch.INFO_NOTI, data: SwitchNotiMessage) { 30 | notifier.notify( 31 | { 32 | title: 'Switch - ' + data.title, 33 | message: data.message, 34 | icon: icoPath, // Absolute path (doesn't work on balloons) 35 | sound: false, // Only Notification Center or Windows Toasters 36 | wait: true, // Wait with callback, until user action is taken against notification 37 | hotApp: (data.hotApp) ? data.hotApp : null 38 | }); 39 | } 40 | 41 | /** 42 | * Register an event handler to handle notifier onclick 43 | */ 44 | export function registerNotifierOnClick() { 45 | const onclick = debounce((notifierObject, options, event) => { 46 | // if hot app is present use its' path path property to open hot app. 47 | if (options.hotApp) openHotApp(options.hotApp.path); 48 | 49 | }, 3000, false); 50 | notifier.on('click', onclick); 51 | } 52 | 53 | /** 54 | * Get the list of saved user's hotapps 55 | * @returns SwitchHotApp[] 56 | */ 57 | export function getHotApps(): SwitchHotApp[] { 58 | const hotApps = config.get('hotApps'); 59 | if (hotApps == null) { 60 | config.set('hotApps', []); 61 | return []; 62 | } else { 63 | return hotApps; 64 | } 65 | } 66 | 67 | /** 68 | * Get saved settings from store 69 | */ 70 | export function getConfig() { 71 | let settings: Settings | null = config.get('config'); 72 | if (settings == null) { 73 | const initial = { 74 | autoHide: true, 75 | maximize: true, 76 | disableAltGr: false 77 | }; 78 | config.set('config', initial); 79 | return initial; 80 | } else { 81 | return settings; 82 | } 83 | } 84 | 85 | /** 86 | * Save config to store 87 | * @param {any} settings Settings to save 88 | */ 89 | export function saveConfig(settings: Settings) { 90 | config.set('config', settings); 91 | } 92 | 93 | /** 94 | * Saves hot apps to store 95 | * @param {SwitchHotApp[]} hotApps List of hot apps to save 96 | */ 97 | export function saveHotApps(hotApps) { 98 | config.set('hotApps', hotApps) 99 | } 100 | 101 | /** 102 | * Returns a hot app that match the given hot rawcode 103 | * @param {number} rawcode Rawcode to match 104 | * @param {SwitchHotApp[]} hotApps List of hot apps 105 | * @returns SwitchHotApp 106 | */ 107 | export function whichHotApp(rawcode: number, hotApps: SwitchHotApp[]): SwitchHotApp | null { 108 | let whichHotWindowToOpen = hotApps.filter(app => app.rawcode == rawcode); 109 | if (whichHotWindowToOpen.length == 0) return null; 110 | return whichHotWindowToOpen[0]; 111 | } 112 | 113 | /** 114 | * Returns all processes that matches the specified path 115 | * @param {string} path Path of the process 116 | * @returns Window[] | null 117 | */ 118 | export function getAllProcessThatMatchPath(_path: string) { 119 | let processes = windowManager.getWindows().filter(window => path.basename(window.path) == path.basename(_path)); 120 | if (processes == null || processes.length == 0) return null; 121 | return processes; 122 | } 123 | 124 | /** 125 | * Find all processes that matches the given PID 126 | * @param {number} pid PID of the process 127 | * @returns Window[] | null 128 | */ 129 | export function getProcessWithPID(pid: number) { 130 | let process = windowManager.getWindows().filter(window => window.processId == pid); 131 | if (process.length == 0) return null; 132 | return process[0]; 133 | } 134 | 135 | /** 136 | * Returns all processes (of visible windows) that match specified process name and path, if only 137 | * process name is matched, it returns else null 138 | * @param {string} name Name of the process 139 | * @param {string} path Path of the process 140 | * @returns Window[] | null 141 | */ 142 | export function getAllProcessThatMatchAppName(name: string, path: string) { 143 | 144 | let filterProcessByname = []; 145 | if (ostype == Switch.WINDOWS) { 146 | // since window.isVisible() is only supported in Windows 147 | filterProcessByname = windowManager.getWindows().filter(window => window.isVisible() && window.getTitle().toLowerCase().includes(name.split('.exe')[0].toLowerCase().replace(/[^a-zA-Z ]/, ' '))); 148 | } 149 | else { 150 | filterProcessByname = windowManager.getWindows().filter(window => window.path == path); 151 | } 152 | 153 | if (filterProcessByname == null || filterProcessByname.length == 0) { 154 | if (process.platform == 'darwin') { 155 | return null; 156 | // return processPathSimilarityMatch(windowManager.getWindows(), path, .65); 157 | } else { 158 | return processPathSimilarityMatch(windowManager.getWindows().filter(window => window.isVisible()), path, .65); 159 | } 160 | } else { 161 | const filterProcessByPath = filterProcessByname.filter(window => window.path.toLowerCase() == path.toLowerCase()); 162 | if (filterProcessByPath == null || filterProcessByPath.length == 0) { 163 | return filterProcessByname; 164 | } else { 165 | return filterProcessByPath; 166 | } 167 | } 168 | } 169 | 170 | /** 171 | * Gets the processes that as the path that is most similar to a given path and a treshold.. 172 | * @param {} processes List of processes to check 173 | * @param {} path Path to match agianst 174 | * @param {} treshold Threshold to mark a given path as similar. 175 | * @returns Window[] | null 176 | */ 177 | export function processPathSimilarityMatch(processes, path, treshold): Window[] | null { 178 | // loop trough the processes list and look for the process with the most similar path 179 | // and the similarity exceeds the given treshnold. 180 | 181 | let simScores = []; 182 | // calculate the similarity for all processes 183 | processes.forEach(process => { 184 | simScores.push(stringSimilarity.compareTwoStrings(path, process.path)); 185 | }); 186 | 187 | // look for the highest score... 188 | const highest = Math.max(...simScores); 189 | if (highest < treshold) return null; 190 | 191 | // return processes that have these highest scores... 192 | const finalProcesses = []; 193 | simScores.forEach((score, index) => { 194 | if (score == highest) finalProcesses.push(processes[index]); 195 | }); 196 | return finalProcesses; 197 | } 198 | 199 | 200 | /** 201 | * Minimize current window 202 | */ 203 | export function clearCurrentWidow() { 204 | const currentWindow = windowManager.getActiveWindow(); 205 | if (currentWindow.isWindow()) { 206 | try { 207 | currentWindow.minimize(); 208 | } catch (e) { } 209 | } 210 | } 211 | 212 | /** 213 | * Makes hot process that is a window active 214 | * 1. Gets the list of hot processes 215 | * 2. Sorts them in descending order of using their PID 216 | * 3. Checks if the least pid is a window 217 | * 4. Brings it to top 218 | * 5. Else looks next least pid that it a window in the list 219 | * 6. Then brings it to the top 220 | * 221 | * @param {} hotProcesses - List of matched hot processess 222 | */ 223 | 224 | export function MakeHotAppActive(hotProcesses: any[], maximize: boolean = true) { 225 | // look for the least pid and is a window. 226 | hotProcesses.sort(function (a, b) { 227 | return b.processId - a.processId 228 | }); 229 | log(Switch.LOG_INFO, 'Sorted processes', hotProcesses); 230 | // least pid 231 | let least = hotProcesses[0]; 232 | // if is a window, bring it up and make active 233 | if (least.isWindow()) { 234 | least.bringToTop(); 235 | if (!maximize) { 236 | least.restore(); 237 | } else { 238 | least.maximize(); 239 | } 240 | } else { 241 | // else loop to the rest and find the 1st windowed process.. 242 | // remove the least one 243 | least = hotProcesses; 244 | least.shift(); 245 | for (let i = 0; i < least.length; i++) { 246 | if (least[i].isWindow()) { 247 | // Then bring the window to the top. 248 | const hot = least[i]; 249 | hot.bringToTop(); 250 | if (!maximize) { 251 | hot.restore(); 252 | } else { 253 | hot.maximize(); 254 | } 255 | break; 256 | } 257 | } 258 | } 259 | } 260 | 261 | /** 262 | * Opens a hotApp with the provided path 263 | * @param {string} path 264 | */ 265 | export function openHotApp(path: string) { 266 | open(path); 267 | } 268 | 269 | /** 270 | * Debouncing enforces that a function not be called again until 271 | * a certain amount of time has passed without it being called 272 | * @param {} callback What to do 273 | * @param {} wait Duration to wait for in seconds 274 | * @param {} immediate Default (false) 275 | */ 276 | export function debounce(callback, wait, immediate = false) { 277 | let timeout = null 278 | 279 | return function () { 280 | const callNow = immediate && !timeout 281 | const next = () => callback.apply(this, arguments) 282 | 283 | clearTimeout(timeout) 284 | timeout = setTimeout(next, wait) 285 | 286 | if (callNow) { 287 | next() 288 | } 289 | } 290 | } 291 | 292 | /** 293 | * Minimizes current window. 294 | * Useful to prevent user from tying uncessary input.. 295 | */ 296 | export function minimizeCurrentWindow() { 297 | if (process.platform == "darwin") return; 298 | const current = windowManager.getActiveWindow(); 299 | const info = current.getInfo(); 300 | // prevent minizing black listed apps.. 301 | if (blackList.filter(item => info.path.includes(item)).length > 0) { log(Switch.LOG_INFO, 'Cannot minize'); return }; 302 | if (current.isWindow() && current.getTitle().toLowerCase() != 'switch') { 303 | current.minimize(); 304 | } 305 | } 306 | 307 | /** 308 | * Checks devmode 309 | */ 310 | export function checkDevMode() { 311 | if (process.argv[2]) { 312 | return (process.argv[2].toLowerCase() == '--dev') ? true : false; 313 | } else { 314 | return false; 315 | } 316 | } 317 | 318 | /** 319 | * 320 | * @param {string} type 321 | * @param {string} msg 322 | */ 323 | export function switchLog(type: string, ...args: any[]) { 324 | if (this.isDevMode) { 325 | console.log('[' + type + ']:', ...args); 326 | } 327 | } 328 | 329 | /** 330 | * This method brings switch to top so that it can eat the next keystrokes 331 | * and prevents user from typing unintended text into their last active window. 332 | * A clever code... 333 | */ 334 | export function bringSwitchToFocus(pid) { 335 | if (process.platform == 'darwin') { 336 | try { 337 | const switchWindow = windowManager.getWindows().filter(window => window.processId == pid); 338 | console.log("FOCUSED SWITCH:", switchWindow.length > 0 ? true : false); 339 | switchWindow[0].bringToTop(); 340 | } catch (e) { } 341 | } 342 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "build", 6 | "removeComments": true, 7 | "rootDir": "src", 8 | "baseUrl": ".", 9 | "allowJs": true, 10 | "sourceMap": true, 11 | "paths": { 12 | "*": ["node_modules/*"] 13 | } 14 | }, 15 | "include": [ 16 | "./src/**/*" 17 | ] 18 | } --------------------------------------------------------------------------------