feat(common/core): Implement capsAlwaysOff system store

This change implements the core part in a way that makes the tests
pass. This still needs corresponding changes in the engine.
This commit is contained in:
Eberhard Beilharz 2021-07-06 18:54:59 +02:00
parent 84aa50b82f
commit 0eca43d4ea
No known key found for this signature in database
GPG key ID: 64A39A9E98B53105
34 changed files with 566 additions and 419 deletions

View file

@ -9,3 +9,5 @@ wasm.build
TIER.md
VERSION.md
shellHelperFunctions.sh
src/rust/keyman_keyboard_processor/target/

View file

@ -478,6 +478,7 @@ typedef struct {
uintptr_t marker; // MARKER type
km_kbp_option_item const * option; // OPT types
km_kbp_usv character; // CHAR type
uint8_t capsLock; // CAPSLOCK type, 1 to turn on, 0 to turn off
};
} km_kbp_action_item;
@ -494,6 +495,7 @@ enum km_kbp_action_type {
// 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_CAPSLOCK = 8, // Enable or disable capsLock
KM_KBP_IT_MAX_TYPE_ID
};
@ -940,7 +942,8 @@ KMN_API
km_kbp_status
km_kbp_process_event(km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state);
uint16_t modifier_state,
uint8_t is_key_down);
#if defined(__cplusplus)
} // extern "C"

View file

