summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglasseyes <dglassey@gmail.com>2019-01-04 01:08:11 +0700
committerglasseyes <dglassey@gmail.com>2019-01-04 01:08:11 +0700
commit225ac56fac62c096faa2ac8a39ca2a1a121df0b7 (patch)
tree1d7403a00def6974653a7d5f56eaeec3866019bd
parenta2822dddef89e155ebf558a26d2cbd825fd6d8ea (diff)
New upstream version 11.0.101
-rw-r--r--include/keyboardprocessor.h.in984
-rw-r--r--include/keyman/keyboardprocessor.h.in70
-rw-r--r--include/keyman/keyboardprocessor_bits.h4
-rw-r--r--include/keyman/meson.build2
-rw-r--r--meson.build4
-rw-r--r--src/context.hpp19
-rw-r--r--src/json.cpp6
-rw-r--r--src/json.hpp11
-rw-r--r--src/keyboard.cpp86
-rw-r--r--src/keyboard.hpp53
-rw-r--r--src/km_kbp_context_api.cpp1
-rw-r--r--src/km_kbp_keyboard_api.cpp47
-rw-r--r--src/km_kbp_options_api.cpp62
-rw-r--r--src/km_kbp_processevent_api.cpp6
-rw-r--r--src/km_kbp_state_api.cpp51
-rw-r--r--src/kmx/kmx_actions.cpp5
-rw-r--r--src/kmx/kmx_capslock.cpp3
-rw-r--r--src/kmx/kmx_context.cpp3
-rw-r--r--src/kmx/kmx_debug.cpp3
-rw-r--r--src/kmx/kmx_environment.cpp54
-rw-r--r--src/kmx/kmx_environment.h7
-rw-r--r--src/kmx/kmx_file.cpp46
-rw-r--r--src/kmx/kmx_modifiers.cpp3
-rw-r--r--src/kmx/kmx_options.cpp139
-rw-r--r--src/kmx/kmx_options.h39
-rw-r--r--src/kmx/kmx_processevent.cpp89
-rw-r--r--src/kmx/kmx_processevent.hpp41
-rw-r--r--src/kmx/kmx_processor.cpp60
-rw-r--r--src/kmx/kmx_processor.h10
-rw-r--r--src/kmx/kmx_xstring.cpp33
-rw-r--r--src/meson.build4
-rw-r--r--src/mock/mock_processor.cpp79
-rw-r--r--src/mock/mock_processor.hpp55
-rw-r--r--src/option.cpp130
-rw-r--r--src/option.hpp76
-rw-r--r--src/path.hpp146
-rw-r--r--src/processor.hpp81
-rw-r--r--src/state.cpp30
-rw-r--r--src/state.hpp116
-rw-r--r--src/utfcodec.hpp48
-rw-r--r--tests/unit/kmnkbd/keyboard_api.cpp11
-rw-r--r--tests/unit/kmnkbd/meson.build32
-rw-r--r--tests/unit/kmnkbd/options_api.cpp22
-rw-r--r--tests/unit/kmnkbd/state_api.cpp76
-rw-r--r--tests/unit/kmx/015 - ralt 2.kmn2
-rw-r--r--tests/unit/kmx/017 - space mnemonic kbd.kmn2
-rw-r--r--tests/unit/kmx/020 - deadkeys and backspace.kmn4
-rw-r--r--tests/unit/kmx/020 - deadkeys and backspace.kmxbin512 -> 512 bytes
-rw-r--r--tests/unit/kmx/022 - options with preset.kmn1
-rw-r--r--tests/unit/kmx/022 - options with preset.kmxbin546 -> 546 bytes
-rw-r--r--tests/unit/kmx/023 - options with save.kmn1
-rw-r--r--tests/unit/kmx/023 - options with save.kmxbin564 -> 564 bytes
-rw-r--r--tests/unit/kmx/024 - options with save and preset.kmn3
-rw-r--r--tests/unit/kmx/024 - options with save and preset.kmxbin564 -> 590 bytes
-rw-r--r--tests/unit/kmx/025 - options with reset.kmn1
-rw-r--r--tests/unit/kmx/025 - options with reset.kmxbin594 -> 564 bytes
-rw-r--r--tests/unit/kmx/028 - smp.kmn2
-rw-r--r--tests/unit/kmx/034 - options double set reset.kmn19
-rw-r--r--tests/unit/kmx/034 - options double set reset.kmxbin0 -> 614 bytes
-rw-r--r--tests/unit/kmx/035 - options double set staged.kmn21
-rw-r--r--tests/unit/kmx/035 - options double set staged.kmxbin0 -> 656 bytes
-rw-r--r--tests/unit/kmx/036 - options - double reset staged.kmn19
-rw-r--r--tests/unit/kmx/036 - options - double reset staged.kmxbin0 -> 550 bytes
-rw-r--r--tests/unit/kmx/037 - options - double reset.kmn18
-rw-r--r--tests/unit/kmx/037 - options - double reset.kmxbin0 -> 532 bytes
-rw-r--r--tests/unit/kmx/038 - punctkeys.kmn23
-rw-r--r--tests/unit/kmx/038 - punctkeys.kmxbin0 -> 626 bytes
-rw-r--r--tests/unit/kmx/039 - generic ctrlalt.kmn13
-rw-r--r--tests/unit/kmx/039 - generic ctrlalt.kmxbin0 -> 346 bytes
-rw-r--r--tests/unit/kmx/kmp.json383
-rw-r--r--tests/unit/kmx/kmx.cpp121
-rw-r--r--tests/unit/kmx/meson.build10
72 files changed, 2696 insertions, 794 deletions
diff --git a/include/keyboardprocessor.h.in b/include/keyboardprocessor.h.in
new file mode 100644
index 0000000..e7b4a40
--- /dev/null
+++ b/include/keyboardprocessor.h.in
@@ -0,0 +1,984 @@
+/*
+ Copyright: © 2018 SIL International.
+ Description: Cross platform API C/C++ declarations for libkmnkbp keyboard
+ processor.
+ Create Date: 2 Oct 2018
+ Authors: Tim Eves (TSE)
+ History: 18 Oct 2018 - TSE - Finialised verion of API.
+ 6 Oct 2018 - TSE - Move into keyman folder.
+
+*/
+#pragma once
+/*
+# Keyman Keyboard Processor API
+
+## Requirements
+1. Cross platform.
+2. Cross language.
+3. Facilitate stateless operation of the Engine.
+4. Keyboard format agnostic -- support both KMN and future LDML based keyboards.
+5. Support querying Engine attributes.
+6. Support querying Keyboard attributes.
+7. Idempotent
+
+
+## Design decisions in support of requirements:
+- Use C or C99 types and calling convention for the interface, it has the
+ broadest language FFI support. [1,2]
+- Have client (Platform layer) code load keyboards, manage & pass state. [3,4,7]
+- Provide query calls to return static attributes data for keyboards and
+ engine [5,6]
+- Provide get/set calls for client accessible keyboard state information [3,4]
+
+
+## Glossary
+- __Platform layer:__
+The code that consumes the Keyman Keyboard Processor API, and provides the
+operating system-specific handling of keystroke events and integration with
+applications.
+- __Client Application:__
+The application that has the focus and receives text events from the Platform
+layer.
+- __Context:__ Text preceding the insertion point
+- __Marker:__ Positional state that can be placed in the Context.
+- __Keyboard:__ A set of rules for execution my an Engine
+- __Option:__ A variable in a dynamic or static key value store.
+- __Processor:__
+The component that implements this API and can parse and execute a particular
+keyboard.
+- __State:__ An object that hold internal state of the Processor for a given
+insertion point
+- __Action:__
+A directive output by the processor detailing how the Platform layer should
+transform the Client Application's text buffer. There may be several items
+produced by a single keyboard event.
+- __Keyboard Event:__
+A virtual key event and modifier map received from the platform to be
+processed with the state object for this Client application.
+- __Virtual Key:__
+A code based on the US English layout, with values matching the Windows
+virtual key codes. See keyboardprocessor_vkeys.h for definitions.
+- __Modifier Key:__
+The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may
+have other names (e.g. Alt is called Option on macOS); other platform-specific
+modifiers such as Windows key are excluded from this set. Some modifiers are
+transient, such as Control, and others have long-lasting state, such as
+Caps Lock.
+
+## API
+### Namespace
+All calls, types and enums are prefixed with the namespace identifier `km_kbp_`
+
+### API idioms
+Almost all calls marshalling variable length aggregate data in or out of an API
+object take the form:
+> km_kbp_status *fn_name*(object_ref, buffer_ptr, size_ptr)
+
+where the buffer is nullable and all other arguments are required (will result
+in an `KM_KBP_STATUS_INVALID_ARGUMENT` status being returned if nulled). When
+`buffer` is `nullptr` or `0` the function will place the size of the required
+buffer in the variable pointed to by `size_ptr`.
+
+Calls which result in the allocation of resources, regardless of resulting
+ownership, are of the form:
+> km_kbp_status *fn_name*(object_ref, out_ptr)
+
+where `out_ptr` is a valid pointer to a caller allocated variable to hold the
+resulting ouput. This is often a reference to a created object. All arguments
+are required (will result in an `KM_KBP_STATUS_INVALID_ARGUMENT` status being
+returned if nulled).
+
+For accessors to fixed size attributes of an object these will take the form:
+> attr_value __fn_name__(object_ref)
+
+`object_ref` is required to be valid and will result in a nonsense value being
+returned if `nullptr` or `0`.
+
+All dispose calls are designed to accept null as a valid value and will do
+nothing in that event.
+```c
+*/
+#include <stdint.h>
+#include <stdlib.h>
+#include <keyman/keyboardprocessor_bits.h>
+#include <keyman/keyboardprocessor_vkeys.h>
+
+#define KM_KBP_LIB_CURRENT @lib_curr@
+#define KM_KBP_LIB_AGE @lib_age@
+#define KM_KBP_LIB_REVISION @lib_rev@
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+// Basic types
+//
+#if defined(__cplusplus)
+typedef char16_t km_kbp_cp;
+typedef char32_t km_kbp_usv;
+#else
+typedef uint16_t km_kbp_cp; // code point
+typedef uint32_t km_kbp_usv; // Unicode Scalar Value
+#endif
+typedef uint16_t km_kbp_virtual_key; // A virtual key code.
+typedef uint32_t km_kbp_status; // Status return code.
+
+// Opaque object types.
+//
+typedef struct km_kbp_context km_kbp_context;
+typedef struct km_kbp_keyboard km_kbp_keyboard;
+typedef struct km_kbp_state km_kbp_state;
+typedef struct km_kbp_options km_kbp_options;
+
+// Forward declarations
+//
+typedef struct km_kbp_option_item km_kbp_option_item;
+
+/*```
+### Error Handling
+Error handling and success failure notification are communicated through a
+general mechanism similar to COM’s `HRESULT` scheme (unlike COM, any non-zero
+value is an error). Any functions that can fail will always return a status
+value and all results are returned via outparams passed to the function.
+```c
+*/
+enum km_kbp_status_codes {
+ KM_KBP_STATUS_OK = 0,
+ KM_KBP_STATUS_NO_MEM = 1,
+ KM_KBP_STATUS_IO_ERROR = 2,
+ KM_KBP_STATUS_INVALID_ARGUMENT = 3,
+ KM_KBP_STATUS_KEY_ERROR = 4,
+ KM_KBP_STATUS_INSUFFICENT_BUFFER = 5,
+ KM_KBP_STATUS_INVALID_UTF = 6,
+ KM_KBP_STATUS_INVALID_KEYBOARD = 7,
+ KM_KBP_STATUS_OS_ERROR = 0x80000000
+};
+
+/*
+```
+The final status code KM_KBP_STATUS_OS_ERROR is intended to allow encapsulating
+a platform error code; the remaining 31 low bits are the error code returned by
+the OS for cases where the failure mode is platform specific. For HRESULT codes
+this only permits failure codes to be passed.
+
+
+### Context
+The context is the text prior to the insertion point (caret, cursor).
+The context is constructed by the Platform layer, typically by interrogating the
+Client Application. The context will be updated by the engine for keystroke
+events. If the Platform layer code caches the context, the context should be
+reset when a context state change is detected. Context state changes can occur
+when the user uses the mouse to move the insertion point, uses cursor keys,
+switches applications or input fields, or presses hotkeys such as Ctrl+N to
+start a new document. The full set of context state change triggers is up to the
+Platform layer.
+
+Context can also contain positional Markers (also known as 'deadkeys' in kmn
+keyboards), which are transitory state flags that are erased whenever a context
+state change is detected. Markers are always controlled by the Engine.
+
+Contexts are always owned by their state. They may be set to a list of
+context_items or interrogated for their current list of context items.
+```c
+*/
+enum km_kbp_context_type {
+ KM_KBP_CT_END,
+ KM_KBP_CT_CHAR,
+ KM_KBP_CT_MARKER
+};
+
+typedef struct {
+ uint8_t type;
+ uint8_t _reserved[3];
+ union {
+ km_kbp_usv character;
+ uint32_t marker;
+ };
+} km_kbp_context_item;
+
+#define KM_KBP_CONTEXT_ITEM_END {KM_KBP_CT_END, {0,}, {0,}}
+/*
+```
+### `km_kbp_context_items_from_utf16`
+##### Description:
+Convert a UTF16 encoded Unicode string into an array of `km_kbp_context_item`
+structures. Allocates memory as needed.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated for the
+ output buffer.
+- `KM_KBP_STATUS_INVALID_UTF`: In the event the UTF16 string cannot be decoded
+ because it contains unpaired surrogate codeunits.
+##### Parameters:
+- __text__: a pointer to a null terminated array of utf16 encoded data.
+- __out_ptr__: a pointer to the result variable:
+ A pointer to the start of the `km_kbp_context_item` array containing the
+ representation of the input string.
+ Terminated with a type of `KM_KBP_CT_END`. Must be disposed of with
+ `km_kbp_context_items_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_items_from_utf16(km_kbp_cp const *text,
+ km_kbp_context_item **out_ptr);
+
+/*
+```
+### `km_kbp_context_items_from_utf8`
+##### Description:
+Convert an UTF8 encoded Unicode string into an array of `km_kbp_context_item`
+structures. Allocates memory as needed.
+##### Status:
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event it cannot allocate enough memory for the
+ output buffer.
+- `KM_KBP_STATUS_INVALID_UTF`: In the event the UTF8 string cannot be
+decoded.
+##### Parameters:
+- __text__: a pointer to a null terminated array of utf8 encoded data.
+- __out_ptr__: a pointer to the result variable:
+ A pointer to the start of the `km_kbp_context_item` array containing the
+ representation of the input string.
+ Terminated with a type of `KM_KBP_CT_END`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_items_from_utf8(char const *text,
+ km_kbp_context_item **out_ptr);
+
+/*
+```
+### `km_kbp_context_items_to_utf16`
+##### Description:
+Convert a context item array into a UTF-16 encoded string placing it into
+the supplied buffer of specified size, and return the number codepoints
+actually used in the conversion. If null is passed as the buffer the
+number codeunits required is returned. This will strip markers from the
+context during the conversion.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough.
+ `buf_size` will contain the space required. The contents of the buffer are
+ undefined.
+##### Parameters:
+- __context_items__: A pointer to the start of an array `km_kbp_context_item`.
+ Must be terminated with a type of `KM_KBP_CT_END`.
+- __buf__: A pointer to the buffer to place the UTF-16 string into. May be be
+ null to request size calculation.
+- __buf_size__: a pointer to the result variable:
+ A pointer the size of the supplied buffer in codeunits or filled with the
+ size required if `buf` is null.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_items_to_utf16(km_kbp_context_item const *item,
+ km_kbp_cp *buf,
+ size_t *buf_size);
+
+/*
+```
+### `km_kbp_context_items_to_utf8`
+##### Description:
+Convert a context item array into a UTF-8 encoded string placing it into
+the supplied buffer of specified size, and return the number codepoints
+actually used in the conversion. If null is passed as the buffer the
+number codeunits required is returned. This will strip markers from the
+context during the conversion.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough.
+ `buf_size` will contain the space required. The contents of the buffer are
+ undefined.
+##### Parameters:
+- __context_items__: A pointer to the start of an array `km_kbp_context_item`.
+ Must be terminated with a type of `KM_KBP_CT_END`.
+- __buf__: A pointer to the buffer to place the UTF-8 string into. May be be
+ null to request size calculation.
+- __buf_size__: a pointer to the result variable:
+ A pointer the size of the supplied buffer in codeunits or filled with the
+ size required if `buf` is null.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_items_to_utf8(km_kbp_context_item const *item,
+ char *buf,
+ size_t *buf_size);
+
+/*
+```
+### `km_kbp_context_items_dispose`
+##### Description:
+Free the allocated memory belonging to a `km_kbp_context_item` array previously
+returned by `km_kbp_context_items_from_utf16` or `km_kbp_context_get`
+##### Parameters:
+- __context_items__: A pointer to the start of the `km_kbp_context_item` array
+ to be disposed of.
+
+```c
+*/
+KMN_API
+void
+km_kbp_context_items_dispose(km_kbp_context_item *context_items);
+
+/*
+```
+### `km_kbp_context_set`
+##### Description:
+Replace the contents of the current context with a new sequence of
+`km_kbp_context_item` entries.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated to
+ grow the context buffer internally.
+##### Parameters:
+- __context__: A pointer to an opaque context object
+- __context_items__: A pointer to the start of the `km_kbp_context_item`
+ array containing the new context. It must be terminated with an item
+ of type `KM_KBP_CT_END`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_set(km_kbp_context *context,
+ km_kbp_context_item const *context_items);
+
+/*
+```
+### `km_kbp_context_get`
+##### Description:
+Copies all items in the context into a new array and returns the new array.
+This must be disposed of by caller using `km_kbp_context_items_dispose`.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated for the
+ output buffer.
+##### Parameters:
+- __context_items__: A pointer to the start of an array `km_kbp_context_item`.
+- __out__: a pointer to the result variable:
+ A pointer to the start of the `km_kbp_context_item` array containing a
+ copy of the context. Terminated with a type of `KM_KBP_CT_END`. Must be
+ disposed of with `km_kbp_context_items_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_get(km_kbp_context const *context_items,
+ km_kbp_context_item **out);
+
+/*
+```
+### `km_kbp_context_clear`
+##### Description:
+Removes all context_items from the internal array. If `context` is
+null, has no effect.
+##### Parameters:
+- __context__: A pointer to an opaque context object
+
+```c
+*/
+KMN_API
+void
+km_kbp_context_clear(km_kbp_context *);
+
+/*
+```
+### `km_kbp_context_length`
+##### Description:
+Return the number of items in the context.
+##### Return:
+The number of items in the context, and will return 0 if passed a null `context`
+pointer.
+##### Parameters:
+- __context__: A pointer to an opaque context object
+
+```c
+*/
+KMN_API
+size_t
+km_kbp_context_length(km_kbp_context *);
+
+/*
+```
+### `km_kbp_context_append`
+##### Description:
+Add more items to the end (insertion point) of the context. If these exceed the
+maximum context length the same number of items will be dropped from the
+beginning of the context.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated to
+ grow the context buffer internally.
+##### Parameters:
+- __context__: A pointer to an opaque context object.
+- __context_items__: A pointer to the start of the `KM_KBP_CT_END` terminated
+ array of `km_kbp_context_item` to append.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_append(km_kbp_context *context,
+ km_kbp_context_item const *context_items);
+
+/*
+```
+### `km_kbp_context_shrink`
+##### Description:
+Remove a specified number of items from the end of the context, optionally
+add up to the same number of the supplied items to the front of the context.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: in the event it cannot allocated enough memory to grow
+ the context internally.
+##### Parameters:
+- __context__: A pointer to an opaque context object.
+- __num__: The number of items to remove from the end of context.
+- __context_items__: Pointer to the start of the `KM_KBP_CT_END` terminated
+ array of `km_kbp_context_item` to add to the front. Up to `num` items will
+ be prepended. This may be null if not required.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_context_shrink(km_kbp_context *context,
+ size_t num,
+ km_kbp_context_item const *prefix);
+
+/*
+```
+### Action Items
+These provide the results of processing a key event to the Platform layer and
+should be processed by the Platform layer to issue commands to the os text
+services framework to transform the text store in the Client Application, among
+other actions.
+```c
+*/
+typedef struct {
+ uint8_t type;
+ uint8_t _reserved[sizeof(void*)-sizeof(uint8_t)];
+ union {
+ uintptr_t marker; // MARKER type
+ km_kbp_option_item const * option; // OPT types
+ km_kbp_usv character; // CHAR type
+ km_kbp_virtual_key vkey; // VKEY types
+ size_t erased; // BACK type
+ };
+} km_kbp_action_item;
+
+enum km_kbp_action_type {
+ KM_KBP_IT_END = 0, // Marks end of action items list.
+ KM_KBP_IT_CHAR = 1, // A Unicode character has been generated.
+ KM_KBP_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers.
+ KM_KBP_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell.
+ KM_KBP_IT_BACK = 4, // Delete the codepoint preceding the insertion point.
+ KM_KBP_IT_PERSIST_OPT = 5, // The indicated option needs to be stored.
+ KM_KBP_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application
+ KM_KBP_IT_INVALIDATE_CONTEXT = 7,
+ // The processor requests that the context buffer be cleared;
+ // for applications where context is cached, this clears the context;
+ // for applications where context is read from the focused text store,
+ // the context is just re-read and markers flushed.
+ KM_KBP_IT_MAX_TYPE_ID
+};
+
+
+/*
+```
+### Options
+A state’s default options are set from the keyboard at creation time and the
+environment. The Platform layer is then is expected to apply any persisted
+options it is maintaining. Options are passed into and out of API functions as
+simple C arrays of `km_kbp_option_item` terminated with a `KM_KBP_OPTIONS_END`
+sentinel value. A state's options are exposed and manipulatable via the
+`km_kbp_options` API. All option values are of type C string.
+
+During processing when the Platform layer finds a PERSIST action type it should
+store the updated option in the appropriate place, based on its scope.
+For RESET the processor will apply the pristine value from the original scope,
+the Platform layer should update that only if it manages a previously persisted
+value.
+```c
+*/
+
+enum km_kbp_option_scope {
+ KM_KBP_OPT_UNKNOWN = 0,
+ KM_KBP_OPT_KEYBOARD = 1,
+ KM_KBP_OPT_ENVIRONMENT = 2,
+ KM_KBP_OPT_MAX_SCOPES
+};
+
+struct km_kbp_option_item {
+ km_kbp_cp const * key;
+ km_kbp_cp const * value;
+ uint8_t scope; // Scope which an option belongs to.
+};
+
+#define KM_KBP_OPTIONS_END { 0, 0, 0 }
+
+
+/*
+```
+### `km_kbp_options_list_size`
+##### Description:
+Return the length of a terminated `km_kbp_option_item` array (options
+list).
+##### Return:
+The number of items in the list or 0 if `opts` is null.
+##### Parameters:
+- __opts__: A pointer to a `KM_KBP_OPTIONS_END` terminated array of
+ `km_kbp_option_item` values.
+
+```c
+*/
+KMN_API
+size_t
+km_kbp_options_list_size(km_kbp_option_item const *opts);
+
+/*
+```
+### `km_kbp_options_lookup`
+##### Description:
+Lookup an option based on its key, in an options list.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null, or
+ if the scope is invalid.
+- `KM_KBP_STATUS_KEY_ERROR`: The key cannot be found.
+##### Parameters:
+- __state__: An opaque pointer to a state object.
+- __scope__: Which key-value store to interrogate.
+- __key__: A UTF-16 string that matches the key in the target `km_kbp_option_item`.
+- __value__: A pointer to the result variable:
+ A pointer to a copy of the UTF-16 string value; undefined if return status
+ is anything other than `KM_KBP_STATUS_OK`. This memory must be disposed of
+ with `km_kbp_cp_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_options_lookup(km_kbp_state const *state,
+ uint8_t scope,
+ km_kbp_cp const *key,
+ km_kbp_cp const **value);
+
+/*
+```
+### `km_kbp_options_update`
+##### Description:
+Adds or updates one or more options from a list of `km_kbp_option_item`s.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
+- `KM_KBP_STATUS_KEY_ERROR`: The key cannot be found.
+##### Parameters:
+- __state__: An opaque pointer to a `km_kbp_state`.
+- __new_opts__: An array of `km_kbp_option_item` objects to update or add. Must be
+ terminated with `KM_KBP_OPTIONS_END`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_options_update(km_kbp_state *state,
+ km_kbp_option_item const *new_opts);
+
+/*
+```
+### `km_kbp_options_to_json`
+##### Description:
+Export the contents of a `km_kbp_options` array to a JSON formatted document and
+place it in the supplied buffer, reporting how much space was used. If null is
+passed as the buffer the number of bytes required is returned in `space`. If
+there is insufficent space to hold the document the contents of the buffer is
+undefined. The returned buffer uses UTF-8 encoding.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
+##### Parameters:
+- __opts__: An opaque pointer to an options object.
+- __buf__: A pointer to the buffer to place the C string containing the JSON
+document into, can be null.
+- __space__: A pointer to a size_t variable. This variable must contain the
+number of bytes available in the buffer pointed to by `buf`, unless `buf` is
+null. On return it will hold how many bytes were used.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_options_to_json(km_kbp_options const *opts,
+ char *buf,
+ size_t *space);
+
+
+/*
+```
+### Keyboards
+A keyboard is a set of rules and transforms in a Processor specific format for
+transforming key events into action items. The keyboard is parsed and loaded by
+the processsor and made available in an immutable fashion for use with any number
+of state objects.
+```c
+*/
+typedef struct {
+ km_kbp_cp const * version_string; // Processor specific version string.
+ km_kbp_cp const * id; // Keyman keyboard ID string.
+ km_kbp_path_name folder_path; // Path to the unpacked folder containing
+ // the keyboard and associated resources.
+ km_kbp_option_item const * default_options;
+} km_kbp_keyboard_attrs;
+
+/*
+```
+### `km_kbp_keyboard_load`
+##### Description:
+Parse and load keyboard from the supplied path and a pointer to the loaded keyboard
+into the out paramter.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
+- `KM_KBP_STATUS_IO_ERROR`:
+ In the event the keyboard file is unparseable for any reason
+- `KM_KBP_STATUS_INVALID_ARGUMENT`:
+ In the event the file doesn't exist or is inaccesible or `keyboard` is null.
+- `KM_KBP_STATUS_OS_ERROR`: Bit 31 (high bit) set, bits 0-30 are an OS-specific
+ error code.
+##### Parameters:
+- __kb_path__: On Windows, a UTF-16 string; on other platforms, a C string:
+ contains a valid path to the keyboard file.
+- __keyboard__: A pointer to result variable:
+ A pointer to the opaque keyboard object returned by the Processor. This
+ memory must be freed with a call to `km_kbp_keyboard_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_keyboard_load(km_kbp_path_name kb_path,
+ km_kbp_keyboard **keyboard);
+
+/*
+```
+### `km_kbp_keyboard_dispose`
+##### Description:
+Free the allocated memory belonging to an opaque keyboard object previously
+returned by `km_kbp_keyboard_load`.
+##### Parameters:
+- __keyboard__: A pointer to the opaque keyboard object to be
+ disposed of.
+
+```c
+*/
+KMN_API
+void
+km_kbp_keyboard_dispose(km_kbp_keyboard *keyboard);
+
+/*
+```
+### `km_kbp_keyboard_get_attrs`
+##### Description:
+Returns the const internal attributes of the keyboard. This structure is valid
+for the lifetime of the opaque keyboard object. Do not modify the returned data.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
+##### Parameters:
+- __keyboard__: A pointer to the opaque keyboard object to be queried.
+- __out__: A pointer to the result:
+ A pointer to a `km_kbp_keyboard_attrs` structure.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_keyboard_get_attrs(km_kbp_keyboard const *keyboard,
+ km_kbp_keyboard_attrs const **out);
+
+/*
+```
+### State
+A State object maintains all per keyboard related state including context
+and dynamic options ("option stores" in kmn format).
+
+```c
+*/
+
+/*
+```
+### `km_kbp_state_create`
+##### Description:
+Create a keyboard processor state object, maintaining state for the keyboard in
+the environment passed.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_NO_MEM`:
+ In the event memory is unavailable to allocate a state object.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`:
+ In the event the `keyboard` or `out` pointer are null.
+##### Parameters:
+- __keyboard__:
+A pointer to the opaque keyboard object this object will hold state for.
+- __env__:
+The array of `km_kbp_option_item` key/value pairs used to initialise the
+environment, terminated by `KM_KBP_OPTIONS_END`.
+- __out__:
+A pointer to result variable: A pointer to the opaque state object
+returned by the Processor, initalised to maintain state for `keyboard`.
+This must be disposed of by a call to `km_kbp_state_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_state_create(km_kbp_keyboard *keyboard,
+ km_kbp_option_item const *env,
+ km_kbp_state **out);
+
+/*
+```
+### `km_kbp_state_clone`
+##### Description:
+Clone an existing opaque state object.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_NO_MEM`:
+In the event memory is unavailable to allocate a state object.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`:
+In the event the `state` or `out` pointer are null.
+##### Parameters:
+- __state__:
+A pointer to the opaque statea object to be cloned.
+- __out__:
+A pointer to result variable: A pointer to the opaque state object
+returned by the Processor, cloned from the existing object `state`. This
+must be disposed of by a call to `km_kbp_state_dispose`.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_state_clone(km_kbp_state const *state,
+ km_kbp_state **out);
+
+/*
+```
+### `km_kbp_state_dispose`
+##### Description:
+Free the allocated resources belonging to a `km_kbp_state` object previously
+returned by `km_kbp_state_create` or `km_kbp_state_clone`. After this all
+pointers previously returned by any km_kbp_state family of calls will become
+invalid.
+##### Parameters:
+- __state__: A pointer to the opaque state object to be disposed.
+
+```c
+*/
+KMN_API
+void
+km_kbp_state_dispose(km_kbp_state *state);
+
+/*
+```
+### `km_kbp_state_context`
+##### Description:
+Get access to the state object's context.
+##### Return:
+A pointer to an opaque state object. This pointer is valid for the lifetime
+of the state object. If null is passed in, then null is returned.
+##### Parameters:
+- __state__: A pointer to the opaque state object to be queried.
+
+```c
+*/
+KMN_API
+km_kbp_context *
+km_kbp_state_context(km_kbp_state *state);
+
+/*
+```
+### `km_kpb_state_options`
+##### Description:
+Get access to the state object's options.
+##### Return:
+A pointer to an opaque state object. This pointer is valid for the lifetime
+of the state object. If null is passed in, then null is returned.
+##### Parameters:
+- __state__: A pointer to the opaque state object to be queried.
+
+```c
+*/
+KMN_API
+km_kbp_options *
+km_kbp_state_options(km_kbp_state *state);
+
+/*
+```
+### `km_kbp_state_action_items`
+##### Description:
+Get the list of action items generated by the last call to
+`km_kbp_process_event`.
+##### Return:
+A pointer to a `km_kbp_action_item` list, of `*num_items` in length. This data
+becomes invalid when the state object is destroyed, or after a call to
+`km_kbp_process_event`. Do not modify the contents of this data. The returned
+array is terminated with a `KM_KBP_IT_END` entry.
+##### Parameters:
+- __state__: A pointer to the opaque `km_kbp_state` object to be queried.
+- __num_items__:
+A pointer to a result variable: The number of items in the action item list
+including the `KM_KBP_IT_END` terminator. May be null if not that
+information is required.
+
+```c
+*/
+KMN_API
+km_kbp_action_item const *
+km_kbp_state_action_items(km_kbp_state const *state,
+ size_t *num_items);
+
+/*
+```
+### `km_kpb_state_to_json`
+##### Description:
+Export the internal state of a `km_kbp_state` object to a JSON format document
+and place it in the supplied buffer, reporting how much space was used. If null
+is passed as the buffer the number of bytes required is returned. If there is
+insufficent space to hold the document, the contents of the buffer is undefined.
+The encoding of the returned data is UTF-8.
+
+__WARNING__: The structure and format of the JSON document while independently
+versioned is not part of this API and is intended solely for use in diagnostics
+or by development and debugging tools which are aware of processor
+implementation details.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
+##### Parameters:
+- __state__: An pointer to an opaque state object.
+- __buf__: A pointer to the buffer to place the C string containing the JSON
+document into. May be null.
+- __space__: A pointer to a size_t variable. This variable must contain the
+number of bytes available in the buffer pointed to by `buf`, unless `buf` is
+null. On return it will hold how many bytes were used.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_state_to_json(km_kbp_state const *state,
+ char *buf,
+ size_t *space);
+
+/*
+```
+### Processor
+```c
+*/
+typedef struct {
+ size_t max_context; // Maximum context size supported by processor.
+ uint16_t current; // Current API number supported.
+ uint16_t revision; // Implementation number of current API.
+ uint16_t age; // current - age == Oldest API number supported.
+ uint16_t technology; // A bit field specifiying which Keyboard
+ // technologies the engine supports.
+ char const *vendor; // Implementor of the processor.
+} km_kbp_attr;
+
+enum km_kbp_tech_value {
+ KM_KBP_TECH_UNSPECIFIED = 0,
+ KM_KBP_TECH_MOCK = 1 << 0,
+ KM_KBP_TECH_KMX = 1 << 1,
+ KM_KBP_TECH_LDML = 1 << 2
+};
+
+
+/*
+```
+### `km_kbp_get_engine_attrs`
+##### Description:
+Get access processors attributes describing version and technology implemented.
+##### Return:
+A pointer to a `km_kbp_attr` structure. Do not modify the contents of this
+structure.
+##### Parameters:
+- __state__: An opaque pointer to an `km_kbp_state`.
+```c
+*/
+KMN_API
+km_kbp_attr const *
+km_kbp_get_engine_attrs(km_kbp_state const *state);
+
+/*
+```
+### `km_kbp_cp_dispose`
+##### Description:
+Free the allocated memory belonging to a `km_kbp_cp` array previously
+returned by `km_kbp_options_lookup`.
+##### Parameters:
+- __cp__: A pointer to the start of the `km_kbp_cp` array
+ to be disposed of.
+
+```c
+*/
+KMN_API
+void
+km_kbp_cp_dispose(km_kbp_cp const *cp);
+
+/*
+```
+### `km_kbp_process_event`
+##### Description:
+Run the keyboard on an opaque state object with the provided virtual key and modifer
+key state. Updates the state object as appropriate and fills out its action list.
+
+The action list will be cleared at the start of this call; options and context in
+the state may also be modified.
+##### Return status:
+- `KM_KBP_STATUS_OK`: On success.
+- `KM_KBP_STATUS_NO_MEM`:
+In the event memory is unavailable to allocate internal buffers.
+- `KM_KBP_STATUS_INVALID_ARGUMENT`:
+In the event the `state` pointer is null or an invalid virtual key or modifier
+state is passed.
+
+##### Parameters:
+- __state__: A pointer to the opaque state object.
+- __vk__: A virtual key to be processed.
+- __modifier_state__:
+The combinations of modifier keys set at the time key `vk` was pressed, bitmask
+from the `km_kbp_modifier_state` enum.
+
+```c
+*/
+KMN_API
+km_kbp_status
+km_kbp_process_event(km_kbp_state *state,
+ km_kbp_virtual_key vk,
+ uint16_t modifier_state);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+/*```
+*/
diff --git a/include/keyman/keyboardprocessor.h.in b/include/keyman/keyboardprocessor.h.in
index e278144..5d1ba06 100644
--- a/include/keyman/keyboardprocessor.h.in
+++ b/include/keyman/keyboardprocessor.h.in
@@ -478,8 +478,6 @@ typedef struct {
uintptr_t marker; // MARKER type
km_kbp_option_item const * option; // OPT types
km_kbp_usv character; // CHAR type
- km_kbp_virtual_key vkey; // VKEY types
- size_t erased; // BACK type
};
} km_kbp_action_item;
@@ -491,11 +489,11 @@ enum km_kbp_action_type {
KM_KBP_IT_BACK = 4, // Delete the codepoint preceding the insertion point.
KM_KBP_IT_PERSIST_OPT = 5, // The indicated option needs to be stored.
KM_KBP_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application
- KM_KBP_IT_INVALIDATE_CONTEXT = 7,
- // The processor requests that the context buffer be cleared;
- // for applications where context is cached, this clears the context;
- // for applications where context is read from the focused text store,
- // the context is just re-read and markers flushed.
+ KM_KBP_IT_INVALIDATE_CONTEXT = 7,
+ // The processor requests that the context buffer be cleared;
+ // for applications where context is cached, this clears the context;
+ // for applications where context is read from the focused text store,
+ // the context is just re-read and markers flushed.
KM_KBP_IT_MAX_TYPE_ID
};
@@ -554,7 +552,7 @@ km_kbp_options_list_size(km_kbp_option_item const *opts);
/*
```
-### `km_kbp_options_lookup`
+### `km_kbp_state_option_lookup`
##### Description:
Lookup an option based on its key, in an options list.
##### Return status:
@@ -567,22 +565,21 @@ Lookup an option based on its key, in an options list.
- __scope__: Which key-value store to interrogate.
- __key__: A UTF-16 string that matches the key in the target `km_kbp_option_item`.
- __value__: A pointer to the result variable:
- A pointer to a copy of the UTF-16 string value; undefined if return status
- is anything other than `KM_KBP_STATUS_OK`. This memory must be disposed of
- with `km_kbp_cp_dispose`.
-
+ A pointer to a UTF-16 string value owned by the state or keyboard object at
+ the time of the call. This pointer is only valid *until* the next call to any
+ function on this API and should be used immediately.
```c
*/
KMN_API
km_kbp_status
-km_kbp_options_lookup(km_kbp_state const *state,
+km_kbp_state_option_lookup(km_kbp_state const *state,
uint8_t scope,
km_kbp_cp const *key,
km_kbp_cp const **value);
/*
```
-### `km_kbp_options_update`
+### `km_kbp_state_options_update`
##### Description:
Adds or updates one or more options from a list of `km_kbp_option_item`s.
##### Return status:
@@ -591,7 +588,7 @@ Adds or updates one or more options from a list of `km_kbp_option_item`s.
- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
- `KM_KBP_STATUS_KEY_ERROR`: The key cannot be found.
##### Parameters:
-- __state__: An opaque pointer to a `km_kbp_state`.
+- __state__: An opaque pointer to a state object.
- __new_opts__: An array of `km_kbp_option_item` objects to update or add. Must be
terminated with `KM_KBP_OPTIONS_END`.
@@ -599,12 +596,12 @@ Adds or updates one or more options from a list of `km_kbp_option_item`s.
*/
KMN_API
km_kbp_status
-km_kbp_options_update(km_kbp_state *state,
+km_kbp_state_options_update(km_kbp_state *state,
km_kbp_option_item const *new_opts);
/*
```
-### `km_kbp_options_to_json`
+### `km_kbp_state_options_to_json`
##### Description:
Export the contents of a `km_kbp_options` array to a JSON formatted document and
place it in the supplied buffer, reporting how much space was used. If null is
@@ -616,7 +613,7 @@ undefined. The returned buffer uses UTF-8 encoding.
- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails.
##### Parameters:
-- __opts__: An opaque pointer to an options object.
+- __opts__: An opaque pointer to a state object.
- __buf__: A pointer to the buffer to place the C string containing the JSON
document into, can be null.
- __space__: A pointer to a size_t variable. This variable must contain the
@@ -627,7 +624,7 @@ null. On return it will hold how many bytes were used.
*/
KMN_API
km_kbp_status
-km_kbp_options_to_json(km_kbp_options const *opts,
+km_kbp_state_options_to_json(km_kbp_state const *state,
char *buf,
size_t *space);
@@ -751,7 +748,7 @@ This must be disposed of by a call to `km_kbp_state_dispose`.
*/
KMN_API
km_kbp_status
-km_kbp_state_create(km_kbp_keyboard const *keyboard,
+km_kbp_state_create(km_kbp_keyboard *keyboard,
km_kbp_option_item const *env,
km_kbp_state **out);
@@ -817,23 +814,6 @@ km_kbp_state_context(km_kbp_state *state);
/*
```
-### `km_kpb_state_options`
-##### Description:
-Get access to the state object's options.
-##### Return:
-A pointer to an opaque state object. This pointer is valid for the lifetime
-of the state object. If null is passed in, then null is returned.
-##### Parameters:
-- __state__: A pointer to the opaque state object to be queried.
-
-```c
-*/
-KMN_API
-km_kbp_options *
-km_kbp_state_options(km_kbp_state *state);
-
-/*
-```
### `km_kbp_state_action_items`
##### Description:
Get the list of action items generated by the last call to
@@ -931,22 +911,6 @@ km_kbp_get_engine_attrs(km_kbp_state const *state);
/*
```
-### `km_kbp_cp_dispose`
-##### Description:
-Free the allocated memory belonging to a `km_kbp_cp` array previously
-returned by `km_kbp_options_lookup`.
-##### Parameters:
-- __cp__: A pointer to the start of the `km_kbp_cp` array
- to be disposed of.
-
-```c
-*/
-KMN_API
-void
-km_kbp_cp_dispose(km_kbp_cp const *cp);
-
-/*
-```
### `km_kbp_process_event`
##### Description:
Run the keyboard on an opaque state object with the provided virtual key and modifer
diff --git a/include/keyman/keyboardprocessor_bits.h b/include/keyman/keyboardprocessor_bits.h
index ac34757..d844d4e 100644
--- a/include/keyman/keyboardprocessor_bits.h
+++ b/include/keyman/keyboardprocessor_bits.h
@@ -28,6 +28,8 @@
#if defined _WIN32 || defined __CYGWIN__
typedef wchar_t const * km_kbp_path_name;
+ #define _KM_KBP_PATH_SEPARATOR (L'\\')
+ #define _KM_KBP_EXT_SEPARATOR (L'.')
#if defined __GNUC__ // These three will be redefined for Windows
#undef _kmn_export_flag
#undef _kmn_import_flag
@@ -42,6 +44,8 @@
#define _kmn_static_flag
#else
typedef char const * km_kbp_path_name;
+ #define _KM_KBP_PATH_SEPARATOR ('/')
+ #define _KM_KBP_EXT_SEPARATOR ('.')
#endif
#if defined KMN_KBP_STATIC
diff --git a/include/keyman/meson.build b/include/keyman/meson.build
index 5e5f127..2b6c595 100644
--- a/include/keyman/meson.build
+++ b/include/keyman/meson.build
@@ -6,7 +6,7 @@
# History: 6 Oct 2018 - TSE - Move into keyman folder.
#
-ver = meson.project_version().split('.')
+ver = lib_version.split('.')
cfg = configuration_data()
cfg.set('lib_curr', ver[0])
diff --git a/meson.build b/meson.build
index 414578e..62ebb83 100644
--- a/meson.build
+++ b/meson.build
@@ -6,13 +6,15 @@
#
project('keyboardprocessor', 'cpp', 'c',
- version: '0.0.0',
+ version: '11.0.101',
license: 'MIT',
default_options : ['buildtype=release',
'cpp_std=c++14'])
compiler = meson.get_compiler('cpp')
+lib_version = '0.0.0'
+
if compiler.get_id() == 'msvc'
add_global_arguments('/source-charset:utf-8', language: ['c', 'cpp'])
endif
diff --git a/src/context.hpp b/src/context.hpp
index b2b7fc9..b344e06 100644
--- a/src/context.hpp
+++ b/src/context.hpp
@@ -20,7 +20,24 @@ namespace kbp
// This will likely be replaced with a class implementing a more space
// efficient data structure such as a ring buffer or bounded queue.
-using context = std::list<km_kbp_context_item>;
+class context: public std::list<km_kbp_context_item>
+{
+public:
+ void push_character(km_kbp_usv);
+ void push_marker(uint32_t);
+};
+
+
+inline
+void context::push_character(km_kbp_usv usv) {
+ emplace_back(km_kbp_context_item { KM_KBP_CT_CHAR, {0,}, {usv} });
+}
+
+
+inline
+void context::push_marker(uint32_t marker) {
+ emplace_back(km_kbp_context_item { KM_KBP_CT_MARKER, {0,}, {marker} });
+}
} // namespace kbp
} // namespace km
diff --git a/src/json.cpp b/src/json.cpp
index cec8dd1..40cb861 100644
--- a/src/json.cpp
+++ b/src/json.cpp
@@ -8,11 +8,13 @@
25 Oct 2018 - TSE - Relicensed under the MIT license for
inclusion in the Keyman project.
*/
-
-#include <cstdio>
+#include <cassert>
+#include <iomanip>
#include <limits>
+
#include "json.hpp"
+
#if defined(_MSC_VER)
#define FORMAT_INTMAX "%lli"
#define FORMAT_UINTMAX "%llu"
diff --git a/src/json.hpp b/src/json.hpp
index 0a34a58..985e7af 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -11,12 +11,12 @@
*/
#pragma once
-#include <cassert>
#include <cstdint>
-#include <iomanip>
#include <ostream>
#include <vector>
+#include "utfcodec.hpp"
+
class json
{
// Prevent copying
@@ -113,8 +113,13 @@ json & json::operator << (json::_context_t ctxt) throw()
return *this;
}
+template<typename C>
+inline
+json & operator << (json & j, std::basic_string<C> const & s) throw() { return j << json::string(convert<C,char>(s).c_str()); }
+
+template<typename C>
inline
-json & operator << (json & j, std::string const & s) throw() { return j << json::string(s.c_str()); }
+json & operator << (json & j, C const * s) throw() { return j << json::string(convert<C,char>(s).c_str()); }
inline
json & operator << (json & j, signed char d) throw() { return j << json::integer(d); }
diff --git a/src/keyboard.cpp b/src/keyboard.cpp
index 0f44939..ac5e978 100644
--- a/src/keyboard.cpp
+++ b/src/keyboard.cpp
@@ -5,72 +5,62 @@
Authors: Tim Eves (TSE)
History: 7 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp
*/
-
-#include <codecvt>
#include "keyboard.hpp"
#include "json.hpp"
-#include "processor.hpp"
using namespace km::kbp;
-std::string utf16_to_utf8(std::u16string utf16_string);
-keyboard::keyboard(std::filesystem::path const & path)
-: _keyboard_id(path.stem().u16string()),
- _version_string(u"3.145"),
- _folder_path(path.parent_path()),
- _default_opts {KM_KBP_OPTIONS_END}
+inline
+void keyboard_attributes::render()
{
- version_string = _version_string.c_str();
- id = _keyboard_id.c_str();
- folder_path = _folder_path.native().c_str();
-
- if (path.extension() == ".kmx" ||
- path.extension() == ".KMX") { // Some legacy packages may include upper-case file extensions
- _processor = new kmx_processor(this);
- }
- else if (path.extension() == ".mock") {
- _processor = new mock_processor(this);
- }
- else {
- _processor = new null_processor(this);
- }
-
+ // Make attributes point to the stored values above.
+ id = _keyboard_id.c_str();
+ version_string = _version_string.c_str();
+ folder_path = _folder_path.c_str();
default_options = _default_opts.data();
}
-json & km::kbp::operator << (json & j, km::kbp::keyboard const & kb)
-{
- j << json::object
- << "id" << utf16_to_utf8(kb.id)
- << "folder" << kb._folder_path.string()
- << "version" << utf16_to_utf8(kb.version_string)
- << "rules" << json::array << json::close;
- return j << json::close;
+keyboard_attributes::keyboard_attributes(std::u16string const & kbid,
+ std::u16string const & version,
+ path_type const & path,
+ options_store const &opts)
+: _keyboard_id(kbid),
+ _version_string(version),
+ _folder_path(path),
+ _default_opts(opts)
+{
+ // Ensure that the default_options array will be properly terminated.
+ _default_opts.emplace_back();
+ render();
}
-/*
- This function exists because of a bug in Visual Studio 2015 and 2017:
- https://social.msdn.microsoft.com/Forums/en-US/8f40dcd8-c67f-4eba-9134-a19b9178e481/vs-2015-rc-linker-stdcodecvt-error?forum=vcgeneral
- https://stackoverflow.com/a/35103224/1836776
-*/
-
-#if _MSC_VER >= 1900 /* VS 2015 */ && _MSC_VER <= 1916 /* VS 2017 19.16 */
-std::string utf16_to_utf8(std::u16string utf16_string)
+keyboard_attributes::keyboard_attributes(keyboard_attributes &&rhs)
+: _keyboard_id(std::move(rhs._keyboard_id)),
+ _version_string(std::move(rhs._version_string)),
+ _folder_path(std::move(rhs._folder_path)),
+ _default_opts(std::move(rhs._default_opts))
{
- std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
- auto p = reinterpret_cast<const int16_t *>(utf16_string.data());
- return convert.to_bytes(p, p + utf16_string.size());
+ rhs.id = rhs.version_string = nullptr;
+ render();
}
-#else
-std::string utf16_to_utf8(std::u16string utf16_string)
+keyboard_attributes & keyboard_attributes::operator = (keyboard_attributes &&rhs)
{
- std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
- return convert.to_bytes(utf16_string);
+ return *new (this) keyboard_attributes(std::move(rhs));
}
-#endif
+
+json & km::kbp::operator << (json & j, km::kbp::keyboard_attributes const & kb)
+{
+ j << json::object
+ << "id" << kb.id
+ << "folder" << kb._folder_path
+ << "version" << kb.version_string
+ << "rules" << json::array << json::close;
+
+ return j << json::close;
+}
diff --git a/src/keyboard.hpp b/src/keyboard.hpp
index 1692bdf..57f8128 100644
--- a/src/keyboard.hpp
+++ b/src/keyboard.hpp
@@ -8,17 +8,13 @@
#pragma once
-#include <experimental/filesystem>
#include <string>
+#include <vector>
#include <keyman/keyboardprocessor.h>
#include "option.hpp"
-#include "processor.hpp"
-
-namespace std {
- namespace filesystem = std::experimental::filesystem;
-}
+#include "path.hpp"
// Forward declarations
class json;
@@ -26,30 +22,41 @@ class json;
namespace km {
namespace kbp
{
-
- class keyboard : public km_kbp_keyboard_attrs
+ class keyboard_attributes : public km_kbp_keyboard_attrs
{
- std::u16string const _keyboard_id;
- std::u16string const _version_string;
- std::filesystem::path const _folder_path;
- std::vector<km_kbp_option_item> _default_opts;
- abstract_processor *_processor;
+ std::u16string _keyboard_id;
+ std::u16string _version_string;
+ kbp::path _folder_path;
+ std::vector<option> _default_opts;
+
+ void render();
+
public:
- keyboard(std::filesystem::path const &);
- ~keyboard() {
- delete _processor;
- }
+ using options_store = decltype(_default_opts);
+ using path_type = decltype(_folder_path);
- friend json & operator << (json &, km::kbp::keyboard const &);
+ keyboard_attributes()
+ : km_kbp_keyboard_attrs {nullptr, nullptr, nullptr, nullptr} {}
+ keyboard_attributes(keyboard_attributes const &) = delete;
+ keyboard_attributes(keyboard_attributes &&);
- std::vector<km_kbp_option_item> *default_opts() { return & _default_opts; }
+ keyboard_attributes(std::u16string const & id,
+ std::u16string const & version,
+ path_type const & path,
+ options_store const &opts);
- kbp::abstract_processor const & processor() const noexcept { return *_processor; }
+ keyboard_attributes & operator = (keyboard_attributes const &) = delete;
+ keyboard_attributes & operator = (keyboard_attributes &&);
+
+ friend json & operator << (json &, km::kbp::keyboard_attributes const &);
+
+ options_store const & default_opts_store() const noexcept { return _default_opts; }
+ options_store & default_opts_store() noexcept { return _default_opts; }
+
+ path_type const & path() const noexcept { return _folder_path; }
};
- json & operator << (json &, km::kbp::keyboard const &);
+ json & operator << (json &, km::kbp::keyboard_attributes const &);
} // namespace kbp
} // namespace km
-
-struct km_kbp_keyboard : public km::kbp::keyboard {};
diff --git a/src/km_kbp_context_api.cpp b/src/km_kbp_context_api.cpp
index 334a2fa..23915ac 100644
--- a/src/km_kbp_context_api.cpp
+++ b/src/km_kbp_context_api.cpp
@@ -10,7 +10,6 @@
*/
#include <cassert>
#include <algorithm>
-#include <iterator>
#include <vector>
#include <keyman/keyboardprocessor.h>
diff --git a/src/km_kbp_keyboard_api.cpp b/src/km_kbp_keyboard_api.cpp
index 8067e73..dbc6c17 100644
--- a/src/km_kbp_keyboard_api.cpp
+++ b/src/km_kbp_keyboard_api.cpp
@@ -9,46 +9,51 @@
into keyboard.hpp
*/
#include <cassert>
-#include <algorithm>
-#include <experimental/filesystem>
-#include <iterator>
-#include <sstream>
-#include <unordered_map>
-#include <string>
-#include <vector>
#include <keyman/keyboardprocessor.h>
-#include <json.hpp>
-
#include "keyboard.hpp"
-#include "option.hpp"
+#include "processor.hpp"
+#include "kmx/kmx_processevent.hpp"
+#include "mock/mock_processor.hpp"
+
+using namespace km::kbp;
+namespace
+{
+ abstract_processor * processor_factory(path const & kb_path)
+ {
+ // Some legacy packages may include upper-case file extensions
+ if (kb_path.suffix() == ".kmx" || kb_path.suffix() == ".KMX") {
+ return new kmx_processor(kb_path);
+ }
+ else if (kb_path.suffix() == ".mock") {
+ return new mock_processor(kb_path);
+ }
+ else {
+ return new null_processor();
+ }
+ }
+}
km_kbp_status
-km_kbp_keyboard_load(km_kbp_path_name kb_path,
- km_kbp_keyboard **keyboard)
+km_kbp_keyboard_load(km_kbp_path_name kb_path, km_kbp_keyboard **keyboard)
{
assert(keyboard);
if (!keyboard)
return KM_KBP_STATUS_INVALID_ARGUMENT;
- //auto stat = std::filesystem::status(kb_path);
- //
- // if (stat.type() != std::filesystem::file_type::regular)
- // return KM_KBP_STATUS_INVALID_ARGUMENT;
-
try
{
- km::kbp::keyboard *kp = new km::kbp::keyboard(kb_path);
- km_kbp_status status = kp->processor().validate();
+ abstract_processor *kp = processor_factory(kb_path);
+ km_kbp_status status = kp->validate();
if (status != KM_KBP_STATUS_OK) {
delete kp;
return status;
}
*keyboard = static_cast<km_kbp_keyboard *>(kp);
}
- catch (std::bad_alloc)
+ catch (std::bad_alloc)
{
return KM_KBP_STATUS_NO_MEM;
}
@@ -69,6 +74,6 @@ km_kbp_keyboard_get_attrs(km_kbp_keyboard const *keyboard,
if (!keyboard || !out)
return KM_KBP_STATUS_INVALID_ARGUMENT;
- *out = keyboard;
+ *out = &keyboard->keyboard();
return KM_KBP_STATUS_OK;
}
diff --git a/src/km_kbp_options_api.cpp b/src/km_kbp_options_api.cpp
index 944ba29..e50b0a3 100644
--- a/src/km_kbp_options_api.cpp
+++ b/src/km_kbp_options_api.cpp
@@ -9,15 +9,14 @@
into option.hpp
*/
#include <cassert>
-#include <algorithm>
-#include <iterator>
#include <sstream>
-#include <unordered_map>
-#include <vector>
+
#include <keyman/keyboardprocessor.h>
+#include "processor.hpp"
-#include "option.hpp"
#include "json.hpp"
+#include "state.hpp"
+
size_t
km_kbp_options_list_size(km_kbp_option_item const *opts)
@@ -33,7 +32,7 @@ km_kbp_options_list_size(km_kbp_option_item const *opts)
km_kbp_status
-km_kbp_options_lookup(km_kbp_state const *state,
+km_kbp_state_option_lookup(km_kbp_state const *state,
uint8_t scope, km_kbp_cp const *key,
km_kbp_cp const **value_out)
{
@@ -43,41 +42,22 @@ km_kbp_options_lookup(km_kbp_state const *state,
if (scope == KM_KBP_OPT_UNKNOWN || scope > KM_KBP_OPT_MAX_SCOPES)
return KM_KBP_STATUS_INVALID_ARGUMENT;
- auto opts = km_kbp_state_options(const_cast<km_kbp_state *>(state));
+ auto & processor = state->processor();
- // Copy the internal value to our new buffer
- km_kbp_cp const *internal_value = opts->lookup(km_kbp_option_scope(scope), key);
- if (!internal_value)
- {
- return KM_KBP_STATUS_KEY_ERROR;
- }
- std::u16string const &value = internal_value;
-
- km_kbp_cp *valuep;
- try
- {
- valuep = new km_kbp_cp[value.size() + 1];
- }
- catch (std::bad_alloc)
- {
- return KM_KBP_STATUS_NO_MEM;
- }
- std::copy(value.begin(), value.end(), valuep);
- valuep[value.size()] = u'\0';
-
- *value_out = valuep;
+ *value_out = processor.lookup_option(km_kbp_option_scope(scope), key);
+ if (!*value_out) return KM_KBP_STATUS_KEY_ERROR;
return KM_KBP_STATUS_OK;
}
km_kbp_status
-km_kbp_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
+km_kbp_state_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
{
assert(state); assert(opt);
if (!state|| !opt) return KM_KBP_STATUS_INVALID_ARGUMENT;
- auto opts = km_kbp_state_options(state);
+ auto & processor = state->processor();
try
{
@@ -86,10 +66,13 @@ km_kbp_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
if (opt->scope == KM_KBP_OPT_UNKNOWN || opt->scope > KM_KBP_OPT_MAX_SCOPES)
return KM_KBP_STATUS_INVALID_ARGUMENT;
- if (!opts->assign(state, km_kbp_option_scope(opt->scope), opt->key, opt->value))
+ if (processor.update_option(
+ km_kbp_option_scope(opt->scope),
+ opt->key,
+ opt->value).empty())
return KM_KBP_STATUS_KEY_ERROR;
}
- }
+ }
catch (std::bad_alloc)
{
return KM_KBP_STATUS_NO_MEM;
@@ -101,10 +84,10 @@ km_kbp_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
// This function doesn't need to use the json pretty printer for such a simple
// list of key:value pairs but it's a good introduction to it.
km_kbp_status
-km_kbp_options_to_json(km_kbp_options const *opts, char *buf, size_t *space)
+km_kbp_state_options_to_json(km_kbp_state const *state, char *buf, size_t *space)
{
- assert(opts); assert(space);
- if (!opts || !space)
+ assert(state); assert(space);
+ if (!state || !space)
return KM_KBP_STATUS_INVALID_ARGUMENT;
std::stringstream _buf;
@@ -112,7 +95,8 @@ km_kbp_options_to_json(km_kbp_options const *opts, char *buf, size_t *space)
try
{
- jo << *opts;
+// TODO: Fix
+// jo << state->options();
}
catch (std::bad_alloc)
{
@@ -132,9 +116,3 @@ km_kbp_options_to_json(km_kbp_options const *opts, char *buf, size_t *space)
*space = doc.size()+1;
return KM_KBP_STATUS_OK;
}
-
-void
-km_kbp_cp_dispose(km_kbp_cp const *cp)
-{
- delete [] cp;
-}
diff --git a/src/km_kbp_processevent_api.cpp b/src/km_kbp_processevent_api.cpp
index da9b049..698ae7b 100644
--- a/src/km_kbp_processevent_api.cpp
+++ b/src/km_kbp_processevent_api.cpp
@@ -16,14 +16,12 @@ km_kbp_status
km_kbp_process_event(km_kbp_state *state,
km_kbp_virtual_key vk, uint16_t modifier_state)
{
- km::kbp::keyboard const & k = state->keyboard();
- return const_cast<km::kbp::abstract_processor&>(k.processor()).process_event(state, vk, modifier_state);
+ return state->processor().process_event(state, vk, modifier_state);
}
km_kbp_attr const *
km_kbp_get_engine_attrs(km_kbp_state const *state)
{
- return state->keyboard().processor().get_attrs();
+ return &state->processor().attributes();
}
-
diff --git a/src/km_kbp_state_api.cpp b/src/km_kbp_state_api.cpp
index 7e8dd0b..7dc9c1f 100644
--- a/src/km_kbp_state_api.cpp
+++ b/src/km_kbp_state_api.cpp
@@ -10,24 +10,20 @@
*/
#include <cassert>
#include <algorithm>
-#include <iterator>
-#include <list>
#include <sstream>
-#include <utility>
-#include <vector>
#include <keyman/keyboardprocessor.h>
-#include <utfcodec.hpp>
-#include <json.hpp>
+#include "json.hpp"
-#include "context.hpp"
-#include "option.hpp"
-#include "keyboard.hpp"
+#include "processor.hpp"
#include "state.hpp"
using namespace km::kbp;
-km_kbp_status km_kbp_state_create(km_kbp_keyboard const * keyboard,
+// Forward declarations
+class context;
+
+km_kbp_status km_kbp_state_create(km_kbp_keyboard * keyboard,
km_kbp_option_item const *env,
km_kbp_state ** out)
{
@@ -37,9 +33,7 @@ km_kbp_status km_kbp_state_create(km_kbp_keyboard const * keyboard,
try
{
- *out = new km_kbp_state(static_cast<km::kbp::keyboard const &>(*keyboard),
- env);
-
+ *out = new km_kbp_state(static_cast<abstract_processor&>(*keyboard), env);
}
catch (std::bad_alloc)
{
@@ -76,27 +70,19 @@ km_kbp_context *km_kbp_state_context(km_kbp_state *state)
}
-km_kbp_options *km_kbp_state_options(km_kbp_state *state)
-{
- assert(state);
- if (!state) return nullptr;
-
- return static_cast<km_kbp_options *>(&state->options());
-}
-
-
km_kbp_action_item const * km_kbp_state_action_items(km_kbp_state const *state,
size_t *num_items)
{
- assert(state);
- if (!state) return nullptr;
+ assert(state && state->actions().size() > 0);
+ if (!state || state->actions().empty()) return nullptr;
if (num_items)
- *num_items = state->actions.size();
+ *num_items = state->actions().size();
// Process events will ensure that the actions vector is always well
// teminated
- return state->actions.data();
+ assert(state->actions().back().type == KM_KBP_IT_END);
+ return state->actions().data();
}
namespace {
@@ -136,15 +122,13 @@ json & operator << (json & j, km_kbp_action_item const &act)
{
case KM_KBP_IT_END:
case KM_KBP_IT_ALERT:
+ case KM_KBP_IT_BACK:
j << json::null;
break;
case KM_KBP_IT_CHAR:
case KM_KBP_IT_MARKER:
j << km_kbp_context_item {act.type, {0,}, {act.character}}; // TODO: is act.type correct here? it may map okay but this is bad practice to mix constants across types. Similarly using act.character instead of act.type
break;
- case KM_KBP_IT_BACK:
- j << json::null; // act.erased;
- break;
case KM_KBP_IT_PERSIST_OPT:
j << json::object
<< scope_names_lut[act.option->scope]
@@ -153,7 +137,6 @@ json & operator << (json & j, km_kbp_action_item const &act)
<< json::close
<< json::close;
break;
- break;
}
j << json::close;
@@ -161,7 +144,7 @@ json & operator << (json & j, km_kbp_action_item const &act)
}
-json & operator << (json & j, std::vector<km_kbp_action_item> const & acts)
+json & operator << (json & j, actions const & acts)
{
j << json::array;
for (auto & act: acts)
@@ -192,10 +175,10 @@ km_kbp_status km_kbp_state_to_json(km_kbp_state const *state,
// Pretty print the document.
jo << json::object
<< "$schema" << "keyman/keyboardprocessor/doc/introspection.schema"
- << "keyboard" << state->keyboard()
- << "options" << state->options()
+ << "keyboard" << state->processor().keyboard()
+// << "options" << state->options() TODO: Fix
<< "context" << state->context()
- << "actions" << state->actions
+ << "actions" << state->actions()
<< json::close;
}
catch (std::bad_alloc)
diff --git a/src/kmx/kmx_actions.cpp b/src/kmx/kmx_actions.cpp
index 5182154..03c70eb 100644
--- a/src/kmx/kmx_actions.cpp
+++ b/src/kmx/kmx_actions.cpp
@@ -4,7 +4,8 @@
*/
#include <kmx/kmx_processor.h>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
void KMX_Actions::ResetQueue()
{
@@ -31,7 +32,7 @@ KMX_BOOL KMX_Actions::QueueAction(int ItemType, KMX_DWORD dwData)
QueueSize++;
int result = TRUE;
-
+
switch(ItemType)
{
case QIT_VKEYDOWN:
diff --git a/src/kmx/kmx_capslock.cpp b/src/kmx/kmx_capslock.cpp
index 8d509d3..999fa08 100644
--- a/src/kmx/kmx_capslock.cpp
+++ b/src/kmx/kmx_capslock.cpp
@@ -4,7 +4,8 @@
*/
#include <kmx/kmx_processor.h>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
void KMX_Processor::ResetCapsLock(void)
{
diff --git a/src/kmx/kmx_context.cpp b/src/kmx/kmx_context.cpp
index 0aa7fb7..77ab434 100644
--- a/src/kmx/kmx_context.cpp
+++ b/src/kmx/kmx_context.cpp
@@ -4,7 +4,8 @@
*/
#include <kmx/kmx_processor.h>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
/* KMX_Context */
diff --git a/src/kmx/kmx_debug.cpp b/src/kmx/kmx_debug.cpp
index a16dee4..05ef171 100644
--- a/src/kmx/kmx_debug.cpp
+++ b/src/kmx/kmx_debug.cpp
@@ -6,7 +6,8 @@
#include <stdarg.h>
#include <iostream>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
#define TAB "\t"
#define NL "\n"
diff --git a/src/kmx/kmx_environment.cpp b/src/kmx/kmx_environment.cpp
index 75ae0f8..4fac391 100644
--- a/src/kmx/kmx_environment.cpp
+++ b/src/kmx/kmx_environment.cpp
@@ -7,7 +7,8 @@
#include <state.hpp>
#include <option.hpp>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
namespace {
km_kbp_cp const
@@ -20,30 +21,47 @@ namespace {
}
KMX_Environment::KMX_Environment() {
+ Load(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
+ Load(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
+ Load(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
+ Load(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
+ Load(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
+ Load(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
}
-void KMX_Environment::InitOption(std::vector<km_kbp_option_item> *default_env, km_kbp_cp const *key, km_kbp_cp const *default_value) {
- km_kbp_option_item opt = { (km_kbp_cp*) key, (km_kbp_cp*) default_value, KM_KBP_OPT_ENVIRONMENT };
- default_env->emplace_back(opt);
- Load(key, default_value);
-}
-void KMX_Environment::Init(std::vector<km_kbp_option_item> *default_env) {
- InitOption(default_env, KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
- InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
- InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
- InitOption(default_env, KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
- InitOption(default_env, KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
- InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
+char16_t const * KMX_Environment::LookUp(std::u16string const & key) const {
+ assert(!key.empty());
+ if (!key.empty()) return nullptr;
- // TODO refactor this and the keyboard option terminator into state.cpp and keyboard.cpp respectively
- km_kbp_option_item opt = KM_KBP_OPTIONS_END;
- default_env->emplace_back(opt);
+ if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) {
+ return _platform.c_str();
+ }
+ else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUT)) {
+ return _baseLayout.c_str();
+ }
+ else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTALT)) {
+ return _baseLayoutAlt.c_str();
+ }
+ else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_SIMULATEALTGR)) {
+ return _simulateAltGr ? u"1" : u"0";
+ }
+ else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_CAPSLOCK)) {
+ return _capsLock ? u"1" : u"0";
+ }
+ else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) {
+ return _baseLayoutGivesCtrlRAltForRAlt ? u"1" : u"0";
+ }
+ else {
+ // Unsupported key
+ return nullptr;
+ }
}
+
void KMX_Environment::Load(std::u16string const & key, std::u16string const & value) {
assert(!key.empty());
-
+
if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) {
_platform = value;
}
@@ -55,7 +73,7 @@ void KMX_Environment::Load(std::u16string const & key, std::u16string const & va
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_SIMULATEALTGR)) {
_simulateAltGr = value == u"1";
- }
+ }
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_CAPSLOCK)) {
_capsLock = value == u"1";
}
diff --git a/src/kmx/kmx_environment.h b/src/kmx/kmx_environment.h
index 34148d8..c8c0e21 100644
--- a/src/kmx/kmx_environment.h
+++ b/src/kmx/kmx_environment.h
@@ -1,6 +1,7 @@
#pragma once
#include <string>
+#include "option.hpp"
#include "kmx_base.h"
namespace km {
@@ -14,13 +15,13 @@ private:
KMX_BOOL _capsLock;
std::u16string _platform;
void InitOption(
- std::vector<km_kbp_option_item> * default_env,
- km_kbp_cp const * key,
+ std::vector<option> & default_env,
+ km_kbp_cp const * key,
km_kbp_cp const * default_value);
public:
KMX_Environment();
void Load(std::u16string const & key, std::u16string const & value);
- void Init(std::vector<km_kbp_option_item> *default_env);
+ char16_t const * LookUp(std::u16string const & key) const;
KMX_BOOL capsLock() const noexcept { return _capsLock; }
KMX_BOOL simulateAltGr() const noexcept { return _simulateAltGr; }
diff --git a/src/kmx/kmx_file.cpp b/src/kmx/kmx_file.cpp
index 4c9715e..945186a 100644
--- a/src/kmx/kmx_file.cpp
+++ b/src/kmx/kmx_file.cpp
@@ -4,16 +4,17 @@
*/
#include "kmx_processor.h"
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32) || defined(_WIN64)
#include <share.h>
#endif
KMX_BOOL KMX_Processor::Load(km_kbp_path_name KeyboardName)
{
if(!LoadKeyboard(KeyboardName, &m_keyboard.Keyboard)) return FALSE; // I5136
-
+
return TRUE;
}
@@ -70,13 +71,12 @@ unsigned long CalculateBufferCRC(unsigned long count, KMX_BYTE *p)
temp2 = CRCTable[((int) crc ^ *p++) & 0xff];
crc = temp1 ^ temp2;
}
-
+
return crc;
}
KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard)
{
- long sz;
PKMX_BYTE buf;
FILE *fp;
LPKEYBOARD kbp;
@@ -89,7 +89,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe
return FALSE;
}
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32) || defined(_WIN64)
fp = _wfsopen(fileName, L"rb", _SH_DENYWR);
#else
fp = fopen(fileName, "rb");
@@ -106,7 +106,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe
return FALSE;
}
- sz = ftell(fp);
+ auto sz = ftell(fp);
if (sz < 0) {
fclose(fp);
return FALSE;
@@ -146,7 +146,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe
if(*PKMX_DWORD(filebase) != KMX_DWORD(FILEID_COMPILED))
{
- delete buf;
+ delete buf;
DebugLog("Invalid file - signature is invalid");
return FALSE;
}
@@ -208,14 +208,14 @@ LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
kbp->dpGroupArray = (LPGROUP) bufp;
bufp += sizeof(GROUP) * kbp->cxGroupArray;
-
+
PCOMP_STORE csp;
LPSTORE sp;
KMX_DWORD i;
for(
- csp = (PCOMP_STORE)(base + ckbp->dpStoreArray), sp = kbp->dpStoreArray, i = 0;
- i < kbp->cxStoreArray;
+ csp = (PCOMP_STORE)(base + ckbp->dpStoreArray), sp = kbp->dpStoreArray, i = 0;
+ i < kbp->cxStoreArray;
i++, sp++, csp++)
{
sp->dwSystemID = csp->dwSystemID;
@@ -284,7 +284,7 @@ LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base, KMX_DWOR
for(gp = kbp->dpGroupArray, cgp = (PCOMP_GROUP) gp, i = 0; i < kbp->cxGroupArray; i++, gp++, cgp++)
{
- gp->dpName = StringOffset(base, cgp->dpName);
+ gp->dpName = StringOffset(base, cgp->dpName);
gp->dpKeyArray = (LPKEY) (base + cgp->dpKeyArray);
if(cgp->dpMatch != NULL) gp->dpMatch = (PKMX_WCHAR) (base + cgp->dpMatch);
if(cgp->dpNoMatch != NULL) gp->dpNoMatch = (PKMX_WCHAR) (base + cgp->dpNoMatch);
@@ -301,7 +301,7 @@ LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base, KMX_DWOR
#endif
-KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, KMX_DWORD sz)
+KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz)
{
KMX_DWORD tempcs;
PCOMP_KEYBOARD ckbp;
@@ -314,20 +314,20 @@ KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, KMX_DWORD sz)
return tempcs == CalculateBufferCRC(sz, buf);
}
-KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, KMX_DWORD sz)
+KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
{
KMX_DWORD i;
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) filebase;
PCOMP_STORE csp;
- /* Check file version */
+ /* Check file version */
- if(ckbp->dwFileVersion < VERSION_MIN ||
- ckbp->dwFileVersion > VERSION_MAX)
- {
- /* Old or new version -- identify the desired program version */
- if(VerifyChecksum(filebase, sz))
- {
+ if(ckbp->dwFileVersion < VERSION_MIN ||
+ ckbp->dwFileVersion > VERSION_MAX)
+ {
+ /* Old or new version -- identify the desired program version */
+ if(VerifyChecksum(filebase, sz))
+ {
for(csp = (PCOMP_STORE)(filebase + ckbp->dpStoreArray), i = 0; i < ckbp->cxStoreArray; i++, csp++)
if(csp->dwSystemID == TSS_COMPILEDVERSION)
{
@@ -339,9 +339,9 @@ KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, KMX_DWORD sz)
}
}
DebugLog("errWrongFileVersion");
- return FALSE;
+ return FALSE;
}
-
+
if(!VerifyChecksum(filebase, sz)) { DebugLog("errBadChecksum"); return FALSE; }
return TRUE;
diff --git a/src/kmx/kmx_modifiers.cpp b/src/kmx/kmx_modifiers.cpp
index aafc0ab..1da7a8a 100644
--- a/src/kmx/kmx_modifiers.cpp
+++ b/src/kmx/kmx_modifiers.cpp
@@ -4,7 +4,8 @@
*/
#include "kmx_processor.h"
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
#define MAX_RSHIFT 24
#define MAX_KSHIFT 18
diff --git a/src/kmx/kmx_options.cpp b/src/kmx/kmx_options.cpp
index 9d553fa..6b48e59 100644
--- a/src/kmx/kmx_options.cpp
+++ b/src/kmx/kmx_options.cpp
@@ -2,14 +2,29 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
+#include "processor.hpp"
#include "kmx_processor.h"
#include <option.hpp>
#include <state.hpp>
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
+
+int KMX_Options::_GetIndex(std::u16string const &key) const {
+ auto i = 0U;
+ ;
+ for (auto sp = _kp->Keyboard->dpStoreArray;
+ i != _kp->Keyboard->cxStoreArray; ++i, ++sp)
+ {
+ if (sp->dpName && sp->dpName == key) return i;
+ }
+
+ return -1;
+}
+
void KMX_Options::AddOptionsStoresFromXString(PKMX_WCHAR s) {
- int idx;
+ auto idx = 0U;
for (; s && *s; s = incxstr(s)) {
if (*s == UC_SENTINEL) {
switch (*(s + 1)) {
@@ -18,7 +33,7 @@ void KMX_Options::AddOptionsStoresFromXString(PKMX_WCHAR s) {
case CODE_SAVEOPT:
case CODE_RESETOPT:
idx = *(s + 2) - 1;
- if (idx >= 0 && static_cast<KMX_DWORD>(idx) < _kp->Keyboard->cxStoreArray && _kp->Keyboard->dpStoreArray[idx].dpName != NULL) {
+ if (idx >= 0 && idx < _kp->Keyboard->cxStoreArray && _kp->Keyboard->dpStoreArray[idx].dpName != NULL) {
_kp->KeyboardOptions[idx].OriginalStore = _kp->Keyboard->dpStoreArray[idx].dpString;
}
break;
@@ -27,18 +42,19 @@ void KMX_Options::AddOptionsStoresFromXString(PKMX_WCHAR s) {
}
}
-void KMX_Options::Load(km_kbp_options *options, std::u16string const &key) {
+void KMX_Options::Load(abstract_processor & ap, std::u16string const &key) {
LPSTORE sp;
- KMX_DWORD i;
+ auto i = 0U;
- assert(options != nullptr);
assert(!key.empty());
- if (options == nullptr || key.empty()) return;
-
+ if (key.empty()) return;
+
for (i = 0, sp = _kp->Keyboard->dpStoreArray; i < _kp->Keyboard->cxStoreArray; i++, sp++) {
- if (_kp->KeyboardOptions[i].OriginalStore != NULL && sp->dpName != NULL && u16icmp(sp->dpName, key.c_str()) == 0) {
- Reset(options, i);
+ if (_kp->KeyboardOptions[i].OriginalStore != NULL
+ && sp->dpName != NULL
+ && u16icmp(sp->dpName, key.c_str()) == 0) {
+ Reset(ap, i);
return;
}
}
@@ -47,20 +63,20 @@ void KMX_Options::Load(km_kbp_options *options, std::u16string const &key) {
assert(false);
}
-void KMX_Options::Init(std::vector<km_kbp_option_item> *opts) {
+void KMX_Options::Init(std::vector<option> &opts) {
- opts->clear();
+ opts.clear();
_kp->KeyboardOptions = new INTKEYBOARDOPTIONS[_kp->Keyboard->cxStoreArray];
memset(_kp->KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * _kp->Keyboard->cxStoreArray);
// Scan all rules to find options references.
- KMX_DWORD i, j;
+ auto i = 0U, j = 0U;
LPGROUP gp;
LPKEY kkp;
- for (i = 0, gp = _kp->Keyboard->dpGroupArray; i < _kp->Keyboard->cxGroupArray; i++, gp++) {
- for (j = 0, kkp = gp->dpKeyArray; j < gp->cxKeyArray; j++, kkp++) {
+ for (i = 0U, gp = _kp->Keyboard->dpGroupArray; i < _kp->Keyboard->cxGroupArray; i++, gp++) {
+ for (j = 0U, kkp = gp->dpKeyArray; j < gp->cxKeyArray; j++, kkp++) {
AddOptionsStoresFromXString(kkp->dpContext);
AddOptionsStoresFromXString(kkp->dpOutput);
}
@@ -77,8 +93,7 @@ void KMX_Options::Init(std::vector<km_kbp_option_item> *opts) {
}
if (n == 0) {
- km_kbp_option_item opt = KM_KBP_OPTIONS_END;
- opts->emplace_back(opt);
+ opts.emplace_back(); // Terminate the options array
return;
}
@@ -87,16 +102,11 @@ void KMX_Options::Init(std::vector<km_kbp_option_item> *opts) {
LPSTORE sp;
for (n = 0, i = 0, ko = _kp->KeyboardOptions, sp = _kp->Keyboard->dpStoreArray; i < _kp->Keyboard->cxStoreArray; i++, sp++, ko++) {
if (ko->OriginalStore == NULL) continue;
- km_kbp_option_item opt;
- opt.key = sp->dpName;
- opt.value = sp->dpString;
- opt.scope = KM_KBP_OPT_KEYBOARD;
- opts->emplace_back(opt);
+ opts.emplace_back(KM_KBP_OPT_KEYBOARD, sp->dpName, sp->dpString);
n++;
}
- km_kbp_option_item opt = KM_KBP_OPTIONS_END;
- opts->emplace_back(opt);
+ opts.emplace_back(); // Terminate the options array
}
KMX_Options::~KMX_Options()
@@ -114,6 +124,36 @@ KMX_Options::~KMX_Options()
_kp->KeyboardOptions = NULL;
}
+
+char16_t const * KMX_Options::LookUp(std::u16string const &key) const
+{
+ auto idx = _GetIndex(key);
+ if (idx < 0) return nullptr;
+
+ return _kp->Keyboard->dpStoreArray[idx].dpString;
+}
+
+
+void KMX_Options::Set(int nStoreToSet, std::u16string const & rValueToSet)
+{
+ assert(_kp != NULL);
+ assert(_kp->Keyboard != NULL);
+ assert(_kp->KeyboardOptions != NULL);
+ assert(nStoreToSet >= 0);
+ assert(nStoreToSet < (int) _kp->Keyboard->cxStoreArray);
+
+ auto & rStoreToSetValue = _kp->KeyboardOptions[nStoreToSet].Value;
+ if(rStoreToSetValue)
+ {
+ delete rStoreToSetValue;
+ }
+
+ rStoreToSetValue = new std::u16string::value_type[rValueToSet.size()+1];
+ u16cpy(rStoreToSetValue, /*u16len(sp->dpString)+1,*/ rValueToSet.c_str());
+ _kp->Keyboard->dpStoreArray[nStoreToSet].dpString = rStoreToSetValue;
+}
+
+
void KMX_Options::Set(int nStoreToSet, int nStoreToRead)
{
assert(_kp != NULL);
@@ -124,18 +164,11 @@ void KMX_Options::Set(int nStoreToSet, int nStoreToRead)
assert(nStoreToRead >= 0);
assert(nStoreToRead < (int) _kp->Keyboard->cxStoreArray);
- LPSTORE sp = &_kp->Keyboard->dpStoreArray[nStoreToRead];
- if(_kp->KeyboardOptions[nStoreToSet].Value)
- {
- delete _kp->KeyboardOptions[nStoreToSet].Value;
- }
-
- _kp->KeyboardOptions[nStoreToSet].Value = new KMX_WCHAR[u16len(sp->dpString)+1];
- u16cpy(_kp->KeyboardOptions[nStoreToSet].Value, /*u16len(sp->dpString)+1,*/ sp->dpString);
- _kp->Keyboard->dpStoreArray[nStoreToSet].dpString = _kp->KeyboardOptions[nStoreToSet].Value;
+ std::u16string const & rStoreToReadValue = _kp->Keyboard->dpStoreArray[nStoreToRead].dpString;
+ Set(nStoreToSet, rStoreToReadValue);
}
-void KMX_Options::Reset(km_kbp_options *options, int nStoreToReset)
+void KMX_Options::Reset(abstract_processor & ap, int nStoreToReset)
{
assert(_kp != NULL);
assert(_kp->Keyboard != NULL);
@@ -143,27 +176,29 @@ void KMX_Options::Reset(km_kbp_options *options, int nStoreToReset)
assert(nStoreToReset >= 0);
assert(nStoreToReset < (int) _kp->Keyboard->cxStoreArray);
- if (_kp->KeyboardOptions[nStoreToReset].Value)
+ auto & rStoreToReset = _kp->Keyboard->dpStoreArray[nStoreToReset];
+ auto & rOptionToReset = _kp->KeyboardOptions[nStoreToReset];
+ if (rOptionToReset.Value)
{
- _kp->Keyboard->dpStoreArray[nStoreToReset].dpString = _kp->KeyboardOptions[nStoreToReset].OriginalStore;
- delete _kp->KeyboardOptions[nStoreToReset].Value;
- _kp->KeyboardOptions[nStoreToReset].Value = NULL;
+ rStoreToReset.dpString = rOptionToReset.OriginalStore;
+ delete rOptionToReset.Value;
+ rOptionToReset.Value = nullptr;
}
- if(_kp->Keyboard->dpStoreArray[nStoreToReset].dpName == NULL) return;
+ if(rStoreToReset.dpName == nullptr) return;
// Now we need to go back and get any saved value from KPAPI. internal_value is owned by options api
- km_kbp_cp const *internal_value = options->lookup(km_kbp_option_scope(KM_KBP_OPT_KEYBOARD), _kp->Keyboard->dpStoreArray[nStoreToReset].dpName);
- if(internal_value) {
+ auto i = ap.persisted_store().find(rStoreToReset.dpName);
+ if(i != ap.persisted_store().end()) {
// Copy the value from KPAPI
- _kp->KeyboardOptions[nStoreToReset].Value = new KMX_WCHAR[u16len(internal_value) + 1];
- u16cpy(_kp->KeyboardOptions[nStoreToReset].Value, /*u16len(val) + 1,*/ internal_value);
- _kp->Keyboard->dpStoreArray[nStoreToReset].dpString = _kp->KeyboardOptions[nStoreToReset].Value;
+ rOptionToReset.Value = new KMX_WCHAR[i->second.size() + 1];
+ u16cpy(rOptionToReset.Value, /*u16len(val) + 1,*/ i->second.c_str());
+ rStoreToReset.dpString = rOptionToReset.Value;
}
}
-void KMX_Options::Save(km_kbp_state *state, int nStoreToSave)
+void KMX_Options::Save(state & state, int nStoreToSave)
{
assert(_kp != NULL);
assert(_kp->Keyboard != NULL);
@@ -171,14 +206,10 @@ void KMX_Options::Save(km_kbp_state *state, int nStoreToSave)
assert(nStoreToSave >= 0);
assert(nStoreToSave < (int)_kp->Keyboard->cxStoreArray);
- if (_kp->Keyboard->dpStoreArray[nStoreToSave].dpName == NULL) return;
+ auto const & rStoreToSave = _kp->Keyboard->dpStoreArray[nStoreToSave];
+ if (rStoreToSave.dpName == nullptr) return;
- // TSE QUERY: Does this happen here or with an ACTION? Or both?
- state->options().assign(state, KM_KBP_OPT_KEYBOARD, _kp->Keyboard->dpStoreArray[nStoreToSave].dpName, _kp->Keyboard->dpStoreArray[nStoreToSave].dpString);
- //TODO: GetActions()->QueueAction(QIT_SAVEOPTION, ...);
- /*RegistryFullAccess r(HKEY_CURRENT_USER);
- if(r.OpenKey(REGSZ_KeymanActiveKeyboards, TRUE) && r.OpenKey(_kp->Name, TRUE) && r.OpenKey(key, TRUE))
- {
- r.WriteString(_kp->Keyboard->dpStoreArray[nStoreToSave].dpName, _kp->Keyboard->dpStoreArray[nStoreToSave].dpString);
- }*/
+ state.processor().persisted_store()[rStoreToSave.dpName] = rStoreToSave.dpString;
+ state.actions().push_persist(
+ option{KM_KBP_OPT_KEYBOARD, rStoreToSave.dpName, rStoreToSave.dpString});
}
diff --git a/src/kmx/kmx_options.h b/src/kmx/kmx_options.h
index 29df0d9..21a2873 100644
--- a/src/kmx/kmx_options.h
+++ b/src/kmx/kmx_options.h
@@ -3,11 +3,18 @@
#include <string>
#include <vector>
+
#include <keyman/keyboardprocessor.h>
+#include "option.hpp"
+
#include "kmx_base.h"
namespace km {
namespace kbp {
+
+class abstract_processor;
+class state;
+
namespace kmx {
class KMX_Options
@@ -17,17 +24,41 @@ private:
void AddOptionsStoresFromXString(PKMX_WCHAR s);
+ int _GetIndex(std::u16string const &key) const;
+
public:
KMX_Options(LPINTKEYBOARDINFO kp) : _kp(kp) {}
~KMX_Options();
- void Init(std::vector<km_kbp_option_item> *opts);
- void Load(km_kbp_options *options, std::u16string const &key);
+ void Init(std::vector<option> &opts);
+ void Load(abstract_processor &, std::u16string const &key);
+ char16_t const * LookUp(std::u16string const &key) const;
void Set(int nStoreToSet, int nStoreToRead);
- void Reset(km_kbp_options *options, int nStoreToReset);
- void Save(km_kbp_state *state, int nStoreToSave);
+ void Set(int nStoreToSet, std::u16string const &value);
+ void Set(std::u16string const &key, std::u16string const &value);
+ void Reset(abstract_processor &, int nStoreToReset);
+ void Save(state & state, int nStoreToSave);
+
+ STORE const * begin() const;
+ STORE const * end() const;
};
+inline
+void KMX_Options::Set(std::u16string const &key, std::u16string const &value) {
+ auto i = _GetIndex(key);
+ if (i >= 0) Set(i, value);
+}
+
+inline
+STORE const * KMX_Options::begin() const {
+ return _kp->Keyboard->dpStoreArray;
+}
+
+inline
+STORE const * KMX_Options::end() const {
+ return _kp->Keyboard->dpStoreArray + _kp->Keyboard->cxStoreArray;
+}
+
} // namespace kmx
} // namespace kbp
} // namespace km
diff --git a/src/kmx/kmx_processevent.cpp b/src/kmx/kmx_processevent.cpp
index 2af29dc..e3b9291 100644
--- a/src/kmx/kmx_processevent.cpp
+++ b/src/kmx/kmx_processevent.cpp
@@ -1,7 +1,6 @@
#include <keyman/keyboardprocessor.h>
-#include "processor.hpp"
#include "state.hpp"
-#include "keyboard.hpp"
+#include "kmx/kmx_processevent.hpp"
namespace km {
namespace kbp
@@ -11,34 +10,63 @@ namespace km {
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
}
- kmx_processor::kmx_processor(km_kbp_keyboard_attrs const * kb_) : abstract_processor(kb_) {
- km::kbp::keyboard *kb = const_cast<km::kbp::keyboard *>(static_cast<km::kbp::keyboard const *>(kb_));
-
- std::filesystem::path p = kb->folder_path;
- p /= kb->id;
+ kmx_processor::kmx_processor(kbp::path p)
+ {
p.replace_extension(".kmx");
- _valid = (bool) _kmx.Load(p.native().c_str());
+ _valid = bool(_kmx.Load(p.c_str()));
+
+ keyboard_attributes::options_store defaults;
+ if (_valid)
+ _kmx.GetOptions()->Init(defaults);
- if (_valid) {
- _kmx.GetOptions()->Init(kb->default_opts());
+ for (auto const & opt: defaults)
+ {
+ if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
+ persisted_store()[opt.key] = opt.value;
}
+ // Fill out attributes
+ auto v = _kmx.GetKeyboard()->Keyboard->version;
+ auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff);
+
+ _attributes = keyboard_attributes(static_cast<std::u16string>(p.stem()),
+ std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
}
- void kmx_processor::init_state(std::vector<km_kbp_option_item> *default_env) {
- _kmx.GetEnvironment()->Init(default_env);
+
+ char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
+ {
+ char16_t const * pValue = nullptr;
+ switch(scope)
+ {
+ case KM_KBP_OPT_KEYBOARD:
+ pValue = _kmx.GetOptions()->LookUp(key);
+ break;
+ case KM_KBP_OPT_ENVIRONMENT:
+ pValue = _kmx.GetEnvironment()->LookUp(key);
+ break;
+ default:
+ break;
+ }
+
+ return pValue ? pValue : nullptr;
}
- void kmx_processor::update_option(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value) {
+ option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
+ {
switch(scope) {
case KM_KBP_OPT_KEYBOARD:
- _kmx.GetOptions()->Load(km_kbp_state_options(state), key);
+ _kmx.GetOptions()->Set(key, value);
+ persisted_store()[key] = value;
break;
case KM_KBP_OPT_ENVIRONMENT:
_kmx.GetEnvironment()->Load(key, value);
break;
default:
+ return option();
break;
}
+
+ return option(scope, key, value);
}
km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) {
@@ -61,18 +89,18 @@ namespace km {
assert(c->marker > 0);
ctxt += UC_SENTINEL;
ctxt += CODE_DEADKEY;
- ctxt += c->marker;
+ ctxt += c->marker;
break;
}
}
_kmx.GetContext()->Set(ctxt.c_str());
_kmx.GetActions()->ResetQueue();
- state->actions.clear();
+ state->actions().clear();
if (!_kmx.ProcessEvent(state, vk, modifier_state)) {
// We need to output the default keystroke
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_EMIT_KEYSTROKE, {0,}, {0} });
+ state->actions().push_emit_keystroke();
}
for (auto i = 0; i < _kmx.GetActions()->Length(); i++) {
@@ -89,16 +117,16 @@ namespace km {
case QIT_VSHIFTUP:
//TODO: eliminate??
break;
- case QIT_CHAR:
- state->context().emplace_back(km_kbp_context_item{ KM_KBP_CT_CHAR, {0,}, {(km_kbp_usv)a.dwData} });
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CHAR, {0,}, {(km_kbp_usv)a.dwData} });
+ case QIT_CHAR:
+ state->context().push_character(a.dwData);
+ state->actions().push_character(a.dwData);
break;
case QIT_DEADKEY:
- state->context().emplace_back(km_kbp_context_item{ KM_KBP_CT_MARKER, {0,}, {(km_kbp_usv)a.dwData} });
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_MARKER, {0,}, {(uintptr_t)a.dwData} });
+ state->context().push_marker(a.dwData);
+ state->actions().push_marker(a.dwData);
break;
case QIT_BELL:
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_ALERT, {0,}, {0} });
+ state->actions().push_alert();
break;
case QIT_BACK:
switch (a.dwData) {
@@ -106,7 +134,7 @@ namespace km {
// This only happens if we know we have context to delete. Last item must be a character
assert(!state->context().empty() && state->context().back().type != KM_KBP_IT_MARKER);
if (!state->context().empty()) state->context().pop_back();
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_BACK, {0,}, {0} });
+ state->actions().push_backspace();
break;
case BK_DEADKEY:
// This only happens if we know we have context to delete. Last item must be a deadkey
@@ -122,14 +150,14 @@ namespace km {
}
// Even if context is empty, we send the backspace event, because we may not
// know the context.
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_BACK, {0,}, {0} });
+ state->actions().push_backspace();
break;
default:
assert(false);
}
break;
case QIT_INVALIDATECONTEXT:
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_INVALIDATE_CONTEXT, {0,}, {0} });
+ state->actions().push_invalidate_context();
break;
default:
//std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl;
@@ -137,7 +165,7 @@ namespace km {
}
}
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_END, {0,}, {0} });
+ state->actions().commit();
return KM_KBP_STATUS_OK;
}
@@ -151,10 +179,9 @@ namespace km {
"SIL International"
};
- km_kbp_attr const * kmx_processor::get_attrs() const {
- //TODO
- return &engine_attrs;
+ km_kbp_attr const & kmx_processor::attributes() const {
+ return engine_attrs;
}
} // namespace kbp
-} // namespace km \ No newline at end of file
+} // namespace km
diff --git a/src/kmx/kmx_processevent.hpp b/src/kmx/kmx_processevent.hpp
new file mode 100644
index 0000000..29ba66e
--- /dev/null
+++ b/src/kmx/kmx_processevent.hpp
@@ -0,0 +1,41 @@
+/*
+ Copyright: © 2018 SIL International.
+ Description: Internal keyboard class and adaptor class for the API.
+ Create Date: 2 Oct 2018
+ Authors: Tim Eves (TSE)
+ History: 2 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp
+*/
+
+#pragma once
+
+#include <string>
+#include <keyman/keyboardprocessor.h>
+#include "kmx/kmx_processor.h"
+#include "keyboard.hpp"
+#include "processor.hpp"
+
+namespace km {
+namespace kbp
+{
+ class kmx_processor : public abstract_processor
+ {
+ private:
+ bool _valid;
+ kmx::KMX_Processor _kmx;
+ public:
+ kmx_processor(path);
+ km_kbp_status process_event(km_kbp_state *state,
+ km_kbp_virtual_key vk,
+ uint16_t modifier_state) override;
+ km_kbp_attr const & attributes() const override;
+ km_kbp_status validate() const override;
+
+ char16_t const * lookup_option(km_kbp_option_scope,
+ std::u16string const & key) const override;
+ option update_option(km_kbp_option_scope scope,
+ std::u16string const & key,
+ std::u16string const & value) override;
+ };
+
+} // namespace kbp
+} // namespace km
diff --git a/src/kmx/kmx_processor.cpp b/src/kmx/kmx_processor.cpp
index c3f16a2..149cf99 100644
--- a/src/kmx/kmx_processor.cpp
+++ b/src/kmx/kmx_processor.cpp
@@ -3,8 +3,10 @@
Authors: mcdurdin
*/
#include "kmx_processor.h"
+#include "state.hpp"
-using namespace km::kbp::kmx;
+using namespace km::kbp;
+using namespace kmx;
/* Globals */
@@ -88,7 +90,7 @@ KMX_BOOL KMX_Processor::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWO
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
KMX_BOOL fOutputKeystroke = FALSE;
-
+
ProcessGroup(gp, &fOutputKeystroke);
m_kbp_state = nullptr;
@@ -166,12 +168,12 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
{
if(kkp->dpContext[0] != 0) break; else continue;
}
-
+
//if(kkp->Key == m_state.vkey)
- //SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
+ //SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
// kkp->Key, kkp->ShiftFlags);
- /* Keyman 6.0: support Virtual Characters */
+ /* Keyman 6.0: support Virtual Characters */
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
{
if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
@@ -218,12 +220,12 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
}
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
{
- /* NoMatch rule found, and is a character key */
+ /* NoMatch rule found, and is a character key */
PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
}
else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys)
{
- /* No rule found, is a character key */
+ /* No rule found, is a character key */
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
}
@@ -270,14 +272,14 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break;
case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
}
- else
+ else
{
m_actions.QueueAction(QIT_BACK, 0);
}
}
p = kkp->dpOutput;
}
- else
+ else
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
/* Use PostString to post the rest of the output string. */
@@ -294,7 +296,7 @@ KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
}
/*
-* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
+* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
* LPKEYBOARD lpkb );
*
* Parameters: str Pointer to string to send
@@ -326,7 +328,7 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
{
case CODE_EXTENDED: // Start of a virtual key section w/shift codes
p++;
-
+
shift = *p; //(*p<<8) | *(p+1);
m_actions.QueueAction(QIT_VSHIFTDOWN, shift);
@@ -336,7 +338,7 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
m_actions.QueueAction(QIT_VKEYUP, *p);
m_actions.QueueAction(QIT_VSHIFTUP, shift);
-
+
p++; // CODE_EXTENDEDEND
////// CODE_EXTENDEDEND will be incremented by loop
@@ -372,7 +374,7 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
p++;
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
FoundUse = TRUE;
- break;
+ break;
case CODE_USE: // use another group
p++;
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
@@ -401,12 +403,12 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
case CODE_RESETOPT:
p++;
n1 = *p - 1;
- GetOptions()->Reset(km_kbp_state_options(m_kbp_state), n1);
+ GetOptions()->Reset(m_kbp_state->processor(), n1);
break;
case CODE_SAVEOPT:
p++;
n1 = *p - 1;
- GetOptions()->Save(m_kbp_state, n1);
+ GetOptions()->Save(*m_kbp_state, n1);
break;
case CODE_IFSYSTEMSTORE:
p+=3;
@@ -440,13 +442,13 @@ KMX_BOOL KMX_Processor::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
PKMX_WCHAR p = u16tok(t, u' ', &context);
while (p != NULL) {
if (u16icmp(platform, p) == 0) {
- delete t;
+ delete [] t;
return TRUE;
}
p = u16tok(NULL, u' ', &context);
}
- delete t;
+ delete [] t;
return FALSE;
}
@@ -461,14 +463,14 @@ KMX_BOOL KMX_Processor::IsMatchingPlatform(LPSTORE s) // I3432
if(!IsMatchingPlatformString(platform))
{
s->dwSystemID = TSS_PLATFORM_NOMATCH;
- delete t;
+ delete [] t;
return FALSE;
}
platform = u16tok(NULL, u' ', &context);
}
s->dwSystemID = TSS_PLATFORM_MATCH;
- delete t;
+ delete [] t;
return TRUE;
}
@@ -493,7 +495,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
KMX_BOOL bEqual;
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
-
+
p = kkp->dpContext;
if(*p == 0)
@@ -515,7 +517,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
{
s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1];
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
-
+
bEqual = u16cmp(s->dpString, t->dpString) == 0;
if(*(pp+3) == 1 && bEqual) return FALSE;
if(*(pp+3) == 2 && !bEqual) return FALSE;
@@ -545,7 +547,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
bEqual = u16cmp(ss, t->dpString) == 0;
}
}
-
+
if(*(pp+3) == 1 && bEqual) return FALSE;
if(*(pp+3) == 2 && !bEqual) return FALSE;
}
@@ -576,7 +578,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
temp = xstrchr(s->dpString, q);
-
+
if(temp != NULL)
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
@@ -587,7 +589,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
if((temp = xstrchr(s->dpString, q)) != NULL)
- return FALSE;
+ return FALSE;
break;
case CODE_INDEX:
@@ -596,7 +598,7 @@ KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
if(n != 0) return FALSE;
- if(xchrcmp(temp, q) != 0) return FALSE;
+ if(xchrcmp(temp, q) != 0) return FALSE;
break;
case CODE_CONTEXTEX:
// only the nth character
@@ -637,10 +639,18 @@ KMX_Options *KMX_Processor::GetOptions() {
return &m_options;
}
+KMX_Options const *KMX_Processor::GetOptions() const {
+ return &m_options;
+}
+
KMX_Environment *KMX_Processor::GetEnvironment() {
return &m_environment;
}
+KMX_Environment const *KMX_Processor::GetEnvironment() const {
+ return &m_environment;
+}
+
LPINTKEYBOARDINFO KMX_Processor::GetKeyboard() {
return &m_keyboard;
}
diff --git a/src/kmx/kmx_processor.h b/src/kmx/kmx_processor.h
index a29522d..f541c69 100644
--- a/src/kmx/kmx_processor.h
+++ b/src/kmx/kmx_processor.h
@@ -13,7 +13,7 @@
#include "kmx_options.h"
#include "kmx_environment.h"
-/***************************************************************************/
+/***************************************************************************/
namespace km {
namespace kbp {
@@ -42,8 +42,8 @@ private:
/* File loading */
LPKEYBOARD FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base, KMX_DWORD dwFileSize);
KMX_BOOL LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard);
- KMX_BOOL VerifyKeyboard(PKMX_BYTE filebase, KMX_DWORD sz);
- KMX_BOOL VerifyChecksum(PKMX_BYTE buf, KMX_DWORD sz);
+ KMX_BOOL VerifyKeyboard(PKMX_BYTE filebase, size_t sz);
+ KMX_BOOL VerifyChecksum(PKMX_BYTE buf, size_t sz);
PKMX_WCHAR StringOffset(PKMX_BYTE base, KMX_DWORD offset);
#ifdef KMX_64BIT
LPKEYBOARD CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base);
@@ -85,7 +85,9 @@ public:
KMX_Actions *GetActions();
KMX_Context *GetContext();
KMX_Options *GetOptions();
+ KMX_Options const *GetOptions() const;
KMX_Environment *GetEnvironment();
+ KMX_Environment const *GetEnvironment() const;
LPINTKEYBOARDINFO GetKeyboard();
};
@@ -113,7 +115,7 @@ extern KMX_BOOL g_debug_ToConsole, g_debug_KeymanLog, g_silent;
#define DebugLog(msg,...) (km::kbp::kmx::ShouldDebug() ? km::kbp::kmx::DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg),__VA_ARGS__) : 0)
#define console_error(msg,...) write_console(TRUE, (msg), __VA_ARGS__)
#define console_log(msg,...) write_console(FALSE, (msg), __VA_ARGS__)
-#else
+#else
#define DebugLog(msg,...) (ShouldDebug() ? DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg), ##__VA_ARGS__) : 0)
#define console_error(msg,...) write_console(TRUE, (msg), ##__VA_ARGS__)
#define console_log(msg,...) write_console(FALSE, (msg), ##__VA_ARGS__)
diff --git a/src/kmx/kmx_xstring.cpp b/src/kmx/kmx_xstring.cpp
index 45d8743..518e957 100644
--- a/src/kmx/kmx_xstring.cpp
+++ b/src/kmx/kmx_xstring.cpp
@@ -7,10 +7,12 @@
#include <codecvt>
#include <locale>
#include "kmx_processor.h"
+#include "utfcodec.hpp"
-std::string utf16_to_utf8(std::u16string utf16_string); // defined in keyboard.cpp
-using namespace km::kbp::kmx;
+
+using namespace km::kbp;
+using namespace kmx;
const km_kbp_cp *km::kbp::kmx::u16chr(const km_kbp_cp *p, km_kbp_cp ch) {
while (*p) {
@@ -148,7 +150,7 @@ PKMX_WCHAR km::kbp::kmx::decxstr(PKMX_WCHAR p)
return p-1;
}
else if(*(p-1) == UC_SENTINEL) return p-1;
- else if(*(p-2) == UC_SENTINEL)
+ else if(*(p-2) == UC_SENTINEL)
{
switch(*(p-1))
{
@@ -159,17 +161,17 @@ PKMX_WCHAR km::kbp::kmx::decxstr(PKMX_WCHAR p)
case CODE_CLEARCONTEXT:
case CODE_CALL:
case CODE_CONTEXTEX:
- case CODE_RESETOPT:
- case CODE_SAVEOPT:
+ case CODE_RESETOPT:
+ case CODE_SAVEOPT:
return p-2;
}
}
- else if(*(p-3) == UC_SENTINEL)
+ else if(*(p-3) == UC_SENTINEL)
{
switch(*(p-2))
{
case CODE_INDEX:
- case CODE_SETOPT:
+ case CODE_SETOPT:
case CODE_SETSYSTEMSTORE:
return p-3;
}
@@ -227,20 +229,11 @@ int km::kbp::kmx::xchrcmp(PKMX_WCHAR ch1, PKMX_WCHAR ch2)
PKMX_WCHAR km::kbp::kmx::strtowstr(PKMX_CHAR in)
{
- km_kbp_cp *result;
+ PKMX_WCHAR result;
-#if _MSC_VER >= 1900 /* VS 2015 */ && _MSC_VER <= 1916 /* VS 2017 19.16 */
- std::wstring_convert<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
-#else
- std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
-#endif
- auto s = convert.from_bytes(in, strchr(in, 0));
- result = new KMX_WCHAR[s.length() + 1];
-#if _MSC_VER >= 1900 /* VS 2015 */ && _MSC_VER <= 1916 /* VS 2017 19.16 */
- s.copy(reinterpret_cast<int16_t *>(result), s.length());
-#else
+ auto s = convert<char,char16_t>(in);
+ result = new char16_t[s.length() + 1];
s.copy(result, s.length());
-#endif
result[s.length()] = 0;
return result;
}
@@ -250,7 +243,7 @@ PKMX_CHAR km::kbp::kmx::wstrtostr(PKMX_WCHAR in)
{
PKMX_CHAR result;
- auto s = ::utf16_to_utf8(in);
+ auto s = convert<char16_t,char>(in);
result = new char[s.length() + 1];
s.copy(result, s.length());
result[s.length()] = 0;
diff --git a/src/meson.build b/src/meson.build
index f9bf8ad..9bc326f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,7 +22,7 @@ if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang'
'-fvisibility=hidden',
'-fvisibility-inlines-hidden'
]
- links = ['-lstdc++fs']
+ links = []
if compiler.get_id() == 'clang'
warns += [
@@ -74,7 +74,7 @@ lib = library('kmnkbp0',
'utfcodec.cpp',
cpp_args: defns + warns + flags,
link_args: links,
- version: meson.project_version(),
+ version: lib_version,
include_directories: inc,
install: true)
diff --git a/src/mock/mock_processor.cpp b/src/mock/mock_processor.cpp
index 7033e20..d139a57 100644
--- a/src/mock/mock_processor.cpp
+++ b/src/mock/mock_processor.cpp
@@ -10,8 +10,7 @@
History: 17 Oct 2018 - TSE - Initial implementation.
*/
-#include <keyman/keyboardprocessor.h>
-#include "processor.hpp"
+#include "mock/mock_processor.hpp"
#include "state.hpp"
namespace
@@ -73,18 +72,68 @@ namespace
namespace km {
namespace kbp
{
+ mock_processor::mock_processor(kbp::path const & path)
+ : abstract_processor(
+ keyboard_attributes(path.stem(), u"3.145", path.parent(), {
+ option{KM_KBP_OPT_KEYBOARD, u"__test_point", u"not tiggered"},
+ })),
+ _options({
+ {u"\x01__test_point", u"not tiggered"},
+ {u"\x02hello", u"-"}
+ })
+ {
+ }
+
+ char16_t const * mock_processor::lookup_option(km_kbp_option_scope scope,
+ std::u16string const & key) const
+ {
+ auto i = _options.find(char16_t(scope) + key);
+ return i != _options.end() ? i->second.c_str() : nullptr;
+ }
+
+ option mock_processor::update_option(km_kbp_option_scope scope,
+ std::u16string const & key,
+ std::u16string const & value)
+ {
+ auto i = _options.find(char16_t(scope) + key);
+ if (i == _options.end()) return option();
+
+ i->second = value;
+ persisted_store()[key] = value;
+ return option(scope, key, i->second);
+ }
+
+
+ km_kbp_status mock_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state)
+ {
+ assert(state);
+ if (!state)
+ return KM_KBP_STATUS_INVALID_ARGUMENT;
- km_kbp_status mock_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) {
try
{
- state->actions.clear();
+ // At the start of every process_event allways clear the action_items
+ state->actions().clear();
switch (vk)
{
case KM_KBP_VKEY_BKSP:
state->context().pop_back();
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_BACK, {0,}, {1} });
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_END, {0,}, {0} });
+ state->actions().push_backspace();
+ break;
+
+ case KM_KBP_VKEY_F2:
+ {
+ state->actions().push_persist(
+ update_option(KM_KBP_OPT_KEYBOARD,
+ u"__test_point",
+ u"F2 pressed test save."));
+ break;
+ }
+
+ case KM_KBP_VKEY_F4:
+ state->context().push_marker(KM_KBP_VKEY_QUOTE);
+ state->actions().push_marker(KM_KBP_VKEY_QUOTE);
break;
default:
@@ -98,24 +147,25 @@ namespace km {
for (auto c = char_seq; *c; ++c)
{
km_kbp_usv usv = *c;
- state->context().emplace_back(km_kbp_context_item{ KM_KBP_CT_CHAR,{0,},{usv} });
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CHAR, {0,}, {usv} });
+ state->context().push_character(usv);
+ state->actions().push_character(usv);
}
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_END, {0,}, {0} });
+ state->actions().commit();
return KM_KBP_STATUS_OK;
}
// Both shift states output nothing, generate an alert.
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_ALERT, {0,}, {0} });
- state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_END, {0,}, {0} });
+ state->actions().push_alert();
break;
}
}
+
+ state->actions().commit();
}
catch (std::bad_alloc)
{
- state->actions.clear();
+ state->actions().clear();
return KM_KBP_STATUS_NO_MEM;
}
@@ -123,11 +173,12 @@ namespace km {
}
- km_kbp_attr const * mock_processor::get_attrs() const {
- return &engine_attrs;
+ km_kbp_attr const & mock_processor::attributes() const {
+ return engine_attrs;
}
km_kbp_status mock_processor::validate() const { return KM_KBP_STATUS_OK; }
+
km_kbp_status null_processor::validate() const { return KM_KBP_STATUS_INVALID_ARGUMENT; }
} // namespace kbp
} // namespace km
diff --git a/src/mock/mock_processor.hpp b/src/mock/mock_processor.hpp
new file mode 100644
index 0000000..7cb2b4d
--- /dev/null
+++ b/src/mock/mock_processor.hpp
@@ -0,0 +1,55 @@
+/*
+ Copyright: © 2018 SIL International.
+ Description: Internal keyboard class and adaptor class for the API.
+ Create Date: 2 Oct 2018
+ Authors: Tim Eves (TSE)
+ History: 2 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp
+*/
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <keyman/keyboardprocessor.h>
+
+#include "processor.hpp"
+#include "option.hpp"
+
+namespace km {
+namespace kbp
+{
+ class mock_processor : public abstract_processor
+ {
+ std::unordered_map<std::u16string, std::u16string> _options;
+
+ public:
+ mock_processor(km::kbp::path const &);
+// ~mock_processor() override;
+
+ km_kbp_status process_event(km_kbp_state *state,
+ km_kbp_virtual_key vk,
+ uint16_t modifier_state) override;
+
+ virtual km_kbp_attr const & attributes() const override;
+ km_kbp_status validate() const override;
+
+
+
+ char16_t const * lookup_option(km_kbp_option_scope,
+ std::u16string const & key) const override;
+ option update_option(km_kbp_option_scope,
+ std::u16string const & key,
+ std::u16string const & value) override;
+ };
+
+ class null_processor : public mock_processor {
+ public:
+ null_processor(): mock_processor(path())
+ {
+ _attributes = keyboard_attributes(u"null", u"0.0", path(), {});
+ }
+
+ km_kbp_status validate() const override;
+ };
+} // namespace kbp
+} // namespace km
diff --git a/src/option.cpp b/src/option.cpp
index b4c12b5..e4c6620 100644
--- a/src/option.cpp
+++ b/src/option.cpp
@@ -7,13 +7,10 @@
History: 7 Nov 2018 - TSE - Refactored into option.hpp & option.cpp.
*/
#include <algorithm>
-#include <memory>
-#include "keyboard.hpp"
#include "option.hpp"
-#include "json.hpp"
-#include "utfcodec.hpp"
-#include "state.hpp"
+#include "processor.hpp"
+
using namespace km::kbp;
@@ -28,111 +25,48 @@ namespace
// Forward declarations
-option::option(km_kbp_option_scope s, std::u16string const & k, std::u16string const & v)
-: km_kbp_option_item { new km_kbp_cp[k.size()+1], new km_kbp_cp[v.size()+1],
- uint8_t(s) }
-{
- std::copy_n(k.c_str(), k.size()+1, const_cast<km_kbp_cp *>(key));
- std::copy_n(v.c_str(), v.size()+1, const_cast<km_kbp_cp *>(value));
-}
-
-
-char16_t const * options::lookup(km_kbp_option_scope scope,
- std::u16string const & key) const noexcept
-{
- // Search first in the updated values
- for (auto & opt: _saved)
- {
- if (opt.key == key && opt.scope == scope)
- return opt.value;
- }
-
- // Then in the pristine copies.
- km_kbp_option_item const * opt = _scopes[scope-1];
- while (opt->key && key != opt->key) ++opt;
- return opt->key ? opt->value : nullptr;
-}
-
-
-km_kbp_option_item const * options::assign(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key,
- std::u16string const & value)
+option::option(km_kbp_option_scope s, char16_t const *k, char16_t const *v)
+: option()
{
- km_kbp_option_item const * opt = _scopes[scope-1];
- while (opt->key && key != opt->key) ++opt;
- if (!opt->key) return nullptr;
-
- for (auto & save: _saved)
+ if (k && v)
{
- if (save.key == key && save.scope == scope)
- {
- save = option(scope, key, value);
-
- //((km::kbp::state *)state)->keyboard().
- const_cast<km::kbp::abstract_processor &>(static_cast<km::kbp::state *>(state)->keyboard().processor()).update_option(state, scope, key, value);
-
- return &save;
- }
+ auto n_k = std::char_traits<char16_t>::length(k)+1,
+ n_v = std::char_traits<char16_t>::length(v)+1;
+ auto _key = new km_kbp_cp[n_k],
+ _val = new km_kbp_cp[n_v];
+ std::copy_n(k, n_k, _key);
+ std::copy_n(v, n_v, _val);
+
+ key = _key;
+ value = _val;
+ scope = s;
}
-
- _saved.emplace_back(scope, key, value);
-
- const_cast<km::kbp::abstract_processor &>(static_cast<km::kbp::state *>(state)->keyboard().processor()).update_option(state, scope, key, value);
-
- return &_saved.back();
}
-void options::reset(km_kbp_option_scope scope, std::u16string const & key)
-{
- for (auto i = _saved.begin(); i == _saved.end(); ++i)
- {
- if (i->key == key && i->scope == scope)
- {
- _saved.erase(i);
- break;
- }
- }
-}
-
-
-
-json & operator << (json &j, char16_t const *u16str) {
- char cps[5] = {0,};
- int8_t l;
- std::string u8str;
-
- for (auto i = utf16::const_iterator(u16str); *i; ++i)
- {
- utf8::codec::put(reinterpret_cast<utf8::codeunit_t *>(cps), *i, l);
- u8str.append(cps, size_t(l));
- }
- j << u8str;
- return j;
-}
-
-
-json & km::kbp::operator << (json &j, options const &opts)
+// TODO: Relocate this and fix it
+json & km::kbp::operator << (json &j, abstract_processor const &)
{
j << json::object;
- auto n = 0;
- for (auto scope: opts._scopes)
- {
- j << scope_names_lut[n++] << json::object;
- for (auto opt = scope; opt->key; ++opt)
- {
- j << opt->key << opt->value;
- }
- j << json::close;
- }
+ // auto n = 0;
+ // for (auto scope: opts._scopes)
+ // {
+ // j << scope_names_lut[n++] << json::object;
+ // for (auto opt = scope; opt->key; ++opt)
+ // {
+ // j << opt->key << opt->value;
+ // }
+ // j << json::close;
+ // }
j << "saved" << json::object;
for (auto scope: {KM_KBP_OPT_KEYBOARD, KM_KBP_OPT_ENVIRONMENT})
{
j << scope_names_lut[scope-1] << json::object;
- for (auto & opt: opts._saved)
- {
- if (opt.scope != scope) continue;
- j << opt.key << opt.value;
- }
+ // for (auto & opt: opts._saved)
+ // {
+ // if (opt.scope != scope) continue;
+ // j << opt.key << opt.value;
+ // }
j << json::close;
}
j << json::close;
diff --git a/src/option.hpp b/src/option.hpp
index 6c9b664..70627bf 100644
--- a/src/option.hpp
+++ b/src/option.hpp
@@ -10,7 +10,6 @@
#pragma once
-#include <vector>
#include <string>
#include <keyman/keyboardprocessor.h>
@@ -26,25 +25,37 @@ namespace kbp
option(): km_kbp_option_item KM_KBP_OPTIONS_END {}
option(option const &);
option(option &&);
+ option(km_kbp_option_scope, char16_t const *, char16_t const *);
option(km_kbp_option_scope, std::u16string const &,
std::u16string const &);
~option() noexcept;
+ option & operator=(option const & rhs);
option & operator=(option && rhs);
+
+ bool empty() const;
};
+
inline
- option::option(option const & rhs)
- : option(km_kbp_option_scope(rhs.scope), rhs.key, rhs.value)
+ option::option(km_kbp_option_scope s,
+ std::u16string const & k, std::u16string const & v)
+ : option(s, k.c_str(), v.c_str())
{}
+
+ inline
+ option::option(option const & rhs)
+ : option(km_kbp_option_scope(rhs.scope), rhs.key, rhs.value) {}
+
+
inline
- option::option(option && rhs)
- : km_kbp_option_item { rhs.key, rhs.value, rhs.scope }
+ option::option(option && rhs) : option()
{
- rhs.key = nullptr;
- rhs.value = nullptr;
+ std::swap(key, rhs.key);
+ std::swap(value, rhs.value);
+ scope = rhs.scope;
}
inline
@@ -54,54 +65,29 @@ namespace kbp
delete [] value;
}
+
inline
- option & option::operator=(option && rhs)
- {
+ option & option::operator=(option && rhs) {
delete [] key;
delete [] value;
- key = rhs.key;
- value = rhs.value; rhs.key = nullptr;
- scope = rhs.scope; rhs.value = nullptr;
- return *this;
+ return *new (this) option(std::move(rhs));
}
-
- class options
+ inline
+ option & option::operator=(option const & rhs)
{
- //km_kbp_keyboard_attrs const &_kb;
- km_kbp_option_item const * _scopes[KM_KBP_OPT_MAX_SCOPES-1];
- std::vector<option> _saved;
-
- public:
- options(km_kbp_option_item const * kb_default_options);
-
- void set_default_env(km_kbp_option_item const *env);
-
- char16_t const * lookup(km_kbp_option_scope scope,
- std::u16string const & key) const noexcept;
- km_kbp_option_item const * assign(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key,
- std::u16string const & value);
- void reset(km_kbp_option_scope scope,
- std::u16string const & key);
-
- friend json & operator << (json &j, km::kbp::options const &opts);
- };
-
- json & operator << (json &j, km::kbp::options const &opts);
+ delete [] key;
+ delete [] value;
+ return *new (this) option(rhs);
+ }
inline
- options::options(km_kbp_option_item const * kb_default_options)
- : _scopes {kb_default_options, nullptr}
- {}
-
- inline void options::set_default_env(km_kbp_option_item const *env) {
- _scopes[KM_KBP_OPT_ENVIRONMENT - 1] = env;
+ bool option::empty() const {
+ return key == nullptr;
}
-} // namespace kbp
-} // namespace km
-// Adaptor between internal km::kbp::options object and API definitiion.
-struct km_kbp_options: public km::kbp::options {};
+} // namespace kbp
+} // namespace km
diff --git a/src/path.hpp b/src/path.hpp
new file mode 100644
index 0000000..f7600f3
--- /dev/null
+++ b/src/path.hpp
@@ -0,0 +1,146 @@
+/*
+ Copyright: © 2018 SIL International.
+ Description: Internal keyboard class and adaptor class for the API.
+ Create Date: 2 Oct 2018
+ Authors: Tim Eves (TSE)
+ History: 2 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp
+*/
+
+#pragma once
+
+#include <algorithm>
+#include <string>
+#include <type_traits>
+
+#include <keyman/keyboardprocessor.h>
+#include "json.hpp"
+#include "utfcodec.hpp"
+
+// Forward declarations
+
+namespace km {
+namespace kbp
+{
+ class path
+ {
+ public:
+ using char_type = std::remove_const_t<
+ std::remove_pointer_t<km_kbp_path_name>>;
+ using string_type = std::basic_string<char_type>;
+ static constexpr char_type const parent_separator = _KM_KBP_PATH_SEPARATOR,
+ suffix_separator = _KM_KBP_EXT_SEPARATOR;
+ private:
+ string_type _path;
+
+ void normalise() {
+ #if '/' != _KM_KBP_PATH_SEPARATOR
+ std::replace(_path.begin(), _path.end(), char_type('/'), _KM_KBP_PATH_SEPARATOR);
+ #endif
+ }
+
+ public:
+ template<class... Args>
+ static path join(path const &start, Args&&... args) {
+ auto r = start;
+ for (path p: {args...})
+ r._path.append(1, path::parent_separator).append(p);
+ return r;
+ }
+
+ path() = default;
+ path(path const &) = default;
+ path(path &&) = default;
+ path & operator = (path const &) = default;
+ path & operator = (path &&) = default;
+
+ path(std::string const & p): _path(convert<char, char_type>(p)) { normalise(); }
+ path(std::u16string const & p): _path(convert<char16_t, char_type>(p)) { normalise(); }
+ path(std::wstring const & p): _path(convert<wchar_t, char_type>(p)) { normalise(); }
+ template<typename C>
+ path(C const * p): path(std::basic_string<C>(p)) {}
+
+
+ path parent() const {
+ auto i = _path.find_last_of(parent_separator);
+ return _path.substr(0, i == string_type::npos ? 0UL : i);
+ }
+
+ path name() const {
+ auto i = _path.find_last_of(parent_separator);
+ return _path.substr(i == string_type::npos ? 0UL : i+1);
+ }
+
+ path suffix() const {
+ auto i = _path.find_last_of(suffix_separator);
+ return _path.substr(i == string_type::npos ? _path.size() : i);
+ }
+
+ path stem() const {
+ auto psep = _path.find_last_of(parent_separator),
+ ssep = _path.find_last_of(suffix_separator);
+ return _path.substr(psep == string_type::npos ? 0UL : psep+1,
+ ssep == string_type::npos ? _path.size() : ssep);
+ }
+
+ void replace_extension(path const & replacment = path()) {
+ _path.resize(std::min(_path.find_last_of(suffix_separator), _path.size()));
+ _path += replacment;
+ }
+
+ // template<typename T>
+ // bool operator == (T const * rhs) const { return _path == rhs; }
+
+ bool operator == (path const &rhs) const { return _path == rhs._path; }
+
+ // template<typename T>
+ // bool operator != (T const * rhs) const { return _path != rhs; }
+
+ bool operator != (path const &rhs) const { return _path != rhs._path; }
+
+ string_type const & native() const noexcept { return _path; }
+
+ operator std::wstring () const { return convert<char_type,wchar_t>(_path); }
+ operator std::string() const { return convert<char_type,char>(_path); };
+ operator std::u16string() const {return convert<char_type,char16_t>(_path); };
+
+ char_type const * c_str() const { return _path.c_str(); }
+
+ path & operator += (path const & rhs) {
+ _path += rhs._path; return *this;
+ }
+
+ path operator + (path const & rhs) const {
+ auto r = *this; return r += rhs;
+ }
+
+ path & operator /= (path const & rhs) {
+ _path.append(1,path::parent_separator).append(rhs._path);
+ return *this;
+ }
+
+ path operator / (path const & rhs) const {
+ auto r = *this; return r /= rhs;
+ }
+
+ friend json & operator << (json &, path const &);
+ };
+
+ template<typename T>
+ bool operator == (T const * lhs, path const & rhs) { return rhs == lhs; }
+
+ template<typename T>
+ bool operator != (T const * lhs, path const & rhs) { return rhs != lhs; }
+
+ inline
+ json & operator << (json &j, path const &p) {
+ return j << static_cast<std::string>(p);
+ }
+
+ template<typename C>
+ auto & operator << (std::basic_ostream<C> &os, path const &p) {
+ os << static_cast<std::basic_string<C>>(p);
+ return os;
+ }
+
+} // namespace kbp
+} // namespace km
diff --git a/src/processor.hpp b/src/processor.hpp
index e3cef30..68a0559 100644
--- a/src/processor.hpp
+++ b/src/processor.hpp
@@ -9,73 +9,52 @@
#pragma once
#include <string>
+#include <unordered_map>
+
#include <keyman/keyboardprocessor.h>
-#include <kmx/kmx_processor.h>
+
+#include "keyboard.hpp"
namespace km {
namespace kbp
{
-
class abstract_processor
{
- private:
- km_kbp_keyboard_attrs const *_kb;
+ std::unordered_map<std::u16string, std::u16string> _persisted;
protected:
- km_kbp_keyboard_attrs const * keyboard() const noexcept { return _kb; }
+ keyboard_attributes _attributes;
+
public:
+ abstract_processor() {}
+ abstract_processor(keyboard_attributes && kb) : _attributes(std::move(kb)) {}
virtual ~abstract_processor() { };
- abstract_processor(km_kbp_keyboard_attrs const * kb) : _kb(kb)
- {}
+ keyboard_attributes const & keyboard() const noexcept {
+ return _attributes;
+ }
- virtual km_kbp_status process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) = 0;
- virtual km_kbp_attr const * get_attrs() const = 0;
- virtual km_kbp_status validate() const = 0;
- virtual void update_option(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value) = 0;
- virtual void init_state(std::vector<km_kbp_option_item> *default_env) = 0;
- };
+ auto & persisted_store() const noexcept { return _persisted; }
+ auto & persisted_store() noexcept { return _persisted; }
- class kmx_processor : public abstract_processor
- {
- private:
- bool _valid;
- kmx::KMX_Processor _kmx;
- public:
- kmx_processor(km_kbp_keyboard_attrs const * kb_);
- km_kbp_status process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state);
- km_kbp_attr const * get_attrs() const;
- km_kbp_status validate() const;
- void update_option(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value);
- void init_state(std::vector<km_kbp_option_item> *default_env);
- };
+ virtual km_kbp_status process_event(km_kbp_state *,
+ km_kbp_virtual_key,
+ uint16_t modifier_state) = 0;
- class mock_processor : public abstract_processor
- {
- public:
- mock_processor(km_kbp_keyboard_attrs const * kb) : abstract_processor(kb) {
- }
- km_kbp_status process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state);
- km_kbp_attr const * get_attrs() const;
- km_kbp_status validate() const;
- void update_option(km_kbp_state *_kmn_unused(state), km_kbp_option_scope _kmn_unused(scope), std::u16string const & _kmn_unused(key), std::u16string const & _kmn_unused(value)) {};
- void init_state(std::vector<km_kbp_option_item> *default_env) {
- km_kbp_option_item opt = { u"hello", u"-", 0 };
- default_env->emplace_back(opt);
- opt = KM_KBP_OPTIONS_END;
- default_env->emplace_back(opt);
- };
- };
+ virtual km_kbp_attr const & attributes() const = 0;
+ virtual km_kbp_status validate() const = 0;
- class null_processor : public mock_processor {
- public:
- null_processor(km_kbp_keyboard_attrs const * kb) : mock_processor(kb) {
- }
- km_kbp_status validate() const;
- void init_state(std::vector<km_kbp_option_item> *default_env) {
- km_kbp_option_item opt = KM_KBP_OPTIONS_END;
- default_env->emplace_back(opt);
- }
+ virtual char16_t const * lookup_option(km_kbp_option_scope,
+ std::u16string const & key) const = 0;
+ virtual option update_option(km_kbp_option_scope,
+ std::u16string const & key,
+ std::u16string const & value) = 0;
+
+ friend json & operator << (json &j, abstract_processor const &opts);
};
+ json & operator << (json &j, abstract_processor const &opts);
+
} // namespace kbp
} // namespace km
+
+struct km_kbp_keyboard : public km::kbp::abstract_processor {};
diff --git a/src/state.cpp b/src/state.cpp
index ceaeeb2..eb93bca 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -10,14 +10,32 @@
using namespace km::kbp;
-state::state(km::kbp::keyboard const & kb, km_kbp_option_item const * env)
- : _options(kb.default_options), _kb(kb)
-{
- const_cast<km::kbp::abstract_processor&>(_kb.processor()).init_state(&_env);
- _options.set_default_env(_env.data());
+void actions::push_persist(option const &opt) {
+ assert(empty() || back().type != KM_KBP_IT_END);
+ _option_items_stack.emplace_back(opt);
+ km_kbp_action_item ai = {KM_KBP_IT_PERSIST_OPT, {0,}, {0}};
+ ai.option = &_option_items_stack.back();
+ emplace_back(std::move(ai));
+}
+
+void actions::push_persist(option const &&opt) {
+ assert(empty() || back().type != KM_KBP_IT_END);
+ _option_items_stack.emplace_back(opt);
+ km_kbp_action_item ai = {KM_KBP_IT_PERSIST_OPT, {0,}, {0}};
+ ai.option = &_option_items_stack.back();
+ emplace_back(std::move(ai));
+}
+
+state::state(km::kbp::abstract_processor & ap, km_kbp_option_item const *env)
+ : _processor(ap)
+{
for (; env && env->key != nullptr; env++) {
//assert(env->scope == KM_KBP_OPT_ENVIRONMENT); // todo do we need scope? or can we find a way to eliminate it?
- assert(_options.assign(static_cast<km_kbp_state *>(this), (km_kbp_option_scope) KM_KBP_OPT_ENVIRONMENT, env->key, env->value) != nullptr);
+ ap.update_option(env->scope
+ ? km_kbp_option_scope(env->scope)
+ : KM_KBP_OPT_ENVIRONMENT,
+ env->key,
+ env->value);
}
}
diff --git a/src/state.hpp b/src/state.hpp
index 0e014de..503338e 100644
--- a/src/state.hpp
+++ b/src/state.hpp
@@ -7,40 +7,136 @@
*/
#pragma once
+
+#include <cassert>
#include <vector>
#include <keyman/keyboardprocessor.h>
#include "context.hpp"
#include "option.hpp"
-#include "keyboard.hpp"
namespace km {
namespace kbp
{
+//Forward declarations
+class abstract_processor;
+
using action = km_kbp_action_item;
+class actions : public std::vector<action>
+{
+ std::vector<option> _option_items_stack;
+
+ template<km_kbp_action_type V>
+ void _push_vkey(km_kbp_virtual_key);
+
+public:
+ template<typename... Args>
+ actions(Args&&... args);
+
+ void push_character(km_kbp_usv usv);
+ void push_marker(uintptr_t marker);
+ void push_alert();
+ void push_backspace();
+ void push_persist(option const &);
+ void push_persist(option const &&);
+ void push_emit_keystroke(km_kbp_virtual_key vk=0);
+ void push_invalidate_context();
+
+ void commit();
+ void clear();
+};
+
+
+template<typename... Args>
+actions::actions(Args&&... args)
+: std::vector<action>(std::forward<Args>(args)...)
+{
+ // Ensure the action items list is terminated in case the client calls
+ // km_kbp_state_action_items before they call process_event.
+ commit();
+}
+
+inline
+void actions::push_character(km_kbp_usv usv) {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item{ KM_KBP_IT_CHAR, {0,}, {usv} });
+}
+
+
+inline
+void actions::push_marker(uintptr_t marker) {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_MARKER, {0,}, {marker}});
+}
+
+
+inline
+void actions::push_alert() {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_ALERT, {0,}, {0}});
+}
+
+
+inline
+void actions::push_backspace() {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_BACK, {0,}, {0}});
+}
+
+
+inline
+void actions::push_emit_keystroke(km_kbp_virtual_key vk) {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_EMIT_KEYSTROKE, {0,}, {vk}});
+}
+
+
+inline
+void actions::push_invalidate_context() {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_INVALIDATE_CONTEXT, {0,}, {0}});
+}
+
+
+inline
+void actions::commit() {
+ assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
+ emplace_back(km_kbp_action_item {KM_KBP_IT_END, {0,}, {0}});
+}
+
+
+inline
+void actions::clear() {
+ std::vector<action>::clear();
+ _option_items_stack.clear();
+}
+
+
+
class state
{
protected:
- kbp::context _ctxt;
- kbp::options _options;
- kbp::keyboard const & _kb;
- std::vector<km_kbp_option_item> _env;
+ kbp::context _ctxt;
+ kbp::abstract_processor & _processor;
+ kbp::actions _actions;
public:
- state(kbp::keyboard const & kb, km_kbp_option_item const * env);
+ state(kbp::abstract_processor & kb, km_kbp_option_item const *env);
+
state(state const &) = default;
state(state const &&) = delete;
kbp::context & context() noexcept { return _ctxt; }
kbp::context const & context() const noexcept { return _ctxt; }
- kbp::options & options() noexcept { return _options; }
- kbp::options const & options() const noexcept { return _options; }
+ kbp::abstract_processor const & processor() const noexcept { return _processor; }
+ kbp::abstract_processor & processor() noexcept { return _processor; }
- kbp::keyboard const & keyboard() const noexcept { return _kb; }
+ kbp::actions & actions() noexcept { return _actions; }
+ kbp::actions const & actions() const noexcept { return _actions; }
};
} // namespace kbp
@@ -51,6 +147,4 @@ struct km_kbp_state : public km::kbp::state
template<typename... Args>
km_kbp_state(Args&&... args) : km::kbp::state(std::forward<Args>(args)...)
{}
-
- std::vector<km_kbp_action_item> actions;
};
diff --git a/src/utfcodec.hpp b/src/utfcodec.hpp
index 099a67f..3f22288 100644
--- a/src/utfcodec.hpp
+++ b/src/utfcodec.hpp
@@ -11,9 +11,10 @@
*/
#pragma once
-#include <cstddef>
#include <cstdint>
+#include <cstddef>
#include <cstdlib>
+#include <string>
typedef uint32_t uchar_t;
@@ -257,3 +258,48 @@ struct utf
typedef utf<uint32_t> utf32;
typedef utf<uint16_t> utf16;
typedef utf<uint8_t> utf8;
+
+
+template<typename F, typename T>
+std::basic_string<T> convert(std::basic_string<F> const &src);
+
+template<>
+inline
+std::basic_string<char> convert(std::basic_string<char> const &src) {
+ return src;
+}
+
+template<typename F, typename T>
+std::basic_string<T> convert(std::basic_string<F> const &src) {
+ using utf_const_iter = typename utf<typename utf<F>::codeunit_t>::const_iterator;
+ using codeunit_t = typename utf<T>::codeunit_t;
+ auto r = std::basic_string<T>();
+ codeunit_t buf[4];
+ int8_t l = 1;
+ for (auto i = utf_const_iter(src.data()),
+ e = decltype(i)(src.data() + src.size()); i != e && l > 0; ++i) {
+ utf<T>::codec::put(buf, uchar_t(*i), l);
+ r.append(reinterpret_cast<T *>(&buf[0]), l);
+ }
+ return r;
+}
+
+template<typename T>
+auto & operator << (std::basic_ostream<T> &os, std::u16string const &p) {
+ return std::operator << (os, convert<char16_t,T>(p));
+}
+
+template<typename T>
+auto & operator << (std::basic_ostream<T> &os, std::u32string const &p) {
+ return std::operator << (os, convert<char32_t,T>(p));
+}
+
+template<typename T>
+auto & operator << (std::basic_ostream<T> &os, char16_t const *s) {
+ return std::operator << (os, convert<char16_t,T>(s));
+}
+
+template<typename T>
+auto & operator << (std::basic_ostream<T> &os, char32_t const *s) {
+ return std::operator << (os, convert<char32_t,T>(s));
+}
diff --git a/tests/unit/kmnkbd/keyboard_api.cpp b/tests/unit/kmnkbd/keyboard_api.cpp
index ce1a408..67d59a5 100644
--- a/tests/unit/kmnkbd/keyboard_api.cpp
+++ b/tests/unit/kmnkbd/keyboard_api.cpp
@@ -7,18 +7,13 @@
#include <string>
#include <keyman/keyboardprocessor.h>
-#include <experimental/filesystem>
-
-namespace std {
- namespace filesystem = std::experimental::filesystem;
-}
-
+#include "path.hpp"
//#include "keyboard.hpp"
namespace
{
- std::filesystem::path const test_kb_path = "/a/dummy/keyboard.mock";
+ km::kbp::path const test_kb_path = "/a/dummy/keyboard.mock";
}
#define try_status(expr) \
@@ -31,7 +26,7 @@ int main(int, char *[])
try_status(km_kbp_keyboard_load(test_kb_path.c_str(), &test_kb));
try_status(km_kbp_keyboard_get_attrs(test_kb, &kb_attrs));
- if (kb_attrs->folder_path != test_kb_path.parent_path())
+ if (kb_attrs->folder_path != test_kb_path.parent())
return __LINE__;
km_kbp_keyboard_dispose(test_kb);
diff --git a/tests/unit/kmnkbd/meson.build b/tests/unit/kmnkbd/meson.build
index 726b115..92aed00 100644
--- a/tests/unit/kmnkbd/meson.build
+++ b/tests/unit/kmnkbd/meson.build
@@ -6,31 +6,19 @@
#
defns=['-DKMN_KBP_STATIC']
+tests = [
+ ['context-api', 'context_api.cpp'],
+ ['keyboard-api', 'keyboard_api.cpp'],
+# ['options-api', 'options_api.cpp'],
+ ['state-api', 'state_api.cpp']
+]
-
-ctxt = executable('context-api', 'context_api.cpp',
- cpp_args: defns,
- include_directories: [inc, libsrc],
- link_args: links,
- objects: lib.extract_all_objects())
-keyb = executable('keyboard-api', 'keyboard_api.cpp',
- cpp_args: defns,
- include_directories: [inc, libsrc],
- link_args: links,
- #objects: lib.extract_objects('km_kbp_keyboard_api.cpp', 'json.cpp', 'keyboard.cpp', 'keyboardprocessor.cpp', 'mock/mock_processor.cpp'))
- objects: lib.extract_all_objects())
-opts = executable('options-api', 'options_api.cpp',
- cpp_args: defns,
- include_directories: [inc, libsrc],
- link_args: links,
- objects: lib.extract_all_objects())
-state = executable('state-api', 'state_api.cpp',
+foreach t : tests
+ bin = executable(t[0], t[1],
cpp_args: defns,
include_directories: [inc, libsrc],
link_args: links,
objects: lib.extract_all_objects())
-test('context-api', ctxt)
-test('keyboard-api', keyb)
-test('options-api', opts)
-test('state-api', state)
+ test(t[0], bin)
+endforeach
diff --git a/tests/unit/kmnkbd/options_api.cpp b/tests/unit/kmnkbd/options_api.cpp
index 82f4459..c2b287b 100644
--- a/tests/unit/kmnkbd/options_api.cpp
+++ b/tests/unit/kmnkbd/options_api.cpp
@@ -10,6 +10,7 @@
#include <keyman/keyboardprocessor.h>
#include "option.hpp"
+#include "state.hpp"
#define try_status(expr) \
{auto __s = (expr); if (__s != KM_KBP_STATUS_OK) std::exit(100*__LINE__+__s);}
@@ -51,20 +52,16 @@ namespace
km_kbp_option_item const empty_options_list[] = {KM_KBP_OPTIONS_END};
#if 0
- km::kbp::options mock_options(test_kb, test_env);
- km::kbp::options empty_options(empty_options_list, empty_options_list);
- km_kbp_options * api_mock_options = static_cast<km_kbp_options *>(
- &mock_options),
- * api_empty_options = static_cast<km_kbp_options *>(
- &empty_options);
+ km::kbp::state mock_state(test_kb, test_env);
+ km::kbp::state empty_state(empty_options_list, empty_options_list);
#endif
- std::string get_json_doc(km_kbp_options * const opts)
+ std::string get_json_doc(km_kbp_state * const state)
{
size_t sz = 0;
- try_status(km_kbp_options_to_json(opts, nullptr, &sz));
+ try_status(km_kbp_state_options_to_json(state, nullptr, &sz));
std::string buf(sz-1, 0);
- try_status(km_kbp_options_to_json(opts, &buf[0], &sz));
+ try_status(km_kbp_state_options_to_json(state, &buf[0], &sz));
return buf;
}
@@ -74,17 +71,14 @@ namespace
{
#if 0
km_kbp_cp const * ret = nullptr;
- auto s = km_kbp_options_lookup(api_mock_options, scope,
+ auto s = km_kbp_state_option_lookup(api_mock_options, scope,
key.c_str(),
&ret);
bool v = s == KM_KBP_STATUS_OK && ret == value;
- if (s == KM_KBP_STATUS_OK) {
- km_kbp_cp_dispose(ret);
- }
return v;
#else
return true;
-#endif;
+#endif
}
constexpr char const *empty_json = "\
diff --git a/tests/unit/kmnkbd/state_api.cpp b/tests/unit/kmnkbd/state_api.cpp
index f81a2bd..8894187 100644
--- a/tests/unit/kmnkbd/state_api.cpp
+++ b/tests/unit/kmnkbd/state_api.cpp
@@ -6,9 +6,11 @@
*/
#include <cstdlib>
#include <cstring>
+#include <iostream>
#include <string>
#include <keyman/keyboardprocessor.h>
+#include "path.hpp"
#include "state.hpp"
@@ -42,18 +44,6 @@ constexpr char const *doc1_expected = u8"\
\"version\" : \"3.145\",\n\
\"rules\" : []\n\
},\n\
- \"options\" : {\n\
- \"keyboard\" : {},\n\
- \"environment\" : {\n\
- \"hello\" : \"-\"\n\
- },\n\
- \"saved\" : {\n\
- \"keyboard\" : {},\n\
- \"environment\" : {\n\
- \"hello\" : \"world\"\n\
- }\n\
- }\n\
- },\n\
\"context\" : [\n\
\"H\",\n\
\"e\",\n\
@@ -67,7 +57,7 @@ constexpr char const *doc1_expected = u8"\
\"L\"\n\
],\n\
\"actions\" : [\n\
- { \"character\" : \"L\" }\n\
+ { \"persist\" : { \"keyboard\" : { \"__test_point\" : \"F2 pressed test save.\" } } }\n\
]\n\
}\n";
@@ -80,22 +70,41 @@ constexpr char const *doc2_expected = u8"\
\"version\" : \"3.145\",\n\
\"rules\" : []\n\
},\n\
- \"options\" : {\n\
- \"keyboard\" : {},\n\
- \"environment\" : {\n\
- \"hello\" : \"-\"\n\
- },\n\
- \"saved\" : {\n\
- \"keyboard\" : {},\n\
- \"environment\" : {\n\
- \"hello\" : \"globe\"\n\
- }\n\
- }\n\
- },\n\
\"context\" : [],\n\
\"actions\" : []\n\
}\n";
+
+constexpr km_kbp_option_item const expected_persist_opt = {
+ u"__test_point",
+ u"F2 pressed test save.",
+ KM_KBP_OPT_KEYBOARD
+};
+
+inline
+bool operator==(km_kbp_option_item const & lhs, km_kbp_option_item const & rhs)
+{
+ return lhs.scope == rhs.scope
+ && std::u16string(lhs.key) == rhs.key
+ && std::u16string(lhs.value) == rhs.value;
+}
+
+
+bool operator==(km_kbp_action_item const & lhs,
+ km_kbp_action_item const & rhs)
+{
+ if (lhs.type != rhs.type) return false;
+ switch(lhs.type)
+ {
+ case KM_KBP_IT_CHAR: return lhs.character == rhs.character;
+ case KM_KBP_IT_MARKER: return lhs.marker == rhs.marker;
+ case KM_KBP_IT_PERSIST_OPT: return *lhs.option == *rhs.option;
+ default: break;
+ }
+
+ return true;
+}
+
#ifdef assert
#undef assert
#endif
@@ -107,7 +116,7 @@ bool action_items(km_kbp_state const * state,
auto act = km_kbp_state_action_items(state, &n);
for (auto &rhs: expected)
- if (std::memcmp(act++, &rhs, sizeof rhs) != 0) return false;
+ if (!(*act++ == rhs)) return false;
return true;
}
@@ -119,7 +128,7 @@ int main(int, char * [])
km_kbp_keyboard * test_kb = nullptr;
km_kbp_state * test_state = nullptr,
* test_clone = nullptr;
- try_status(km_kbp_keyboard_load(std::filesystem::path("dummy.mock").c_str(), &test_kb));
+ try_status(km_kbp_keyboard_load(km::kbp::path("dummy.mock").c_str(), &test_kb));
// Simple sanity tests.
try_status(km_kbp_state_create(test_kb, test_env_opts, &test_state));
@@ -127,8 +136,6 @@ int main(int, char * [])
// Check sub objects have been copied and not shared.
if (km_kbp_state_context(test_state) == km_kbp_state_context(test_clone))
return __LINE__;
- if (km_kbp_state_options(test_state) == km_kbp_state_options(test_clone))
- return __LINE__;
size_t n_actions = 0;
if (km_kbp_state_action_items(test_state, &n_actions) == nullptr
&& n_actions != 0)
@@ -148,8 +155,7 @@ int main(int, char * [])
km_kbp_option_item new_opt[] = {
{u"hello", u"globe", KM_KBP_OPT_ENVIRONMENT},
KM_KBP_OPTIONS_END};
- try_status(
- km_kbp_options_update(test_clone, new_opt));
+ try_status(km_kbp_state_options_update(test_clone, new_opt));
// Test the engine
auto attrs = km_kbp_get_engine_attrs(test_state);
@@ -168,15 +174,21 @@ int main(int, char * [])
assert(action_items(test_state, {{KM_KBP_IT_CHAR, {0,}, {km_kbp_usv('l')}}}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_BKSP, 0));
- assert(action_items(test_state, {{KM_KBP_IT_BACK, {0,}, {1}}}));
+ assert(action_items(test_state, {{KM_KBP_IT_BACK, {0,}, {0}}}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_L,
KM_KBP_MODIFIER_SHIFT));
assert(action_items(test_state, {{KM_KBP_IT_CHAR, {0,}, {km_kbp_usv('L')}}}));
+ try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F2,0));
+ assert(action_items(test_state, {{KM_KBP_IT_PERSIST_OPT, {0,},
+ {uintptr_t(&expected_persist_opt)}}}));
// Test debug dump
auto doc1 = get_json_doc(*test_state),
doc2 = get_json_doc(*test_clone);
+ std::cout << doc1 << std::endl;
+ std::cout << doc2 << std::endl;
+
// These should not be equal.
if (doc1 == doc2) return __LINE__;
// These should be.
diff --git a/tests/unit/kmx/015 - ralt 2.kmn b/tests/unit/kmx/015 - ralt 2.kmn
index f2908af..ee87dd6 100644
--- a/tests/unit/kmx/015 - ralt 2.kmn
+++ b/tests/unit/kmx/015 - ralt 2.kmn
@@ -1,5 +1,5 @@
c Description: Tests Right Alt processing with non-US kbds.
-c keys: abc[RALT K_A]
+c keys: [K_A][K_B][K_C][RALT K_A]
c expected: abcd
store(&VERSION) '9.0'
diff --git a/tests/unit/kmx/017 - space mnemonic kbd.kmn b/tests/unit/kmx/017 - space mnemonic kbd.kmn
index c26e71a..33b2153 100644
--- a/tests/unit/kmx/017 - space mnemonic kbd.kmn
+++ b/tests/unit/kmx/017 - space mnemonic kbd.kmn
@@ -1,5 +1,5 @@
c Description: Tests Space handling in mnemonic keyboards (failed with Win 98)
-c keys: ab c d de
+c keys: [K_A][K_B][K_SPACE][K_C][K_SPACE][K_D][K_SPACE][K_D][K_E]
c expected: XYZ
store(&VERSION) '9.0'
diff --git a/tests/unit/kmx/020 - deadkeys and backspace.kmn b/tests/unit/kmx/020 - deadkeys and backspace.kmn
index 6d13a0f..b5d34a1 100644
--- a/tests/unit/kmx/020 - deadkeys and backspace.kmn
+++ b/tests/unit/kmx/020 - deadkeys and backspace.kmn
@@ -6,8 +6,8 @@ c 4. Two deadkeys in a row in context dk(4a) dk(4b) + BKSP = nul
c 5. One char and two deadkeys in context 'a' dk(5a) dk(5b) 'b' + BKSP = 'a'
c 6. One char and two deadkeys and one char and two deadkeys in context 'a' dk(6a) dk(6b) 'b' dk(6c) dk(6d) + BKSP = 'a'
c keys: [K_1][K_BKSP][K_2][K_BKSP][K_3][K_BKSP][K_4][K_BKSP][K_5][K_BKSP][K_6][K_BKSP]
-c expected: aa
-c context:
+c expected: 12aa
+c context: 1234
store(&VERSION) '9.0'
diff --git a/tests/unit/kmx/020 - deadkeys and backspace.kmx b/tests/unit/kmx/020 - deadkeys and backspace.kmx
index 45e24ab..e7b1577 100644
--- a/tests/unit/kmx/020 - deadkeys and backspace.kmx
+++ b/tests/unit/kmx/020 - deadkeys and backspace.kmx
Binary files differ
diff --git a/tests/unit/kmx/022 - options with preset.kmn b/tests/unit/kmx/022 - options with preset.kmn
index 26599fc..878acc6 100644
--- a/tests/unit/kmx/022 - options with preset.kmn
+++ b/tests/unit/kmx/022 - options with preset.kmn
@@ -4,6 +4,7 @@ c expected: foo.foo.no foo.
c context:
c option: foo=1
c expected option: foo=0
+c saved option: foo=0
store(&version) '10.0'
diff --git a/tests/unit/kmx/022 - options with preset.kmx b/tests/unit/kmx/022 - options with preset.kmx
index 3d01729..06b56bf 100644
--- a/tests/unit/kmx/022 - options with preset.kmx
+++ b/tests/unit/kmx/022 - options with preset.kmx
Binary files differ
diff --git a/tests/unit/kmx/023 - options with save.kmn b/tests/unit/kmx/023 - options with save.kmn
index 7e2a430..aa6c138 100644
--- a/tests/unit/kmx/023 - options with save.kmn
+++ b/tests/unit/kmx/023 - options with save.kmn
@@ -3,6 +3,7 @@ c keys: [K_A][K_1][K_A][K_0][K_A][K_2]
c expected: no foo.foo.no foo.
c context:
c expected option: foo=0
+c saved option: foo=0
store(&version) '10.0'
diff --git a/tests/unit/kmx/023 - options with save.kmx b/tests/unit/kmx/023 - options with save.kmx
index bd74c38..cadbe2d 100644
--- a/tests/unit/kmx/023 - options with save.kmx
+++ b/tests/unit/kmx/023 - options with save.kmx
Binary files differ
diff --git a/tests/unit/kmx/024 - options with save and preset.kmn b/tests/unit/kmx/024 - options with save and preset.kmn
index e3f5bf9..e8ecd57 100644
--- a/tests/unit/kmx/024 - options with save and preset.kmn
+++ b/tests/unit/kmx/024 - options with save and preset.kmn
@@ -4,6 +4,7 @@ c expected: foo.foo.no foo.
c context:
c option: foo=1
c expected option: foo=0
+c saved option: foo=0
store(&version) '10.0'
@@ -17,4 +18,4 @@ if(foo = '1') + 'a' > 'foo.'
if(foo = '0') + 'a' > 'no foo.'
+ '1' > set(foo = '1')
+ '0' > set(foo = '0')
-+ '2' > save(foo) \ No newline at end of file
+if(foo = '0') + '2' > save(foo)
diff --git a/tests/unit/kmx/024 - options with save and preset.kmx b/tests/unit/kmx/024 - options with save and preset.kmx
index bd74c38..75356f7 100644
--- a/tests/unit/kmx/024 - options with save and preset.kmx
+++ b/tests/unit/kmx/024 - options with save and preset.kmx
Binary files differ
diff --git a/tests/unit/kmx/025 - options with reset.kmn b/tests/unit/kmx/025 - options with reset.kmn
index 6201362..2fea1b0 100644
--- a/tests/unit/kmx/025 - options with reset.kmn
+++ b/tests/unit/kmx/025 - options with reset.kmn
@@ -17,5 +17,4 @@ if(foo = '1') + 'a' > 'foo.'
if(foo = '0') + 'a' > 'no foo.'
+ '1' > set(foo = '1')
+ '0' > set(foo = '0')
-+ '2' > save(foo)
+ '3' > reset(foo)
diff --git a/tests/unit/kmx/025 - options with reset.kmx b/tests/unit/kmx/025 - options with reset.kmx
index 97bb6a9..4f2ba8c 100644
--- a/tests/unit/kmx/025 - options with reset.kmx
+++ b/tests/unit/kmx/025 - options with reset.kmx
Binary files differ
diff --git a/tests/unit/kmx/028 - smp.kmn b/tests/unit/kmx/028 - smp.kmn
index e17a8c4..0a2a47a 100644
--- a/tests/unit/kmx/028 - smp.kmn
+++ b/tests/unit/kmx/028 - smp.kmn
@@ -1,6 +1,6 @@
c Description: Tests SMP characters
c keys: [K_1][K_2]
-c expected: \u1F642hi\u1F600
+c expected: \U0001F642hi\U0001F600
c context:
store(&version) '10.0'
diff --git a/tests/unit/kmx/034 - options double set reset.kmn b/tests/unit/kmx/034 - options double set reset.kmn
new file mode 100644
index 0000000..2e8e754
--- /dev/null
+++ b/tests/unit/kmx/034 - options double set reset.kmn
@@ -0,0 +1,19 @@
+c Description: Tests basic option rules with save reset+set+reset
+c keys: [K_A][K_A]
+c expected: foo.foo.
+c context:
+c option: foo=1
+c expected option: foo=1
+
+store(&version) '10.0'
+
+store(foo) '0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+if(foo = '1') + 'a' > 'foo.' set(foo='2') reset(foo) set(foo='3') reset(foo)
+if(foo = '2') + 'a' > 'bar.'
+if(foo = '3') + 'a' > 'baz.'
+if(foo = '0') + 'a' > 'no foo.'
diff --git a/tests/unit/kmx/034 - options double set reset.kmx b/tests/unit/kmx/034 - options double set reset.kmx
new file mode 100644
index 0000000..7cacb4c
--- /dev/null
+++ b/tests/unit/kmx/034 - options double set reset.kmx
Binary files differ
diff --git a/tests/unit/kmx/035 - options double set staged.kmn b/tests/unit/kmx/035 - options double set staged.kmn
new file mode 100644
index 0000000..e92e9ba
--- /dev/null
+++ b/tests/unit/kmx/035 - options double set staged.kmn
@@ -0,0 +1,21 @@
+c Description: Tests basic option rules with save reset+set+reset
+c keys: [K_A][K_B][K_C][K_B][K_A]
+c expected: foo.foo.
+c context:
+c option: foo=1
+c expected option: foo=2
+
+store(&version) '10.0'
+
+store(foo) '0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+if(foo = '1') + 'a' > 'foo.' set(foo='2')
++ 'b' > reset(foo)
++ 'c' > set(foo='3')
+if(foo = '2') + 'a' > 'bar.'
+if(foo = '3') + 'a' > 'baz.'
+if(foo = '0') + 'a' > 'no foo.'
diff --git a/tests/unit/kmx/035 - options double set staged.kmx b/tests/unit/kmx/035 - options double set staged.kmx
new file mode 100644
index 0000000..5862a99
--- /dev/null
+++ b/tests/unit/kmx/035 - options double set staged.kmx
Binary files differ
diff --git a/tests/unit/kmx/036 - options - double reset staged.kmn b/tests/unit/kmx/036 - options - double reset staged.kmn
new file mode 100644
index 0000000..5a9144f
--- /dev/null
+++ b/tests/unit/kmx/036 - options - double reset staged.kmn
@@ -0,0 +1,19 @@
+c Description: Tests basic option rules with save reset+reset
+c keys: [K_A][K_B][K_B][K_A]
+c expected: foo.foo.
+c context:
+c option: foo=1
+c expected option: foo=2
+
+store(&version) '10.0'
+
+store(foo) '0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+if(foo = '1') + 'a' > 'foo.' set(foo='2')
++ 'b' > reset(foo)
+if(foo = '2') + 'a' > 'bar.'
+if(foo = '0') + 'a' > 'no foo.'
diff --git a/tests/unit/kmx/036 - options - double reset staged.kmx b/tests/unit/kmx/036 - options - double reset staged.kmx
new file mode 100644
index 0000000..f8def7e
--- /dev/null
+++ b/tests/unit/kmx/036 - options - double reset staged.kmx
Binary files differ
diff --git a/tests/unit/kmx/037 - options - double reset.kmn b/tests/unit/kmx/037 - options - double reset.kmn
new file mode 100644
index 0000000..5d2eb1d
--- /dev/null
+++ b/tests/unit/kmx/037 - options - double reset.kmn
@@ -0,0 +1,18 @@
+c Description: Tests basic option rules with save reset+reset
+c keys: [K_A][K_A]
+c expected: foo.foo.
+c context:
+c option: foo=1
+c expected option: foo=1
+
+store(&version) '10.0'
+
+store(foo) '0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+if(foo = '1') + 'a' > 'foo.' set(foo='2') reset(foo) reset(foo)
+if(foo = '2') + 'a' > 'bar.'
+if(foo = '0') + 'a' > 'no foo.'
diff --git a/tests/unit/kmx/037 - options - double reset.kmx b/tests/unit/kmx/037 - options - double reset.kmx
new file mode 100644
index 0000000..00692e5
--- /dev/null
+++ b/tests/unit/kmx/037 - options - double reset.kmx
Binary files differ
diff --git a/tests/unit/kmx/038 - punctkeys.kmn b/tests/unit/kmx/038 - punctkeys.kmn
new file mode 100644
index 0000000..fc89399
--- /dev/null
+++ b/tests/unit/kmx/038 - punctkeys.kmn
@@ -0,0 +1,23 @@
+c Description: Tests punctuation keys (Unicode)
+c keys: [K_A][K_COLON][K_A][K_EQUAL][K_A][K_COMMA][K_A][K_HYPHEN][K_A][K_PERIOD][K_A][K_SLASH][K_A][K_BKQUOTE][K_A][K_LBRKT][K_A][K_BKSLASH][K_A][K_RBRKT][K_A][K_QUOTE][K_A][K_oE2]
+c expected: efghijklmnop
+c context:
+
+store(&version) '6.0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+'a' + [K_COLON] > 'e'
+'a' + [K_EQUAL] > 'f'
+'a' + [K_COMMA] > 'g'
+'a' + [K_HYPHEN] > 'h'
+'a' + [K_PERIOD] > 'i'
+'a' + [K_SLASH] > 'j'
+'a' + [K_BKQUOTE] > 'k'
+'a' + [K_LBRKT] > 'l'
+'a' + [K_BKSLASH] > 'm'
+'a' + [K_RBRKT] > 'n'
+'a' + [K_QUOTE] > 'o'
+'a' + [K_oE2] > 'p'
diff --git a/tests/unit/kmx/038 - punctkeys.kmx b/tests/unit/kmx/038 - punctkeys.kmx
new file mode 100644
index 0000000..a632d70
--- /dev/null
+++ b/tests/unit/kmx/038 - punctkeys.kmx
Binary files differ
diff --git a/tests/unit/kmx/039 - generic ctrlalt.kmn b/tests/unit/kmx/039 - generic ctrlalt.kmn
new file mode 100644
index 0000000..d540413
--- /dev/null
+++ b/tests/unit/kmx/039 - generic ctrlalt.kmn
@@ -0,0 +1,13 @@
+c Description: Tests generic alt and control (Unicode)
+c keys: [K_A][K_A][LCTRL K_B][K_A][LALT K_C][K_A][RCTRL K_B][K_A][RALT K_C]
+c expected: abcbc
+c context:
+
+store(&version) '6.0'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
+'a' + [CTRL K_B] > 'b'
+'a' + [ALT K_C] > 'c'
diff --git a/tests/unit/kmx/039 - generic ctrlalt.kmx b/tests/unit/kmx/039 - generic ctrlalt.kmx
new file mode 100644
index 0000000..2eda691
--- /dev/null
+++ b/tests/unit/kmx/039 - generic ctrlalt.kmx
Binary files differ
diff --git a/tests/unit/kmx/kmp.json b/tests/unit/kmx/kmp.json
new file mode 100644
index 0000000..82cd8bf
--- /dev/null
+++ b/tests/unit/kmx/kmp.json
@@ -0,0 +1,383 @@
+{
+ "system": {
+ "keymanDeveloperVersion": "10.0.1099.0",
+ "fileVersion": "7.0"
+ },
+ "options": {
+ "readmeFile": "kmx.cpp"
+ },
+ "info": {
+ "name": {
+ "description": "Test Keyboards"
+ },
+ "version": {
+ "description": "0.0"
+ },
+ "copyright": {
+ "description": "\u00A9 2018 SIL International"
+ },
+ "author": {
+ "description": ""
+ }
+ },
+ "files": [
+ {
+ "name": "kmp.json",
+ "description": "Package information (JSON)"
+ }
+ ],
+ "keyboards": [
+ {
+ "name": "000 - null keyboard",
+ "id": "000 - null keyboard",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "001 - basic input UnicodeI",
+ "id": "001 - basic input UnicodeI",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "002 - basic input Unicode",
+ "id": "002 - basic input Unicode",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "003 - nul",
+ "id": "003 - nul",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "004 - basic input (shift 2)",
+ "id": "004 - basic input (shift 2)",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "005 - nul with initial context",
+ "id": "005 - nul with initial context",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "006 - vkey input (shift ctrl)",
+ "id": "006 - vkey input (shift ctrl)",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "007 - vkey input (ctrl alt)",
+ "id": "007 - vkey input (ctrl alt)",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "008 - vkey input (ctrl alt 2)",
+ "id": "008 - vkey input (ctrl alt 2)",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "012 - ralt",
+ "id": "012 - ralt",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "013 - deadkeys",
+ "id": "013 - deadkeys",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "014 - groups and virtual keys",
+ "id": "014 - groups and virtual keys",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "015 - ralt 2",
+ "id": "015 - ralt 2",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "017 - space mnemonic kbd",
+ "id": "017 - space mnemonic kbd",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "018 - nul testing",
+ "id": "018 - nul testing",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "019 - multiple deadkeys",
+ "id": "019 - multiple deadkeys",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "020 - deadkeys and backspace",
+ "id": "020 - deadkeys and backspace",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "021 - options",
+ "id": "021 - options",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "022 - options with preset",
+ "id": "022 - options with preset",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "023 - options with save",
+ "id": "023 - options with save",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "024 - options with save and preset",
+ "id": "024 - options with save and preset",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "025 - options with reset",
+ "id": "025 - options with reset",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "026 - system stores",
+ "id": "026 - system stores",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "027 - system stores 2",
+ "id": "027 - system stores 2",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "028 - smp",
+ "id": "028 - smp",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "029 - beep",
+ "id": "029 - beep",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "030 - multiple groups",
+ "id": "030 - multiple groups",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "031 - caps lock",
+ "id": "031 - caps lock",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "032 - caps control",
+ "id": "032 - caps control",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "033 - caps always off",
+ "id": "033 - caps always off",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "038 - punctkeys",
+ "id": "038 - punctkeys",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ },
+ {
+ "name": "039 - generic ctrlalt",
+ "id": "039 - generic ctrlalt",
+ "version": "0.0",
+ "languages": [
+ {
+ "name": "Undetermined",
+ "id": "und"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/unit/kmx/kmx.cpp b/tests/unit/kmx/kmx.cpp
index aaf96ef..a876b46 100644
--- a/tests/unit/kmx/kmx.cpp
+++ b/tests/unit/kmx/kmx.cpp
@@ -13,9 +13,11 @@
#include <string>
#include <type_traits>
-#include <keyman/keyboardprocessor.h>
+#include <kmx/kmx_processor.h>
+#include "path.hpp"
#include "state.hpp"
+#include "utfcodec.hpp"
#define try_status(expr) \
{auto __s = (expr); if (__s != KM_KBP_STATUS_OK) std::exit(100*__LINE__+__s);}
@@ -25,11 +27,8 @@
#endif
#define assert(expr) {if (!(expr)) std::exit(100*__LINE__); }
-std::string utf16_to_utf8(std::u16string utf16_string); // defined in keyboard.cpp
-
namespace
{
-
bool g_beep_found = false;
struct key_event {
@@ -39,17 +38,18 @@ struct key_event {
typedef enum {
KOT_INPUT,
- KOT_OUTPUT
+ KOT_OUTPUT,
+ KOT_SAVED
} kmx_option_type;
struct kmx_option {
kmx_option_type type;
- std::u16string key, value;
+ std::u16string key, value, saved_value;
};
using kmx_options = std::vector<kmx_option>;
-int load_source(const std::string &, std::string &, std::u16string &,
+int load_source(const km::kbp::path &, std::string &, std::u16string &,
std::u16string &, kmx_options &, bool &);
km_kbp_option_item test_env_opts[] =
@@ -86,7 +86,7 @@ key_event char_to_event(char ch) {
};
}
-uint16_t const get_modifier(std::string const m) {
+uint16_t get_modifier(std::string const m) {
for (int i = 0; km::kbp::kmx::s_modifier_names[i].name; i++) {
if (m == km::kbp::kmx::s_modifier_names[i].name) {
return km::kbp::kmx::s_modifier_names[i].modifier;
@@ -95,7 +95,7 @@ uint16_t const get_modifier(std::string const m) {
return 0;
}
-km_kbp_virtual_key const get_vk(std::string const & vk) {
+km_kbp_virtual_key get_vk(std::string const & vk) {
for (int i = 1; i < 256; i++) {
if (vk == km::kbp::kmx::s_key_names[i]) {
return i;
@@ -104,7 +104,7 @@ km_kbp_virtual_key const get_vk(std::string const & vk) {
return 0;
}
-key_event const vkey_to_event(std::string const & vk_event) {
+key_event vkey_to_event(std::string const & vk_event) {
// vkey format is MODIFIER MODIFIER K_NAME
//std::cout << "VK=" << vk_event << std::endl;
@@ -135,7 +135,7 @@ key_event const vkey_to_event(std::string const & vk_event) {
key_event next_key(std::string &keys) {
// Parse the next element of the string, chop it off, and return it
- if (keys.length() == 0) return { 0 };
+ if (keys.length() == 0) return { 0, 0 };
char ch = keys[0];
if (ch == '[') {
if (keys.length() > 1 && keys[1] == '[') {
@@ -154,7 +154,7 @@ key_event next_key(std::string &keys) {
}
}
-void apply_action(km_kbp_state const * state, km_kbp_action_item const & act, std::u16string & text_store) {
+void apply_action(km_kbp_state const *, km_kbp_action_item const & act, std::u16string & text_store, kmx_options &options) {
switch (act.type)
{
case KM_KBP_IT_END:
@@ -189,7 +189,28 @@ void apply_action(km_kbp_state const * state, km_kbp_action_item const & act, st
}
break;
case KM_KBP_IT_PERSIST_OPT:
- assert(false); // TODO
+ {
+ bool found = false;
+ for (auto it = options.begin(); it != options.end(); it++) {
+ if (it->type == KOT_SAVED) {
+ if (it->key.compare(act.option->key) == 0) {
+ found = true;
+ it->saved_value = act.option->value;
+ break;
+ }
+ }
+ }
+ std::cout << "action: option "
+ << (act.option->scope == KM_KBP_OPT_ENVIRONMENT ? "environment " : "keyboard ")
+ << act.option->key
+ << "=" << act.option->value
+ << " persistence requested" << std::endl;
+ if (!found) {
+ std::cout << "option "
+ << act.option->key
+ << " saved but no expected output found. Suggestion: update test to include saved option value." << std::endl;
+ }
+ }
break;
case KM_KBP_IT_INVALIDATE_CONTEXT:
std::cout << "action: context invalidated (markers cleared)" << std::endl;
@@ -203,32 +224,9 @@ void apply_action(km_kbp_state const * state, km_kbp_action_item const & act, st
}
}
-template<typename P>
-std::basic_string<
- typename std::remove_const<
- typename std::remove_pointer<P>::type>::type
->
-utf8_to(const std::string &);
-
-template<>
-inline
-std::basic_string<wchar_t> utf8_to<const wchar_t *>(const std::string & s)
-{
- std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
- return convert.from_bytes(s);
-}
-
-template<>
-inline
-std::basic_string<char> utf8_to<const char *>(const std::string & s)
-{
- return s;
-}
-
-int run_test(const std::string & source, const std::string & _compiled) {
+int run_test(const km::kbp::path & source, const km::kbp::path & compiled) {
std::string keys = "";
std::u16string expected = u"", context = u"";
- auto compiled = utf8_to<km_kbp_path_name>(_compiled);
kmx_options options;
bool expected_beep = false;
@@ -236,7 +234,7 @@ int run_test(const std::string & source, const std::string & _compiled) {
if (result != 0) return result;
std::cout << "source file = " << source << std::endl
- << "compiled file = " << _compiled << std::endl;
+ << "compiled file = " << compiled << std::endl;
km_kbp_keyboard * test_kb = nullptr;
km_kbp_state * test_state = nullptr;
@@ -256,7 +254,7 @@ int run_test(const std::string & source, const std::string & _compiled) {
for (auto it = options.begin(); it != options.end(); it++) {
if (it->type != KOT_INPUT) continue;
- std::cout << "input option-key: " << utf16_to_utf8(it->key) << std::endl;
+ std::cout << "input option-key: " << it->key << std::endl;
std::u16string key = it->key;
if (key[0] == u'&') {
@@ -285,7 +283,7 @@ int run_test(const std::string & source, const std::string & _compiled) {
keyboard_opts[i] = KM_KBP_OPTIONS_END;
- try_status(km_kbp_options_update(test_state, keyboard_opts));
+ try_status(km_kbp_state_options_update(test_state, keyboard_opts));
delete [] keyboard_opts;
}
@@ -304,7 +302,7 @@ int run_test(const std::string & source, const std::string & _compiled) {
try_status(km_kbp_process_event(test_state, p.vk, p.modifier_state));
for (auto act = km_kbp_state_action_items(test_state, nullptr); act->type != KM_KBP_IT_END; act++) {
- apply_action(test_state, *act, text_store);
+ apply_action(test_state, *act, text_store, options);
}
}
@@ -319,9 +317,9 @@ int run_test(const std::string & source, const std::string & _compiled) {
try_status(km_kbp_context_items_to_utf16(citems, buf, &n));
km_kbp_context_items_dispose(citems);
- std::cout << "expected: " << utf16_to_utf8(expected) << std::endl;
- std::cout << "text store: " << utf16_to_utf8(text_store) << std::endl;
- std::cout << "result: " << utf16_to_utf8(buf) << std::endl;
+ std::cout << "expected: " << expected << std::endl;
+ std::cout << "text store: " << text_store << std::endl;
+ std::cout << "result: " << buf << std::endl;
// Compare internal context with expected result
if (buf != expected) return __LINE__;
@@ -330,16 +328,18 @@ int run_test(const std::string & source, const std::string & _compiled) {
if (text_store != expected) return __LINE__;
// Test resultant options
- // TODO: test also KM_KBP_IT_PERSIST_OPT and KM_KBP_IT_RESET_OPT actions
-
for (auto it = options.begin(); it != options.end(); it++) {
- if (it->type != KOT_OUTPUT) continue;
- std::cout << "output option-key: " << utf16_to_utf8(it->key) << " expected: " << utf16_to_utf8(it->value);
- km_kbp_cp const *value;
- try_status(km_kbp_options_lookup(test_state, KM_KBP_OPT_KEYBOARD, it->key.c_str(), &value));
- std::cout << " actual: " << utf16_to_utf8(value) << std::endl;
- if (it->value.compare(value) != 0) return __LINE__;
- km_kbp_cp_dispose(value);
+ if (it->type == KOT_OUTPUT) {
+ std::cout << "output option-key: " << it->key << " expected: " << it->value;
+ km_kbp_cp const *value;
+ try_status(km_kbp_state_option_lookup(test_state, KM_KBP_OPT_KEYBOARD, it->key.c_str(), &value));
+ std::cout << " actual: " << value << std::endl;
+ if (it->value.compare(value) != 0) return __LINE__;
+ }
+ else if (it->type == KOT_SAVED) {
+ std::cout << "persisted option-key: " << it->key << " expected: " << it->value << " actual: " << it->saved_value << std::endl;
+ if (it->value.compare(it->saved_value) != 0) return __LINE__;
+ }
}
// Destroy them
@@ -356,11 +356,11 @@ std::u16string parse_source_string(std::string const & s) {
p++;
km_kbp_usv v;
assert(p != s.end());
- if (*p == 'u') {
+ if (*p == 'u' || *p == 'U') {
// Unicode value
p++;
size_t n;
- std::string s1 = s.substr(p - s.begin(), 6);
+ std::string s1 = s.substr(p - s.begin(), 8);
v = std::stoul(s1, &n, 16);
assert(v >= 0x20 && v <= 0x10FFFF);
p += n-1;
@@ -408,16 +408,18 @@ bool is_token(const std::string token, std::string &line) {
return false;
}
-int load_source(const std::string & path, std::string & keys, std::u16string & expected, std::u16string & context, kmx_options &options, bool &expected_beep) {
+int load_source(const km::kbp::path & path, std::string & keys, std::u16string & expected, std::u16string & context, kmx_options &options, bool &expected_beep) {
const std::string s_keys = "c keys: ",
s_expected = "c expected: ",
s_context = "c context: ",
s_option = "c option: ",
- s_option_expected = "c expected option: ";
+ s_option_expected = "c expected option: ",
+ s_option_saved = "c saved option: ";
// Parse out the header statements in file.kmn that tell us (a) environment, (b) key sequence, (c) start context, (d) expected result
- std::ifstream kmn(path);
+ std::ifstream kmn(path.native());
if (!kmn.good()) {
+ std::cerr << "could not open file: " << path << std::endl;
return __LINE__;
}
std::string line;
@@ -446,6 +448,9 @@ int load_source(const std::string & path, std::string & keys, std::u16string & e
else if (is_token(s_option_expected, line)) {
if (!parse_option_string(line, options, KOT_OUTPUT)) return __LINE__;
}
+ else if (is_token(s_option_saved, line)) {
+ if (!parse_option_string(line, options, KOT_SAVED)) return __LINE__;
+ }
}
if (keys == "") {
diff --git a/tests/unit/kmx/meson.build b/tests/unit/kmx/meson.build
index 47e4ba5..f4df7c8 100644
--- a/tests/unit/kmx/meson.build
+++ b/tests/unit/kmx/meson.build
@@ -46,13 +46,17 @@ tests = [
'029 - beep',
'030 - multiple groups',
- '031 - caps lock'
+ '031 - caps lock',
# '032 - caps control', # TODO: support capsononly, capsalwaysoff, shiftfreescaps
# '033 - caps alwyas off' # TODO: support capsononly, capsalwaysoff, shiftfreescaps
+ '034 - options double set reset',
+ '035 - options double set staged',
+ '036 - options - double reset staged',
+ '037 - options - double reset',
+ '038 - punctkeys',
+ '039 - generic ctrlalt'
]
-# todo: if kmcomp is not found, use the .kmx in the source folder
-# therefore make kmcomp compile to the source folder and add to repo?
if build_machine.system() == 'windows'
kmcomp_cmd = [kmcomp]
else