diff --git a/core/src/km_core_processevent_api.cpp b/core/src/km_core_processevent_api.cpp index 7d48979cc4a..a2bca95ea8b 100644 --- a/core/src/km_core_processevent_api.cpp +++ b/core/src/km_core_processevent_api.cpp @@ -52,6 +52,30 @@ km_core_process_event(km_core_state *state, } km_core_status status = state->processor().process_event(state, vk, modifier_state, is_key_down, event_flags); + if (state_should_invalidate_context(state, vk, modifier_state, is_key_down, event_flags)) { + // clear the context and the app context + state->context().clear(); + state->app_context().clear(); + + // we are already committed. So we need to un-commit (remove the end of the vector) + if (state->actions().back().type == KM_CORE_IT_END) { + state->actions().pop_back(); + } + bool foundEmit = false; + km::core::action oldAction; + // try to put the invalidate item before the emit keystroke + if (state->actions().back().type == KM_CORE_IT_EMIT_KEYSTROKE) { + oldAction = state->actions().back(); + state->actions().pop_back(); + foundEmit = true; + } + state->actions().push_invalidate_context(); + if (foundEmit) { + state->actions().push_back(oldAction); + } + state->actions().commit(); + } + state->apply_actions_and_merge_app_context(); return status; diff --git a/core/src/km_core_state_api.cpp b/core/src/km_core_state_api.cpp index 8b8807b1d24..0f3fd7f1126 100644 --- a/core/src/km_core_state_api.cpp +++ b/core/src/km_core_state_api.cpp @@ -20,7 +20,8 @@ #include "processor.hpp" #include "state.hpp" - +#include "vkey_to_contextreset.hpp" +#include "kmx_file.h" using namespace km::core; @@ -363,4 +364,40 @@ km_core_cp * km_core_state_context_debug( result[s.size()] = 0; return result; -} \ No newline at end of file +} + +static bool +state_has_action_type(km_core_state *state, uint8_t type) { + return std::any_of( + state->actions().begin(), state->actions().end(), + [type](const km::core::action &a) { return a.type == type; }); +} + +bool +state_should_invalidate_context(km_core_state *state, + km_core_virtual_key vk, + uint16_t modifier_state, + uint8_t is_key_down, + uint16_t _kmn_unused(event_flags)) { + if (!is_key_down) { + return false; // don't invalidate on keyup + } + // if emit_keystroke is present, check if a context reset is needed + if (state_has_action_type(state, KM_CORE_IT_EMIT_KEYSTROKE)) { + if ( + // when a backspace keystroke is emitted, it is because we are at the start of + // context, and we want to give the application the chance to process it, e.g. + // by moving to previous field. Note that context manipulation does not result + // in an emit_keystroke backspace action, as this is handled through the + // `code_points_to_delete` field. So we always invalidate context when a + // processor emits a backspace. + vk == KM_CORE_VKEY_BKSP || + // certain modifiers invalidate context + modifier_should_contextreset(modifier_state) || + // most frame keys invalidate context + vkey_should_contextreset(vk)) { + return true; + } + } + return false; +} diff --git a/core/src/kmx/kmx_consts.cpp b/core/src/kmx/kmx_consts.cpp index ae9e7806896..009ef1a4433 100644 --- a/core/src/kmx/kmx_consts.cpp +++ b/core/src/kmx/kmx_consts.cpp @@ -104,278 +104,6 @@ const struct char_to_vkey s_char_to_vkey[] = { {0, 0, 0} }; -const bool vkey_to_contextreset[256] = { - true, //L"K_?00", // &H0 - true, //L"K_LBUTTON", // &H1 - true, //L"K_RBUTTON", // &H2 - true, //L"K_CANCEL", // &H3 - true, //L"K_MBUTTON", // &H4 - true, //L"K_?05", // &H5 - true, //L"K_?06", // &H6 - true, //L"K_?07", // &H7 - true, //L"K_BKSP", // &H8 - true, //L"K_TAB", // &H9 - true, //L"K_?0A", // &HA - true, //L"K_?0B", // &HB - true, //L"K_KP5", // &HC - true, //L"K_ENTER", // &HD - true, //L"K_?0E", // &HE - true, //L"K_?0F", // &HF - false, //L"K_SHIFT", // &H10 - false, //L"K_CONTROL", // &H11 - false, //L"K_ALT", // &H12 - true, //L"K_PAUSE", // &H13 - false, //L"K_CAPS", // &H14 - true, //L"K_KANJI?15", // &H15 - true, //L"K_KANJI?16", // &H16 - true, //L"K_KANJI?17", // &H17 - true, //L"K_KANJI?18", // &H18 - true, //L"K_KANJI?19", // &H19 - true, //L"K_?1A", // &H1A - true, //L"K_ESC", // &H1B - true, //L"K_KANJI?1C", // &H1C - true, //L"K_KANJI?1D", // &H1D - true, //L"K_KANJI?1E", // &H1E - true, //L"K_KANJI?1F", // &H1F - false, //L"K_SPACE", // &H20 - true, //L"K_PGUP", // &H21 - true, //L"K_PGDN", // &H22 - true, //L"K_END", // &H23 - true, //L"K_HOME", // &H24 - true, //L"K_LEFT", // &H25 - true, //L"K_UP", // &H26 - true, //L"K_RIGHT", // &H27 - true, //L"K_DOWN", // &H28 - true, //L"K_SEL", // &H29 - true, //L"K_PRINT", // &H2A - true, //L"K_EXEC", // &H2B - true, //L"K_PRTSCN", // &H2C - false, //L"K_INS", // &H2D - true, //L"K_DEL", // &H2E - true, //L"K_HELP", // &H2F - false, //L"K_0", // &H30 - false, //L"K_1", // &H31 - false, //L"K_2", // &H32 - false, //L"K_3", // &H33 - false, //L"K_4", // &H34 - false, //L"K_5", // &H35 - false, //L"K_6", // &H36 - false, //L"K_7", // &H37 - false, //L"K_8", // &H38 - false, //L"K_9", // &H39 - false, //L"K_?3A", // &H3A - false, //L"K_?3B", // &H3B - false, //L"K_?3C", // &H3C - false, //L"K_?3D", // &H3D - false, //L"K_?3E", // &H3E - false, //L"K_?3F", // &H3F - false, //L"K_?40", // &H40 - - false, //L"K_A", // &H41 - false, //L"K_B", // &H42 - false, //L"K_C", // &H43 - false, //L"K_D", // &H44 - false, //L"K_E", // &H45 - false, //L"K_F", // &H46 - false, //L"K_G", // &H47 - false, //L"K_H", // &H48 - false, //L"K_I", // &H49 - false, //L"K_J", // &H4A - false, //L"K_K", // &H4B - false, //L"K_L", // &H4C - false, //L"K_M", // &H4D - false, //L"K_N", // &H4E - false, //L"K_O", // &H4F - false, //L"K_P", // &H50 - false, //L"K_Q", // &H51 - false, //L"K_R", // &H52 - false, //L"K_S", // &H53 - false, //L"K_T", // &H54 - false, //L"K_U", // &H55 - false, //L"K_V", // &H56 - false, //L"K_W", // &H57 - false, //L"K_X", // &H58 - false, //L"K_Y", // &H59 - false, //L"K_Z", // &H5A - false, //L"K_?5B", // &H5B - false, //L"K_?5C", // &H5C - false, //L"K_?5D", // &H5D - false, //L"K_?5E", // &H5E - false, //L"K_?5F", // &H5F - false, //L"K_NP0", // &H60 - false, //L"K_NP1", // &H61 - false, //L"K_NP2", // &H62 - false, //L"K_NP3", // &H63 - false, //L"K_NP4", // &H64 - false, //L"K_NP5", // &H65 - false, //L"K_NP6", // &H66 - false, //L"K_NP7", // &H67 - false, //L"K_NP8", // &H68 - false, //L"K_NP9", // &H69 - false, //L"K_NPSTAR", // &H6A - false, //L"K_NPPLUS", // &H6B - false, //L"K_SEPARATOR", // &H6C - false, //L"K_NPMINUS", // &H6D - false, //L"K_NPDOT", // &H6E - false, //L"K_NPSLASH", // &H6F - true, //L"K_F1", // &H70 - true, //L"K_F2", // &H71 - true, //L"K_F3", // &H72 - true, //L"K_F4", // &H73 - true, //L"K_F5", // &H74 - true, //L"K_F6", // &H75 - true, //L"K_F7", // &H76 - true, //L"K_F8", // &H77 - true, //L"K_F9", // &H78 - true, //L"K_F10", // &H79 - true, //L"K_F11", // &H7A - true, //L"K_F12", // &H7B - true, //L"K_F13", // &H7C - true, //L"K_F14", // &H7D - true, //L"K_F15", // &H7E - true, //L"K_F16", // &H7F - true, //L"K_F17", // &H80 - true, //L"K_F18", // &H81 - true, //L"K_F19", // &H82 - true, //L"K_F20", // &H83 - true, //L"K_F21", // &H84 - true, //L"K_F22", // &H85 - true, //L"K_F23", // &H86 - true, //L"K_F24", // &H87 - - false, //L"K_?88", // &H88 - false, //L"K_?89", // &H89 - false, //L"K_?8A", // &H8A - false, //L"K_?8B", // &H8B - false, //L"K_?8C", // &H8C - false, //L"K_?8D", // &H8D - false, //L"K_?8E", // &H8E - false, //L"K_?8F", // &H8F - - false, //L"K_NUMLOCK", // &H90 - false, //L"K_SCROLL", // &H91 - - false, //L"K_?92", // &H92 - false, //L"K_?93", // &H93 - false, //L"K_?94", // &H94 - false, //L"K_?95", // &H95 - false, //L"K_?96", // &H96 - false, //L"K_?97", // &H97 - false, //L"K_?98", // &H98 - false, //L"K_?99", // &H99 - false, //L"K_?9A", // &H9A - false, //L"K_?9B", // &H9B - false, //L"K_?9C", // &H9C - false, //L"K_?9D", // &H9D - false, //L"K_?9E", // &H9E - false, //L"K_?9F", // &H9F - false, //L"K_?A0", // &HA0 - false, //L"K_?A1", // &HA1 - false, //L"K_?A2", // &HA2 - false, //L"K_?A3", // &HA3 - false, //L"K_?A4", // &HA4 - false, //L"K_?A5", // &HA5 - false, //L"K_?A6", // &HA6 - false, //L"K_?A7", // &HA7 - false, //L"K_?A8", // &HA8 - false, //L"K_?A9", // &HA9 - false, //L"K_?AA", // &HAA - false, //L"K_?AB", // &HAB - false, //L"K_?AC", // &HAC - false, //L"K_?AD", // &HAD - false, //L"K_?AE", // &HAE - false, //L"K_?AF", // &HAF - false, //L"K_?B0", // &HB0 - false, //L"K_?B1", // &HB1 - false, //L"K_?B2", // &HB2 - false, //L"K_?B3", // &HB3 - false, //L"K_?B4", // &HB4 - false, //L"K_?B5", // &HB5 - false, //L"K_?B6", // &HB6 - false, //L"K_?B7", // &HB7 - false, //L"K_?B8", // &HB8 - false, //L"K_?B9", // &HB9 - - false, //L"K_COLON", // &HBA - false, //L"K_EQUAL", // &HBB - false, //L"K_COMMA", // &HBC - false, //L"K_HYPHEN", // &HBD - false, //L"K_PERIOD", // &HBE - false, //L"K_SLASH", // &HBF - false, //L"K_BKQUOTE", // &HC0 - - false, //L"K_?C1", // &HC1 - false, //L"K_?C2", // &HC2 - false, //L"K_?C3", // &HC3 - false, //L"K_?C4", // &HC4 - false, //L"K_?C5", // &HC5 - false, //L"K_?C6", // &HC6 - false, //L"K_?C7", // &HC7 - false, //L"K_?C8", // &HC8 - false, //L"K_?C9", // &HC9 - false, //L"K_?CA", // &HCA - false, //L"K_?CB", // &HCB - false, //L"K_?CC", // &HCC - false, //L"K_?CD", // &HCD - false, //L"K_?CE", // &HCE - false, //L"K_?CF", // &HCF - false, //L"K_?D0", // &HD0 - false, //L"K_?D1", // &HD1 - false, //L"K_?D2", // &HD2 - false, //L"K_?D3", // &HD3 - false, //L"K_?D4", // &HD4 - false, //L"K_?D5", // &HD5 - false, //L"K_?D6", // &HD6 - false, //L"K_?D7", // &HD7 - false, //L"K_?D8", // &HD8 - false, //L"K_?D9", // &HD9 - false, //L"K_?DA", // &HDA - - false, //L"K_LBRKT", // &HDB - false, //L"K_BKSLASH", // &HDC - false, //L"K_RBRKT", // &HDD - false, //L"K_QUOTE", // &HDE - false, //L"K_oDF", // &HDF - false, //L"K_oE0", // &HE0 - false, //L"K_oE1", // &HE1 - false, //L"K_oE2", // &HE2 - false, //L"K_oE3", // &HE3 - false, //L"K_oE4", // &HE4 - - false, //L"K_?E5", // &HE5 - - false, //L"K_oE6", // &HE6 - - false, //L"K_?E7", // &HE7 - false, //L"K_?E8", // &HE8 - - false, //L"K_oE9", // &HE9 - false, //L"K_oEA", // &HEA - false, //L"K_oEB", // &HEB - false, //L"K_oEC", // &HEC - false, //L"K_oED", // &HED - false, //L"K_oEE", // &HEE - false, //L"K_oEF", // &HEF - false, //L"K_oF0", // &HF0 - false, //L"K_oF1", // &HF1 - false, //L"K_oF2", // &HF2 - false, //L"K_oF3", // &HF3 - false, //L"K_oF4", // &HF4 - false, //L"K_oF5", // &HF5 - - false, //L"K_?F6", // &HF6 - false, //L"K_?F7", // &HF7 - false, //L"K_?F8", // &HF8 - false, //L"K_?F9", // &HF9 - false, //L"K_?FA", // &HFA - false, //L"K_?FB", // &HFB - false, //L"K_?FC", // &HFC - false, //L"K_?FD", // &HFD - false, //L"K_?FE", // &HFE - false, //L"K_?FF" // &HFF -}; - - } // namespace kmx } // namespace core } // namespace km diff --git a/core/src/kmx/kmx_processevent.cpp b/core/src/kmx/kmx_processevent.cpp index 4b98312c369..604c6f6fe2e 100644 --- a/core/src/kmx/kmx_processevent.cpp +++ b/core/src/kmx/kmx_processevent.cpp @@ -276,7 +276,6 @@ KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke) // If there is now no character in the context, we want to // emit the backspace for application to use if(!pdeletecontext || *pdeletecontext == 0) { // I4933 - m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); if(m_debug_items) { m_debug_items->push_group_exit(m_actions.Length(), KM_CORE_DEBUG_FLAG_NOMATCH, gp); } @@ -301,7 +300,6 @@ KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke) return FALSE; } else { // I4024 // I4128 // I4287 // I4290 DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128 - m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0); if(m_debug_items) { m_debug_items->push_group_exit(m_actions.Length(), KM_CORE_DEBUG_FLAG_NOMATCH, gp); } diff --git a/core/src/kmx/kmx_processevent.h b/core/src/kmx/kmx_processevent.h index 681c1782acc..50e84c996d8 100644 --- a/core/src/kmx/kmx_processevent.h +++ b/core/src/kmx/kmx_processevent.h @@ -126,9 +126,6 @@ struct char_to_vkey { extern const struct char_to_vkey s_char_to_vkey[]; -/** for vkeys 0..FF, 'true' if a context reset should be performed before emit */ -extern const bool vkey_to_contextreset[]; - } // namespace kmx } // namespace core } // namespace km diff --git a/core/src/meson.build b/core/src/meson.build index 9943c2fd070..7027136dd4c 100644 --- a/core/src/meson.build +++ b/core/src/meson.build @@ -49,6 +49,7 @@ kmx_files = files( 'keyboard.cpp', 'state.cpp', 'debuglog.cpp', + 'vkey_to_contextreset.cpp', 'km_core_action_api.cpp', 'km_core_context_api.cpp', 'km_core_keyboard_api.cpp', diff --git a/core/src/state.hpp b/core/src/state.hpp index 5d1e5355254..49eea8ffd2f 100644 --- a/core/src/state.hpp +++ b/core/src/state.hpp @@ -184,3 +184,23 @@ struct km_core_state : public km::core::state km_core_state(Args&&... args) : km::core::state(std::forward(args)...) {} }; + + +/** + * Evaluate the state and vkey used. + * Determine whether the context should be invalidated. + * @param state A pointer to the opaque state object. + * @param vk A virtual key that was processed. + * @param modifier_state The combinations of modifier keys set at the time key + * `vk` was pressed, bitmask from the + * km_core_modifier_state enum. + * @param is_key_down 1 if it was a key-down event + * @param event_flags Event level flags, see km_core_event_flags + * @return true if this is a state which should clear the context + */ +bool +state_should_invalidate_context(km_core_state *state, + km_core_virtual_key vk, + uint16_t modifier_state, + uint8_t is_key_down, + uint16_t event_flags); diff --git a/core/src/vkey_to_contextreset.cpp b/core/src/vkey_to_contextreset.cpp new file mode 100644 index 00000000000..914aa3e816b --- /dev/null +++ b/core/src/vkey_to_contextreset.cpp @@ -0,0 +1,278 @@ +#include "vkey_to_contextreset.hpp" + +namespace km { +namespace core { + +bool vkey_should_invalidate_context[vkey_should_invalidate_context_count] = { + true, //L"K_?00", // &H0 + true, //L"K_LBUTTON", // &H1 + true, //L"K_RBUTTON", // &H2 + true, //L"K_CANCEL", // &H3 + true, //L"K_MBUTTON", // &H4 + true, //L"K_?05", // &H5 + true, //L"K_?06", // &H6 + true, //L"K_?07", // &H7 + true, //L"K_BKSP", // &H8 + true, //L"K_TAB", // &H9 + true, //L"K_?0A", // &HA + true, //L"K_?0B", // &HB + true, //L"K_KP5", // &HC + true, //L"K_ENTER", // &HD + true, //L"K_?0E", // &HE + true, //L"K_?0F", // &HF + false, //L"K_SHIFT", // &H10 + false, //L"K_CONTROL", // &H11 + false, //L"K_ALT", // &H12 + true, //L"K_PAUSE", // &H13 + false, //L"K_CAPS", // &H14 + true, //L"K_KANJI?15", // &H15 + true, //L"K_KANJI?16", // &H16 + true, //L"K_KANJI?17", // &H17 + true, //L"K_KANJI?18", // &H18 + true, //L"K_KANJI?19", // &H19 + true, //L"K_?1A", // &H1A + true, //L"K_ESC", // &H1B + true, //L"K_KANJI?1C", // &H1C + true, //L"K_KANJI?1D", // &H1D + true, //L"K_KANJI?1E", // &H1E + true, //L"K_KANJI?1F", // &H1F + false, //L"K_SPACE", // &H20 + true, //L"K_PGUP", // &H21 + true, //L"K_PGDN", // &H22 + true, //L"K_END", // &H23 + true, //L"K_HOME", // &H24 + true, //L"K_LEFT", // &H25 + true, //L"K_UP", // &H26 + true, //L"K_RIGHT", // &H27 + true, //L"K_DOWN", // &H28 + true, //L"K_SEL", // &H29 + true, //L"K_PRINT", // &H2A + true, //L"K_EXEC", // &H2B + true, //L"K_PRTSCN", // &H2C + false, //L"K_INS", // &H2D + true, //L"K_DEL", // &H2E + true, //L"K_HELP", // &H2F + false, //L"K_0", // &H30 + false, //L"K_1", // &H31 + false, //L"K_2", // &H32 + false, //L"K_3", // &H33 + false, //L"K_4", // &H34 + false, //L"K_5", // &H35 + false, //L"K_6", // &H36 + false, //L"K_7", // &H37 + false, //L"K_8", // &H38 + false, //L"K_9", // &H39 + false, //L"K_?3A", // &H3A + false, //L"K_?3B", // &H3B + false, //L"K_?3C", // &H3C + false, //L"K_?3D", // &H3D + false, //L"K_?3E", // &H3E + false, //L"K_?3F", // &H3F + false, //L"K_?40", // &H40 + + false, //L"K_A", // &H41 + false, //L"K_B", // &H42 + false, //L"K_C", // &H43 + false, //L"K_D", // &H44 + false, //L"K_E", // &H45 + false, //L"K_F", // &H46 + false, //L"K_G", // &H47 + false, //L"K_H", // &H48 + false, //L"K_I", // &H49 + false, //L"K_J", // &H4A + false, //L"K_K", // &H4B + false, //L"K_L", // &H4C + false, //L"K_M", // &H4D + false, //L"K_N", // &H4E + false, //L"K_O", // &H4F + false, //L"K_P", // &H50 + false, //L"K_Q", // &H51 + false, //L"K_R", // &H52 + false, //L"K_S", // &H53 + false, //L"K_T", // &H54 + false, //L"K_U", // &H55 + false, //L"K_V", // &H56 + false, //L"K_W", // &H57 + false, //L"K_X", // &H58 + false, //L"K_Y", // &H59 + false, //L"K_Z", // &H5A + false, //L"K_?5B", // &H5B + false, //L"K_?5C", // &H5C + false, //L"K_?5D", // &H5D + false, //L"K_?5E", // &H5E + false, //L"K_?5F", // &H5F + false, //L"K_NP0", // &H60 + false, //L"K_NP1", // &H61 + false, //L"K_NP2", // &H62 + false, //L"K_NP3", // &H63 + false, //L"K_NP4", // &H64 + false, //L"K_NP5", // &H65 + false, //L"K_NP6", // &H66 + false, //L"K_NP7", // &H67 + false, //L"K_NP8", // &H68 + false, //L"K_NP9", // &H69 + false, //L"K_NPSTAR", // &H6A + false, //L"K_NPPLUS", // &H6B + false, //L"K_SEPARATOR", // &H6C + false, //L"K_NPMINUS", // &H6D + false, //L"K_NPDOT", // &H6E + false, //L"K_NPSLASH", // &H6F + true, //L"K_F1", // &H70 + true, //L"K_F2", // &H71 + true, //L"K_F3", // &H72 + true, //L"K_F4", // &H73 + true, //L"K_F5", // &H74 + true, //L"K_F6", // &H75 + true, //L"K_F7", // &H76 + true, //L"K_F8", // &H77 + true, //L"K_F9", // &H78 + true, //L"K_F10", // &H79 + true, //L"K_F11", // &H7A + true, //L"K_F12", // &H7B + true, //L"K_F13", // &H7C + true, //L"K_F14", // &H7D + true, //L"K_F15", // &H7E + true, //L"K_F16", // &H7F + true, //L"K_F17", // &H80 + true, //L"K_F18", // &H81 + true, //L"K_F19", // &H82 + true, //L"K_F20", // &H83 + true, //L"K_F21", // &H84 + true, //L"K_F22", // &H85 + true, //L"K_F23", // &H86 + true, //L"K_F24", // &H87 + + false, //L"K_?88", // &H88 + false, //L"K_?89", // &H89 + false, //L"K_?8A", // &H8A + false, //L"K_?8B", // &H8B + false, //L"K_?8C", // &H8C + false, //L"K_?8D", // &H8D + false, //L"K_?8E", // &H8E + false, //L"K_?8F", // &H8F + + false, //L"K_NUMLOCK", // &H90 + false, //L"K_SCROLL", // &H91 + + false, //L"K_?92", // &H92 + false, //L"K_?93", // &H93 + false, //L"K_?94", // &H94 + false, //L"K_?95", // &H95 + false, //L"K_?96", // &H96 + false, //L"K_?97", // &H97 + false, //L"K_?98", // &H98 + false, //L"K_?99", // &H99 + false, //L"K_?9A", // &H9A + false, //L"K_?9B", // &H9B + false, //L"K_?9C", // &H9C + false, //L"K_?9D", // &H9D + false, //L"K_?9E", // &H9E + false, //L"K_?9F", // &H9F + false, //L"K_?A0", // &HA0 + false, //L"K_?A1", // &HA1 + false, //L"K_?A2", // &HA2 + false, //L"K_?A3", // &HA3 + false, //L"K_?A4", // &HA4 + false, //L"K_?A5", // &HA5 + false, //L"K_?A6", // &HA6 + false, //L"K_?A7", // &HA7 + false, //L"K_?A8", // &HA8 + false, //L"K_?A9", // &HA9 + false, //L"K_?AA", // &HAA + false, //L"K_?AB", // &HAB + false, //L"K_?AC", // &HAC + false, //L"K_?AD", // &HAD + false, //L"K_?AE", // &HAE + false, //L"K_?AF", // &HAF + false, //L"K_?B0", // &HB0 + false, //L"K_?B1", // &HB1 + false, //L"K_?B2", // &HB2 + false, //L"K_?B3", // &HB3 + false, //L"K_?B4", // &HB4 + false, //L"K_?B5", // &HB5 + false, //L"K_?B6", // &HB6 + false, //L"K_?B7", // &HB7 + false, //L"K_?B8", // &HB8 + false, //L"K_?B9", // &HB9 + + false, //L"K_COLON", // &HBA + false, //L"K_EQUAL", // &HBB + false, //L"K_COMMA", // &HBC + false, //L"K_HYPHEN", // &HBD + false, //L"K_PERIOD", // &HBE + false, //L"K_SLASH", // &HBF + false, //L"K_BKQUOTE", // &HC0 + + false, //L"K_?C1", // &HC1 + false, //L"K_?C2", // &HC2 + false, //L"K_?C3", // &HC3 + false, //L"K_?C4", // &HC4 + false, //L"K_?C5", // &HC5 + false, //L"K_?C6", // &HC6 + false, //L"K_?C7", // &HC7 + false, //L"K_?C8", // &HC8 + false, //L"K_?C9", // &HC9 + false, //L"K_?CA", // &HCA + false, //L"K_?CB", // &HCB + false, //L"K_?CC", // &HCC + false, //L"K_?CD", // &HCD + false, //L"K_?CE", // &HCE + false, //L"K_?CF", // &HCF + false, //L"K_?D0", // &HD0 + false, //L"K_?D1", // &HD1 + false, //L"K_?D2", // &HD2 + false, //L"K_?D3", // &HD3 + false, //L"K_?D4", // &HD4 + false, //L"K_?D5", // &HD5 + false, //L"K_?D6", // &HD6 + false, //L"K_?D7", // &HD7 + false, //L"K_?D8", // &HD8 + false, //L"K_?D9", // &HD9 + false, //L"K_?DA", // &HDA + + false, //L"K_LBRKT", // &HDB + false, //L"K_BKSLASH", // &HDC + false, //L"K_RBRKT", // &HDD + false, //L"K_QUOTE", // &HDE + false, //L"K_oDF", // &HDF + false, //L"K_oE0", // &HE0 + false, //L"K_oE1", // &HE1 + false, //L"K_oE2", // &HE2 + false, //L"K_oE3", // &HE3 + false, //L"K_oE4", // &HE4 + + false, //L"K_?E5", // &HE5 + + false, //L"K_oE6", // &HE6 + + false, //L"K_?E7", // &HE7 + false, //L"K_?E8", // &HE8 + + false, //L"K_oE9", // &HE9 + false, //L"K_oEA", // &HEA + false, //L"K_oEB", // &HEB + false, //L"K_oEC", // &HEC + false, //L"K_oED", // &HED + false, //L"K_oEE", // &HEE + false, //L"K_oEF", // &HEF + false, //L"K_oF0", // &HF0 + false, //L"K_oF1", // &HF1 + false, //L"K_oF2", // &HF2 + false, //L"K_oF3", // &HF3 + false, //L"K_oF4", // &HF4 + false, //L"K_oF5", // &HF5 + + false, //L"K_?F6", // &HF6 + false, //L"K_?F7", // &HF7 + false, //L"K_?F8", // &HF8 + false, //L"K_?F9", // &HF9 + false, //L"K_?FA", // &HFA + false, //L"K_?FB", // &HFB + false, //L"K_?FC", // &HFC + false, //L"K_?FD", // &HFD + false, //L"K_?FE", // &HFE + false, //L"K_?FF" // &HFF +}; + +} +} diff --git a/core/src/vkey_to_contextreset.hpp b/core/src/vkey_to_contextreset.hpp new file mode 100644 index 00000000000..c7c6b1953d0 --- /dev/null +++ b/core/src/vkey_to_contextreset.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "kmx_file.h" + +namespace km { +namespace core { + +static const auto vkey_should_invalidate_context_count = 256; +/** true for any vkeys which require a context reset (such as frame keys) */ +extern bool vkey_should_invalidate_context[vkey_should_invalidate_context_count]; + +/** @return true for any vkeys which require a context reset (such as frame keys) */ +inline bool vkey_should_contextreset(km_core_virtual_key vk) { + return ((vk < vkey_should_invalidate_context_count) && vkey_should_invalidate_context[vk]); +} + +/** @return true for modifier states that are involved with context reset */ +inline bool modifier_should_contextreset(uint16_t modifier_state) { + // ctrl or alt + return (modifier_state & (K_CTRLFLAG | K_ALTFLAG | LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)); +} + + +} +} diff --git a/core/tests/unit/kmnkbd/debug_api.cpp b/core/tests/unit/kmnkbd/debug_api.cpp index 50d6ba72ba0..6542f3c378f 100644 --- a/core/tests/unit/kmnkbd/debug_api.cpp +++ b/core/tests/unit/kmnkbd/debug_api.cpp @@ -120,8 +120,8 @@ void test_debugging_function_key() { assert(debug_items(test_state, { km_core_state_debug_item{KM_CORE_DEBUG_BEGIN, KM_CORE_DEBUG_FLAG_UNICODE, {KM_CORE_VKEY_F1, 0, 0}}, km_core_state_debug_item{KM_CORE_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}}, - km_core_state_debug_item{KM_CORE_DEBUG_GROUP_EXIT, KM_CORE_DEBUG_FLAG_NOMATCH, {}, {u"", &gp, nullptr, {}, 1}}, - km_core_state_debug_item{KM_CORE_DEBUG_END, KM_CORE_DEBUG_FLAG_OUTPUTKEYSTROKE, {}, {u"", nullptr, nullptr, {}, 1}} + km_core_state_debug_item{KM_CORE_DEBUG_GROUP_EXIT, KM_CORE_DEBUG_FLAG_NOMATCH, {}, {u"", &gp, nullptr, {}, 0}}, + km_core_state_debug_item{KM_CORE_DEBUG_END, KM_CORE_DEBUG_FLAG_OUTPUTKEYSTROKE, {}, {u"", nullptr, nullptr, {}, 0}} })); assert(action_items(test_state, { @@ -439,8 +439,8 @@ void test_backspace_markers() { assert(debug_items(test_state, { km_core_state_debug_item{KM_CORE_DEBUG_BEGIN, KM_CORE_DEBUG_FLAG_UNICODE, {KM_CORE_VKEY_BKSP, 0, 0}}, km_core_state_debug_item{KM_CORE_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}}, - km_core_state_debug_item{KM_CORE_DEBUG_GROUP_EXIT, 2, {}, {u"", &gp, nullptr, {}, 3}}, - km_core_state_debug_item{KM_CORE_DEBUG_END, 1, {}, {u"", nullptr, nullptr, {}, 3}}, + km_core_state_debug_item{KM_CORE_DEBUG_GROUP_EXIT, 2, {}, {u"", &gp, nullptr, {}, 2}}, + km_core_state_debug_item{KM_CORE_DEBUG_END, 1, {}, {u"", nullptr, nullptr, {}, 2}}, })); km_core_action_item bksp = {KM_CORE_IT_BACK}; diff --git a/core/tests/unit/ldml/invalid-keyboards/ik_000_null_invalid.xml b/core/tests/unit/ldml/invalid-keyboards/ik_000_null_invalid.xml index a16eb613468..6e10b734e97 100644 --- a/core/tests/unit/ldml/invalid-keyboards/ik_000_null_invalid.xml +++ b/core/tests/unit/ldml/invalid-keyboards/ik_000_null_invalid.xml @@ -1,4 +1,4 @@ - +Note that the 'expected' lines are cumulative unless there's a reset event. + +@@keys: [K_BKQUOTE][K_1][K_ENTER][K_Z] +@@expected: z +Comment: 1=gap, enter=not mapped/mappable - reset. (z used as a subtest separator) + +@@keys: [K_Q][K_BKQUOTE][K_Z] +@@expected: zAz +Comment: q\m{q}a => A due to transform + +@@keys: [K_Q][K_2][K_BKQUOTE][K_Z] +@@expected: zAzAz +Comment: 2=not mapped, but NO reset. + +@@keys: [K_Q][K_1][K_BKQUOTE][K_Z] +@@expected: zAzAzAz +Comment: 1 is a gap (no effect) so no ctx reset + +@@keys: [K_Q][K_ENTER][K_BKQUOTE][K_Z] +@@expected: az +Comment: enter=not mappable, causes ctx reset. - diff --git a/core/tests/unit/ldml/ldml.cpp b/core/tests/unit/ldml/ldml.cpp index bef485a8783..9a5c7f60e34 100644 --- a/core/tests/unit/ldml/ldml.cpp +++ b/core/tests/unit/ldml/ldml.cpp @@ -195,64 +195,64 @@ apply_action( /** * verify the current context -*/ + */ void -verify_context(std::u16string& text_store, km_core_state* &test_state, std::vector &test_context) { - // Compare context and text store at each step - should be identical - size_t n = 0; - km_core_context_item* citems = nullptr; - try_status(km_core_context_get(km_core_state_context(test_state), &citems)); - try_status(context_items_to_utf16(citems, nullptr, &n)); - km_core_cp *buf = new km_core_cp[n]; - try_status(context_items_to_utf16(citems, buf, &n)); - std::cout << "context (raw): "; // output including markers (which aren't in 'buf' here) - for (auto ci = citems; ci->type != KM_CORE_CT_END; ci++) { - switch(ci->type) { - case KM_CORE_CT_CHAR: - std::cout << "U+" << std::setw(4) << std::hex << ci->character << std::dec << " "; - break; - case KM_CORE_CT_MARKER: - std::cout << "\\m{" << ci->character << "} "; - break; - default: - std::cout << "type#" << ci->type << " "; - } +verify_context(std::u16string &text_store, km_core_state *&test_state, std::vector &test_context) { + // Compare context and text store at each step - should be identical + size_t n = 0; + km_core_context_item *citems = nullptr; + try_status(km_core_context_get(km_core_state_context(test_state), &citems)); + try_status(context_items_to_utf16(citems, nullptr, &n)); + km_core_cp *buf = new km_core_cp[n]; + try_status(context_items_to_utf16(citems, buf, &n)); + std::cout << "context (raw): "; // output including markers (which aren't in 'buf' here) + for (auto ci = citems; ci->type != KM_CORE_CT_END; ci++) { + switch (ci->type) { + case KM_CORE_CT_CHAR: + std::cout << "U+" << std::setw(4) << std::hex << ci->character << std::dec << " "; + break; + case KM_CORE_CT_MARKER: + std::cout << "\\m{" << ci->character << "} "; + break; + default: + std::cout << "type#" << ci->type << " "; } - std::cout << std::endl; - std::cout << "context : " << string_to_hex(buf) << " [" << buf << "]" << std::endl; - std::cout << "testcontext "; - std::cout.fill('0'); - for (auto i = test_context.begin(); i < test_context.end(); i++) { - switch(i->type) { - case KM_CORE_CT_CHAR: - std::cout << "U+" << std::setw(4) << std::hex << i->character << std::dec << " "; - break; - case KM_CORE_CT_MARKER: - std::cout << "\\m{" << i->character << "} "; - break; - default: - std::cout << "type#" << i->type << " "; - } + } + std::cout << std::endl; + std::cout << "context : " << string_to_hex(buf) << " [" << buf << "]" << std::endl; + std::cout << "testcontext "; + std::cout.fill('0'); + for (auto i = test_context.begin(); i < test_context.end(); i++) { + switch (i->type) { + case KM_CORE_CT_CHAR: + std::cout << "U+" << std::setw(4) << std::hex << i->character << std::dec << " "; + break; + case KM_CORE_CT_MARKER: + std::cout << "\\m{" << i->character << "} "; + break; + default: + std::cout << "type#" << i->type << " "; } - std::cout << std::endl; - - // Verify that both our local test_context and the core's test_state.context have - // not diverged - auto ci = citems; - for (auto test_ci = test_context.begin(); ci->type != KM_CORE_CT_END || test_ci != test_context.end(); ci++, test_ci++) { - // skip over markers, they won't be in test_context - while (ci->type == KM_CORE_CT_MARKER) { - ci++; - } - // exit if BOTH are at end. - if (ci->type == KM_CORE_CT_END && test_ci == test_context.end()) { - break; // success - } - // fail if only ONE is at end - assert(ci->type != KM_CORE_CT_END && test_ci != test_context.end()); - // fail if type and marker don't match. - assert(test_ci->type == ci->type && test_ci->marker == ci->marker); + } + std::cout << std::endl; + + // Verify that both our local test_context and the core's test_state.context have + // not diverged + auto ci = citems; + for (auto test_ci = test_context.begin();; ci++, test_ci++) { + // skip over markers, they won't be in test_context + while (ci->type == KM_CORE_CT_MARKER) { + ci++; + } + // exit if BOTH are at end. + if (ci->type == KM_CORE_CT_END && test_ci == test_context.end()) { + break; // success } + // fail if only ONE is at end + assert(ci->type != KM_CORE_CT_END && test_ci != test_context.end()); + // fail if type and marker don't match. + assert(test_ci->type == ci->type && test_ci->marker == ci->marker); + } km_core_context_items_dispose(citems); if (text_store != buf) { @@ -329,6 +329,11 @@ run_test(const km::core::path &source, const km::core::path &compiled, km::tests test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down, KM_CORE_EVENT_FLAG_DEFAULT)); // TODO-LDML: for now. Should send touch and hardware events. + if (state_should_invalidate_context(test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down, + KM_CORE_EVENT_FLAG_DEFAULT)) { + test_context.clear(); + text_store.clear(); + } for (auto act = km_core_state_action_items(test_state, nullptr); act->type != KM_CORE_IT_END; act++) { apply_action(test_state, *act, text_store, test_context, test_source, test_context); } diff --git a/core/tests/unit/ldml/ldml_test_source.cpp b/core/tests/unit/ldml/ldml_test_source.cpp index 71c5a7ee4e0..2db7efe6e31 100644 --- a/core/tests/unit/ldml/ldml_test_source.cpp +++ b/core/tests/unit/ldml/ldml_test_source.cpp @@ -280,13 +280,15 @@ LdmlEmbeddedTestSource::load_source( const km::core::path &path ) { if (!line.length()) continue; if (line.compare(0, s_keys.length(), s_keys) == 0) { - keys = line.substr(s_keys.length()); - trim(keys); + auto k = line.substr(s_keys.length()); + trim(k); + keys.emplace_back(k); } else if (is_token(s_expected, line)) { if (line == "\\b") { expected_beep = true; } else { - expected = parse_source_string(line); + // allow multiple expected lines + expected.emplace_back(parse_source_string(line)); } } else if (is_token(s_expecterror, line)) { expected_error = true; @@ -297,8 +299,15 @@ LdmlEmbeddedTestSource::load_source( const km::core::path &path ) { } } - if (keys == "") { + if (keys.empty() && expected.empty() && !expected_error) { + // don't note this, the parent will complain if there's neither json nor embedded + return __LINE__; + } else if (keys.empty()) { // We must at least have a key sequence to run the test + std::cerr << "Need at least one key sequence." << std::endl; + return __LINE__; + } else if(!expected_error && (keys.size() != expected.size())) { + std::cerr << "Need the same number of " << s_keys << " and " << s_expected << " lines." << std::endl; return __LINE__; } @@ -387,15 +396,19 @@ LdmlEmbeddedTestSource::vkey_to_event(std::string const &vk_event) { void LdmlEmbeddedTestSource::next_action(ldml_action &fillin) { - if (is_done) { + if (is_done || keys.empty()) { // We were already done. return done. fillin.type = LDML_ACTION_DONE; return; - } else if(keys.empty()) { - // Got to the end of the keys. time to check + } else if(keys[0].empty()) { + // Got to the end of a key set. time to check fillin.type = LDML_ACTION_CHECK_EXPECTED; - fillin.string = expected; // copy expected - is_done = true; // so we get DONE next time + fillin.string = expected[0]; // copy expected + expected.pop_front(); + keys.pop_front(); + if (keys.empty()) { + is_done = true; // so we get DONE next time + } } else { fillin.type = LDML_ACTION_KEY_EVENT; fillin.k = next_key(); @@ -406,7 +419,7 @@ LdmlEmbeddedTestSource::next_action(ldml_action &fillin) { key_event LdmlEmbeddedTestSource::next_key() { // mutate this->keys - return next_key(keys); + return next_key(keys[0]); } key_event diff --git a/core/tests/unit/ldml/ldml_test_source.hpp b/core/tests/unit/ldml/ldml_test_source.hpp index a0eed2ec8e1..4637af9bd44 100644 --- a/core/tests/unit/ldml/ldml_test_source.hpp +++ b/core/tests/unit/ldml/ldml_test_source.hpp @@ -149,8 +149,9 @@ class LdmlEmbeddedTestSource : public LdmlTestSource { key_event next_key(std::string &keys); key_event next_key(); - std::string keys = ""; - std::u16string expected = u"", context = u""; + std::deque keys; + std::deque expected; + std::u16string context = u""; bool expected_beep = false; bool expected_error = false; bool is_done = false;