@ -12,15 +12,15 @@
#include "processor.hpp"
#include "state.hpp"
km_kbp_status
km_kbp_status
km_kbp_process_event(km_kbp_state *state,
km_kbp_virtual_key vk, uint16_t modifier_state)
{
return state->processor().process_event(state, vk, modifier_state);
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down) {
return state->processor().process_event(state, vk, modifier_state, is_key_down);
}
km_kbp_attr const *
km_kbp_attr const *
km_kbp_get_engine_attrs(km_kbp_state const *state)
{
return &state->processor().attributes();

View file

@ -2,57 +2,80 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include <keyman/keyboardprocessor_consts.h>
#include <kmx/kmx_processevent.h>
using namespace km::kbp;
using namespace kmx;
void KMX_ProcessEvent::ResetCapsLock(void)
{
DebugLog("ResetCapsLock: enter");
/**
* Set caps lock state.
* Called by ProcessEvent.
* May update `modifiers` to add or remove `CAPITALFLAG` according to capsLockOn
* parameter. May queue actions to set caps lock state.
*
* @param[in,out] modifiers The modifier key bitmap
* @param capsLockOn The desired caps lock state
*/
void KMX_ProcessEvent::SetCapsLock(KMX_DWORD &modifiers, KMX_BOOL capsLockOn) {
KMX_BOOL capsLockCurrentlyOn = IsCapsLockOn(modifiers);
if (capsLockCurrentlyOn == capsLockOn) {
return;
}
if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
DebugLog("Caps lock is %s, switching %s", capsLockCurrentlyOn ? "on" : "off", capsLockOn ? "on" : "off");
m_actions.QueueAction(QIT_CAPSLOCK, capsLockOn);
if (capsLockOn) {
modifiers |= CAPITALFLAG;
} else {
modifiers &= ~CAPITALFLAG;
}
}
/**
* Deal with CapsAlwaysOff store option and turns caps lock off if necessary.
* Called by ProcessEvent.
* May update `modifiers` to add or remove `CAPITALFLAG` according to keyboard
* requirements. May queue actions to set caps lock state.
*
* @param[in,out] modifiers The modifier key bitmap
*/
void KMX_ProcessEvent::ResetCapsLock(KMX_DWORD &modifiers) {
if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF) {
DebugLog("ResetCapsLock: caps lock should be always off");
if (m_environment.capsLock())
{
DebugLog("ResetCapsLock: caps lock is on, switching off caps lock");
m_actions.QueueAction(QIT_CAPSLOCK, 0);
}
}
DebugLog("ResetCapsLock: exit");
}
void KMX_ProcessEvent::KeyCapsLockPress(KMX_BOOL FIsUp)
{
if (m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY)
{
if(FIsUp && !m_environment.capsLock())
{
m_actions.QueueAction(QIT_CAPSLOCK, 1);
}
}
else if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
if(!FIsUp && m_environment.capsLock())
{
m_actions.QueueAction(QIT_CAPSLOCK, 0);
}
SetCapsLock(modifiers, FALSE);
}
}
void KMX_ProcessEvent::KeyShiftPress(KMX_BOOL FIsUp)
{
if(!m_environment.capsLock()) return;
if (m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS)
{
if(!FIsUp)
{
m_actions.QueueAction(QIT_CAPSLOCK, 0);
}
/**
* Deal with CapsLock store options on CapsLock key press. Called by ProcessEvent.
* May update `modifiers` to add or remove `CAPITALFLAG` according to keyboard
* requirements. May queue actions to set caps lock state.
*
* @param[in,out] modifiers The modifier key bitmap
* @param isKeyDown TRUE if this is called on KeyDown event, FALSE if
* called on KeyUp event
*/
void KMX_ProcessEvent::KeyCapsLockPress(KMX_DWORD &modifiers, KMX_BOOL isKeyDown) {
if (m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY && !isKeyDown) {
SetCapsLock(modifiers, TRUE);
} else if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF && isKeyDown) {
SetCapsLock(modifiers, FALSE);
}
}
/**
* Deal with CapsLock store options on Shift key press. Called by ProcessEvent.
* May update `modifiers` to remove `CAPITALFLAG` according to keyboard
* requirements. May queue actions to reset caps lock state.
*
* @param[in,out] modifiers The modifier key bitmap
* @param isKeyDown TRUE if this is called on KeyDown event, FALSE if
* called on KeyUp event
*/
void KMX_ProcessEvent::KeyShiftPress(KMX_DWORD &modifiers, KMX_BOOL isKeyDown) {
if (m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS && isKeyDown) {
SetCapsLock(modifiers, FALSE);
}
}

View file

@ -16,7 +16,6 @@ namespace {
*DEFAULT_BASELAYOUT = u"kbdus.dll",
*DEFAULT_BASELAYOUTALT = u"en-US",
*DEFAULT_SIMULATEALTGR = u"0",
*DEFAULT_CAPSLOCK = u"0",
*DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT = u"0";
}
@ -25,7 +24,6 @@ KMX_Environment::KMX_Environment() {
Set(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
Set(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
Set(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
Set(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
Set(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
}
@ -46,9 +44,6 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const {
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";
}
@ -74,9 +69,6 @@ void KMX_Environment::Set(std::u16string const & key, std::u16string const & val
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";
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) {
_baseLayoutGivesCtrlRAltForRAlt = value == u"1";
}

View file

@ -12,7 +12,6 @@ class KMX_Environment {
private:
KMX_BOOL _simulateAltGr, _baseLayoutGivesCtrlRAltForRAlt;
std::u16string _baseLayout, _baseLayoutAlt;
KMX_BOOL _capsLock;
std::u16string _platform;
void InitOption(
std::vector<option> & default_env,
@ -23,7 +22,6 @@ public:
void Set(std::u16string const & key, std::u16string const & value);
char16_t const * LookUp(std::u16string const & key) const;
KMX_BOOL capsLock() const noexcept { return _capsLock; }
KMX_BOOL simulateAltGr() const noexcept { return _simulateAltGr; }
KMX_BOOL baseLayoutGivesCtrlRAltForRAlt() const noexcept { return _baseLayoutGivesCtrlRAltForRAlt; }
const std::u16string & baseLayout() const noexcept { return _baseLayout; }

View file

@ -4,6 +4,7 @@
*/
#include "kmx_processevent.h"
#include "state.hpp"
#include <keyman/keyboardprocessor_consts.h>
using namespace km::kbp;
using namespace kmx;
@ -60,19 +61,26 @@ char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
}
/*
* KMX_BOOL ProcessEvent();
* Process Events
*
* Parameters: none
* ProcessEvent organizes the messages and gives them to the appropriate routines to
* process, and checks the state of Windows for the keyboard handling.
* Called by FilterFunc.
*
* Returns: TRUE if keystroke should be eaten
* @param state A pointer to the state object.
* @param vkey A virtual key to be processed.
* @param modifiers The combinations of modifier keys set at the time vkey was pressed,
* bitmask from the km_kbp_modifier_state enum.
* @param isKeyDown TRUE if this is called on KeyDown event, FALSE if called on KeyUp event
*
* Called by: FilterFunc
*
* ProcessEvent organizes the messages and gives them to the appropriate routines to
* process, and checks the state of Windows for the keyboard handling.
* @return TRUE if keystroke should be eaten
*/
KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers)
{
KMX_BOOL KMX_ProcessEvent::ProcessEvent(
km_kbp_state *state,
KMX_UINT vkey,
KMX_DWORD modifiers,
KMX_BOOL isKeyDown
) {
LPKEYBOARD kbd = m_keyboard.Keyboard;
m_kbp_state = state;
@ -91,8 +99,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_
state->debug_items().push_end(m_actions.Length(), 0);
}
if (m_environment.capsLock())
modifiers |= CAPITALFLAG;
ResetCapsLock(modifiers);
m_state.vkey = vkey;
m_state.charCode = VKeyToChar(modifiers, vkey);
@ -101,6 +108,21 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
DebugLog("Non-Unicode keyboards are not supported.");
m_kbp_state = nullptr;
return FALSE;
}
switch (vkey) {
case KM_KBP_VKEY_CAPS:
KeyCapsLockPress(modifiers, isKeyDown);
break;
case KM_KBP_VKEY_SHIFT:
KeyShiftPress(modifiers, isKeyDown);
break;
}
if (!isKeyDown) {
m_kbp_state = nullptr;
return FALSE;
}
@ -273,7 +295,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
if(m_debug_items) {
m_debug_items->push_nomatch_exit(m_actions.Length(), gp);
}
}
}
else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys)
{
@ -355,7 +377,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
if(m_debug_items) {
m_debug_items->push_match_enter(m_actions.Length(), gp);
}
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
if(m_debug_items) {
m_debug_items->push_match_exit(m_actions.Length(), gp);
}

View file

@ -75,9 +75,11 @@ private:
/* Caps Lock and modifier management */
void ResetCapsLock(void);
void KeyCapsLockPress(KMX_BOOL FIsUp);
void KeyShiftPress(KMX_BOOL FIsUp);
KMX_BOOL IsCapsLockOn(KMX_DWORD modifiers);
void SetCapsLock(KMX_DWORD &modifiers, KMX_BOOL capsLockOn);
void ResetCapsLock(KMX_DWORD &modifiers);
void KeyCapsLockPress(KMX_DWORD &modifiers, KMX_BOOL isKeyDown);
void KeyShiftPress(KMX_DWORD &modifiers, KMX_BOOL isKeyDown);
KMX_BOOL IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift);
@ -86,7 +88,7 @@ public:
~KMX_ProcessEvent();
KMX_BOOL Load(km_kbp_path_name keyboardName);
KMX_BOOL ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers); // returns FALSE on error or key not matched
KMX_BOOL ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers, KMX_BOOL isKeyDown); // returns FALSE on error or key not matched
KMX_Actions *GetActions();
KMX_Context *GetContext();
@ -97,6 +99,10 @@ public:
LPINTKEYBOARDINFO GetKeyboard();
};
inline KMX_BOOL KMX_ProcessEvent::IsCapsLockOn(KMX_DWORD modifiers) {
return modifiers & CAPITALFLAG ? TRUE : FALSE;
}
/* Global Constants */
struct char_to_vkey {

View file

@ -2,188 +2,195 @@
#include "state.hpp"
#include "kmx/kmx_processor.hpp"
namespace km {
namespace kbp
using namespace km::kbp;
using namespace kmx;
km_kbp_status kmx_processor::validate() const {
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
}
kmx_processor::kmx_processor(kbp::path p) {
p.replace_extension(".kmx");
_valid = bool(_kmx.Load(p.c_str()));
if (!_valid)
return;
keyboard_attributes::options_store defaults;
_kmx.GetOptions()->Init(defaults);
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);
km_kbp_status kmx_processor::validate() const {
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
}
_attributes = keyboard_attributes(static_cast<std::u16string>(p.stem()),
std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
}
kmx_processor::kmx_processor(kbp::path p)
{
p.replace_extension(".kmx");
_valid = bool(_kmx.Load(p.c_str()));
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;
}
if (!_valid)
return;
return pValue ? pValue : nullptr;
}
keyboard_attributes::options_store defaults;
_kmx.GetOptions()->Init(defaults);
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()->Set(key, value);
persisted_store()[key] = value;
break;
case KM_KBP_OPT_ENVIRONMENT:
_kmx.GetEnvironment()->Set(key, value);
break;
default:
return option();
break;
}
for (auto const & opt: defaults)
{
if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
persisted_store()[opt.key] = opt.value;
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,
uint8_t is_key_down
) {
// Construct a context buffer from the items
std::u16string ctxt;
auto cp = state->context();
for (auto c = cp.begin(); c != cp.end(); c++) {
switch (c->type) {
case KM_KBP_CT_CHAR:
if (Uni_IsSMP(c->character)) {
ctxt += Uni_UTF32ToSurrogate1(c->character);
ctxt += Uni_UTF32ToSurrogate2(c->character);
} else {
ctxt += (km_kbp_cp)c->character;
}
// 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);
break;
case KM_KBP_CT_MARKER:
assert(c->marker > 0);
ctxt += UC_SENTINEL;
ctxt += CODE_DEADKEY;
ctxt += c->marker;
break;
}
}
_kmx.GetContext()->Set(ctxt.c_str());
_kmx.GetActions()->ResetQueue();
state->actions().clear();
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;
}
if (!_kmx.ProcessEvent(state, vk, modifier_state, is_key_down)) {
// We need to output the default keystroke
state->actions().push_emit_keystroke();
}
return pValue ? pValue : nullptr;
}
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()->Set(key, value);
persisted_store()[key] = value;
break;
case KM_KBP_OPT_ENVIRONMENT:
_kmx.GetEnvironment()->Set(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) {
// Construct a context buffer from the items
std::u16string ctxt;
auto cp = state->context();
for (auto c = cp.begin(); c != cp.end(); c++) {
switch (c->type) {
case KM_KBP_CT_CHAR:
if (Uni_IsSMP(c->character)) {
ctxt += Uni_UTF32ToSurrogate1(c->character);
ctxt += Uni_UTF32ToSurrogate2(c->character);
}
else {
ctxt += (km_kbp_cp) c->character;
}
break;
case KM_KBP_CT_MARKER:
assert(c->marker > 0);
ctxt += UC_SENTINEL;
ctxt += CODE_DEADKEY;
ctxt += c->marker;
break;
for (auto i = 0; i < _kmx.GetActions()->Length(); i++) {
auto a = _kmx.GetActions()->Get(i);
switch (a.ItemType) {
case QIT_CAPSLOCK:
state->actions().push_capslock(a.dwData);
break;
case QIT_VKEYDOWN:
case QIT_VKEYUP:
case QIT_VSHIFTDOWN:
case QIT_VSHIFTUP:
// TODO: eliminate??
break;
case QIT_CHAR:
state->context().push_character(a.dwData);
state->actions().push_character(a.dwData);
break;
case QIT_DEADKEY:
state->context().push_marker(a.dwData);
state->actions().push_marker(a.dwData);
break;
case QIT_BELL:
state->actions().push_alert();
break;
case QIT_BACK:
switch (a.dwData) {
case BK_DEFAULT:
// 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().push_backspace();
break;
case BK_DEADKEY:
// This only happens if we know we have context to delete. Last item must be a deadkey
assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER);
if (!state->context().empty())
state->context().pop_back();
break;
case BK_BACKSPACE:
// User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER)
state->context().pop_back();
if (!state->context().empty()) {
state->context().pop_back();
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER)
state->context().pop_back();
}
// Even if context is empty, we send the backspace event, because we may not
// know the context.
state->actions().push_backspace();
break;
default:
assert(false);
}
_kmx.GetContext()->Set(ctxt.c_str());
_kmx.GetActions()->ResetQueue();
state->actions().clear();
if (!_kmx.ProcessEvent(state, vk, modifier_state)) {
// We need to output the default keystroke
state->actions().push_emit_keystroke();
}
for (auto i = 0; i < _kmx.GetActions()->Length(); i++) {
auto a = _kmx.GetActions()->Get(i);
switch (a.ItemType) {
case QIT_CAPSLOCK:
//TODO: add Caps Event
//dwData = 0 == off; 1 == on
//state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CAPSLOCK, {0,}, {0} });
break;
case QIT_VKEYDOWN:
case QIT_VKEYUP:
case QIT_VSHIFTDOWN:
case QIT_VSHIFTUP:
//TODO: eliminate??
break;
case QIT_CHAR:
state->context().push_character(a.dwData);
state->actions().push_character(a.dwData);
break;
case QIT_DEADKEY:
state->context().push_marker(a.dwData);
state->actions().push_marker(a.dwData);
break;
case QIT_BELL:
state->actions().push_alert();
break;
case QIT_BACK:
switch (a.dwData) {
case BK_DEFAULT:
// 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().push_backspace();
break;
case BK_DEADKEY:
// This only happens if we know we have context to delete. Last item must be a deadkey
assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER);
if (!state->context().empty()) state->context().pop_back();
break;
case BK_BACKSPACE:
// User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
if (!state->context().empty()) {
state->context().pop_back();
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
}
// Even if context is empty, we send the backspace event, because we may not
// know the context.
state->actions().push_backspace();
break;
default:
assert(false);
}
break;
case QIT_INVALIDATECONTEXT:
state->actions().push_invalidate_context();
break;
default:
//std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl;
assert(false);
}
}
state->actions().commit();
return KM_KBP_STATUS_OK;
break;
case QIT_INVALIDATECONTEXT:
state->actions().push_invalidate_context();
break;
default:
// std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl;
assert(false);
}
}
constexpr km_kbp_attr const engine_attrs = {
256,
KM_KBP_LIB_CURRENT,
KM_KBP_LIB_AGE,
KM_KBP_LIB_REVISION,
KM_KBP_TECH_KMX,
"SIL International"
};
state->actions().commit();
km_kbp_attr const & kmx_processor::attributes() const {
return engine_attrs;
}
return KM_KBP_STATUS_OK;
}
} // namespace kbp
} // namespace km
constexpr km_kbp_attr const engine_attrs = {
256,
KM_KBP_LIB_CURRENT,
KM_KBP_LIB_AGE,
KM_KBP_LIB_REVISION,
KM_KBP_TECH_KMX,
"SIL International"
};
km_kbp_attr const & kmx_processor::attributes() const {
return engine_attrs;
}

