diff --git a/core/include/keyman/keyman_core_api_context.h b/core/include/keyman/keyman_core_api_context.h index dfa271e7b2..7b42d1c820 100644 --- a/core/include/keyman/keyman_core_api_context.h +++ b/core/include/keyman/keyman_core_api_context.h @@ -92,7 +92,7 @@ km_core_state_get_intermediate_context(km_core_state *state, km_core_context_ite ##### Description: Free the allocated memory belonging to a `km_core_context_item` array previously returned by `km_core_state_get_intermediate_context` (internally, also -`context_items_from_utf16` and `context_get`) +`context_items_from_utf16` and `km_core_context_get`) ##### Parameters: - __context_items__: A pointer to the start of the `km_core_context_item` array to be disposed of. @@ -182,6 +182,41 @@ KMN_API size_t km_core_context_item_list_size(km_core_context_item const *context_items); +/** + * + * Copies all items in the context into a new array and returns the new array. + * This must be disposed of by caller using `km_core_context_items_dispose`. + * + * @return km_core_status + * * `KM_CORE_STATUS_OK`: On success. + * * `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are + * null. + * * `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be + * allocated for the output buffer. + * + * @param context A pointer to an opaque context object + * @param out A pointer to the result variable: A pointer to the start + * of the `km_core_context_item` array containing a copy of + * the context. Terminated with a type of `KM_CORE_CT_END`. + * Must be disposed of with + * `km_core_context_items_dispose`. + */ +KMN_API +km_core_status +km_core_context_get(km_core_context const *context, + km_core_context_item **out); + +/** + * Return the number of items in the context. + * + * @return size_t The number of items in the context, and will return 0 if + * passed a null `context` pointer. + * @param context A pointer to an opaque context object +*/ +KMN_API +size_t +km_core_context_length(km_core_context *); + #if defined(__cplusplus) } // extern "C" #endif diff --git a/core/src/actions_normalize.cpp b/core/src/actions_normalize.cpp index 8dc944620a..ded302fe15 100644 --- a/core/src/actions_normalize.cpp +++ b/core/src/actions_normalize.cpp @@ -228,7 +228,7 @@ icu::UnicodeString context_items_to_unicode_string(km_core_context const *contex km_core_context_item *items = nullptr; km_core_status status; - if((status = context_get(context, &items)) != KM_CORE_STATUS_OK) { + if((status = km_core_context_get(context, &items)) != KM_CORE_STATUS_OK) { DebugLog("Failed to retrieve context with %s", status); return nullString; } @@ -297,8 +297,8 @@ bool km::core::actions_update_app_context_nfu( km_core_status status = KM_CORE_STATUS_OK; km_core_context_item *items = nullptr; - if((status = context_get(cached_context, &items)) != KM_CORE_STATUS_OK) { - DebugLog("context_get failed with %d", status); + if((status = km_core_context_get(cached_context, &items)) != KM_CORE_STATUS_OK) { + DebugLog("km_core_context_get failed with %d", status); return false; } diff --git a/core/src/context.hpp b/core/src/context.hpp index 5f71ab86dd..9366835839 100644 --- a/core/src/context.hpp +++ b/core/src/context.hpp @@ -169,38 +169,6 @@ km_core_status context_items_to_utf32(km_core_context_item const *item, km_core_usv *buf, size_t *buf_size); -/** - * - * Copies all items in the context into a new array and returns the new array. - * This must be disposed of by caller using `km_core_context_items_dispose`. - * - * @return km_core_status - * * `KM_CORE_STATUS_OK`: On success. - * * `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are - * null. - * * `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be - * allocated for the output buffer. - * - * @param context A pointer to an opaque context object - * @param out A pointer to the result variable: A pointer to the start - * of the `km_core_context_item` array containing a copy of - * the context. Terminated with a type of `KM_CORE_CT_END`. - * Must be disposed of with - * `km_core_context_items_dispose`. - */ -km_core_status -context_get(km_core_context const *context, - km_core_context_item **out); - -/** - * Return the number of items in the context. - * - * @return size_t The number of items in the context, and will return 0 if - * passed a null `context` pointer. - * @param context A pointer to an opaque context object -*/ -size_t -context_length(km_core_context *); /** * Add more items to the end (insertion point) of the context. If these exceed diff --git a/core/src/context_helpers.cpp b/core/src/context_helpers.cpp index c3e26ac207..6c687f3200 100644 --- a/core/src/context_helpers.cpp +++ b/core/src/context_helpers.cpp @@ -24,7 +24,7 @@ km_core_cp* km::core::get_context_as_string(km_core_context *context) { size_t buf_size = 0; km_core_context_item* context_items = nullptr; - if(context_get(context, &context_items) != KM_CORE_STATUS_OK) { + if(km_core_context_get(context, &context_items) != KM_CORE_STATUS_OK) { return nullptr; } diff --git a/core/src/km_core_context_api.cpp b/core/src/km_core_context_api.cpp index 50d0658816..134200b711 100644 --- a/core/src/km_core_context_api.cpp +++ b/core/src/km_core_context_api.cpp @@ -153,7 +153,7 @@ km_core_status km_core_context_set(km_core_context *ctxt, km_core_context_item c } -km_core_status context_get(km_core_context const *ctxt, +km_core_status km_core_context_get(km_core_context const *ctxt, km_core_context_item **out_ptr) { assert(ctxt); assert(out_ptr); @@ -184,7 +184,7 @@ void km_core_context_clear(km_core_context *ctxt) } -size_t context_length(km_core_context *ctxt) +size_t km_core_context_length(km_core_context *ctxt) { assert(ctxt); return ctxt ? ctxt->size() : 0; diff --git a/core/src/km_core_state_api.cpp b/core/src/km_core_state_api.cpp index 475c70db0d..8b8807b1d2 100644 --- a/core/src/km_core_state_api.cpp +++ b/core/src/km_core_state_api.cpp @@ -310,11 +310,11 @@ km_core_cp * km_core_state_context_debug( return _new_error_string(u""); } } else if(context_type == KM_CORE_DEBUG_CONTEXT_CACHED) { - if(context_get(km_core_state_context(state), &context_items) != KM_CORE_STATUS_OK) { + if(km_core_context_get(km_core_state_context(state), &context_items) != KM_CORE_STATUS_OK) { return _new_error_string(u""); } } else if(context_type == KM_CORE_DEBUG_CONTEXT_APP) { - if(context_get(km_core_state_app_context(state), &context_items) != KM_CORE_STATUS_OK) { + if(km_core_context_get(km_core_state_app_context(state), &context_items) != KM_CORE_STATUS_OK) { return _new_error_string(u""); } } else { diff --git a/core/tests/unit/kmnkbd/action_api.cpp b/core/tests/unit/kmnkbd/action_api.cpp index a9e0058b56..6107c510aa 100644 --- a/core/tests/unit/kmnkbd/action_api.cpp +++ b/core/tests/unit/kmnkbd/action_api.cpp @@ -218,6 +218,16 @@ int main(int argc, char *argv []) { } console_color::enabled = console_color::isaterminal() || arg_color; + km_core_actions act = {0}; + + std::cout << "sizeof(km_core_actions): " << sizeof(km_core_actions) << std::endl; + std::cout << "&km_core_actions.code_points_to_delete: " << ((intptr_t)(&act.code_points_to_delete)-(intptr_t)(&act)) << std::endl; + std::cout << "&km_core_actions.output: " << ((intptr_t)(&act.output)-(intptr_t)(&act)) << std::endl; + std::cout << "&km_core_actions.persist_options: " << ((intptr_t)(&act.persist_options)-(intptr_t)(&act)) << std::endl; + std::cout << "&km_core_actions.do_alert: " << ((intptr_t)(&act.do_alert)-(intptr_t)(&act)) << std::endl; + std::cout << "&km_core_actions.emit_keystroke: " << ((intptr_t)(&act.emit_keystroke)-(intptr_t)(&act)) << std::endl; + std::cout << "&km_core_actions.new_caps_lock_state: " << ((intptr_t)(&act.new_caps_lock_state)-(intptr_t)(&act)) << std::endl; + // actions test_two_backspaces(); test_marker_text_interleaved(); diff --git a/core/tests/unit/kmnkbd/context_api.cpp b/core/tests/unit/kmnkbd/context_api.cpp index e0f675ded3..0b3bcf3673 100644 --- a/core/tests/unit/kmnkbd/context_api.cpp +++ b/core/tests/unit/kmnkbd/context_api.cpp @@ -79,19 +79,19 @@ int main(int, char * []) km_core_context_items_dispose(ctxt2); // Test lengths, these are Unicode Scalar Values, not utf16 codeunits. - if(context_length(&mock_ctxt1) != bmp_ctxt_size) return __LINE__; - if(context_length(&mock_ctxt2) != smp_ctxt_size) return __LINE__; + if(km_core_context_length(&mock_ctxt1) != bmp_ctxt_size) return __LINE__; + if(km_core_context_length(&mock_ctxt2) != smp_ctxt_size) return __LINE__; // retrieve bmp context and check it's okay. km_core_context_item *tmp_ctxt; - try_status(context_get(&mock_ctxt1, &tmp_ctxt)); + try_status(km_core_context_get(&mock_ctxt1, &tmp_ctxt)); ctxt_size=sizeof ctxt_buffer/sizeof(km_core_cp); try_status(context_items_to_utf16(tmp_ctxt, ctxt_buffer, &ctxt_size)); km_core_context_items_dispose(tmp_ctxt); if (initial_bmp_context != ctxt_buffer) return __LINE__; // retrieve smp context and check it's okay. - try_status(context_get(&mock_ctxt2, &tmp_ctxt)); + try_status(km_core_context_get(&mock_ctxt2, &tmp_ctxt)); ctxt_size=sizeof ctxt_buffer/sizeof(km_core_cp); try_status(context_items_to_utf16(tmp_ctxt, ctxt_buffer, &ctxt_size)); km_core_context_items_dispose(tmp_ctxt); @@ -99,7 +99,7 @@ int main(int, char * []) // Call km_core_context_clear km_core_context_clear(&mock_ctxt2); - if(context_length(&mock_ctxt2) != 0) return __LINE__; + if(km_core_context_length(&mock_ctxt2) != 0) return __LINE__; // Mutation tests try_status(context_shrink(&mock_ctxt1, 42, nullptr)); @@ -117,7 +117,7 @@ int main(int, char * []) // Check it matches. The marker will be elided during the conversion. ctxt_size=sizeof ctxt_buffer/sizeof(km_core_cp); - try_status(context_get(&mock_ctxt1, &tmp_ctxt)); + try_status(km_core_context_get(&mock_ctxt1, &tmp_ctxt)); try_status(context_items_to_utf16(tmp_ctxt, ctxt_buffer, &ctxt_size)); if (std::u16string(u"Hello World!") != ctxt_buffer) return __LINE__; km_core_context_items_dispose(tmp_ctxt); @@ -128,7 +128,7 @@ int main(int, char * []) // expected if you go by the test string above. try_status(context_shrink(&mock_ctxt1, 8, ctxt1)); ctxt_size=sizeof ctxt_buffer/sizeof(km_core_cp); - try_status(context_get(&mock_ctxt1, &tmp_ctxt)); + try_status(km_core_context_get(&mock_ctxt1, &tmp_ctxt)); try_status(context_items_to_utf16(tmp_ctxt, ctxt_buffer, &ctxt_size)); if (std::u16string(u"Bye, Hello") != ctxt_buffer) return __LINE__; diff --git a/core/tests/unit/kmnkbd/state_api.cpp b/core/tests/unit/kmnkbd/state_api.cpp index a28cc04490..1da70c33fa 100644 --- a/core/tests/unit/kmnkbd/state_api.cpp +++ b/core/tests/unit/kmnkbd/state_api.cpp @@ -133,9 +133,9 @@ int main(int argc, char * argv[]) try_status(context_items_from_utf16(u"Hello 😁", &citems)); try_status(km_core_context_set(km_core_state_context(test_state), citems)); km_core_context_items_dispose(citems); - if(context_length(km_core_state_context(test_state)) != 7) + if(km_core_context_length(km_core_state_context(test_state)) != 7) return __LINE__; - if(context_length(km_core_state_context(test_clone)) != 0) + if(km_core_context_length(km_core_state_context(test_clone)) != 0) return __LINE__; // Overwrite some data. diff --git a/core/tests/unit/kmnkbd/state_context_api.cpp b/core/tests/unit/kmnkbd/state_context_api.cpp index 9b1a2b5da8..32af9d88e6 100644 --- a/core/tests/unit/kmnkbd/state_context_api.cpp +++ b/core/tests/unit/kmnkbd/state_context_api.cpp @@ -51,7 +51,7 @@ setup(const char *keyboard, const km_core_cp *context) { bool is_identical_context(km_core_cp const *cached_context) { size_t buf_size; - try_status(context_get(km_core_state_context(test_state), &citems)); + try_status(km_core_context_get(km_core_state_context(test_state), &citems)); try_status(context_items_to_utf16(citems, nullptr, &buf_size)); km_core_cp *new_cached_context = new km_core_cp[buf_size]; try_status(context_items_to_utf16(citems, new_cached_context, &buf_size)); @@ -145,7 +145,7 @@ test_context_set_if_needed_cached_context_has_markers() { km_core_context_item *citems_new; - try_status(context_get(km_core_state_context(test_state), &citems_new)); + try_status(km_core_context_get(km_core_state_context(test_state), &citems_new)); for (int i = 0; citems[i].type || citems_new[i].type; i++) { assert(citems_new[i].type == citems[i].type); diff --git a/core/tests/unit/kmx/kmx.cpp b/core/tests/unit/kmx/kmx.cpp index e2dd8cb275..ac93441c1a 100644 --- a/core/tests/unit/kmx/kmx.cpp +++ b/core/tests/unit/kmx/kmx.cpp @@ -244,7 +244,7 @@ run_test(const km::core::path &source, const km::core::path &compiled) { // Compare context and text store at each step // should be identical unless an action has caused the context to be invalidated size_t n = 0; - try_status(context_get(km_core_state_context(test_state), &citems)); + 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 *core_context_str = new km_core_cp[n]; try_status(context_items_to_utf16(citems, core_context_str, &n)); @@ -271,7 +271,7 @@ run_test(const km::core::path &source, const km::core::path &compiled) { // Compare final output - retrieve internal context size_t n = 0; - try_status(context_get(km_core_state_context(test_state), &citems)); + 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 *core_context_str = new km_core_cp[n]; try_status(context_items_to_utf16(citems, core_context_str, &n)); diff --git a/core/tests/unit/ldml/ldml.cpp b/core/tests/unit/ldml/ldml.cpp index a87cd9d647..ef1d4eb030 100644 --- a/core/tests/unit/ldml/ldml.cpp +++ b/core/tests/unit/ldml/ldml.cpp @@ -199,7 +199,7 @@ verify_context(std::u16string& text_store, km_core_state* &test_state, std::vect // Compare context and text store at each step - should be identical size_t n = 0; km_core_context_item* citems = nullptr; - try_status(context_get(km_core_state_context(test_state), &citems)); + 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)); diff --git a/core/tests/unit/ldml/test_context_normalization.cpp b/core/tests/unit/ldml/test_context_normalization.cpp index e2954b54d6..f9c9a694ff 100644 --- a/core/tests/unit/ldml/test_context_normalization.cpp +++ b/core/tests/unit/ldml/test_context_normalization.cpp @@ -63,9 +63,9 @@ bool is_identical_context(km_core_cp const *cached_context, km_core_debug_contex debug_context(context_type); if(context_type == KM_CORE_DEBUG_CONTEXT_APP) { - try_status(context_get(km_core_state_app_context(test_state), &citems)); + try_status(km_core_context_get(km_core_state_app_context(test_state), &citems)); } else { - try_status(context_get(km_core_state_context(test_state), &citems)); + try_status(km_core_context_get(km_core_state_context(test_state), &citems)); } try_status(context_items_to_utf16(citems, nullptr, &buf_size)); km_core_cp* new_cached_context = new km_core_cp[buf_size]; diff --git a/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.dfm b/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.dfm index 53b4975e43..bf8bba4cbd 100644 --- a/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.dfm +++ b/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.dfm @@ -37,7 +37,6 @@ inherited frmLdmlKeyboardDebug: TfrmLdmlKeyboardDebug OnChange = memoChange OnClick = memoClick OnEnter = memoGotFocus - OnExit = memoLostFocus OnKeyUp = memoKeyUp OnMessage = memoMessage IsDebugging = False diff --git a/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.pas b/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.pas index 8f086ee9b2..a6306eb13f 100644 --- a/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.pas +++ b/developer/src/tike/child/Keyman.Developer.UI.Debug.UfrmLdmlKeyboardDebug.pas @@ -32,8 +32,6 @@ uses PaintPanel, KeymanDeveloperDebuggerMemo, Keyman.System.Debug.DebugCore, - Keyman.System.Debug.DebugEvent, - Keyman.System.Debug.DebugUIStatus, Keyman.System.Debug.DebugUtils, Keyman.System.KeymanCore, Keyman.System.KeymanCoreDebug, @@ -42,6 +40,13 @@ uses UfrmTike, UserMessages; +type + TLdmlDebugUIStatus = ( + duiStarting, + duiTest, + duiClosing + ); + type TfrmLdmlKeyboardDebug = class(TTikeForm) memo: TKeymanDeveloperDebuggerMemo; @@ -56,7 +61,6 @@ type mnuPaste: TMenuItem; // I4808 procedure FormCreate(Sender: TObject); procedure memoGotFocus(Sender: TObject); - procedure memoLostFocus(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure memoChange(Sender: TObject); @@ -70,27 +74,15 @@ type procedure memoKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormResize(Sender: TObject); private - - FDebugVisible: Boolean; - FRunning: Boolean; FFileName: string; debugkeyboard: TDebugKeyboard; - _FCurrentEvent: Integer; FDefaultFont: Boolean; - FEvents: TDebugEventList; - FUIStatus: TDebugUIStatus; + FUIStatus: TLdmlDebugUIStatus; FUIDisabled: Boolean; { Deadkey member variables } FSelectedDeadkey: TDeadKeyInfo; - FSavedSelection: TMemoSelection; - procedure ResetEvents; - procedure ExecuteEvent(n: Integer); - procedure ExecuteEventAction(n: Integer); - procedure SetUIStatus(const Value: TDebugUIStatus); - procedure DisableUI; - procedure EnableUI; function GetStatusText: string; procedure SetStatusText(Value: string); @@ -102,7 +94,6 @@ type FDebugFileName: WideString; procedure ClearDeadkeys; procedure ClearDeadkeyStyle; - function GetCurrentEvent: TDebugEvent; procedure UninitDeadkeys; procedure UpdateDeadkeyDisplay; procedure UpdateDeadkeys; @@ -114,8 +105,7 @@ type procedure CleanupCoreState; function SetKeyEventContext: Boolean; function HandleMemoKeydown(var Message: TMessage): Boolean; - procedure SetCurrentEvent(Value: Integer); - procedure Run; + procedure Run(vkey: Word); protected function GetHelpTopic: string; override; @@ -132,7 +122,7 @@ type function GetDebugKeyboard: TDebugKeyboard; - property UIStatus: TDebugUIStatus read FUIStatus write SetUIStatus; + property UIStatus: TLdmlDebugUIStatus read FUIStatus write FUIStatus; property DebugFileName: WideString read FDebugFileName write FDebugFileName; property CompiledFileName: string read FFileName write FFileName; // I4695 @@ -141,11 +131,7 @@ type procedure ShowDebugForm; procedure HideDebugForm; - property DebugVisible: Boolean read FDebugVisible; - function ShortcutDisabled(Key: Word): Boolean; - - property CurrentEvent: TDebugEvent read GetCurrentEvent; end; implementation @@ -153,20 +139,18 @@ implementation {$R *.DFM} uses + System.Character, + System.Math, + Keyman.Developer.System.HelpTopics, - ActiveX, dmActionsDebugger, dmActionsMain, - Glossary, Keyman.Developer.System.Project.ProjectLog, + Keyman.Developer.UI.Project.ProjectUI, Keyman.Developer.UI.UfrmLdmlKeyboardEditor, Keyman.UI.Debug.CharacterGridRenderer, KeyNames, - kmxfile, - kmxfileconsts, - ErrorControlledRegistry, - RegistryKeys, KeymanDeveloperOptions, KeymanDeveloperUtils, ScanCodeMap, @@ -174,13 +158,9 @@ uses UfrmEditor, UfrmMain, UfrmMessages, - UfrmRegressionTestFailure, UfrmSelectSystemKeyboard, Unicode, - utilstr, - UTikeDebugMode, - VKeys, - XString; + utilstr; const // WM_KEYDOWN bits; KEYFLAG_KEYMAN is a reserved value @@ -197,18 +177,19 @@ begin FDeadkeys := TDebugDeadkeyInfoList.Create; - FUIStatus := duiInvalid; - FEvents := TDebugEventList.Create; + FUIStatus := duiStarting; FDefaultFont := True; memo.Align := alClient; + memo.ReadOnly := False; + memo.IsDebugging := True; + StatusText := 'Simple Test'; - UIStatus := duiReadyForInput; + UIStatus := duiTest; end; procedure TfrmLdmlKeyboardDebug.FormDestroy(Sender: TObject); begin ResetDebug; - FreeAndNil(FEvents); UninitDeadkeys; end; @@ -224,9 +205,6 @@ end; procedure TfrmLdmlKeyboardDebug.memoGotFocus(Sender: TObject); begin - if UIStatus = duiReadyForInput then - UIStatus := duiFocusedForInput; - memoSelMove(memo); end; @@ -236,30 +214,15 @@ begin memoSelMove(memo); end; -procedure TfrmLdmlKeyboardDebug.memoLostFocus(Sender: TObject); -begin - if UIStatus = duiFocusedForInput then - UIStatus := duiReadyForInput; -end; - function TfrmLdmlKeyboardDebug.HandleMemoKeydown(var Message: TMessage): Boolean; begin - if (Message.wParam = VK_ESCAPE) and - (GetKeyState(VK_SHIFT) < 0) and - (UIStatus <> duiPaused) then // I4033 - UIStatus := duiPaused - else if (Message.wParam = VK_F6) and + if (Message.wParam = VK_F6) and (GetKeyState(VK_CONTROL) >= 0) and - (GetKeyState(VK_MENU) >= 0) and - (UIStatus <> duiFocusedForInput) then + (GetKeyState(VK_MENU) >= 0) then begin EditorMemo.SetFocus; end - else if (Message.wParam = VK_ESCAPE) and - (GetKeyState(VK_SHIFT) < 0) and - (UIStatus = duiPaused) then - UIStatus := duiFocusedForInput - else if UIStatus in [duiTest, duiFocusedForInput] then + else if UIStatus = duiTest then begin Exit(ProcessKeyEvent(Message)); end @@ -274,9 +237,9 @@ procedure TfrmLdmlKeyboardDebug.memoMessage(Sender: TObject; var Message: TMessa begin Handled := False; - if UIStatus = duiClosing then + if UIStatus <> duiTest then begin - Exit; // Don't process while destroying... + Exit; end; case Message.Msg of @@ -384,63 +347,35 @@ begin Exit(True); end; - FEvents.Clear; - FEvents.AddLdmlStateItems(FDebugCore.State, vkey, modifier, debugkeyboard); + Run(vkey); - FRunning := True; - SetCurrentEvent(0); - Run; + UpdateDeadkeys; end else begin - FEvents.Clear; Result := False; end; end; -procedure TfrmLdmlKeyboardDebug.SetCurrentEvent(Value: Integer); +function UsvToString(p: pkm_core_usv): string; begin - _FCurrentEvent := Value; + Result := ''; + while p^ <> 0 do + begin + Result := Result + Char.ConvertFromUtf32(p^); + Inc(p); + end; end; -procedure TfrmLdmlKeyboardDebug.Run; -begin - FRunning := True; - try - while (_FCurrentEvent < FEvents.Count) do - begin - ExecuteEvent(_FCurrentEvent); - SetCurrentEvent(_FCurrentEvent + 1); - if UIStatus = duiPaused then // I4033 - Exit; - end; - finally - FRunning := False; - EnableUI; - UpdateCharacterGrid; - // We want to refresh the memo and character grid for rapid typing - memo.Update; - sgChars.Update; +procedure TfrmLdmlKeyboardDebug.Run(vkey: Word); + + procedure DoAlert; + begin + MessageBeep(0); end; - if UIStatus <> duiTest then - if memo.Focused - then UIStatus := duiFocusedForInput - else UIStatus := duiReadyForInput; -end; -{ Stores } -procedure TfrmLdmlKeyboardDebug.ExecuteEvent(n: Integer); -begin - memo.ReadOnly := False; - memo.Selection := FSavedSelection; - ExecuteEventAction(n); - FSavedSelection := memo.Selection; - memo.ReadOnly := True; -end; - -procedure TfrmLdmlKeyboardDebug.ExecuteEventAction(n: Integer); type TMemoSelectionState = record Selection: TMemoSelection; @@ -481,126 +416,6 @@ procedure TfrmLdmlKeyboardDebug.ExecuteEventAction(n: Integer); UpdateDeadkeys; end; - procedure DoBackspace(BackspaceType: km_core_backspace_type); - var - m, n: Integer; - dk: TDeadKeyInfo; - state: TMemoSelectionState; - begin - // Offset is zero-based, but string is 1-based. Beware! - state := SaveMemoSelectionState; - n := memo.SelStart; - m := n; - - if memo.SelLength > 0 then - begin - // If the memo has a selection, we have given Core an empty context, - // which forces it to emit a KM_CORE_BT_UNKNOWN backspace, which is - // exactly what we want here. We just delete the selection - Assert(BackspaceType = KM_CORE_BT_UNKNOWN); - memo.SelText := ''; - RealignMemoSelectionState(state); - Exit; - end; - - case BackspaceType of - KM_CORE_BT_MARKER: - begin - Assert(m >= 1); - Assert(memo.Text[m] = #$FFFC); - dk := FDeadkeys.GetFromPosition(m-1); - Assert(Assigned(dk)); - dk.Delete; - Dec(m); - end; - KM_CORE_BT_CHAR: - begin - Assert(m >= 1); - Assert(memo.Text[m] <> #$FFFC); - // Delete surrogate pairs - if (m > 1) and - Uni_IsSurrogate2(memo.Text[m]) and - Uni_IsSurrogate1(memo.Text[m-1]) then - Dec(m, 2) - // Delete \r\n line breaks - else if (m > 1) and - (memo.Text[m] = #$0A) and - (memo.Text[m-1] = #$0D) then - Dec(m, 2) - else - Dec(m); - end; - KM_CORE_BT_UNKNOWN: - begin - while (m >= 1) and (memo.Text[m] = #$FFFC) do - begin - dk := FDeadkeys.GetFromPosition(m-1); - Assert(Assigned(dk)); - dk.Delete; - Dec(m); - end; - - // Delete character - if (m > 1) and - Uni_IsSurrogate2(memo.Text[m]) and - Uni_IsSurrogate1(memo.Text[m-1]) then - Dec(m, 2) - else - Dec(m); - - // Also delete deadkeys to left of current character - while (m >= 1) and (memo.Text[m] = #$FFFC) do - begin - dk := FDeadkeys.GetFromPosition(m-1); - Assert(Assigned(dk)); - dk.Delete; - Dec(m); - end; - end; - else - Assert(False, 'Unrecognised backspace type'); - end; - - memo.Text := Copy(memo.Text, 1, m) + Copy(memo.Text, n+1, MaxInt); - memo.SelStart := m; - - RealignMemoSelectionState(state); - end; - - procedure DoDeadkey(dkCode: Integer); - var - dk: TDeadKeyInfo; - i: Integer; - state: TMemoSelectionState; - begin - dk := TDeadKeyInfo.Create; - dk.Memo := memo; - dk.Deadkey := nil; - for i := 0 to debugkeyboard.Deadkeys.Count - 1 do - if debugkeyboard.Deadkeys[i].Value = dkCode then - begin - dk.Deadkey := debugkeyboard.Deadkeys[i]; - Break; - end; - if not Assigned(dk.Deadkey) then - dk.Free //silent failure - else - begin - state := SaveMemoSelectionState; - - memo.SelText := WideChar($FFFC); - memo.SelStart := memo.SelStart + memo.SelLength; // I1603 - memo.SelLength := 0; - dk.Position := memo.SelStart - 1; - - RealignMemoSelectionState(state); - - FDeadkeys.Add(dk); - UpdateDeadkeyDisplay; - - end; - end; - procedure DoHandleShortcut(vk: UINT); begin // Because we disable shortcuts in the debug memo, there are a small set of @@ -610,7 +425,13 @@ procedure TfrmLdmlKeyboardDebug.ExecuteEventAction(n: Integer); // apart from Ctrl+A if GetKeyState(VK_CONTROL) >= 0 then + begin + if (vk = VK_RETURN) and (GetKeyState(VK_MENU) >= 0) then + begin + memo.SelText := #13#10; + end; Exit; + end; case vk of Ord('A'): memo.SelectAll; @@ -653,85 +474,187 @@ procedure TfrmLdmlKeyboardDebug.ExecuteEventAction(n: Integer); end; end; - procedure DoChar(const text: string); + function ContextToDebugString(context_items: pkm_core_context_item; offset, length: Integer): string; var - state: TMemoSelectionState; + outText: array of UInt32; + ox: Integer; + pc: pkm_core_context_item; + dk: TDeadKeyInfo; begin - state := SaveMemoSelectionState; - // Line breaks: replace \r (0x0D) with \r\n (0x0D 0x0A) so line breaks work - memo.SelText := ReplaceStr(Text, #$0D, #$0D#$0A); - memo.SelStart := memo.SelStart + memo.SelLength; // I1603 - memo.SelLength := 0; - RealignMemoSelectionState(state); - end; + SetLength(outText, length + 1); - procedure DoBell; - begin - MessageBeep(0); - end; - -begin - DisableUI; - with FEvents[n].Action do - begin - case ActionType of - KM_CORE_IT_EMIT_KEYSTROKE: DoEmitKeystroke(dwData); - KM_CORE_IT_CHAR: DoChar(Text); - KM_CORE_IT_MARKER: DoDeadkey(dwData); - KM_CORE_IT_ALERT: DoBell; - KM_CORE_IT_BACK: DoBackspace(km_core_backspace_type(dwData)); - KM_CORE_IT_PERSIST_OPT: ; //TODO - KM_CORE_IT_CAPSLOCK: ; //TODO - KM_CORE_IT_INVALIDATE_CONTEXT: ; // no-op + ox := 0; + pc := context_items; + while pc._type <> KM_CORE_CT_END do + begin + if pc._type = KM_CORE_CT_CHAR then + outText[ox] := pc.character + else + begin + dk := TDeadKeyInfo.Create; + dk.Deadkey := TDebugDeadkey.Create; + dk.Deadkey.Name := '\m{'+IntToStr(pc.marker)+'}'; + dk.Deadkey.Value := pc.marker; + dk.Position := ox + offset; + dk.SavedPosition := ox + offset; + FDeadkeys.Add(dk); + outText[ox] := $FFFC; + end; + Inc(pc); + Inc(ox); end; -// AddDEBUG(Format('%d: %d [%s]', [ActionType, dwData, Text])); - end; - EnableUI; -end; -procedure TfrmLdmlKeyboardDebug.DisableUI; + outText[ox] := 0; + + Result := USVToString(@outText[0]); + end; + +var + actions: pkm_core_actions; + context_items: pkm_core_context_item; + selection: TMemoSelection; + output: string; + lhs, rhs, context: string; + status: km_core_status; + dk: TDeadKeyInfo; + context_items_length: Integer; + state: TMemoSelectionState; + Adjustment: Integer; begin FUIDisabled := True; -// UpdateControlCaptions; -end; -procedure TfrmLdmlKeyboardDebug.EnableUI; -begin - if not FRunning then + actions := km_core_state_get_actions(FDebugCore.State); + if actions = nil then begin + GetGlobalProjectUI.Log(plsError, FDebugFileName, + 'Failed to get actions from Keyman Core', 0, 0); + Exit; + end; + + context_items := nil; + status := km_core_context_get(km_core_state_context(FDebugCore.State), @context_items); + if status <> KM_CORE_STATUS_OK then + begin + GetGlobalProjectUI.Log(plsError, FDebugFileName, + 'Failed to get context from Keyman Core with error '+IntToStr(Ord(Status)), 0, 0); + km_core_actions_dispose(actions); + Exit; + end; + + selection := memo.Selection; + + // TODO: #10471 deleting U+000D U+000A should delete as a unit + + try + if (actions.emit_keystroke = 1) and (vkey <> VK_BACK) and + (actions.code_points_to_delete = 0) and (actions.output^ = 0) then + begin + // If the keystroke is being emitted, then it was not matched by LDML, and + // must be a frame key. We exclude Bksp as a special case which needs + // additional processing. All other frame keys should be passed to the + // text control for default processing. + end + else + begin + if selection.Finish > selection.Start then + begin + // We have a selection, so we should remove it first and then continue + // as normal. We will have given Core a zero-length context, so we + // need to save everything before the start of selection before + // continuing, and cleanup any markers that were in the selection + if (actions.emit_keystroke = 0) or (vkey = VK_BACK) then + begin + state := SaveMemoSelectionState; + memo.SelText := ''; + RealignMemoSelectionState(state); + selection := memo.Selection; + end; + lhs := Copy(memo.Text, 1, selection.Start); + end + else + lhs := ''; + + context := Copy(memo.Text, lhs.Length + 1, selection.Start - lhs.Length); + rhs := Copy(memo.Text, lhs.Length + context.Length + 1, MaxInt); + + // Reinsert the context + + context_items_length := km_core_context_length(km_core_state_context(FDebugCore.State)); + Adjustment := context_items_length - selection.Start + lhs.Length; + + // Delete all markers that are in the context, because we will reinsert + // them shortly, and adjust marker positions to right of insertion point + + for dk in FDeadkeys do + begin + if (dk.Position > lhs.Length) and (dk.Position < selection.Start) then + begin + dk.Delete; + end + else if dk.Position >= selection.Start then + begin + dk.Position := dk.Position + Adjustment; + end; + end; + + // Build the new context string and create new markers + + output := ContextToDebugString(context_items, lhs.Length, context_items_length); + + // Merge left of context, context, and right of context and update memo + // insertion point position + + memo.Text := lhs + output + rhs; + selection.Start := lhs.Length + output.Length; + selection.Finish := selection.Start; + selection.Anchor := selection.Start; + memo.Selection := selection; + end; + + // actions.persist_options are not currently supported by LDML + + if actions.do_alert <> 0 then + begin + DoAlert; + end; + + if actions.emit_keystroke <> 0 then + begin + DoEmitKeystroke(vkey); + end; + + finally + km_core_actions_dispose(actions); + km_core_context_items_dispose(context_items); + FUIDisabled := False; + UpdateCharacterGrid; + + // We want to refresh the memo and character grid for rapid typing + memo.Update; + sgChars.Update; end; end; +{ Stores } + procedure TfrmLdmlKeyboardDebug.CleanupCoreState; begin FreeAndNil(FDebugCore); end; -procedure TfrmLdmlKeyboardDebug.ResetEvents; -begin - if _FCurrentEvent > 0 then - begin - FEvents.Clear; - SetCurrentEvent(0); - end; -end; - procedure TfrmLdmlKeyboardDebug.HideDebugForm; begin frmKeymanDeveloper.ShowDebug(False); // I4796 - FDebugVisible := False; ResetDebug; - UIStatus := duiReadyForInput; end; procedure TfrmLdmlKeyboardDebug.ShowDebugForm; begin + UIStatus := duiTest; + frmKeymanDeveloper.ShowDebug(True); // I4796 - FDebugVisible := True; - - //UpdateFont(nil); if not SetupDebug then Exit; @@ -741,8 +664,6 @@ end; procedure TfrmLdmlKeyboardDebug.ResetDebug; begin FreeAndNil(debugkeyboard); - SetCurrentEvent(1); - ResetEvents; ClearDeadkeys; // I1699 CleanupCoreState; memo.Text := ''; @@ -783,67 +704,6 @@ begin memo.Font := FFont; end; -procedure TfrmLdmlKeyboardDebug.SetUIStatus(const Value: TDebugUIStatus); -var - FOldUIStatus: TDebugUIStatus; -begin - if FUIStatus <> Value then - begin - FOldUIStatus := FUIStatus; - FUIStatus := Value; - - case Value of - duiTest: - begin - StatusText := 'Simple Test'; - memo.ReadOnly := False; - memo.IsDebugging := True; - end; - duiFocusedForInput: - begin - DisableUI; - StatusText := 'Focused for input'; - memo.ReadOnly := False; - memo.IsDebugging := True; - end; - duiReadyForInput: - begin - if memo.Focused then UIStatus := duiFocusedForInput - else - begin - EnableUI; - StatusText := 'Ready for input'; - memo.ReadOnly := True; - memo.IsDebugging := False; - end; - end; - duiPaused: - begin - EnableUI; - FUIDisabled := False; // I4033 - StatusText := 'Paused'; - memo.ReadOnly := True; - memo.IsDebugging := False; - end; - end; - if FOldUIStatus = duiTest then - begin - if memo.Focused then - begin - GetParentForm(memo).ActiveControl := nil; - memo.SetFocus; - end; - end - end; -end; - -function TfrmLdmlKeyboardDebug.GetCurrentEvent: TDebugEvent; -begin - if (_FCurrentEvent >= 0) and (_FCurrentEvent < FEvents.Count) - then Result := FEvents[_FCurrentEvent] - else Result := nil; -end; - function TfrmLdmlKeyboardDebug.GetDebugKeyboard: TDebugKeyboard; begin Result := debugkeyboard; @@ -971,7 +831,6 @@ begin if not memo.ReadOnly then begin - FSavedSelection := memo.Selection; UpdateCharacterGrid; // I4808 end; end; @@ -981,7 +840,8 @@ begin if csDestroying in ComponentState then Exit; - TCharacterGridRenderer.Fill(sgChars, memo.Text, FDeadkeys, memo.SelStart, memo.SelLength, memo.Selection.Anchor); + TCharacterGridRenderer.Fill(sgChars, memo.Text, FDeadkeys, memo.SelStart, + memo.SelLength, memo.Selection.Anchor, True); TCharacterGridRenderer.Size(sgChars, memo.Font); end; diff --git a/developer/src/tike/child/Keyman.Developer.UI.UfrmLdmlKeyboardEditor.pas b/developer/src/tike/child/Keyman.Developer.UI.UfrmLdmlKeyboardEditor.pas index 12ef7a3bbb..a9c41b09ec 100644 --- a/developer/src/tike/child/Keyman.Developer.UI.UfrmLdmlKeyboardEditor.pas +++ b/developer/src/tike/child/Keyman.Developer.UI.UfrmLdmlKeyboardEditor.pas @@ -83,8 +83,6 @@ procedure TfrmLdmlKeyboardEditor.StartDebugging; begin if not IsDebugVisible then begin - FDebugForm.UIStatus := duiReadyForInput; - (ProjectFileUI as TxmlLdmlProjectFileUI).Debug := True; // I4687 frmMessages.Clear; // I4686 diff --git a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas index 2d6e5a39bf..4f5ee40211 100644 --- a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas +++ b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas @@ -93,9 +93,6 @@ type vk: uint16_t; modifier_state: uint16_t ): Boolean; overload; - - function AddLdmlStateItems(state: pkm_core_state; vk, - modifier_state: uint16_t; debugkeyboard: TDebugKeyboard): Boolean; end; implementation @@ -375,36 +372,6 @@ begin Assert(action._type = KM_CORE_IT_END); end; -function TDebugEventList.AddLdmlStateItems( - state: pkm_core_state; - vk: uint16_t; - modifier_state: uint16_t; - debugkeyboard: TDebugKeyboard -): Boolean; -var - action: pkm_core_action_item; -begin - // TODO: use action struct - Result := True; - action := km_core_state_action_items(state, nil); - while (action._type <> KM_CORE_IT_END) do - begin - Result := Result and AddActionItem(vk, action); - Inc(action); - end; - - if action._type = KM_CORE_IT_EMIT_KEYSTROKE then - begin - // The EMIT_KEYSTROKE action comes after all rules have completed processing - Result := Result and AddActionItem(vk, action); - Inc(action); - end; - - // By the time we get to the end of rule processing, all actions should have - // already been undertaken - Assert(action._type = KM_CORE_IT_END); -end; - { TDebugEventRuleData } procedure TDebugEventRuleData.FillStoreList(event: pkm_core_state_debug_item; KeyboardMemory: PChar); diff --git a/developer/src/tike/debug/Keyman.UI.Debug.CharacterGridRenderer.pas b/developer/src/tike/debug/Keyman.UI.Debug.CharacterGridRenderer.pas index 93397fee92..4f7453c775 100644 --- a/developer/src/tike/debug/Keyman.UI.Debug.CharacterGridRenderer.pas +++ b/developer/src/tike/debug/Keyman.UI.Debug.CharacterGridRenderer.pas @@ -22,7 +22,8 @@ type class procedure Fill(grid: TStringGrid; const text: string; deadkeys: TDebugDeadkeyInfoList; - SelStart, SelLength, SelAnchor: Integer); static; + SelStart, SelLength, SelAnchor: Integer; + DeadkeysAreCalledMarkers: Boolean = False); static; class procedure Render(grid: TStringGrid; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState; CharFont: TFont); static; class procedure Size(grid: TStringGrid; CharFont: TFont); static; @@ -36,7 +37,8 @@ uses class procedure TCharacterGridRenderer.Fill(grid: TStringGrid; const text: string; deadkeys: TDebugDeadkeyInfoList; - SelStart, SelLength, SelAnchor: Integer); + SelStart, SelLength, SelAnchor: Integer; + DeadkeysAreCalledMarkers: Boolean); type TCellType = (ctChar, ctDeadkey); TCell = record @@ -87,7 +89,9 @@ type if not Assigned(cell.dk) then grid.Cells[x, 0] := '???' else grid.Cells[x, 0] := cell.dk.Deadkey.Name; - grid.Cells[x, 1] := 'Deadkey'; + if DeadkeysAreCalledMarkers + then grid.Cells[x, 1] := 'Marker' + else grid.Cells[x, 1] := 'Deadkey'; end else begin diff --git a/developer/src/tike/main/Keyman.System.KeymanCore.pas b/developer/src/tike/main/Keyman.System.KeymanCore.pas index cbc1f586f5..363abcc2f5 100644 --- a/developer/src/tike/main/Keyman.System.KeymanCore.pas +++ b/developer/src/tike/main/Keyman.System.KeymanCore.pas @@ -20,6 +20,7 @@ type km_core_virtual_key = uint16_t; km_core_usv = uint32_t; // UTF-32 + pkm_core_usv = ^km_core_usv; km_core_cp = WideChar; pkm_core_cp = ^km_core_cp; @@ -70,6 +71,7 @@ type end; pkm_core_context_item = ^km_core_context_item; + ppkm_core_context_item = ^pkm_core_context_item; const KM_CORE_CONTEXT_ITEM_END: km_core_context_item = ( @@ -82,7 +84,7 @@ const keymancore = 'keymancore-1.dll'; procedure km_core_context_items_dispose( - const context_items: km_core_context_item + context_items: pkm_core_context_item ); cdecl; external keymancore delayed; function km_core_context_set( @@ -90,10 +92,18 @@ function km_core_context_set( context_items: pkm_core_context_item ): km_core_status; cdecl; external keymancore delayed; +function km_core_context_get( + context: pkm_core_context; + context_items: ppkm_core_context_item +): km_core_status; cdecl; external keymancore delayed; + procedure km_core_context_clear( context: pkm_core_context ); cdecl; external keymancore delayed; +function km_core_context_length( + context: pkm_core_context +): uint32; cdecl; external keymancore delayed; type km_core_option_scope = ( @@ -162,6 +172,35 @@ type pkm_core_action_item = ^km_core_action_item; + km_core_bool = uint32; + + km_core_caps_state = ( + KM_CORE_CAPS_UNCHANGED = -1, + KM_CORE_CAPS_OFF = 0, + KM_CORE_CAPS_ON = 1 + ); + + km_core_actions = record + code_points_to_delete: uint32; // number of codepoints (not codeunits!) to delete from app context. + + // null-term string of characters to insert into document + output: pkm_core_usv; + + // list of options to persist, terminated with KM_CORE_OPTIONS_END + persist_options: pkm_core_option_item; + + // issue a beep, 0 = no, 1 = yes + do_alert: km_core_bool; + + // emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes + emit_keystroke: km_core_bool; + + // -1=unchanged, 0=off, 1=on + new_caps_lock_state: km_core_caps_state; + + end; + pkm_core_actions = ^km_core_actions; + // These types are used only for debugging convenience type km_core_action_item_array = array[0..100] of km_core_action_item; @@ -241,6 +280,14 @@ function km_core_state_action_items( num_items: pinteger ): pkm_core_action_item; cdecl; external keymancore delayed; +function km_core_state_get_actions( + state: pkm_core_state +): pkm_core_actions; cdecl; external keymancore delayed; + +function km_core_actions_dispose( + actions: pkm_core_actions +): km_core_status; cdecl; external keymancore delayed; + function km_core_state_to_json( state: pkm_core_state; buf: PAnsiChar; @@ -613,4 +660,24 @@ begin RaiseLastOSError; end; +// Verify size of km_core_actions - from x86 core c++ build, test_actions.cpp + +procedure VerifyKmCoreActionsSize; +var + act: km_core_actions; +begin +{$IFDEF WIN64} + {$ERROR Struct size not yet verified for 64-bit} +{$ENDIF} + assert(sizeof(km_core_actions) = 24); + // &km_core_actions.code_points_to_delete: 0 + assert(Uint32(@act.output) - Uint32(@act) = 4); + assert(Uint32(@act.persist_options) - Uint32(@act) = 8); + assert(Uint32(@act.do_alert) - Uint32(@act) = 12); + assert(Uint32(@act.emit_keystroke) - Uint32(@act) = 16); + assert(Uint32(@act.new_caps_lock_state) - Uint32(@act) = 20); +end; + +initialization + VerifyKmCoreActionsSize; end. diff --git a/linux/debian/libkeymancore.symbols b/linux/debian/libkeymancore.symbols index 0ba4e316f3..7f8d8945dd 100644 --- a/linux/debian/libkeymancore.symbols +++ b/linux/debian/libkeymancore.symbols @@ -4,8 +4,10 @@ libkeymancore.so.1 libkeymancore #MINVER# (c++|optional)"typeinfo name for std::codecvt_utf8_utf16@Base" 17.0.244 km_core_actions_dispose@Base 17.0.197 km_core_context_clear@Base 17.0.195 + km_core_context_get@Base 17.0.195 km_core_context_item_list_size@Base 17.0.195 km_core_context_items_dispose@Base 17.0.195 + km_core_context_length@Base 17.0.195 km_core_context_set@Base 17.0.195 km_core_cp_dispose@Base 17.0.244 km_core_event@Base 17.0.195