Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): new actions APIs #9828

Merged
merged 12 commits into from
Oct 25, 2023
154 changes: 154 additions & 0 deletions core/include/keyman/keyman_core_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,38 @@ km_core_context_items_to_utf8(km_core_context_item const *item,
char *buf,
size_t *buf_size);

/*
```
### `km_core_context_items_to_utf32`
##### Description:
Convert a context item array into a UTF-32 encoded string placing it into
the supplied buffer of specified size, and return the number of codepoints
actually used in the conversion. If null is passed as the buffer the
number codepoints required is returned. This will strip markers from the
context during the conversion.
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
##### Return status:
- `KM_CORE_STATUS_OK`: On success.
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
- `KM_CORE_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_core_context_item`.
Must be terminated with a type of `KM_CORE_CT_END`.
- __buf__: A pointer to the buffer to place the UTF-32 string into.
May be null to request size calculation.
- __buf_size__: a pointer to the result variable:
The size of the supplied buffer in codepoints if `buf` is given.
On return will be the size required if `buf` is null.
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved

```c
*/
KMN_API
km_core_status
km_core_context_items_to_utf32(km_core_context_item const *item,
km_core_usv *buf,
size_t *buf_size);

/*
```
### `km_core_context_items_dispose`
Expand Down Expand Up @@ -526,6 +558,128 @@ enum km_core_action_type {
KM_CORE_IT_MAX_TYPE_ID
};

/*
```
### Actions
This structure provides 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.

This API replaces the Action items APIs, which is now deprecated and will be
removed in the future.
```c
*/

typedef struct {
// number of codepoints (not codeunits!) to delete from app context, 0+
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
int delete_back;

// null-term string of characters to insert into document
km_core_usv* output;

// list of options to persist, terminated with KM_CORE_OPTIONS_END
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
km_core_option_item* persist_options;

// issue a beep
bool do_alert;

// emit the input keystroke to the application, unmodified?
bool emit_keystroke;

// -1=unchanged, 0=off, 1=on
int new_caps_lock_state;
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
} km_core_actions;

/*
```
### `km_core_state_get_actions`
##### Description:
Returns a pointer to an actions object which details all the actions
that the Platform layer must take after a keystroke. The `delete_back`
action must be performed before the `output` action, but the other
actions may be performed in any order.
##### Return:
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
A pointer to a `km_core_actions` object, which must be freed with
`km_core_actions_dispose`.
##### Parameters:
- __state__: An opaque pointer to a state object.

```c
*/
KMN_API
km_core_actions*
km_core_state_get_actions(
km_core_state const *state
);

/*
```
### `km_core_actions_dispose`
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
##### Description:
Free the allocated memory belonging to an actions object previously
returned by `km_core_state_get_actions`.
##### Parameters:
- __actions__: A pointer to the actions object to be disposed of.

```c
*/
KMN_API
km_core_status
km_core_actions_dispose(
km_core_actions* actions
);

/*
```
### `km_core_state_context_validate`
##### Description:
Determines if the input context has changed, and if so, resets
the internal cached context (including markers), to the new
context string.

mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
This and `km_core_state_context_invalidate` will replace existing Core context
APIs.

##### Parameters:
- __state__: An opaque pointer to a state object.
- __application_context__: A pointer to an null-terminated `km_core_cp` string
representing the current context from the application.
##### Return status:
- `KM_CORE_STATUS_OK`: On success.
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved

```c
*/
KMN_API
km_core_status
km_core_state_context_validate(
km_core_state *state,
km_core_cp const *application_context
);

/*
```
### `km_core_state_context_invalidate`
##### Description:
Flushes the internal cached context for the state.
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved

This and `km_core_state_context_validate` will replace existing Core context
APIs.

##### Parameters:
- __state__: An opaque pointer to a state object.
##### Return status:
- `KM_CORE_STATUS_OK`: On success.
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.

```c
*/
KMN_API
km_core_status
km_core_state_context_invalidate(
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
km_core_state *state
);

/*
```
Expand Down
140 changes: 140 additions & 0 deletions core/src/action.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
Copyright: © 2023 SIL International.
Description: Implementation of the action API functions using internal
data structures and functions.
Create Date: 23 Oct 2023
Authors: Marc Durdin (MCD)
History: 23 Oct 2023 - MCD - Initial implementation from #9720
*/
#include <cassert>
#include <algorithm>
#include <sstream>

#include <keyman/keyman_core_api.h>

#include "action.hpp"
#include "state.hpp"
#include "option.hpp"