View file

@ -24,17 +24,30 @@ namespace kbp
kmx::KMX_ProcessEvent _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_status
process_event(
km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down
) 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;
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

View file

@ -103,16 +103,24 @@ namespace km {
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)
{
km_kbp_status
mock_processor::process_event(
km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down
) {
assert(state);
if (!state)
return KM_KBP_STATUS_INVALID_ARGUMENT;
try
{
// At the start of every process_event allways clear the action_items
if (!is_key_down) {
// TODO: Implement caps lock handling
return KM_KBP_STATUS_OK;
}
try {
// At the start of every process_event always clear the action_items
state->actions().clear();
switch (vk)
@ -162,15 +170,12 @@ namespace km {
}
state->actions().commit();
}
catch (std::bad_alloc &)
{
} catch (std::bad_alloc &) {
state->actions().clear();
return KM_KBP_STATUS_NO_MEM;
}
return KM_KBP_STATUS_OK;
}
km_kbp_attr const & mock_processor::attributes() const {

View file

@ -26,20 +26,30 @@ namespace kbp
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;
km_kbp_status
process_event(
km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down
) 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;
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;
option
update_option(
km_kbp_option_scope,
std::u16string const & key,
std::u16string const & value
) override;
};
class null_processor : public mock_processor {

View file

@ -36,18 +36,29 @@ namespace kbp
auto & persisted_store() const noexcept { return _persisted; }
auto & persisted_store() noexcept { return _persisted; }
virtual km_kbp_status process_event(km_kbp_state *,
km_kbp_virtual_key,
uint16_t modifier_state) = 0;
virtual km_kbp_status
process_event(
km_kbp_state *,
km_kbp_virtual_key,
uint16_t modifier_state,
uint8_t is_key_down
) = 0;
virtual km_kbp_attr const & attributes() const = 0;
virtual km_kbp_status validate() const = 0;
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;
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);
};

View file

@ -107,7 +107,7 @@
return KM_KBP_STATUS_OK;
*/
pub fn rust_mock_process_event(_vk: u16, _modifier: u16) -> u32 {
pub fn rust_mock_process_event(_vk: u16, _modifier: u16, _is_key_down: u8) -> u32 {
return 0; //KM_KBP_STATUS_OK
}
@ -117,6 +117,6 @@ mod tests {
#[test]
fn test_rust_mock_process_event() {
assert_eq!(0, rust_mock_process_event(0, 0));
assert_eq!(0, rust_mock_process_event(0, 0, 0));
}
}

View file

@ -1,4 +1,4 @@
#[no_mangle]
pub extern "C" fn rust_mock_process_event(vk: u16, modifier: u16) -> u32 {
return keyman_keyboard_processor::rust_mock_process_event(vk, modifier);
pub extern "C" fn rust_mock_process_event(vk: u16, modifier: u16, is_key_down: u8) -> u32 {
return keyman_keyboard_processor::rust_mock_process_event(vk, modifier, is_key_down);
}

View file

@ -59,13 +59,15 @@ namespace km {
return option(scope, key, i->second);
}
extern "C" uint32_t rust_mock_process_event(uint16_t vk, uint16_t modifier);
extern "C" uint32_t rust_mock_process_event(uint16_t vk, uint16_t modifier, uint8_t is_key_down);
km_kbp_status rust_mock_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state)
{
km_kbp_status rust_mock_processor::process_event(km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down) {
(void)(state);
//km_kbd_rust_state rust_state(state);
return rust_mock_process_event(vk, modifier_state);
// km_kbd_rust_state rust_state(state);
return rust_mock_process_event(vk, modifier_state, is_key_down);
}
km_kbp_attr const & rust_mock_processor::attributes() const {

View file

@ -24,20 +24,29 @@ namespace kbp
rust_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;
km_kbp_status
process_event(
km_kbp_state *state,
km_kbp_virtual_key vk,
uint16_t modifier_state,
uint8_t is_key_down
) 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;
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;
option
update_option(
km_kbp_option_scope,
std::u16string const & key,
std::u16string const & value
) override;
};
class rust_null_processor : public rust_mock_processor {

View file

@ -2,6 +2,6 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[no_mangle]
pub extern "C" fn rust_mock_process_event(vk: u16, modifier: u16) -> u32 {
return keyman_keyboard_processor::rust_mock_process_event(vk, modifier);
pub extern "C" fn rust_mock_process_event(vk: u16, modifier: u16, is_key_down: u8) -> u32 {
return keyman_keyboard_processor::rust_mock_process_event(vk, modifier, is_key_down);
}

View file

@ -7,6 +7,7 @@
#include "state.hpp"
#include "processor.hpp"
#include <keyman/keyboardprocessor_consts.h>
using namespace km::kbp;
@ -26,6 +27,13 @@ void actions::push_persist(option const &&opt) {
emplace_back(std::move(ai));
}
void actions::push_capslock(bool turnOn) {
assert(empty() || (!empty() && back().type != KM_KBP_IT_END));
km_kbp_action_item ai = {KM_KBP_IT_CAPSLOCK, {0,}, {0}};
ai.capsLock = turnOn;
emplace_back(std::move(ai));
}
state::state(km::kbp::abstract_processor & ap, km_kbp_option_item const *env)
: _processor(ap)
@ -34,7 +42,7 @@ state::state(km::kbp::abstract_processor & ap, km_kbp_option_item const *env)
//assert(env->scope == KM_KBP_OPT_ENVIRONMENT); // todo do we need scope? or can we find a way to eliminate it?
ap.update_option(env->scope
? km_kbp_option_scope(env->scope)
: KM_KBP_OPT_ENVIRONMENT,
: KM_KBP_OPT_ENVIRONMENT,
env->key,
env->value);
}

View file

@ -43,6 +43,7 @@ public:
void push_persist(option const &);
void push_persist(option const &&);
void push_emit_keystroke(km_kbp_virtual_key vk=0);
void push_capslock(bool);
void push_invalidate_context();
void commit();
@ -93,7 +94,6 @@ void actions::push_emit_keystroke(km_kbp_virtual_key vk) {
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));

View file

@ -229,7 +229,7 @@ void setup(const char *keyboard) {
void test_debugging_disabled() {
setup("000 - null keyboard.kmx");
try_status(km_kbp_state_debug_set(test_state, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_END}
}));
@ -242,7 +242,7 @@ void test_debugging_no_rule_match() {
setup("000 - null keyboard.kmx");
DEBUG_GROUP gp = {u"Main"};
try_status(km_kbp_state_debug_set(test_state, 1));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_S, KM_KBP_MODIFIER_SHIFT, 'S'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -261,7 +261,7 @@ void test_debugging_function_key() {
setup("000 - null keyboard.kmx");
DEBUG_GROUP gp = {u"Main"};
try_status(km_kbp_state_debug_set(test_state, 1));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F1, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F1, 0, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_F1, 0, 0}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -281,7 +281,7 @@ void test_basic_rule_matches() {
// 'DE' + 'F' > U+0E04 U+0E05 U+0E06
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_D, KM_KBP_MODIFIER_SHIFT));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_D, KM_KBP_MODIFIER_SHIFT, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_D, KM_KBP_MODIFIER_SHIFT, 'D'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -289,7 +289,7 @@ void test_basic_rule_matches() {
km_kbp_state_debug_item{KM_KBP_DEBUG_END, 0, {}, {u"", nullptr, nullptr, {}, 1}}, // action item will emit a default 'D'
}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_E, KM_KBP_MODIFIER_SHIFT));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_E, KM_KBP_MODIFIER_SHIFT, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_E, KM_KBP_MODIFIER_SHIFT, 'E'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -297,7 +297,7 @@ void test_basic_rule_matches() {
km_kbp_state_debug_item{KM_KBP_DEBUG_END, 0, {}, {u"", nullptr, nullptr, {}, 1}}, // action item will emit a default 'E'
}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F, KM_KBP_MODIFIER_SHIFT));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F, KM_KBP_MODIFIER_SHIFT, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_F, KM_KBP_MODIFIER_SHIFT, 'F'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -323,7 +323,7 @@ void test_multiple_groups() {
// '12' -> 'abc'
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_1, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_1, 0, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_1, 0, '1'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -342,7 +342,7 @@ void test_multiple_groups() {
km_kbp_state_debug_item{KM_KBP_DEBUG_END, 0, {}, {u"", nullptr, nullptr, {}, 3}}, // action item will emit a 'b'
}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_2, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_2, 0, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_2, 0, '2'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -378,7 +378,7 @@ void test_store_offsets() {
// 'ab' -> 'ex'
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_A, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_A, 0, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_A, 0, 'a'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},
@ -390,7 +390,7 @@ void test_store_offsets() {
km_kbp_state_debug_item{KM_KBP_DEBUG_END, 0, {}, {u"", nullptr, nullptr, {}, 4}}, // action item will emit a 'exay'
}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_B, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_B, 0, 1));
assert(debug_items(test_state, {
km_kbp_state_debug_item{KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_FLAG_UNICODE, {KM_KBP_VKEY_B, 0, 'b'}},
km_kbp_state_debug_item{KM_KBP_DEBUG_GROUP_ENTER, 0, {}, {u"", &gp}},

View file

@ -165,20 +165,20 @@ int main(int, char * [])
if (attrs->max_context < 16) return __LINE__;
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S,
KM_KBP_MODIFIER_SHIFT));
KM_KBP_MODIFIER_SHIFT, 1));
assert(action_items(test_state, {{KM_KBP_IT_CHAR, {0,}, {km_kbp_usv('S')}}}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_I,
KM_KBP_MODIFIER_SHIFT));
KM_KBP_MODIFIER_SHIFT, 1));
assert(action_items(test_state, {{KM_KBP_IT_CHAR, {0,}, {km_kbp_usv('I')}}}));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_L, 0));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_L, 0, 1));
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));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_BKSP, 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));
KM_KBP_MODIFIER_SHIFT, 1));
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));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_F2, 0, 1));
assert(action_items(test_state, {{KM_KBP_IT_PERSIST_OPT, {0,},
{uintptr_t(&expected_persist_opt)}}}));

