Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check hotkey conflicts, mac <= > win hotkeys convert procedure #14

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
6 changes: 4 additions & 2 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"src/HotkeysMac.mm",
"src/AccessibilityMac.mm",
"src/AddonMac.mm",
"src/main.cpp"
"src/main.cpp",
"src/KeyMapping.cpp"
],
'configurations': {
'Debug': {
Expand Down Expand Up @@ -42,7 +43,8 @@
"sources": [
"src/main.cpp",
"src/Hotkeys.cpp",
"src/HotkeyManager.cpp"
"src/HotkeyManager.cpp",
"src/KeyMapping.cpp"
]
}],
["OS=='linux'", {
Expand Down
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class ShortcutHelper {
constructor() {
console.log('\r\n(DHK) init ShortcutHelper');
const binary = require('@mapbox/node-pre-gyp');
const path = require('path');
const binding_path = binary.find(path.resolve(path.join(__dirname, './package.json')));
Expand Down Expand Up @@ -89,6 +90,14 @@ class ShortcutHelper {
setHotkeysEnabled(enable) {
this.impl.setHotkeysEnabled(enable);
}

convertHotkeysCodes(keyCodes, keysAreVKC) {
return this.impl.convertHotkeysCodes(keyCodes, keysAreVKC);
}

checkHotkeyConflicts(excludeId, keyCodes) {
return this.impl.checkHotkeyConflicts(excludeId, keyCodes);
}
}

var shortcutHelper = new ShortcutHelper();
Expand Down
46 changes: 23 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{
"name": "desktop-hotkeys",
"version": "1.5.13",
"version": "1.9.2",
"description": "This package provides press/release callbacks for system-wide hotkeys on Windows and Mac",
"main": "index.js",
"gypfile": true,
"scripts": {
"install": "node-pre-gyp install --fallback-to-build",
"make": "node-pre-gyp rebuild --build-from-source package publish",
"make32": "node-pre-gyp rebuild --build-from-source --verbose --target_arch=ia32 package publish",
"make64": "node-pre-gyp rebuild --build-from-source --verbose --target_arch=x64 package publish",
"make64": "node-pre-gyp rebuild --build-from-source --verbose --target_arch=x64 package unpublish publish",
"makeM1": "node-pre-gyp rebuild --build-from-source --verbose --target_arch=arm64 package publish",
"makeM11": "node-pre-gyp rebuild --build-from-source --verbose --target_arch=arm64",
"maked": "node-pre-gyp rebuild --build-from-source --debug --verbose",
"test": "echo \"Test not applicable\""
},
Expand Down Expand Up @@ -40,16 +41,16 @@
},
"homepage": "https://github.com/zelloptt/desktop-hotkeys#readme",
"devDependencies": {
"aws-sdk": "2.1291.0",
"np": "7.6.3"
"aws-sdk": "2.1353.0",
"np": "7.7.0"
},
"np": {
"yarn": false,
"anyBranch": true
},
"dependencies": {
"@mapbox/node-pre-gyp": "1.0.10",
"node-addon-api": "5.0.0",
"node-addon-api": "6.0.0",
"node-gyp": "9.3.1"
}
}
22 changes: 11 additions & 11 deletions sample/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 13 additions & 5 deletions sample/sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function fnReleased2() {
console.log('Hotkey#2 released');
}

console.log("desktop-hotkeys module started: " + dh.start(true));
console.log('desktop-hotkeys module started: ' + dh.start(true));

function logCb(text) {
console.log('(PTT) Callback: ' + text);
Expand All @@ -31,11 +31,11 @@ const F6 = F1 + 5;
const F7 = F6 + 1;
const useVKC = isWindows;
try {
// dh.setLoggerCb(logCb);
dh.setLoggerCb(logCb);
dh.setupAccessibilityCallback(true, (granted) => {
// it has been reported that actual permission status might be applied
// only after a short period of time
log.info('(PTT) Accessibility permission has changed to ' + granted);
console.log('(PTT) Accessibility permission has changed to ' + granted);
});

const hk1 = dh.registerShortcut([CTRL, ALT, F1], fnPressed, fnReleased, useVKC);
Expand All @@ -56,13 +56,21 @@ try {
} catch (ex) {
console.log('exception ' + ex);
}

const keyCodes = [17, 18, 112];
console.log('Test codes are: ' + keyCodes.toString());
const macCodes = dh.convertHotkeysCodes(keyCodes, true);
console.log('Mac codes are: ' + macCodes.toString());
const winCodes = dh.convertHotkeysCodes(macCodes, false);
console.log('Mac codes are: ' + winCodes.toString());

console.log('waiting for hotkeys...');
setTimeout(() => {
/*setTimeout(() => {
console.log("Disable hotkeys.");
dh.setHotkeysEnabled(false);
setTimeout(() => {
console.log("Enable hotkeys back");
dh.setHotkeysEnabled(true);

}, "10000");
}, "10000");
}, "10000");*/
2 changes: 2 additions & 0 deletions src/AddonMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
exports.Set("registerShortcut", Napi::Function::New(env, HotKeys::registerShortcut));
exports.Set("unregisterShortcut", Napi::Function::New(env, HotKeys::unregisterShortcut));
exports.Set("unregisterAllShortcuts", Napi::Function::New(env, HotKeys::unregisterAllShortcuts));
exports.Set("convertHotkeysCodes", Napi::Function::New(env, HotKeys::convertHotkeysCodes));
exports.Set("checkHotkeyConflicts", Napi::Function::New(env, HotKeys::checkHotkeyConflicts));
exports.Set("macCheckAccessibilityGranted", Napi::Function::New(env, HotKeys::macCheckAccessibilityGranted));
exports.Set("macShowAccessibilitySettings", Napi::Function::New(env, HotKeys::macShowAccessibilitySettings));
exports.Set("macSubscribeAccessibilityUpdates", Napi::Function::New(env, HotKeys::macSubscribeAccessibilityUpdates));
Expand Down
77 changes: 67 additions & 10 deletions src/HotkeyManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,27 @@ bool HotKeyManager::Valid() const
return _uThreadId != 0 && _hWnd != NULL;
}

void HotKeyManager::NotifyHotKeyEvent(unsigned uCode, bool bPressed)
bool HotKeyManager::NotifyHotKeyEvent(unsigned uCode, bool bPressed)
{
auto callback = []( Napi::Env env, Napi::Function jsCallback, int* value ) {
// Transform native data into JS data, passing it to the provided
// `jsCallback` -- the TSFN's JavaScript function.
jsCallback.Call( {Napi::Number::New( env, *value )} );
delete value;
};

if (this->_DisabledState) {
return false;
}
TCONT::iterator cit = _hotkeys.find(uCode);
if (cit != _hotkeys.end()) {
Napi::ThreadSafeFunction& tsfn = bPressed ? cit->second.first : cit->second.second;
tsfn.NonBlockingCall();
const Napi::ThreadSafeFunction& tsfn = bPressed ? cit->second.first : cit->second.second;
int* pCode = new int;
*pCode = uCode;
tsfn.NonBlockingCall(pCode, callback);
return true;
}
return false;
}

void HotKeyManager::UpdateCallbacks(unsigned uCode, bool bSetInUse)
Expand All @@ -91,17 +105,59 @@ void HotKeyManager::UpdateCallbacks(unsigned uCode, bool bSetInUse)
}
}

std::string HotKeyManager::GenerateAtomName(WPARAM wKeys)
{
std::string sAtomName("DesktopHotkey#");
char buf[32] = {0};
sAtomName.append(itoa(wKeys, buf, 16));
return sAtomName;
}

DWORD HotKeyManager::checkShortcut(DWORD dwExcludeShortcutId, WORD wKeyCode, WORD wMod, bool fullCheck)
{
if (!Valid()) {
return 0; // unable to verify!
}
WPARAM wParam = MAKEWPARAM(wKeyCode, wMod);
// check if hotkey already registered
for (std::map<unsigned, WPARAM>::const_iterator it = _hotkeyIds.begin(); it != _hotkeyIds.end(); it ++) {
if (it->second == wParam) {
return it->first == dwExcludeShortcutId ? 0 : it->first;
}
}
if (!fullCheck) {
return 0;
}
// existing hotkey not found, try to register new hotkey in order to check for other conflicts
std::string sName = HotKeyManager::GenerateAtomName(wParam);
ATOM atm = GlobalFindAtomA(sName.c_str());
if (atm != 0) {
return 0xFFFFFFFF; // conflict!
}
atm = GlobalAddAtomA(sName.c_str());
if (atm == 0) {
return 0xFFFFFFFF; // unable to generate unique key id
}
bool hotKeyRegistered = 0 != SendMessage(_hWnd, WM_REGISTER_HOTKEY, wParam, atm);
GlobalDeleteAtom(atm);
if (!hotKeyRegistered) {
return 0xFFFFFFFF; // unable to register such a hotkey
}
SendMessage(_hWnd, WM_UNREGISTER_HOTKEY, atm, 0);
return 0;
}

DWORD HotKeyManager::registerShortcut(WORD wKeyCode, WORD wMod, const Napi::ThreadSafeFunction& tsfPress, const Napi::ThreadSafeFunction& tsfRelease)
{
DWORD dwId = 0;
if (!Valid()) {
return 0;
}
if (0 != this->checkShortcut(0, wKeyCode, wMod, false)) {
return 0;
}
WPARAM wParam = MAKEWPARAM(wKeyCode, wMod);
std::string s("DesktopHotkey#");
char buf[32] = {0};
s.append(itoa(wParam, buf, 16));
ATOM atm = GlobalAddAtomA(s.c_str());
ATOM atm = GlobalAddAtomA(HotKeyManager::GenerateAtomName(wParam).c_str());
if (atm) {
_hotkeys[atm] = std::make_pair(tsfPress, tsfRelease);
if (0 != SendMessage(_hWnd, WM_REGISTER_HOTKEY, wParam, atm)) {
Expand Down Expand Up @@ -198,9 +254,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
unsigned uKeyCode = wParam;
pushedKey = HIWORD(lParam);
pushedCode = wParam;
g_pHotKeyManager->NotifyHotKeyEvent(wParam, true);
timerId = SetTimer(hWnd, wParam, 100, NULL);
log("(DHK): new evt%d activated\r\n", timerId);
if (g_pHotKeyManager->NotifyHotKeyEvent(wParam, true)) {
timerId = SetTimer(hWnd, wParam, 100, NULL);
log("(DHK): new evt%d activated\r\n", timerId);
}
break;
}
case WM_TIMER:
Expand Down
Loading