km_core_actions * km::kbp::action_items_to_actions(
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
km_core_action_item const *action_items
) {
assert(action_items != nullptr);
if(action_items == nullptr) {
return nullptr;
}

km_core_status status = KM_CORE_STATUS_OK;

km_core_actions* actions = new km_core_actions;
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved

// Set actions defaults
std::vector<km_core_context_item> output;
std::vector<km_core_option_item> options;
actions->delete_back = 0;
actions->output = nullptr;
actions->persist_options = nullptr;
actions->do_alert = false;
actions->emit_keystroke = false;
actions->new_caps_lock_state = -1;

for (; action_items->type != KM_CORE_IT_END; ++action_items) {
assert(action_items->type < KM_CORE_IT_MAX_TYPE_ID);

switch(action_items->type) {
case KM_CORE_IT_ALERT:
actions->do_alert = true;
break;
case KM_CORE_IT_BACK:
switch(action_items->backspace.expected_type) {
case KM_CORE_BT_UNKNOWN:
// this is equivalent to emit_keystroke, because the only time we
// are allowed to do an unknown bksp is when a bksp is passed in
actions->emit_keystroke = true;
break;
case KM_CORE_BT_CHAR:
if(output.empty()) {
actions->delete_back++;
} else {
auto last_context_item = output.back();
output.pop_back();
assert(last_context_item.type == KM_CORE_CT_CHAR);
assert(last_context_item.character == action_items->backspace.expected_value);
}
break;
case KM_CORE_BT_MARKER:
if(output.empty()) {
// deleting a marker has no effect on the application
} else {
auto last_context_item = output.back();
output.pop_back();
assert(last_context_item.type == KM_CORE_CT_MARKER);
assert(last_context_item.marker == action_items->backspace.expected_value);
}
break;
default:
assert(false);
}
break;
case KM_CORE_IT_CAPSLOCK:
actions->new_caps_lock_state = action_items->capsLock;
break;
case KM_CORE_IT_CHAR:
output.push_back({KM_CORE_CT_CHAR,{0},action_items->character});
break;
case KM_CORE_IT_EMIT_KEYSTROKE:
actions->emit_keystroke = true;
break;
case KM_CORE_IT_INVALIDATE_CONTEXT:
// no-op
break;
case KM_CORE_IT_MARKER:
{
km_core_context_item ci = {0};
ci.type = KM_CORE_CT_MARKER;
ci.marker = action_items->marker;
output.push_back(ci);
}
break;
case KM_CORE_IT_PERSIST_OPT:
// TODO: lowpri: replace existing item if already present in options vector?
options.push_back(km::kbp::option(
static_cast<km_core_option_scope>(action_items->option->scope),
action_items->option->key,
action_items->option->value
));
break;
default:
assert(false);
}
}

output.push_back({KM_CORE_CT_END});

// Strip the markers from the output to be passed to the app

size_t buf_size;

mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
if((status = km_core_context_items_to_utf32(output.data(), nullptr, &buf_size)) != KM_CORE_STATUS_OK) {
delete actions;
return nullptr;
}

actions->output = new km_core_usv[buf_size];

if((status = km_core_context_items_to_utf32(output.data(), actions->output, &buf_size)) != KM_CORE_STATUS_OK) {
delete[] actions->output;
delete actions;
return nullptr;
}

// Create an array of the persisted options

if(!options.empty()) {
options.push_back(KM_CORE_OPTIONS_END);
actions->persist_options = new km_core_option_item[options.size()];
std::copy(options.begin(), options.end(), actions->persist_options);
}

return actions;
}
20 changes: 20 additions & 0 deletions core/src/action.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright: © 2023 SIL International.
Description: Internal actions methods for Keyman Core
Create Date: 23 Oct 2023
Authors: Marc Durdin (MCD)
History: 23 Oct 2023 - MCD - Initial implementation
*/

#pragma once

#include <keyman/keyman_core_api.h>

namespace km {
namespace kbp
{
km_core_actions* action_items_to_actions(
mcdurdin marked this conversation as resolved.
Show resolved Hide resolved
km_core_action_item const *action_items
);
} // namespace kbp
} // namespace km
64 changes: 64 additions & 0 deletions core/src/km_core_action_api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright: © 2023 SIL International.
Description: Implementation of the action API functions using internal
data structures and functions.
Create Date: 23 Oct 2023
Authors: Marc Durdin (MCD)
History: 23 Oct 2023 - MCD - Initial implementation.
*/
#include <cassert>
#include <algorithm>
#include <sstream>

#include <keyman/keyman_core_api.h>
#include "jsonpp.hpp"

#include "processor.hpp"
#include "state.hpp"
#include "action.hpp"

using namespace km::kbp;

km_core_actions* km_core_state_get_actions(
km_core_state const *state
) {
assert(state);
if(!state) {
return nullptr;
}

km_core_actions* actions = nullptr;
auto action_items = km_core_state_action_items(state, nullptr);
if(!action_items) {
return nullptr;
}

actions = action_items_to_actions(action_items);
return actions;
}

km_core_status km_core_actions_dispose(
km_core_actions* actions
) {
if(actions == nullptr) {
return KM_CORE_STATUS_OK;
}

if(actions->output) {
delete[] actions->output;
}

if(actions->persist_options) {
for(auto option = actions->persist_options; option->scope; option++) {
delete[] option->key;
delete[] option->value;
}
delete[] actions->persist_options;
}

delete actions;

return KM_CORE_STATUS_OK;
}


Loading