View file

@ -1,8 +1,8 @@
c Description: Tests Caps Lock
c keys: [K_1][SHIFT K_2][K_A][SHIFT K_B]
c expected: pass.pass.pass.pass.
c option: &capsLock=1
c context:
c capsLock: 1
c context:
store(&version) '10.0'

View file

@ -1,8 +1,8 @@
c Description: Tests Caps Lock env set
c keys: [K_1][K_CAPS][K_2][SHIFT K_3][K_4][K_CAPS][K_5][K_CAPS][K_6]
c expected: pass.pass.pass.pass.pass.pass.
c option: &capsLock=0
c context:
c capsLock: 0
c context:
store(&version) '10.0'

View file

@ -1,8 +1,8 @@
c Description: Tests Caps Lock always off
c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3]
c expected: pass.pass.pass.
c option: &capsLock=0
c context:
c capsLock: 0
c context:
store(&version) '10.0'

View file

@ -0,0 +1,23 @@
c Description: Tests Caps Lock always off, entry to the test has Caps Lock
c on to test that it is then switched off on first keystroke event.
c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3]
c expected: pass.pass.pass.
c capsLock: 1
c context:
store(&version) '10.0'
store(&capsalwaysoff) '1'
begin Unicode > use(Main)
group(Main) using keys
+ [CAPS K_1] > 'fail.'
+ [NCAPS K_1] > 'pass.'
+ [CAPS K_2] > 'fail.'
+ [NCAPS K_2] > 'pass.'
+ [CAPS K_3] > 'fail.'
+ [NCAPS K_3] > 'pass.'

