From 2b0950617d5645816e2711e4ea830b6dbdf4f7ee Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:11:23 +1000 Subject: [PATCH] feat(windows): add cached actions --- windows/src/engine/keyman32/globals.h | 2 + windows/src/engine/keyman32/kmprocess.cpp | 2 + .../src/engine/keyman32/kmprocessactions.cpp | 130 +++++++++++++++--- 3 files changed, 115 insertions(+), 19 deletions(-) diff --git a/windows/src/engine/keyman32/globals.h b/windows/src/engine/keyman32/globals.h index 8f1dfc4c90..e0f86df07c 100644 --- a/windows/src/engine/keyman32/globals.h +++ b/windows/src/engine/keyman32/globals.h @@ -212,6 +212,8 @@ typedef struct tagKEYMAN64THREADDATA BOOL TIPFUpdateable, TIPFPreserved; // I4290 BOOL CoreProcessEventRun; // True if core process event has been run + // TODO: #10583 remove core_actions cache + km_core_actions const *core_actions; BOOL FInRefreshKeyboards; BOOL RefreshRequired; diff --git a/windows/src/engine/keyman32/kmprocess.cpp b/windows/src/engine/keyman32/kmprocess.cpp index c95c7ce5fd..b0c77d376d 100644 --- a/windows/src/engine/keyman32/kmprocess.cpp +++ b/windows/src/engine/keyman32/kmprocess.cpp @@ -83,6 +83,8 @@ Process_Event_Core(PKEYMAN64THREADDATA _td) { if (_td->app->ReadContext(application_context)) { km_core_context_status result; result = km_core_state_context_set_if_needed(_td->lpActiveKeyboard->lpCoreKeyboardState, reinterpret_cast(application_context)); + // TODO remove debuging set if needed + SendDebugMessageFormat(0, sdmGlobal, 0, "Process_Event_Core: km_core_state_context_set_if_needed returned [%d]", result); if (result == KM_CORE_CONTEXT_STATUS_ERROR || result == KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT) { SendDebugMessageFormat(0, sdmGlobal, 0, "Process_Event_Core: km_core_state_context_set_if_needed returned [%d]", result); } diff --git a/windows/src/engine/keyman32/kmprocessactions.cpp b/windows/src/engine/keyman32/kmprocessactions.cpp index b21942fc38..f37d0827c6 100644 --- a/windows/src/engine/keyman32/kmprocessactions.cpp +++ b/windows/src/engine/keyman32/kmprocessactions.cpp @@ -23,37 +23,105 @@ static void processAlert(AITIP* app) { app->QueueAction(QIT_BELL, 0); } +// TODO: #10583 +// Remove extra debuging code +static void +processDebugDeleteContext(const km_core_usv* delete_context) { + km_core_usv const* delete_context_ptr = delete_context; + while (*delete_context_ptr) { + delete_context_ptr++; + } + delete_context_ptr--; + for (; delete_context_ptr >= delete_context; delete_context_ptr--) { + if (Uni_IsSMP(*delete_context_ptr)) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processDebugContext: s1 U+%04x", Uni_UTF32ToSurrogate1(*delete_context_ptr)); + SendDebugMessageFormat(0, sdmGlobal, 0, "processDebugContext: s2 U+%04x", Uni_UTF32ToSurrogate2(*delete_context_ptr)); + } else { + SendDebugMessageFormat(0, sdmGlobal, 0, "processDebugContext: U+%04x", *delete_context_ptr); + } + } + +} static BOOL processBack(AITIP* app, const unsigned int code_points_to_delete, const km_core_usv* delete_context) { if (app->IsLegacy()) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processBack: Legacy app cptd [%d].", code_points_to_delete); for (unsigned int i = 0; i < code_points_to_delete; i++) { app->QueueAction(QIT_BACK, BK_DEFAULT); } return TRUE; } if (!app->IsLegacy()) { - while (*delete_context) { - if (Uni_IsSMP(*delete_context)) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processBack: TSF app cptd [%d].", code_points_to_delete); + km_core_usv const* delete_context_ptr = delete_context; + while (*delete_context_ptr) { + delete_context_ptr++; + } + delete_context_ptr--; + for (; delete_context_ptr >= delete_context; delete_context_ptr--) { + if (Uni_IsSMP(*delete_context_ptr)) { app->QueueAction(QIT_BACK, BK_DEFAULT | BK_SURROGATE); } else { app->QueueAction(QIT_BACK, BK_DEFAULT); } - delete_context++; } return TRUE; } return FALSE; } +// TODO remove reading the applications context +static BOOL +processBack2(AITIP* app, const unsigned int code_points_to_delete) { + if (app->IsLegacy()) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processBack2: Legacy app cptd [%d].", code_points_to_delete); + for (unsigned int i = 0; i < code_points_to_delete; i++) { + //app->QueueAction(QIT_BACK, BK_DEFAULT); + } + return TRUE; + } + + if (!app->IsLegacy()) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processBack2: TSF app cptd [%d].", code_points_to_delete); + WCHAR application_context[MAXCONTEXT]; + DWORD cp_to_delete = code_points_to_delete; + if (!app->ReadContext(application_context)) { + SendDebugMessageFormat(0, sdmGlobal, 0, "processBack2: Error reading context from application."); + } else { + // Find the length of the string + int length = 0; + while (application_context[length] != L'\0') { + length++; + } + // Read each character starting from caret + for (int i = length - 1; i >= 0 && cp_to_delete > 0; i--) { + // Need to consider malformed context where only one surrogate is present, at either the start or end of the context. + // In both these scenarios just perform one backspace one the same as if it was a non surrogate pair. + if ((i > 0) && (Uni_IsSurrogate1(application_context[i - 1]) && Uni_IsSurrogate2(application_context[i]))) { + //app->QueueAction(QIT_BACK, BK_DEFAULT | BK_SURROGATE); + i--; + cp_to_delete--; + } else { + //app->QueueAction(QIT_BACK, BK_DEFAULT); + cp_to_delete--; + } + } + } + return TRUE; + } + + return FALSE; +} + static void processPersistOpt(km_core_actions const* actions, LPINTKEYBOARDINFO activeKeyboard ) { for (auto option = actions->persist_options; option->key; option++) { // Put the keyboard option into Windows Registry // log"Saving keyboard option to registry"); - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Saving option to registry for keyboard [%s].", activeKeyboard->Name); + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessPersistOpt: Saving option to registry for keyboard [%s].", activeKeyboard->Name); size_t value_length = wcslen(reinterpret_cast(option->value)); LPWSTR value = new WCHAR[value_length + 1]; wcscpy_s(value, value_length + 1, reinterpret_cast(option->value)); @@ -101,24 +169,38 @@ BOOL ProcessActions(BOOL* emitKeyStroke) { PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) return FALSE; + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActions: Enter"); + + // TODO: #10583 Remove caching action_struct + if (!_td->core_actions) { + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActions: core_actions not set"); + _td->core_actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); + } _td->CoreProcessEventRun = FALSE; + + + // Process the action items from the core. This actions will modify the windows context (AppContext). // Therefore it is not required to copy the context from the core to the windows context. - km_core_actions const* actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); - - processBack(_td->app, actions->code_points_to_delete, actions->deleted_context); - processUnicodeChar(_td->app, actions->output); - if (actions->persist_options != NULL) { - processPersistOpt(actions, _td->lpActiveKeyboard); + processDebugDeleteContext(_td->core_actions->deleted_context); + processBack(_td->app, _td->core_actions->code_points_to_delete, _td->core_actions->deleted_context); + processBack2(_td->app, _td->core_actions->code_points_to_delete); + processUnicodeChar(_td->app, _td->core_actions->output); + if (_td->core_actions->persist_options != NULL) { + processPersistOpt(_td->core_actions, _td->lpActiveKeyboard); } - if (actions->do_alert) { + if (_td->core_actions->do_alert) { processAlert(_td->app); } - if (actions->emit_keystroke) { + if (_td->core_actions->emit_keystroke) { *emitKeyStroke = TRUE; } - processCapsLock(actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE); + processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE); + // TODO: #10583 remove dispose + km_core_actions_dispose(_td->core_actions); + _td->core_actions = nullptr; + return TRUE; } @@ -128,15 +210,21 @@ ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) { if (!_td) { return FALSE; } - + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse: Enter"); if (_td->TIPFUpdateable) { // ensure only run when not updateable return FALSE; } _td->CoreProcessEventRun = TRUE; + // TODO: #10583 remove dispose + if (_td->core_actions) { + km_core_actions_dispose(_td->core_actions); + _td->core_actions = nullptr; + } - km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); - processCapsLock(acts->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE); - if (acts->emit_keystroke) { + _td->core_actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse: km_core_state_get_actions"); + processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE); + if (_td->core_actions->emit_keystroke) { *emitKeyStroke = TRUE; SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE"); _td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse @@ -150,7 +238,11 @@ ProcessActionsExternalEvent() { if (!_td) { return FALSE; } - km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); - processCapsLock(acts->new_caps_lock_state, !_td->state.isDown, FALSE, TRUE); + if (!_td->core_actions) { // when ideponent we will not need this + return FALSE; + } + // TODO: #10583 remove dispose + //km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState); + processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, FALSE, TRUE); return TRUE; }