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/kmprocessactions.cpp b/windows/src/engine/keyman32/kmprocessactions.cpp index 1eecfa6807..7e089fb286 100644 --- a/windows/src/engine/keyman32/kmprocessactions.cpp +++ b/windows/src/engine/keyman32/kmprocessactions.cpp @@ -6,94 +6,71 @@ */ #include "pch.h" -static BOOL processUnicodeChar(AITIP* app, const km_core_action_item* actionItem) { - if (Uni_IsSMP(actionItem->character)) { - app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate1(actionItem->character))); - app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate2(actionItem->character))); - } - else { - app->QueueAction(QIT_CHAR, actionItem->character); - } - return TRUE; -} - -static BOOL processMarker(AITIP* app, const km_core_action_item* actionItem) { - app->QueueAction(QIT_DEADKEY, (DWORD)actionItem->marker); - return TRUE; -} - -static BOOL processAlert(AITIP* app) { - app->QueueAction(QIT_BELL, 0); - return TRUE; -} - -static BOOL processBack(AITIP* app, const km_core_action_item* actionItem) { - if (actionItem->backspace.expected_type == KM_CORE_BT_MARKER) { - app->QueueAction(QIT_BACK, BK_DEADKEY); - } else if(actionItem->backspace.expected_type == KM_CORE_BT_CHAR) { - // If this is a TSF-aware app we need to set the BK_SURROGATE flag to delete - // both parts of the surrogate pair. Legacy apps receive a BKSP WM_KEYDOWN event - // which results in deleting both parts in one action. - if (!app->IsLegacy() && Uni_IsSMP(actionItem->backspace.expected_value)) { - app->QueueAction(QIT_BACK, BK_DEFAULT | BK_SURROGATE); +static void +processUnicodeChar(AITIP* app, const km_core_usv* actionItem) { + while (*actionItem) { + if (Uni_IsSMP(*actionItem)) { + app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate1(*actionItem))); + app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate2(*actionItem))); } else { + app->QueueAction(QIT_CHAR, *actionItem); + } + actionItem++; + } +} + +static void processAlert(AITIP* app) { + app->QueueAction(QIT_BELL, 0); +} + +static void +processBack(AITIP* app, const unsigned int code_points_to_delete, const km_core_usv* delete_context) { + if (app->IsLegacy()) { + for (unsigned int i = 0; i < code_points_to_delete; i++) { app->QueueAction(QIT_BACK, BK_DEFAULT); } - } else { // KM_CORE_BT_UNKNOWN - app->QueueAction(QIT_BACK, BK_DEFAULT); } - return TRUE; -} - -static BOOL processPersistOpt( - const km_core_action_item* actionItem, - km_core_state* keyboardState, - LPINTKEYBOARDINFO activeKeyboard -) { - if (actionItem->option != NULL) - { - // Allocate for 1 option plus 1 pad struct of 0's for KM_CORE_IT_END - km_core_option_item keyboardOpts[2] = { 0 }; - keyboardOpts[0].key = actionItem->option->key; - keyboardOpts[0].value = actionItem->option->value; - km_core_status eventStatus = (km_core_status_codes)km_core_state_options_update(keyboardState, keyboardOpts); - if (eventStatus != KM_CORE_STATUS_OK) - { - // log warning "problem saving option for km_core_keyboard"); - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Error %d saving option for keyboard [%s].", eventStatus, activeKeyboard->Name); + else { + km_core_usv const* delete_context_ptr = delete_context; + while (*delete_context_ptr) { + delete_context_ptr++; } - - // Put the keyboard option into Windows Registry - if (actionItem->option != NULL && actionItem->option->key != NULL && - actionItem->option->value != NULL) - { - // log"Saving keyboard option to registry"); - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Saving option to registry for keyboard [%s].", activeKeyboard->Name); - LPWSTR value = new WCHAR[sizeof(actionItem->option->value) + 1]; - wcscpy_s(value, sizeof(actionItem->option->value) + 1, reinterpret_cast(actionItem->option->value)); - SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast(actionItem->option->key), value); + 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); + } } } - return TRUE; } -static BOOL processInvalidateContext( - AITIP* app + + +static void +processPersistOpt(km_core_actions const* actions, LPINTKEYBOARDINFO activeKeyboard ) { - app->ResetContext(); - return TRUE; + for (auto option = actions->persist_options; option->key; option++) { + 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)); + SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast(option->key), value); + delete[] value; + } } -static BOOL -processCapsLock(const km_core_action_item* actionItem, BOOL isUp, BOOL Updateable, BOOL externalEvent) { +static void processCapsLock(const km_core_caps_state caps_lock_state, BOOL isUp, BOOL Updateable, BOOL externalEvent) { // We only want to process the Caps Lock key event once -- // in the first pass (!Updateable). if (Updateable){ - return TRUE; + return; } - if (actionItem->capsLock) { + if (caps_lock_state == KM_CORE_CAPS_ON) { // This case would occur for the keyboard system store setting `store(&CapsOnOnly) '1'` if (isUp && !IsCapsLockOn()) { // I267 - 24/11/2006 invert GetKeyState test SendDebugMessageFormat(0, sdmGlobal, 0, "processCapsLock: TURN CAPS ON: FIsUp=%d CapsState=%d", isUp, IsCapsLockOn()); @@ -109,8 +86,7 @@ processCapsLock(const km_core_action_item* actionItem, BOOL isUp, BOOL Updateabl keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); } - } - else { + } else if (caps_lock_state == KM_CORE_CAPS_OFF) { // This case would occur for the keyboard system store setting `store(&ShiftFreesCaps) '1'` // OR selecting a keyboard with CAPs always off rule if ((!isUp && IsCapsLockOn()) || (externalEvent && IsCapsLockOn())) { @@ -119,63 +95,43 @@ processCapsLock(const km_core_action_item* actionItem, BOOL isUp, BOOL Updateabl keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); } } - - return TRUE; } -BOOL ProcessActions(BOOL* emitKeyStroke) +BOOL ProcessActions(BOOL* emitKeystroke) { PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) return FALSE; + // 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. - for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; act++) { - BOOL continueProcessingActions = TRUE; - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActions : act->type=%d", act->type); - switch (act->type) { - case KM_CORE_IT_CHAR: - continueProcessingActions = processUnicodeChar(_td->app, act); - break; - case KM_CORE_IT_MARKER: - continueProcessingActions = processMarker(_td->app, act); - break; - case KM_CORE_IT_ALERT: - continueProcessingActions = processAlert(_td->app); - break; - case KM_CORE_IT_BACK: - continueProcessingActions = processBack(_td->app, act); - break; - case KM_CORE_IT_PERSIST_OPT: - continueProcessingActions = processPersistOpt(act, _td->lpActiveKeyboard->lpCoreKeyboardState, _td->lpActiveKeyboard); - break; - case KM_CORE_IT_EMIT_KEYSTROKE: - *emitKeyStroke = TRUE; - continueProcessingActions = TRUE; - break; - case KM_CORE_IT_INVALIDATE_CONTEXT: - continueProcessingActions = processInvalidateContext(_td->app); - break; - case KM_CORE_IT_CAPSLOCK: - continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable, FALSE); - break; - case KM_CORE_IT_END: - // fallthrough - default: - assert(false); // NOT SUPPORTED - break; - } - if (!continueProcessingActions) { - return FALSE; - } + + + processBack(_td->app, _td->core_actions->code_points_to_delete, _td->core_actions->deleted_context); + processUnicodeChar(_td->app, _td->core_actions->output); + if (_td->core_actions->persist_options != NULL) { + processPersistOpt(_td->core_actions, _td->lpActiveKeyboard); } + if (_td->core_actions->do_alert) { + processAlert(_td->app); + } + if (_td->core_actions->emit_keystroke) { + *emitKeystroke = TRUE; + } + 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; } BOOL -ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) { +ProcessActionsNonUpdatableParse(BOOL* emitKeystroke) { PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) { return FALSE; @@ -184,28 +140,20 @@ ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) { 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; + } - BOOL continueProcessingActions = TRUE; - for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; act++) { - switch (act->type) { - case KM_CORE_IT_EMIT_KEYSTROKE: - *emitKeyStroke = TRUE; - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE: act->type=[%d]", act->type); - continueProcessingActions = TRUE; - _td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse - break; - case KM_CORE_IT_CAPSLOCK: - continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable, FALSE); - break; - case KM_CORE_IT_INVALIDATE_CONTEXT: - continueProcessingActions = processInvalidateContext(_td->app); - break; - } - if (!continueProcessingActions) { - return FALSE; - } + _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 } return TRUE; } @@ -216,22 +164,11 @@ ProcessActionsExternalEvent() { if (!_td) { return FALSE; } - // Currently only a subset of actions are handled. - // Other actions will be added when needed. - BOOL continueProcessingActions = TRUE; - for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; - act++) { - switch (act->type) { - case KM_CORE_IT_CAPSLOCK: - continueProcessingActions = processCapsLock(act, !_td->state.isDown, FALSE, TRUE); - break; - case KM_CORE_IT_INVALIDATE_CONTEXT: - continueProcessingActions = processInvalidateContext(_td->app); - break; - } - if (!continueProcessingActions) { - return FALSE; - } + if (!_td->core_actions) { // when ideponent we will not need this + return FALSE; } + // TODO: #10583 remove and call get actions here directly + //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; }