View file

@ -19,11 +19,18 @@ Following the comments are the normal rules of a Keyman keyboard.
c Description: Tests Caps Lock env set
```
- **option**: allows to put the system in a defined state. The example below turns
caps-lock off.
- **option**: allows to put the system in a defined state. The example below sets the platform.
```text
c option: &capsLock=0
c option: &platform=linux
```
- **capsLock**: allows to set the caps lock state. The example below turns
caps-lock on at the start of the test. `[K_CAPS]` in the **keys** comment
allows toggling of caps lock.
```text
c capsLock: 1
```
- **context**: allows to setup a string which serves as context

View file

@ -18,6 +18,7 @@
#include <type_traits>
#include <kmx/kmx_processevent.h>
#include <kmx/kmx_xstring.h>
#include "path.hpp"
#include "state.hpp"
@ -34,6 +35,7 @@
namespace
{
bool g_beep_found = false;
bool g_caps_lock_on = false;
struct key_event {
km_kbp_virtual_key vk;
@ -232,13 +234,63 @@ void apply_action(km_kbp_state const *, km_kbp_action_item const & act, std::u16
case KM_KBP_IT_EMIT_KEYSTROKE:
std::cout << "action: emit keystroke" << std::endl;
break;
case KM_KBP_IT_CAPSLOCK:
std::cout << "action: capsLock " << act.capsLock << std::endl;
g_caps_lock_on = act.capsLock;
break;
default:
assert(false); // NOT SUPPORTED
break;
}
}
int run_test(const km::kbp::path & source, const km::kbp::path & compiled) {
int caps_lock_state() {
return g_caps_lock_on ? KM_KBP_MODIFIER_CAPS : 0;
}
void toggle_caps_lock_state() {
g_caps_lock_on = !g_caps_lock_on;
}
km_kbp_option_item *get_keyboard_options(kmx_options options) {
km_kbp_option_item *keyboard_opts = new km_kbp_option_item[options.size() + 1];
int i = 0;
for (auto it = options.begin(); it != options.end(); it++) {
if (it->type != KOT_INPUT) continue;
std::cout << "input option-key: " << it->key << std::endl;
std::u16string key = it->key;
if (key[0] == u'&') {
// environment value (aka system store)
key.erase(0, 1);
keyboard_opts[i].scope = KM_KBP_OPT_ENVIRONMENT;
}
else {
keyboard_opts[i].scope = KM_KBP_OPT_KEYBOARD;
}
km_kbp_cp *cp = new km_kbp_cp[key.length() + 1];
key.copy(cp, key.length());
cp[key.length()] = 0;
keyboard_opts[i].key = cp;
cp = new km_kbp_cp[it->value.length() + 1];
it->value.copy(cp, it->value.length());
cp[it->value.length()] = 0;
keyboard_opts[i].value = cp;
i++;
}
keyboard_opts[i] = KM_KBP_OPTIONS_END;
return keyboard_opts;
}
int run_test(const km::kbp::path &source, const km::kbp::path &compiled) {
std::string keys = "";
std::u16string expected = u"", context = u"";
kmx_options options;
@ -256,46 +308,11 @@ int run_test(const km::kbp::path & source, const km::kbp::path & compiled) {
try_status(km_kbp_keyboard_load(compiled.c_str(), &test_kb));
// Setup state, environment
try_status(km_kbp_state_create(test_kb, test_env_opts, &test_state));
// Setup keyboard options
if (options.size() > 0) {
km_kbp_option_item *keyboard_opts = new km_kbp_option_item[options.size() + 1];
int i = 0;
for (auto it = options.begin(); it != options.end(); it++) {
if (it->type != KOT_INPUT) continue;
std::cout << "input option-key: " << it->key << std::endl;
std::u16string key = it->key;
if (key[0] == u'&') {
// environment value (aka system store)
key.erase(0, 1);
keyboard_opts[i].scope = KM_KBP_OPT_ENVIRONMENT;
}
else {
keyboard_opts[i].scope = KM_KBP_OPT_KEYBOARD;
}
km_kbp_cp *cp = new km_kbp_cp[key.length() + 1];
key.copy(cp, key.length());
cp[key.length()] = 0;
keyboard_opts[i].key = cp;
cp = new km_kbp_cp[it->value.length() + 1];
it->value.copy(cp, it->value.length());
cp[it->value.length()] = 0;
keyboard_opts[i].value = cp;
i++;
}
keyboard_opts[i] = KM_KBP_OPTIONS_END;
km_kbp_option_item *keyboard_opts = get_keyboard_options(options);
try_status(km_kbp_state_options_update(test_state, keyboard_opts));
@ -313,10 +330,18 @@ int run_test(const km::kbp::path & source, const km::kbp::path & compiled) {
// Run through key events, applying output for each event
for (auto p = next_key(keys); p.vk != 0; p = next_key(keys)) {
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, options);
// Because a normal system tracks caps lock state itself,
// we mimic that in the tests. We assume caps lock state is
// updated on key_down before the processor receives the
// event.
if (p.vk == KM_KBP_VKEY_CAPS) {
toggle_caps_lock_state();
}
for (auto key_down = 1; key_down >= 0; key_down--) {
try_status(km_kbp_process_event(test_state, p.vk, p.modifier_state | caps_lock_state(), key_down));
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, options);
}
}
}
@ -445,7 +470,8 @@ int load_source(const km::kbp::path & path, std::string & keys, std::u16string &
s_context = "c context: ",
s_option = "c option: ",
s_option_expected = "c expected option: ",
s_option_saved = "c saved option: ";
s_option_saved = "c saved option: ",
s_capsLock = "c capsLock: ";
// 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.native());
@ -482,6 +508,9 @@ int load_source(const km::kbp::path & path, std::string & keys, std::u16string &
else if (is_token(s_option_saved, line)) {
if (!parse_option_string(line, options, KOT_SAVED)) return __LINE__;
}
else if (is_token(s_capsLock, line)) {
g_caps_lock_on = parse_source_string(line).compare(u"1") == 0;
}
}
if (keys == "") {

View file

@ -56,7 +56,7 @@ tests = [
'030 - multiple groups',
'031 - caps lock',
# '032 - caps control', # TODO: support capsononly, capsalwaysoff, shiftfreescaps
# '033 - caps alwyas off' # TODO: support capsononly, capsalwaysoff, shiftfreescaps
'033 - caps always off',
'034 - options double set reset',
'035 - options double set staged',
'036 - options - double reset staged',
@ -70,7 +70,8 @@ tests = [
'044 - if and context',
'045 - deadkey and context',
'046 - deadkey and contextex'
'046 - deadkey and contextex',
'047 - caps always off initially on'
]
if build_machine.system() == 'windows'

View file

@ -165,7 +165,9 @@ int main(int, char * [])
if (attrs->max_context < 16) return __LINE__;
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S,
KM_KBP_MODIFIER_SHIFT));
KM_KBP_MODIFIER_SHIFT, true));
try_status(km_kbp_process_event(test_state, KM_KBP_VKEY_S,
KM_KBP_MODIFIER_SHIFT, false));
//TEMP
action_items(test_state, {{KM_KBP_IT_CHAR, {0,}, {km_kbp_usv('S')}}});

View file

@ -1,5 +0,0 @@
[*]
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

View file

@ -40,6 +40,7 @@
#define KEYMAN_LALT 56
#define KEYMAN_RCTRL 97
#define KEYMAN_RALT 100
#define KEYMAN_CAPS 0x3a
typedef struct _IBusKeymanEngine IBusKeymanEngine;
typedef struct _IBusKeymanEngineClass IBusKeymanEngineClass;
@ -809,52 +810,30 @@ ibus_keyman_engine_process_key_event (IBusEngine *engine,
g_message("DAR: ibus_keyman_engine_process_key_event - keyval=%02i, keycode=%02i, state=%02x", keyval, keycode, state);
if (state & IBUS_RELEASE_MASK)
{
switch(keycode) {
case KEYMAN_LCTRL:
keyman->lctrl_pressed = FALSE;
break;
case KEYMAN_RCTRL:
keyman->rctrl_pressed = FALSE;
break;
case KEYMAN_LALT:
keyman->lalt_pressed = FALSE;
break;
case KEYMAN_RALT:
keyman->ralt_pressed = FALSE;
break;
default:
break;
}
return FALSE;
gboolean isKeyDown = !!(state & IBUS_RELEASE_MASK);
// REVIEW: why don't we handle these keys?
switch (keycode) {
case KEYMAN_LCTRL:
keyman->lctrl_pressed = isKeyDown;
break;
case KEYMAN_RCTRL:
keyman->rctrl_pressed = isKeyDown;
break;
case KEYMAN_LALT:
keyman->lalt_pressed = isKeyDown;
break;
case KEYMAN_RALT:
keyman->ralt_pressed = isKeyDown;
break;
}
if (keycode < 0 || keycode > 255)
{
g_warning("keycode %d out of range", keycode);
return FALSE;
if (keycode < 0 || keycode > 255) {
g_warning("keycode %d out of range", keycode);
return FALSE;
}
if (keycode_to_vk[keycode] == 0) // key we don't handle
{
//save if a possible Ctrl or Alt modifier
switch(keycode) {
case KEYMAN_LCTRL:
keyman->lctrl_pressed = TRUE;
break;
case KEYMAN_RCTRL:
keyman->rctrl_pressed = TRUE;
break;
case KEYMAN_LALT:
keyman->lalt_pressed = TRUE;
break;
case KEYMAN_RALT:
keyman->ralt_pressed = TRUE;
break;
default:
break;
}
if (keycode_to_vk[keycode] == 0) { // key we don't handle
return FALSE;
}
@ -899,8 +878,8 @@ ibus_keyman_engine_process_key_event (IBusEngine *engine,
km_kbp_context *context = km_kbp_state_context(keyman->state);
g_free(get_current_context_text(context));
g_message("DAR: ibus_keyman_engine_process_key_event - km_mod_state=%x", km_mod_state);
km_kbp_status event_status = km_kbp_process_event(keyman->state,
keycode_to_vk[keycode], km_mod_state);
km_kbp_status event_status =
km_kbp_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state, isKeyDown);
context = km_kbp_state_context(keyman->state);
g_message("after process key event");
g_free(get_current_context_text(context));
@ -919,7 +898,7 @@ ibus_keyman_engine_process_key_event (IBusEngine *engine,
g_message("after processing all actions");
g_free(get_current_context_text(context));
return TRUE;
}
}
static void
ibus_keyman_engine_set_surrounding_text (IBusEngine *engine,