2020-04-01 15:56:23 -04:00
|
|
|
import {KeyEvent as NKeyEvent, RegisterCallback, UnregisterCallback} from "tc-native/ppt";
|
|
|
|
import {EventType, KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener";
|
|
|
|
import {tr} from "tc-shared/i18n/localize";
|
2021-01-11 21:49:38 -05:00
|
|
|
import {LogCategory, logTrace} from "tc-shared/log";
|
2020-04-01 15:56:23 -04:00
|
|
|
import * as log from "tc-shared/log";
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
let key_listener: ((_: KeyEvent) => any)[] = [];
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
function listener_key(type: EventType, nevent: NKeyEvent) {
|
|
|
|
if(nevent.key_code === 'VoidSymbol' || nevent.key_code === 'error')
|
|
|
|
nevent.key_code = undefined; /* trigger event for state update */
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
let event: KeyEvent = {
|
|
|
|
type: type,
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
key: nevent.key_code,
|
|
|
|
key_code: nevent.key_code,
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
key_ctrl: nevent.key_ctrl,
|
|
|
|
key_shift: nevent.key_shift,
|
|
|
|
key_alt: nevent.key_alt,
|
|
|
|
key_windows: nevent.key_windows
|
2019-10-25 19:51:40 -04:00
|
|
|
} as any;
|
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
for (const listener of key_listener)
|
|
|
|
listener && listener(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
function native_keyhook(event: NKeyEvent) {
|
|
|
|
//console.log("Native event!: %o", event);
|
|
|
|
|
|
|
|
if(event.type == 0)
|
|
|
|
listener_key(EventType.KEY_PRESS, event);
|
|
|
|
else if(event.type == 1)
|
|
|
|
listener_key(EventType.KEY_RELEASE, event);
|
|
|
|
else if(event.type == 2)
|
|
|
|
listener_key(EventType.KEY_TYPED, event);
|
|
|
|
else
|
|
|
|
console.warn(tr("Received unknown native event: %o"), event);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export async function initialize() : Promise<void> {
|
|
|
|
register_key_listener(listener_hook);
|
|
|
|
RegisterCallback(native_keyhook);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export function finalize() {
|
|
|
|
unregister_key_listener(listener_hook);
|
|
|
|
UnregisterCallback(native_keyhook);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export function register_key_listener(listener: (_: KeyEvent) => any) {
|
|
|
|
key_listener.push(listener);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export function unregister_key_listener(listener: (_: KeyEvent) => any) {
|
|
|
|
const index = key_listener.findIndex(e => e === listener);
|
|
|
|
if(index !== -1) key_listener.splice(index, 1);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
let key_hooks: KeyHook[] = [];
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
interface CurrentState {
|
|
|
|
keys: {[code: string]:KeyEvent};
|
|
|
|
special: { [key:number]:boolean };
|
|
|
|
}
|
|
|
|
let current_state: CurrentState = {
|
|
|
|
special: []
|
|
|
|
} as any;
|
|
|
|
|
|
|
|
let key_hooks_active: KeyHook[] = [];
|
|
|
|
|
|
|
|
function listener_hook(event: KeyEvent) {
|
|
|
|
if(event.type == EventType.KEY_TYPED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let old_hooks = [...key_hooks_active];
|
|
|
|
let new_hooks = [];
|
|
|
|
|
|
|
|
current_state.special[SpecialKey.ALT] = event.key_alt;
|
|
|
|
current_state.special[SpecialKey.CTRL] = event.key_ctrl;
|
|
|
|
current_state.special[SpecialKey.SHIFT] = event.key_shift;
|
|
|
|
current_state.special[SpecialKey.WINDOWS] = event.key_windows;
|
|
|
|
|
|
|
|
current_state[event.key_code] = undefined;
|
|
|
|
if(event.type == EventType.KEY_PRESS) {
|
|
|
|
current_state[event.key_code] = event;
|
|
|
|
|
|
|
|
for(const hook of key_hooks) {
|
|
|
|
if(hook.key_code !== event.key_code) continue;
|
|
|
|
if(hook.key_alt != event.key_alt) continue;
|
|
|
|
if(hook.key_ctrl != event.key_ctrl) continue;
|
|
|
|
if(hook.key_shift != event.key_shift) continue;
|
|
|
|
if(hook.key_windows != event.key_windows) continue;
|
|
|
|
|
|
|
|
new_hooks.push(hook);
|
|
|
|
const index = old_hooks.findIndex(e => e === hook);
|
2020-04-04 13:58:22 -04:00
|
|
|
if(index === -1 && hook.callback_press) {
|
2020-04-01 15:56:23 -04:00
|
|
|
hook.callback_press();
|
2021-01-11 21:49:38 -05:00
|
|
|
logTrace(LogCategory.GENERAL, tr("Trigger key press for %o!"), hook);
|
2019-10-25 19:51:40 -04:00
|
|
|
}
|
|
|
|
}
|
2020-04-01 15:56:23 -04:00
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
//We have a new situation
|
|
|
|
for(const hook of old_hooks) {
|
|
|
|
//Do not test for meta key states because they could differ in a key release event
|
|
|
|
if(hook.key_code === event.key_code) {
|
|
|
|
if(hook.callback_release) {
|
|
|
|
hook.callback_release();
|
2021-01-11 21:49:38 -05:00
|
|
|
logTrace(LogCategory.GENERAL, tr("Trigger key release for %o!"), hook);
|
2019-10-25 19:51:40 -04:00
|
|
|
}
|
2020-04-01 15:56:23 -04:00
|
|
|
} else {
|
|
|
|
new_hooks.push(hook);
|
2019-11-24 09:52:42 -05:00
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
}
|
2020-04-01 15:56:23 -04:00
|
|
|
key_hooks_active = new_hooks;
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2019-11-24 09:52:42 -05:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export function register_key_hook(hook: KeyHook) {
|
|
|
|
key_hooks.push(hook);
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
export function unregister_key_hook(hook: KeyHook) {
|
|
|
|
let index;
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-04-01 15:56:23 -04:00
|
|
|
index = key_hooks.findIndex(e => e === hook);
|
|
|
|
if(index !== -1) key_hooks.splice(index, 1);
|
|
|
|
|
|
|
|
index = key_hooks_active.findIndex(e => e === hook);
|
|
|
|
if(index !== -1) key_hooks_active.splice(index, 1);
|
2019-10-25 19:51:40 -04:00
|
|
|
}
|
2020-04-01 15:56:23 -04:00
|
|
|
|
|
|
|
export function key_pressed(code: string | SpecialKey) : boolean {
|
|
|
|
if(typeof(code) === 'string')
|
|
|
|
return typeof(current_state[code]) === "object";
|
|
|
|
return current_state.special[code];
|
|
|
|
}
|