mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-08 06:57:29 +00:00
Merge pull request #10557 from keymanapp/feat/windows/use-km-core-actions-struct
refactor(windows): Use km_core_actions struct instead of queue
This commit is contained in:
commit
b5790a0a3e
2 changed files with 91 additions and 152 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<LPCWSTR>(actionItem->option->value));
|
||||
SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast<LPCWSTR>(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<LPCWSTR>(option->value));
|
||||
LPWSTR value = new WCHAR[value_length + 1];
|
||||
wcscpy_s(value, value_length + 1, reinterpret_cast<LPCWSTR>(option->value));
|
||||
SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast<LPCWSTR>(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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue