Merge remote-tracking branch 'origin/master' into feat/android/improve-globe-key

This commit is contained in:
Darcy Wong 2021-07-09 06:32:23 +07:00
commit 9e8fed7dcd
104 changed files with 1589 additions and 1461 deletions

View file

@ -1,5 +1,28 @@
# Keyman Version History
## 15.0.83 alpha 2021-07-08
* change(ios): setText now uses JSON serialization (#5321)
* feat(common/core/web): engine correction-prep optimizations (#5319)
* change(web): fat-finger-performance unit-test threshold (#5404)
* feat(web): spacebar text controls (#5348)
* chore(windows): remove backup build step (#5434)
* refactor(common/core/desktop): Fix file and class names 🚧 (#5445)
* refactor(web): longpresses, groundwork for additional gestures 🍕 (#5387)
* fix(android/engine): Display dictionary help link (#5427)
* refactor(common/core/desktop): Rename Load method 🚧 (#5444)
## 15.0.82 alpha 2021-07-07
* fix(common/models/types): fixes test script config (#5441)
## 15.0.81 alpha 2021-07-06
* fix(android/engine): Fix font file path (#5424)
* chore(common): npm audit fixes (#5419)
* chore(web): updates regression test-deps to same versions as web (#5420)
* chore(windows): make build paths consistent (#5405)
## 15.0.80 alpha 2021-07-02
* refactor(android): setKeyboard and setKeymanLanguage (#5338)

View file

@ -1 +1 @@
15.0.81
15.0.84

View file

@ -65,7 +65,7 @@ public class LexicalModel extends LanguageResource implements Serializable {
String version = lexicalModelJSON.optString(KMManager.KMKey_Version, "1.0");
version = lexicalModelJSON.optString(KMManager.KMKey_LexicalModelVersion, version);
String helpLink = ""; // TOODO: Handle help links
String helpLink = lexicalModelJSON.optString(KMManager.KMKey_CustomHelpLink, "");
// language ID and language name from lexicalModelJSON. Iterate through language array
String languageID = "", languageName = "";
@ -95,9 +95,8 @@ public class LexicalModel extends LanguageResource implements Serializable {
public LexicalModel(String packageID, String lexicalModelID, String lexicalModelName,
String languageID, String languageName, String version,
String helpLink, String kmp) {
// TODO: handle help links
super(packageID, lexicalModelID, lexicalModelName, languageID, languageName,
version, "", kmp);
version, helpLink, kmp);
}
@Override

View file

@ -13,7 +13,7 @@
#include <keyman/keyboardprocessor.h>
#include "keyboard.hpp"
#include "processor.hpp"
#include "kmx/kmx_processevent.hpp"
#include "kmx/kmx_processor.hpp"
#include "mock/mock_processor.hpp"
#include "rust/rust_mock_processor.hpp"

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include <kmx/kmx_processor.h>
#include <kmx/kmx_processevent.h>
using namespace km::kbp;
using namespace kmx;

View file

@ -2,19 +2,19 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include <kmx/kmx_processor.h>
#include <kmx/kmx_processevent.h>
using namespace km::kbp;
using namespace kmx;
void KMX_Processor::ResetCapsLock(void)
void KMX_ProcessEvent::ResetCapsLock(void)
{
DebugLog("ResetCapsLock: enter");
if(m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
DebugLog("ResetCapsLock: caps lock should be always off");
if(m_environment.capsLock())
if (m_environment.capsLock())
{
DebugLog("ResetCapsLock: caps lock is on, switching off caps lock");
m_actions.QueueAction(QIT_CAPSLOCK, 0);
@ -24,16 +24,16 @@ void KMX_Processor::ResetCapsLock(void)
}
void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp)
void KMX_ProcessEvent::KeyCapsLockPress(KMX_BOOL FIsUp)
{
if(m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY)
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)
else if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
{
if(!FIsUp && m_environment.capsLock())
{
@ -43,11 +43,11 @@ void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp)
}
void KMX_Processor::KeyShiftPress(KMX_BOOL FIsUp)
void KMX_ProcessEvent::KeyShiftPress(KMX_BOOL FIsUp)
{
if(!m_environment.capsLock()) return;
if(m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS)
if (m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS)
{
if(!FIsUp)
{

View file

@ -1,5 +1,5 @@
#include "kmx_processor.h"
#include "kmx_processevent.h"
namespace km {
namespace kbp {

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include <kmx/kmx_processor.h>
#include <kmx/kmx_processevent.h>
using namespace km::kbp;
using namespace kmx;

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "kmx_processor.h"
#include "kmx_processevent.h"
#include <stdarg.h>
#include <iostream>

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include <kmx/kmx_processor.h>
#include <kmx/kmx_processevent.h>
#include <keyman/keyboardprocessor_consts.h>
#include <state.hpp>
#include <option.hpp>
@ -21,12 +21,12 @@ 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);
Set(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
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);
}
@ -59,7 +59,7 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const {
}
void KMX_Environment::Load(std::u16string const & key, std::u16string const & value) {
void KMX_Environment::Set(std::u16string const & key, std::u16string const & value) {
assert(!key.empty());
if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) {

View file

@ -20,7 +20,7 @@ private:
km_kbp_cp const * default_value);
public:
KMX_Environment();
void Load(std::u16string const & key, std::u16string const & value);
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; }

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "kmx_processor.h"
#include "kmx_processevent.h"
using namespace km::kbp;
using namespace kmx;
@ -11,7 +11,7 @@ using namespace kmx;
#include <share.h>
#endif
KMX_BOOL KMX_Processor::Load(km_kbp_path_name KeyboardName)
KMX_BOOL KMX_ProcessEvent::Load(km_kbp_path_name KeyboardName)
{
if(!LoadKeyboard(KeyboardName, &m_keyboard.Keyboard)) return FALSE; // I5136
@ -75,7 +75,7 @@ unsigned long CalculateBufferCRC(unsigned long count, KMX_BYTE *p)
return crc;
}
KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard)
KMX_BOOL KMX_ProcessEvent::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard)
{
PKMX_BYTE buf;
FILE *fp;
@ -174,7 +174,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe
return TRUE;
}
PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
PKMX_WCHAR KMX_ProcessEvent::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
{
if(offset == 0) return NULL;
return (PKMX_WCHAR)(base + offset);
@ -188,7 +188,7 @@ PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
we don't copy the strings
This method is used on 64-bit architectures.
*/
LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
LPKEYBOARD KMX_ProcessEvent::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
{
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
@ -271,7 +271,7 @@ LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
Fixup the keyboard by expanding pointers. On disk the pointers are stored relative to the
beginning of the file, but we need real pointers. This method is used on 32-bit architectures.
*/
LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
LPKEYBOARD KMX_ProcessEvent::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
{
KMX_DWORD i, j;
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
@ -311,7 +311,7 @@ LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
#endif
KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz)
KMX_BOOL KMX_ProcessEvent::VerifyChecksum(PKMX_BYTE buf, size_t sz)
{
KMX_DWORD tempcs;
PCOMP_KEYBOARD ckbp;
@ -324,7 +324,7 @@ KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz)
return tempcs == CalculateBufferCRC(sz, buf);
}
KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
KMX_BOOL KMX_ProcessEvent::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
{
KMX_DWORD i;
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) filebase;

View file

@ -2,7 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "kmx_processor.h"
#include "kmx_processevent.h"
using namespace km::kbp;
using namespace kmx;
@ -78,7 +78,7 @@ static KMX_BYTE states[MAX_RSHIFT][MAX_KSHIFT] = {
* K_CTRLFLAG and K_ALTFLAG as well as specific keys correctly.
*/
KMX_BOOL KMX_Processor::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) {
KMX_BOOL KMX_ProcessEvent::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) {
//
// The rule shift must have ISVIRTUALKEY bit set for virt.keys
//

View file

@ -3,7 +3,7 @@
Authors: mcdurdin
*/
#include "processor.hpp"
#include "kmx_processor.h"
#include "kmx_processevent.h"
#include <option.hpp>
#include <state.hpp>

View file

@ -1,189 +1,682 @@
#include <keyman/keyboardprocessor.h>
/*
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "kmx_processevent.h"
#include "state.hpp"
#include "kmx/kmx_processevent.hpp"
namespace km {
namespace kbp
using namespace km::kbp;
using namespace kmx;
/* Globals */
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE;
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE;
KMX_BOOL km::kbp::kmx::g_silent = FALSE;
/*
* KMX_ProcessEvent
*/
KMX_ProcessEvent::KMX_ProcessEvent() : m_actions(&m_context), m_options(&m_keyboard) {
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
m_miniContextIfLen = 0;
}
KMX_ProcessEvent::~KMX_ProcessEvent() {
delete[] m_indexStack;
delete[] m_miniContext;
}
char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
// We only map SHIFT and UNSHIFTED, and CAPS LOCK
if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) {
return 0;
}
bool
shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG,
caps = (modifiers & CAPITALFLAG) == CAPITALFLAG;
if (vk == KM_KBP_VKEY_SPACE) {
// Override for space because it is the same for
// shifted and unshifted.
return 32;
}
for (int i = 0; s_char_to_vkey[i].vk; i++) {
if (s_char_to_vkey[i].caps && caps) {
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32;
}
else {
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32;
}
}
return 0;
}
/*
* KMX_BOOL ProcessEvent();
*
* Parameters: none
*
* Returns: TRUE if keystroke should be eaten
*
* 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.
*/
KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers)
{
LPKEYBOARD kbd = m_keyboard.Keyboard;
m_kbp_state = state;
if (m_environment.capsLock())
modifiers |= CAPITALFLAG;
m_state.vkey = vkey;
m_state.charCode = VKeyToChar(modifiers, vkey);
m_modifiers = modifiers;
m_state.LoopTimes = 0;
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
DebugLog("Non-Unicode keyboards are not supported.");
return FALSE;
}
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
KMX_BOOL fOutputKeystroke = FALSE;
ProcessGroup(gp, &fOutputKeystroke);
m_kbp_state = nullptr;
return !fOutputKeystroke;
}
/*
* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp);
*
* Parameters: gp Pointer to group to process inside
*
* Returns: TRUE if messages are to be sent,
* and FALSE if no messages are to be sent.
*
* Called by: ProcessEvent, recursive inside groups
*
* ProcessKey is where the keystroke conversion and output takes place. This routine
* has a lot of crucial code in it!
*/
KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
{
KMX_DWORD i;
LPKEY kkp = NULL;
PKMX_WCHAR p;
int sdmfI;
/*
If the number of nested groups goes higher than 50, then break out - this is
a limitation of stack size. This is basically a catch-all for freaky apps that
cause message loopbacks and nasty things like that. Okay, it's really a catch all
for bugs! This means the user's system shouldn't hang.
*/
LPKEYBOARD kbd = m_keyboard.Keyboard;
sdmfI = -1;
for(i = 0; i < kbd->cxGroupArray; i++)
if(gp == &kbd->dpGroupArray[i])
{
sdmfI = i;
break;
}
if(++m_state.LoopTimes > 50)
{
DebugLog("Aborting output: m_state.LoopTimes exceeded.");
m_state.StopOutput = TRUE;
return FALSE;
}
km_kbp_status kmx_processor::validate() const {
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
}
/*
The rule matching loop.
kmx_processor::kmx_processor(kbp::path p)
This loop iterates through all the rules in the group that is currently being
processed. Each rule in a group can be of three different types:
1. A virtual key rule, where the key to be matched is a virtual key
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
3. A rule in a keyless group, where only the context is matched.
The loop goes through and checks the rules like that. This loop could be optimized
with standard searching techniques - the ContextMatch may be difficult.
*/
DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582
DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext()));
if(gp)
{
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
{
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(!ContextMatch(kkp)) continue;
if(!gp->fUsingKeys)
{
if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
persisted_store()[opt.key] = opt.value;
if(kkp->dpContext[0] != 0) break; else continue;
}
// 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);
}
//if(kkp->Key == m_state.vkey)
//SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
// kkp->Key, kkp->ShiftFlags);
char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
{
char16_t const * pValue = nullptr;
switch(scope)
/* Keyman 6.0: support Virtual Characters */
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
{
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(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
else if(kkp->Key == m_state.vkey) break; // I4169
}
return pValue ? pValue : nullptr;
else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break;
}
}
option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
if(!gp || i == gp->cxKeyArray)
{
/*
No rule was found that corresponded to the current state of the context and
keyboard. NoMatch should be checked for everything except virtual keys; and
context should also be kept.
If the message was a virtual key, then just return without checking NoMatch.
NoMatch shouldn't be used for virtual keys because it will mean that no key
can ever get through that isn't matched - including arrows, func. keys, etc !!
Context is not kept for virtual keys being output.
*/
DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray);
if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585
// 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
{
switch(scope) {
case KM_KBP_OPT_KEYBOARD:
_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;
}
KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
return option(scope, key, value);
if(fIsBackspace) { // I4838 // I4933
PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
*pOutputKeystroke = TRUE; // I4933
return FALSE; // I4933
}
m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933
} else { // I4024 // I4128 // I4287 // I4290
DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
*pOutputKeystroke = TRUE;
return FALSE;
}
}
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
{
/* 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 */
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
}
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
return TRUE;
}
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;
DebugLog("match found in rule %d", i);
/*
Save the context that will be used for output when the 'context' keyword is used.
For each deadkey, we need to add 2 characters; look in related stores as well...
*/
assert(kkp != NULL);
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
else
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
/*
The next section includes several optimizations that make the code a little harder
to read, but are probably worth it in the time that they save.
If the output string doesn't have a "context" byte at the start, post backspaces
to erase the appropriate number of characters in the application. If it does have
a "context" byte at the start, then the string won't change, and no backspaces are
necessary. You could go one step further with this optimization, in PostAllKeys,
by comparing the starts of the strings to see what is same, and not backspacing
that, but it is probably not necessary.
*/
p = kkp->dpOutput;
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT)
{
for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext))
{
if(*p == UC_SENTINEL)
switch(*(p+1))
{
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
{
m_actions.QueueAction(QIT_BACK, 0);
}
}
p = kkp->dpOutput;
}
else
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
/* Use PostString to post the rest of the output string. */
if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches)
{
if(gp->dpMatch && *gp->dpMatch)
{
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
}
}
return TRUE;
}
/*
* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
* LPKEYBOARD lpkb );
*
* Parameters: str Pointer to string to send
* useMode Pointer to KMX_BOOL about whether a "use" command was found
* mp Pointer to MSG structure to copy in outputting messages
* lpkb Pointer to global keyboard structure
*
* Returns: 0 to continue, 1 and 2 to return.
*
* Called by: ProcessKey
*
* PostString posts a string of "context", "index", "beep", characters and virtual keys
* to the active application, via the Keyman PostKey buffer.
*/
int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke)
{
PKMX_WCHAR p, q, temp;
LPSTORE s;
int n1, n2;
int i, n, shift;
KMX_BOOL FoundUse = FALSE;
// TODO: Refactor to use incxstr
for(p = str; *p && (p < endstr || !endstr); p++)
{
if(*p == UC_SENTINEL)
switch(*(++p))
{
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);
p++;
m_actions.QueueAction(QIT_VKEYDOWN, *p);
m_actions.QueueAction(QIT_VKEYUP, *p);
m_actions.QueueAction(QIT_VSHIFTUP, shift);
p++; // CODE_EXTENDEDEND
////// CODE_EXTENDEDEND will be incremented by loop
break;
case CODE_DEADKEY: // A deadkey to be output
p++;
m_actions.QueueAction(QIT_DEADKEY, *p);
break;
case CODE_BEEP: // Sound an 'iconasterisk' beep
m_actions.QueueAction(QIT_BELL, 0);
break;
case CODE_CONTEXT: // copy the context to the output
{
KMX_BOOL ignoreOutputKeystroke;
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
break;
}
case CODE_CONTEXTEX:
p++;
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
if (*q) {
KMX_BOOL ignoreOutputKeystroke;
temp = incxstr(q);
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
}
break;
case CODE_RETURN: // stop processing and start PostAllKeys
m_state.StopOutput = TRUE;
return psrPostMessages;
case CODE_CALL:
p++;
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
FoundUse = TRUE;
break;
case CODE_USE: // use another group
p++;
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
if(m_state.StopOutput) return psrPostMessages;
FoundUse = TRUE;
break;
case CODE_CLEARCONTEXT:
// no longer supported, no-op
break;
case CODE_INDEX:
p++;
s = &m_keyboard.Keyboard->dpStoreArray[*p - 1];
p++;
n = m_indexStack[*p - 1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
PostString(temp, lpkb, incxstr(temp), pOutputKeystroke);
break;
case CODE_SETOPT:
p++;
n1 = *p - 1;
p++;
n2 = *p - 1;
GetOptions()->Set(n1, n2);
break;
case CODE_RESETOPT:
p++;
n1 = *p - 1;
GetOptions()->Reset(m_kbp_state->processor(), n1);
break;
case CODE_SAVEOPT:
p++;
n1 = *p - 1;
GetOptions()->Save(*m_kbp_state, n1);
break;
case CODE_IFSYSTEMSTORE:
p+=3;
break;
case CODE_SETSYSTEMSTORE:
p+=2;
break;
}
else {
if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) {
m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1)));
p++;
} else {
m_actions.QueueAction(QIT_CHAR, *p);
}
}
}
return FoundUse ? psrPostMessages : psrCheckMatches;
}
KMX_BOOL KMX_ProcessEvent::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432
{
KMX_BOOL bEqual = u16icmp(layoutName, static_cast<const km_kbp_cp *>(m_environment.baseLayout().c_str())) == 0 || // I4583
u16icmp(layoutName, static_cast<const km_kbp_cp*>(m_environment.baseLayoutAlt().c_str())) == 0; // I4583
return bEqual;
}
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
{
// TODO retrieve platform string from client environment
// TODO cleanup to use a vector<string>.
PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL;
u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str());
PKMX_WCHAR p = u16tok(t, u' ', &context);
while (p != NULL) {
if (u16icmp(platform, p) == 0) {
delete [] t;
return TRUE;
}
p = u16tok(NULL, u' ', &context);
}
delete [] t;
return FALSE;
}
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatform(LPSTORE s) // I3432
{
PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1];
u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString);
PKMX_WCHAR context = NULL;
PKMX_WCHAR platform = u16tok(t, u' ', &context);
while(platform != NULL)
{
if(!IsMatchingPlatformString(platform))
{
s->dwSystemID = TSS_PLATFORM_NOMATCH;
delete [] t;
return FALSE;
}
platform = u16tok(NULL, u' ', &context);
}
s->dwSystemID = TSS_PLATFORM_MATCH;
delete [] t;
return TRUE;
}
/*
* KMX_BOOL ContextMatch( LPKEY kkp );
*
* Parameters: kkp Rule to compare
*
* Returns: 0 on OK, 1 on not equal
*
* Called by: ProcessKey
*
* ContextMatch compares the context of a rule with the current context.
*/
KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
{
KMX_WORD /*i,*/ n;
PKMX_WCHAR p, q, qbuf, temp;
PKMX_WORD indexp;
LPSTORE s, t;
KMX_BOOL bEqual;
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
p = kkp->dpContext;
if(*p == 0)
{
return TRUE;
}
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
{
// If context buf is longer than the context, then obviously not start of doc.
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
p = incxstr(p);
if(*p == 0) return TRUE;
}
for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp))
{
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
{
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;
}
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE)
{
KMX_DWORD dwSystemID = *(pp+2)-1;
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
switch(dwSystemID)
{
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
bEqual = TRUE;
break;
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
bEqual = FALSE;
break;
case TSS_PLATFORM:
bEqual = IsMatchingPlatform(t);
break;
case TSS_BASELAYOUT:
bEqual = IsMatchingBaseLayout(t->dpString);
break;
default:
{
PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID);
if(ss == NULL) return FALSE;
bEqual = u16cmp(ss, t->dpString) == 0;
}
}
_kmx.GetContext()->Set(ctxt.c_str());
_kmx.GetActions()->ResetQueue();
state->actions().clear();
if(*(pp+3) == 1 && bEqual) return FALSE;
if(*(pp+3) == 2 && !bEqual) return FALSE;
}
}
if (!_kmx.ProcessEvent(state, vk, modifier_state)) {
// We need to output the default keystroke
state->actions().push_emit_keystroke();
}
q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p));
if(!q)
{
return FALSE; // context buf is too short!
}
indexp = m_indexStack;
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);
for(; *p && *q; p = incxstr(p))
{
*indexp = 0;
if(*p == UC_SENTINEL)
{
switch(*(p+1))
{
case CODE_DEADKEY:
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
{
return FALSE;
}
break;
case CODE_ANY:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
temp = xstrchr(s->dpString, q);
if(temp != NULL)
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
else
return FALSE;
break;
case CODE_NOTANY:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
if((temp = xstrchr(s->dpString, q)) != NULL)
return FALSE;
break;
case CODE_INDEX:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
*indexp = n = m_indexStack[(*(p+3))-1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
if(n != 0) return FALSE;
if(xchrcmp(temp, q) != 0) return FALSE;
break;
case CODE_CONTEXTEX:
// only the nth character
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
if(n == 0)
if(xchrcmp(temp, q) != 0) return FALSE;
break;
case CODE_IFOPT:
case CODE_IFSYSTEMSTORE:
indexp++;
continue; // don't increment q
default:
return FALSE;
}
state->actions().commit();
return KM_KBP_STATUS_OK;
}
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;
else if(xchrcmp(p, q) != 0)
{
return FALSE;
}
indexp++;
q = incxstr(q);
}
} // namespace kbp
} // namespace km
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested
return *p == *q; /*at least one must ==0 at this point*/
}
KMX_Actions *KMX_ProcessEvent::GetActions() {
return &m_actions;
}
KMX_Context *KMX_ProcessEvent::GetContext() {
return &m_context;
}
KMX_Options *KMX_ProcessEvent::GetOptions() {
return &m_options;
}
KMX_Options const *KMX_ProcessEvent::GetOptions() const {
return &m_options;
}
KMX_Environment *KMX_ProcessEvent::GetEnvironment() {
return &m_environment;
}
KMX_Environment const *KMX_ProcessEvent::GetEnvironment() const {
return &m_environment;
}
LPINTKEYBOARDINFO KMX_ProcessEvent::GetKeyboard() {
return &m_keyboard;
}
KMX_BOOL KMX_ProcessEvent::ReleaseKeyboardMemory(LPKEYBOARD kbd)
{
if (!kbd) return TRUE;
delete kbd;
return TRUE;
}
PKMX_WCHAR KMX_ProcessEvent::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
{
for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++)
if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString;
return NULL;
}

View file

@ -24,7 +24,7 @@ namespace kmx {
#define GLOBAL_ContextStackSize 80
class KMX_Processor {
class KMX_ProcessEvent {
private:
PKMX_WORD m_indexStack;
PKMX_WCHAR m_miniContext;
@ -78,8 +78,8 @@ private:
KMX_BOOL IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift);
public:
KMX_Processor();
~KMX_Processor();
KMX_ProcessEvent();
~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

View file

@ -1,682 +1,189 @@
/*
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "kmx_processor.h"
#include <keyman/keyboardprocessor.h>
#include "state.hpp"
#include "kmx/kmx_processor.hpp"
using namespace km::kbp;
using namespace kmx;
/* Globals */
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE;
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE;
KMX_BOOL km::kbp::kmx::g_silent = FALSE;
/*
* KMX_Processor
*/
KMX_Processor::KMX_Processor() : m_actions(&m_context), m_options(&m_keyboard) {
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
m_miniContextIfLen = 0;
}
KMX_Processor::~KMX_Processor() {
delete[] m_indexStack;
delete[] m_miniContext;
}
char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
// We only map SHIFT and UNSHIFTED, and CAPS LOCK
if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) {
return 0;
}
bool
shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG,
caps = (modifiers & CAPITALFLAG) == CAPITALFLAG;
if (vk == KM_KBP_VKEY_SPACE) {
// Override for space because it is the same for
// shifted and unshifted.
return 32;
}
for (int i = 0; s_char_to_vkey[i].vk; i++) {
if (s_char_to_vkey[i].caps && caps) {
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32;
}
else {
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32;
}
}
return 0;
}
/*
* KMX_BOOL ProcessEvent();
*
* Parameters: none
*
* Returns: TRUE if keystroke should be eaten
*
* 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.
*/
KMX_BOOL KMX_Processor::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers)
{
LPKEYBOARD kbd = m_keyboard.Keyboard;
m_kbp_state = state;
if (m_environment.capsLock())
modifiers |= CAPITALFLAG;
m_state.vkey = vkey;
m_state.charCode = VKeyToChar(modifiers, vkey);
m_modifiers = modifiers;
m_state.LoopTimes = 0;
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
DebugLog("Non-Unicode keyboards are not supported.");
return FALSE;
}
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
KMX_BOOL fOutputKeystroke = FALSE;
ProcessGroup(gp, &fOutputKeystroke);
m_kbp_state = nullptr;
return !fOutputKeystroke;
}
/*
* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp);
*
* Parameters: gp Pointer to group to process inside
*
* Returns: TRUE if messages are to be sent,
* and FALSE if no messages are to be sent.
*
* Called by: ProcessEvent, recursive inside groups
*
* ProcessKey is where the keystroke conversion and output takes place. This routine
* has a lot of crucial code in it!
*/
KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
{
KMX_DWORD i;
LPKEY kkp = NULL;
PKMX_WCHAR p;
int sdmfI;
/*
If the number of nested groups goes higher than 50, then break out - this is
a limitation of stack size. This is basically a catch-all for freaky apps that
cause message loopbacks and nasty things like that. Okay, it's really a catch all
for bugs! This means the user's system shouldn't hang.
*/
LPKEYBOARD kbd = m_keyboard.Keyboard;
sdmfI = -1;
for(i = 0; i < kbd->cxGroupArray; i++)
if(gp == &kbd->dpGroupArray[i])
{
sdmfI = i;
break;
}
if(++m_state.LoopTimes > 50)
namespace km {
namespace kbp
{
DebugLog("Aborting output: m_state.LoopTimes exceeded.");
m_state.StopOutput = TRUE;
return FALSE;
}
/*
The rule matching loop.
km_kbp_status kmx_processor::validate() const {
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
}
This loop iterates through all the rules in the group that is currently being
processed. Each rule in a group can be of three different types:
1. A virtual key rule, where the key to be matched is a virtual key
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
3. A rule in a keyless group, where only the context is matched.
The loop goes through and checks the rules like that. This loop could be optimized
with standard searching techniques - the ContextMatch may be difficult.
*/
DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582
DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext()));
if(gp)
{
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
kmx_processor::kmx_processor(kbp::path p)
{
if(!ContextMatch(kkp)) continue;
if(!gp->fUsingKeys)
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(kkp->dpContext[0] != 0) break; else continue;
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);
//if(kkp->Key == m_state.vkey)
//SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
// kkp->Key, kkp->ShiftFlags);
_attributes = keyboard_attributes(static_cast<std::u16string>(p.stem()),
std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
}
/* Keyman 6.0: support Virtual Characters */
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
{
char16_t const * pValue = nullptr;
switch(scope)
{
if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
else if(kkp->Key == m_state.vkey) break; // I4169
case KM_KBP_OPT_KEYBOARD:
pValue = _kmx.GetOptions()->LookUp(key);
break;
case KM_KBP_OPT_ENVIRONMENT:
pValue = _kmx.GetEnvironment()->LookUp(key);
break;
default:
break;
}
else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break;
return pValue ? pValue : nullptr;
}
}
if(!gp || i == gp->cxKeyArray)
{
/*
No rule was found that corresponded to the current state of the context and
keyboard. NoMatch should be checked for everything except virtual keys; and
context should also be kept.
If the message was a virtual key, then just return without checking NoMatch.
NoMatch shouldn't be used for virtual keys because it will mean that no key
can ever get through that isn't matched - including arrows, func. keys, etc !!
Context is not kept for virtual keys being output.
*/
DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray);
if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585
// 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
{
KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
if(fIsBackspace) { // I4838 // I4933
PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
*pOutputKeystroke = TRUE; // I4933
return FALSE; // I4933
}
m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933
} else { // I4024 // I4128 // I4287 // I4290
DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
*pOutputKeystroke = TRUE;
return FALSE;
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;
}
}
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
{
/* 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 */
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
return option(scope, key, value);
}
return TRUE;
}
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
DebugLog("match found in rule %d", i);
/*
Save the context that will be used for output when the 'context' keyword is used.
For each deadkey, we need to add 2 characters; look in related stores as well...
*/
assert(kkp != NULL);
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
else
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
/*
The next section includes several optimizations that make the code a little harder
to read, but are probably worth it in the time that they save.
If the output string doesn't have a "context" byte at the start, post backspaces
to erase the appropriate number of characters in the application. If it does have
a "context" byte at the start, then the string won't change, and no backspaces are
necessary. You could go one step further with this optimization, in PostAllKeys,
by comparing the starts of the strings to see what is same, and not backspacing
that, but it is probably not necessary.
*/
p = kkp->dpOutput;
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT)
{
for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext))
{
if(*p == UC_SENTINEL)
switch(*(p+1))
{
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
{
m_actions.QueueAction(QIT_BACK, 0);
}
}
p = kkp->dpOutput;
}
else
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
/* Use PostString to post the rest of the output string. */
if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches)
{
if(gp->dpMatch && *gp->dpMatch)
{
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
}
}
return TRUE;
}
/*
* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
* LPKEYBOARD lpkb );
*
* Parameters: str Pointer to string to send
* useMode Pointer to KMX_BOOL about whether a "use" command was found
* mp Pointer to MSG structure to copy in outputting messages
* lpkb Pointer to global keyboard structure
*
* Returns: 0 to continue, 1 and 2 to return.
*
* Called by: ProcessKey
*
* PostString posts a string of "context", "index", "beep", characters and virtual keys
* to the active application, via the Keyman PostKey buffer.
*/
int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke)
{
PKMX_WCHAR p, q, temp;
LPSTORE s;
int n1, n2;
int i, n, shift;
KMX_BOOL FoundUse = FALSE;
// TODO: Refactor to use incxstr
for(p = str; *p && (p < endstr || !endstr); p++)
{
if(*p == UC_SENTINEL)
switch(*(++p))
{
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);
p++;
m_actions.QueueAction(QIT_VKEYDOWN, *p);
m_actions.QueueAction(QIT_VKEYUP, *p);
m_actions.QueueAction(QIT_VSHIFTUP, shift);
p++; // CODE_EXTENDEDEND
////// CODE_EXTENDEDEND will be incremented by loop
break;
case CODE_DEADKEY: // A deadkey to be output
p++;
m_actions.QueueAction(QIT_DEADKEY, *p);
break;
case CODE_BEEP: // Sound an 'iconasterisk' beep
m_actions.QueueAction(QIT_BELL, 0);
break;
case CODE_CONTEXT: // copy the context to the output
{
KMX_BOOL ignoreOutputKeystroke;
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
break;
}
case CODE_CONTEXTEX:
p++;
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
if (*q) {
KMX_BOOL ignoreOutputKeystroke;
temp = incxstr(q);
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
}
break;
case CODE_RETURN: // stop processing and start PostAllKeys
m_state.StopOutput = TRUE;
return psrPostMessages;
case CODE_CALL:
p++;
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
FoundUse = TRUE;
break;
case CODE_USE: // use another group
p++;
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
if(m_state.StopOutput) return psrPostMessages;
FoundUse = TRUE;
break;
case CODE_CLEARCONTEXT:
// no longer supported, no-op
break;
case CODE_INDEX:
p++;
s = &m_keyboard.Keyboard->dpStoreArray[*p - 1];
p++;
n = m_indexStack[*p - 1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
PostString(temp, lpkb, incxstr(temp), pOutputKeystroke);
break;
case CODE_SETOPT:
p++;
n1 = *p - 1;
p++;
n2 = *p - 1;
GetOptions()->Set(n1, n2);
break;
case CODE_RESETOPT:
p++;
n1 = *p - 1;
GetOptions()->Reset(m_kbp_state->processor(), n1);
break;
case CODE_SAVEOPT:
p++;
n1 = *p - 1;
GetOptions()->Save(*m_kbp_state, n1);
break;
case CODE_IFSYSTEMSTORE:
p+=3;
break;
case CODE_SETSYSTEMSTORE:
p+=2;
break;
}
else {
if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) {
m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1)));
p++;
} else {
m_actions.QueueAction(QIT_CHAR, *p);
}
}
}
return FoundUse ? psrPostMessages : psrCheckMatches;
}
KMX_BOOL KMX_Processor::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432
{
KMX_BOOL bEqual = u16icmp(layoutName, static_cast<const km_kbp_cp *>(m_environment.baseLayout().c_str())) == 0 || // I4583
u16icmp(layoutName, static_cast<const km_kbp_cp*>(m_environment.baseLayoutAlt().c_str())) == 0; // I4583
return bEqual;
}
KMX_BOOL KMX_Processor::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
{
// TODO retrieve platform string from client environment
// TODO cleanup to use a vector<string>.
PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL;
u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str());
PKMX_WCHAR p = u16tok(t, u' ', &context);
while (p != NULL) {
if (u16icmp(platform, p) == 0) {
delete [] t;
return TRUE;
}
p = u16tok(NULL, u' ', &context);
}
delete [] t;
return FALSE;
}
KMX_BOOL KMX_Processor::IsMatchingPlatform(LPSTORE s) // I3432
{
PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1];
u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString);
PKMX_WCHAR context = NULL;
PKMX_WCHAR platform = u16tok(t, u' ', &context);
while(platform != NULL)
{
if(!IsMatchingPlatformString(platform))
{
s->dwSystemID = TSS_PLATFORM_NOMATCH;
delete [] t;
return FALSE;
}
platform = u16tok(NULL, u' ', &context);
}
s->dwSystemID = TSS_PLATFORM_MATCH;
delete [] t;
return TRUE;
}
/*
* KMX_BOOL ContextMatch( LPKEY kkp );
*
* Parameters: kkp Rule to compare
*
* Returns: 0 on OK, 1 on not equal
*
* Called by: ProcessKey
*
* ContextMatch compares the context of a rule with the current context.
*/
KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
{
KMX_WORD /*i,*/ n;
PKMX_WCHAR p, q, qbuf, temp;
PKMX_WORD indexp;
LPSTORE s, t;
KMX_BOOL bEqual;
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
p = kkp->dpContext;
if(*p == 0)
{
return TRUE;
}
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
{
// If context buf is longer than the context, then obviously not start of doc.
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
p = incxstr(p);
if(*p == 0) return TRUE;
}
for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp))
{
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
{
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;
}
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE)
{
KMX_DWORD dwSystemID = *(pp+2)-1;
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
switch(dwSystemID)
{
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
bEqual = TRUE;
break;
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
bEqual = FALSE;
break;
case TSS_PLATFORM:
bEqual = IsMatchingPlatform(t);
break;
case TSS_BASELAYOUT:
bEqual = IsMatchingBaseLayout(t->dpString);
break;
default:
{
PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID);
if(ss == NULL) return FALSE;
bEqual = u16cmp(ss, t->dpString) == 0;
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;
}
}
if(*(pp+3) == 1 && bEqual) return FALSE;
if(*(pp+3) == 2 && !bEqual) return FALSE;
}
}
_kmx.GetContext()->Set(ctxt.c_str());
_kmx.GetActions()->ResetQueue();
state->actions().clear();
q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p));
if(!q)
{
return FALSE; // context buf is too short!
}
indexp = m_indexStack;
for(; *p && *q; p = incxstr(p))
{
*indexp = 0;
if(*p == UC_SENTINEL)
{
switch(*(p+1))
{
case CODE_DEADKEY:
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
{
return FALSE;
}
break;
case CODE_ANY:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
temp = xstrchr(s->dpString, q);
if(temp != NULL)
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
else
return FALSE;
break;
case CODE_NOTANY:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
if((temp = xstrchr(s->dpString, q)) != NULL)
return FALSE;
break;
case CODE_INDEX:
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
*indexp = n = m_indexStack[(*(p+3))-1];
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
if(n != 0) return FALSE;
if(xchrcmp(temp, q) != 0) return FALSE;
break;
case CODE_CONTEXTEX:
// only the nth character
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
if(n == 0)
if(xchrcmp(temp, q) != 0) return FALSE;
break;
case CODE_IFOPT:
case CODE_IFSYSTEMSTORE:
indexp++;
continue; // don't increment q
default:
return FALSE;
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;
}
else if(xchrcmp(p, q) != 0)
{
return 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"
};
km_kbp_attr const & kmx_processor::attributes() const {
return engine_attrs;
}
indexp++;
q = incxstr(q);
}
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested
return *p == *q; /*at least one must ==0 at this point*/
}
KMX_Actions *KMX_Processor::GetActions() {
return &m_actions;
}
KMX_Context *KMX_Processor::GetContext() {
return &m_context;
}
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;
}
KMX_BOOL KMX_Processor::ReleaseKeyboardMemory(LPKEYBOARD kbd)
{
if (!kbd) return TRUE;
delete kbd;
return TRUE;
}
PKMX_WCHAR KMX_Processor::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
{
for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++)
if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString;
return NULL;
}
} // namespace kbp
} // namespace km

View file

@ -10,7 +10,7 @@
#include <string>
#include <keyman/keyboardprocessor.h>
#include "kmx/kmx_processor.h"
#include "kmx/kmx_processevent.h"
#include "keyboard.hpp"
#include "processor.hpp"
@ -21,7 +21,7 @@ namespace kbp
{
private:
bool _valid;
kmx::KMX_Processor _kmx;
kmx::KMX_ProcessEvent _kmx;
public:
kmx_processor(path);
km_kbp_status process_event(km_kbp_state *state,

View file

@ -6,7 +6,7 @@
#include <iterator>
#include <codecvt>
#include <locale>
#include "kmx_processor.h"
#include "kmx_processevent.h"
#include "utfcodec.hpp"

View file

@ -17,7 +17,7 @@
#include <string>
#include <type_traits>
#include <kmx/kmx_processor.h>
#include <kmx/kmx_processevent.h>
#include "path.hpp"
#include "state.hpp"

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
"version": "15.0.81",
"version": "15.0.84",
"description": "The core text and prediction processing engine for KeymanWeb",
"repository": {
"type": "git",
@ -18,8 +18,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/lexical-model-compiler": "^15.0.81",
"@keymanapp/resources-gosh": "^15.0.81",
"@keymanapp/lexical-model-compiler": "^15.0.84",
"@keymanapp/resources-gosh": "^15.0.84",
"@types/node": "^11.9.4",
"chai": "^4.3.4",
"mocha": "^8.4.0",
@ -34,11 +34,11 @@
"mocha": "mocha"
},
"dependencies": {
"@keymanapp/keyboard-processor": "^15.0.81",
"@keymanapp/lexical-model-layer": "^15.0.81",
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/keyboard-processor": "^15.0.84",
"@keymanapp/lexical-model-layer": "^15.0.84",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"eventemitter3": "^4.0.0"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
"version": "15.0.81",
"version": "15.0.84",
"description": "Core module for Keyman keyboard support in KeymanWeb.",
"repository": {
"type": "git",
@ -18,7 +18,7 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^15.0.81",
"@keymanapp/resources-gosh": "^15.0.84",
"chai": "^4.3.4",
"mocha": "^8.4.0",
"mocha-teamcity-reporter": "^4.0.0",
@ -32,9 +32,9 @@
"mocha": "mocha"
},
"dependencies": {
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"@types/node": "^11.9.4"
}
}

View file

@ -221,10 +221,10 @@ namespace com.keyman.keyboards {
return Lkc;
}
public getSubkey(id: string): ActiveKey {
public getSubkey(coreID: string): ActiveKey {
if(this.sk) {
for(let key of this.sk) {
if(key.id == id) {
if(key.coreID == coreID) {
return key;
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
"version": "15.0.81",
"version": "15.0.84",
"description": "Core classes used to develop KeymanWeb test cases based on keystrokes",
"main": "index.js",
"scripts": {
@ -17,10 +17,10 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"dependencies": {
"@keymanapp/keyboard-processor": "^15.0.81",
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/keyboard-processor": "^15.0.84",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"@types/node": "^11.9.4"
},
"devDependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
"version": "15.0.81",
"version": "15.0.84",
"description": "Common setup for KeymanWeb's Sentry-based error reporting",
"main": "dist/index.js",
"scripts": {
@ -20,7 +20,7 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-environment": "^15.0.84",
"@sentry/browser": "^5.27.4"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
"version": "15.0.81",
"version": "15.0.84",
"description": "Common utility functions used throughout other Keyman packages",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@ -20,8 +20,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/resources-gosh": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@types/node": "^14.0.5",
"typescript": "^3.8.3"
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
"version": "15.0.81",
"version": "15.0.84",
"description": "Backing model implementations (templates) for Keyman's modelling layer",
"keywords": [
"lm",
@ -42,8 +42,8 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.4",
@ -52,6 +52,6 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/models-wordbreakers": "^15.0.81"
"@keymanapp/models-wordbreakers": "^15.0.84"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-types",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,10 +1,10 @@
{
"name": "@keymanapp/models-types",
"version": "15.0.81",
"version": "15.0.84",
"description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.",
"types": "./index.d.ts",
"scripts": {
"test": "tsc test.ts"
"test": "tsc test.ts -lib es6"
},
"repository": {
"type": "git",

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
"version": "15.0.81",
"version": "15.0.84",
"description": "Word breakers bundled in the modeling layer",
"keywords": [
"lexical model",
@ -40,7 +40,7 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/models-types": "^15.0.84",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"chai": "^4.3.4",

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
"version": "15.0.81",
"version": "15.0.84",
"description": "Keyman/Predictive Text integration layer",
"main": "build/index.js",
"scripts": {
@ -21,8 +21,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/resources-gosh": "^15.0.81",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/resources-gosh": "^15.0.84",
"@types/node": "^10.17.21",
"chai": "^4.3.4",
"karma": "^6.3.4",
@ -46,10 +46,10 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@keymanapp/models-templates": "^15.0.81",
"@keymanapp/models-wordbreakers": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/models-templates": "^15.0.84",
"@keymanapp/models-wordbreakers": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"es6-shim": "^0.35.5",
"string.prototype.codepointat": "^0.2.1",
"string.prototype.startswith": "^0.2.0"

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
"version": "15.0.81",
"version": "15.0.84",
"description": "Keyman Developer lexical model compiler",
"keywords": [
"keyboard",
@ -37,14 +37,14 @@
"node": ">=12.0.0"
},
"dependencies": {
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/models-types": "^15.0.84",
"commander": "^3.0.0",
"typescript": "^3.8.3",
"xml2js": "^0.4.19"
},
"devDependencies": {
"@keymanapp/models-templates": "^15.0.81",
"@keymanapp/models-wordbreakers": "^15.0.81",
"@keymanapp/models-templates": "^15.0.84",
"@keymanapp/models-wordbreakers": "^15.0.84",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.6",

View file

@ -20,5 +20,5 @@
"ts-node",
"typescript"
],
"version": "15.0.81"
"version": "15.0.84"
}

View file

@ -1,5 +1,5 @@
{
"name": "@keymanapp/resources-gosh",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 1
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/resources-gosh",
"version": "15.0.81",
"version": "15.0.84",
"description": "Good Ol' Shell",
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
"contributors": [

View file

@ -1,5 +1,5 @@
{
"name": "@keymanapp/web-environment",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 1
}

View file

@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-environment",
"version": "15.0.81",
"version": "15.0.84",
"description": "A compilation utility for passing select environment variables into our TS builds",
"author": "Joshua Horton <joshua_horton@sil.org> (https://github.com/jahorton)",
"homepage": "https://github.com/keymanapp/keyman",
@ -17,6 +17,6 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
"@keymanapp/resources-gosh": "^15.0.81"
"@keymanapp/resources-gosh": "^15.0.84"
}
}

2
web/package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "keyman",
"version": "15.0.81",
"version": "15.0.84",
"lockfileVersion": 2,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "keyman",
"version": "15.0.81",
"version": "15.0.84",
"description": "Facilitates text input in any language.",
"repository": {
"type": "git",
@ -18,8 +18,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "^15.0.81",
"@keymanapp/web-sentry-manager": "^15.0.81",
"@keymanapp/resources-gosh": "^15.0.84",
"@keymanapp/web-sentry-manager": "^15.0.84",
"@sentry/cli": "^1.52.3",
"chai": "^4.3.4",
"google-closure-compiler-java": "^20200224.0.0",
@ -52,13 +52,13 @@
"sentry-cli": "sentry-cli"
},
"dependencies": {
"@keymanapp/input-processor": "^15.0.81",
"@keymanapp/keyboard-processor": "^15.0.81",
"@keymanapp/lexical-model-layer": "^15.0.81",
"@keymanapp/models-types": "^15.0.81",
"@keymanapp/recorder-core": "^15.0.81",
"@keymanapp/web-environment": "^15.0.81",
"@keymanapp/web-utils": "^15.0.81",
"@keymanapp/input-processor": "^15.0.84",
"@keymanapp/keyboard-processor": "^15.0.84",
"@keymanapp/lexical-model-layer": "^15.0.84",
"@keymanapp/models-types": "^15.0.84",
"@keymanapp/recorder-core": "^15.0.84",
"@keymanapp/web-environment": "^15.0.84",
"@keymanapp/web-utils": "^15.0.84",
"@types/node": "^11.9.4",
"es6-shim": "^0.35.5",
"eventemitter3": "^4.0.0"

View file

@ -2,6 +2,7 @@
// References the base Keyman object (and consequently, the rest of the core objects).
/// <reference path="kmwbase.ts" />
/// <reference path="osk/embedded/keytip.ts" />
/// <reference path="osk/embedded/pendingLongpress.ts" />
// KeymanWeb 11.0
// Copyright 2019 SIL International
@ -52,17 +53,22 @@ namespace com.keyman.osk {
*
* @param {Object} key base key element
*/
VisualKeyboard.prototype.touchHold = function(this: VisualKeyboard, key: KeyElement) {
let util = com.keyman.singleton.util;
if(key['subKeys'] && (typeof(window['oskCreatePopup']) == 'function')) {
VisualKeyboard.prototype.startLongpress = function(this: VisualKeyboard, key: KeyElement): PendingGesture {
if(typeof(window['oskCreatePopup']) == 'function') {
var xBase = dom.Utils.getAbsoluteX(key) - dom.Utils.getAbsoluteX(this.kbdDiv) + key.offsetWidth/2,
yBase = dom.Utils.getAbsoluteY(key);
// #3718: No longer prepend base key to subkey array
this.popupBaseKey = key;
this.popupPending=true;
window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight);
return new embedded.PendingLongpress(this, key);
} else {
// When embedded within our Android app, we expect the `oskCreatePopup` function to
// exist; all subkey control is delegated to the app.
//
// No function = big problem.
console.error("Missing `oskCreatePopup` function for engine integration.");
return null;
}
};
@ -73,10 +79,6 @@ namespace com.keyman.osk {
}
};
VisualKeyboard.prototype.highlightSubKeys = function(this: VisualKeyboard, k, x, y) {
// a dummy function; it's only really used for 'native' KMW.
}
VisualKeyboard.prototype.waitForFonts = function(this: VisualKeyboard, kfd, ofd) {
// a dummy function; it's only really used for 'native' KMW.
return true;
@ -134,8 +136,6 @@ namespace com.keyman.text {
// Skip full page initialization - skips native-mode only code
keymanweb.isEmbedded = true;
com.keyman.osk.VisualKeyboard.prototype.popupDelay = 400; // Delay must be less than native touch-hold delay
// Set default device options
keymanweb.setDefaultDeviceOptions = function(opt: com.keyman.OptionType) {
opt['attachType'] = 'manual';
@ -270,14 +270,45 @@ namespace com.keyman.text {
};
/**
* Function called by Android and iOS when a device-implemented keyboard popup is displayed or hidden
* Function called by Android and iOS when a device-implemented keyboard popup
* is displayed or hidden. As this is controlled by the app, we use it as a
* trigger for 'embedded'-mode gesture state management.
*
* @param {boolean} isVisible
*
**/
keymanweb['popupVisible'] = function(isVisible)
{
osk.vkbd.popupVisible = isVisible;
keymanweb['popupVisible'] = function(isVisible) {
let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator;
let pendingLongpress = osk.vkbd.pendingSubkey;
/*
* If a longpress popup was visible, but is no longer, this means that the
* associated longpress gesture was cancelled. It is possible for the base
* key to emit if selected at this time; detecton of this is managed by
* the `SubkeyDelegator` class.
*/
if(!isVisible) {
if(gesture) {
gesture.resolve(null);
osk.vkbd.subkeyGesture = null;
} else if(pendingLongpress) {
pendingLongpress.cancel();
osk.vkbd.pendingSubkey = null;
}
}
/*
* If the popup was not visible, but now is, that means our previously-pending
* longpress is now 'realized' (complete). The OSK relies upon this state
* information, which will be properly updated by `resolve`.
*
* Prominent uses of such state info helps prevent change of base key, key
* previews, and key output from occurring while a subkey popup remains active.
*/
if(isVisible && pendingLongpress) {
// Fulfills the first-stage promise.
pendingLongpress.resolve();
}
};
/**
@ -318,8 +349,6 @@ namespace com.keyman.text {
* @param {string} keyName key identifier
**/
keymanweb['executePopupKey'] = function(keyName: string) {
let core = (<KeymanBase> keymanweb).core;
var origArg = keyName;
if(!keymanweb.core.activeKeyboard || !osk.vkbd) {
return false;
@ -333,99 +362,24 @@ namespace com.keyman.text {
// Can't just split on '-' because some layers like ctrl-shift contain it.
let separatorIndex = keyName.lastIndexOf('-');
var layer = core.keyboardProcessor.layerId;
if (separatorIndex > 0) {
layer = keyName.substring(0, separatorIndex);
keyName = keyName.substring(separatorIndex+1);
}
if(layer == 'undefined') {
layer=core.keyboardProcessor.layerId;
}
// Note: this assumes Lelem is properly attached and has an element interface.
// Currently true in the Android and iOS apps.
var Lelem=keymanweb.domManager.getLastActiveElement(),keyShiftState=com.keyman.text.KeyboardProcessor.getModifierState(layer);
var Lelem=keymanweb.domManager.getLastActiveElement();
keymanweb.domManager.initActiveElement(Lelem);
var nextLayer: string;
// This should be set if we're within this method... but it's best to guard against nulls here, just in case.
if(osk.vkbd.popupBaseKey && osk.vkbd.popupBaseKey['key']) {
// This is set with the base key of our current subkey elsewhere within the engine.
var baseKey: com.keyman.osk.OSKKeySpec = osk.vkbd.popupBaseKey['key'].spec;
var found = false;
if(baseKey.coreID == keyName) {
nextLayer = baseKey.nextlayer;
found = true;
} else {
// Search for the specified subkey so we can retrieve its useful properties.
// It should be within the popupBaseKey's subkey list.
for(let subKey of baseKey.sk) {
if(subKey.coreID == keyName) {
// ... to consider: why are we not just taking the keyspec wholesale right here?
nextLayer = subKey.nextlayer;
found = true;
break;
}
}
}
if(!found) {
console.warn("Could not find subkey '" + origArg + "' under the current base key '" + baseKey.coreID + "'!");
}
if(osk.vkbd.subkeyGesture) {
let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator;
gesture.resolve(keyName);
osk.vkbd.subkeyGesture = null;
} else {
console.warn("No base key exists for the subkey being executed: '" + origArg + "'");
}
let Codes = com.keyman.text.Codes;
// Check the virtual key
let Lkc: com.keyman.text.KeyEvent = {
Lmodifiers: keyShiftState,
Lstates: 0,
Lcode: Codes.keyCodes[keyName],
LisVirtualKey: true,
kName: keyName,
kNextLayer: nextLayer,
vkCode: null, // was originally undefined
isSynthetic: true,
device: keymanweb.util.device.coreSpec
};
// Process modifier key action
if(core.keyboardProcessor.selectLayer(Lkc, true)) { // ignores key's 'nextLayer' property for this check
return true;
}
// While we can't source the base KeyEvent properties for embedded subkeys the same way as native,
// we can handle many other pre-processing steps the same way with this common method.
core.keyboardProcessor.setSyntheticEventDefaults(Lkc);
//if(!Lkc.Lcode) return false; // Value is now zero if not known (Build 347)
//Build 353: revert to prior test to try to fix lack of KMEI output, May 1, 2014
if(isNaN(Lkc.Lcode) || !Lkc.Lcode) {
// Addresses modifier SHIFT keys.
if(nextLayer) {
core.keyboardProcessor.selectLayer(Lkc);
}
return false;
}
Lkc.vkCode=Lkc.Lcode;
// Now that we have a valid key event, hand it off to the Processor for execution.
// This allows the Processor to also handle any predictive-text tasks necessary.
let retVal = com.keyman.osk.PreProcessor.handleClick(Lkc, com.keyman.dom.Utils.getOutputTarget(Lelem), null);
// Special case for embedded to pass K_TAB back to device to process
if(Lkc.Lcode == Codes.keyCodes["K_TAB"] || Lkc.Lcode == Codes.keyCodes["K_TABBACK"]
|| Lkc.Lcode == Codes.keyCodes["K_TABFWD"]) {
return false;
}
return retVal;
};
/**

View file

@ -493,7 +493,7 @@ namespace com.keyman.osk {
// Utilized by the mobile apps; allows them to 'take over' touch handling,
// blocking it within KMW when the apps are already managing an ongoing touch-hold.
let keyman = com.keyman.singleton;
return keyman['osk'].vkbd.popupVisible;
return keyman['osk'].vkbd.subkeyGesture && keyman.isEmbedded;
}
protected dealiasSubTarget(target: HTMLDivElement): HTMLDivElement {

View file

@ -0,0 +1,65 @@
/// <reference path="subkeyPopup.ts" />
/// <reference path="../pendingGesture.interface.ts" />
namespace com.keyman.osk.browser {
/**
* (Conceptually) represents a finite-state-machine that determines
* whether or not a series of touch events corresponds to a longpress
* touch input. The `resolve` method may be used to trigger the
* subkey menu early, as with the upward quick-display shortcut.
*
* This is the default implementation of longpress behavior for KMW.
* Alterate implementations are modeled through the `embedded`
* namespace's equivalent, which is designed to facilitate custom
* modeling for such gestures.
*
* Once the conditions to recognize a longpress gesture have been
* fulfilled, this class's `promise` will resolve with a `SubkeyPopup`
* matching the gesture's 'base' key, which itself provides a
* `promise` field that will resolve to a `KeyEvent` once the touch
* sequence is completed.
*/
export class PendingLongpress implements PendingGesture {
public readonly baseKey: KeyElement;
public readonly promise: Promise<SubkeyPopup>;
public readonly subkeyUI: SubkeyPopup;
private readonly vkbd: VisualKeyboard;
private resolver: (subkeyPopup: SubkeyPopup) => void;
private timerId: number;
private popupDelay: number = 500;
constructor(vkbd: VisualKeyboard, baseKey: KeyElement) {
this.vkbd = vkbd;
this.baseKey = baseKey;
let _this = this;
this.promise = new Promise<SubkeyPopup>(function(resolve, reject) {
_this.resolver = resolve;
// After the timeout, it's no longer deferred; it's being fulfilled.
// Even if the actual subkey itself is still async.
_this.timerId = window.setTimeout(_this.resolve.bind(_this), _this.popupDelay);
});
}
public cancel() {
if(this.timerId) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
if(this.resolver) {
this.resolver(null);
this.resolver = null;
}
}
public resolve() {
if(this.resolver) {
this.resolver(new SubkeyPopup(this.vkbd, this.baseKey));
}
}
}
}

View file

@ -1,16 +1,54 @@
/// <reference path="oskSubKey.ts" />
/// <reference path="../realizedGesture.interface.ts" />
namespace com.keyman.osk.browser {
export class SubkeyPopup {
/**
* Represents a 'realized' longpress gesture's default implementation
* within KeymanWeb. Once a touch sequence has been confirmed to
* correspond to a longpress gesture, implementations of this class
* provide the following:
* * The UI needed to present a subkey menu
* * The state management needed to present feedback about the
* currently-selected subkey to the user
* * A `Promise` that will resolve to the user's selected subkey
* once the longpress operation is complete.
*
* As selection of the subkey occurs after the subkey popup is
* displayed, selection of the subkey is inherently asynchronous.
* The `Promise` may also resolve to `null` if the user indicates
* the desire to cancel subkey selection.
*/
export class SubkeyPopup implements RealizedGesture {
public readonly element: HTMLDivElement;
public readonly shim: HTMLDivElement;
private baseKey: KeyElement;
private vkbd: VisualKeyboard;
private currentSelection: KeyElement;
private callout: HTMLDivElement;
public readonly baseKey: KeyElement;
public readonly promise: Promise<text.KeyEvent>;
// Resolves the promise that generated this SubkeyPopup.
private resolver: (keyEvent: text.KeyEvent) => void;
constructor(vkbd: VisualKeyboard, e: KeyElement) {
let keyman = com.keyman.singleton;
let _this = this;
this.promise = new Promise<text.KeyEvent>(function(resolve) {
_this.resolver = resolve;
})
this.vkbd = vkbd;
this.baseKey = e;
// If the user doesn't move their finger and releases, we'll output the base key
// by default.
this.currentSelection = e;
e.key.highlight(true);
// A tag we directly set on a key element during its construction.
let subKeySpec: OSKKeySpec[] = e['subKeys'];
@ -18,7 +56,7 @@ namespace com.keyman.osk.browser {
// is possible while the array is visible. So it is simplest to let the keys have
// position:static and display:inline-block
var subKeys = this.element = document.createElement('div');
this.baseKey = e;
var i;
subKeys.id='kmw-popup-keys';
@ -72,6 +110,18 @@ namespace com.keyman.osk.browser {
}
}
finalize(touch: Touch) {
if(this.resolver) {
let keyEvent: text.KeyEvent = null;
if(this.currentSelection) {
keyEvent = this.vkbd.initKeyEvent(this.currentSelection, touch);
this.currentSelection.key.highlight(false);
}
this.resolver(keyEvent);
}
this.resolver = null;
}
reposition(vkbd: VisualKeyboard) {
let keyman = com.keyman.singleton;
@ -181,7 +231,16 @@ namespace com.keyman.osk.browser {
}
}
isVisible(): boolean {
return this.element.style.visibility == 'visible';
}
clear() {
// Discard the reference to the Promise's resolve method, allowing
// GC to clean it up. The corresponding Promise's contract allows
// passive cancellation.
this.resolver = null;
// Remove the displayed subkey array, if any
if(this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
@ -195,5 +254,34 @@ namespace com.keyman.osk.browser {
this.callout.parentNode.removeChild(this.callout);
}
}
updateTouch(touch: Touch) {
this.currentSelection = null;
this.baseKey.key.highlight(false);
for(let i=0; i < this.baseKey['subKeys'].length; i++) {
try {
let sk = this.element.childNodes[i].firstChild as KeyElement;
let onKey = sk.key.isUnderTouch(touch);
if(onKey) {
this.currentSelection = sk;
}
sk.key.highlight(onKey);
} catch(ex) {
if(ex.message) {
console.error("Unexpected error when attempting to update selected subkey:" + ex.message);
} else {
console.error("Unexpected error (and error type) when attempting to update selected subkey.");
}
}
}
// Use the popup duplicate of the base key if a phone with a visible popup array
if(!this.currentSelection && this.baseKey.key.isUnderTouch(touch)) {
this.baseKey.key.highlight(true);
this.currentSelection = this.baseKey;
}
}
}
}
}

View file

@ -0,0 +1,48 @@
/// <reference path="subkeyDelegator.ts" />
/// <reference path="../pendingGesture.interface.ts" />
namespace com.keyman.osk.embedded {
/**
* As control over the subkey display timer and the subkey popup are
* both handled by the host app within the Android app, this class
* serves mostly to communicate longpress state management from the
* app to the VisualKeyboard.
*
* The `resolve()` function should be triggered by the host app
* whenever it has recognized a completed longpress and has thus
* begun displaying its subkey popup. (Should these two events
* ever become separated in time, the latter is the more critical
* aspect.)
*/
export class PendingLongpress implements PendingGesture {
private resolver: (delegator: SubkeyDelegator) => void;
private readonly vkbd: VisualKeyboard;
public readonly baseKey: KeyElement;
public readonly promise: Promise<SubkeyDelegator>;
constructor(vkbd: VisualKeyboard, e: KeyElement) {
this.vkbd = vkbd;
let _this = this;
this.promise = new Promise<SubkeyDelegator>(function(resolve) {
_this.resolver = resolve;
});
this.baseKey = e;
}
public resolve() {
if(this.resolver) {
this.resolver(new SubkeyDelegator(this.vkbd, this.baseKey));
}
this.resolver = null;
}
public cancel() {
if(this.resolver) {
this.resolver(null);
this.resolver = null;
}
}
}
}

View file

@ -0,0 +1,110 @@
/// <reference path="../realizedGesture.interface.ts" />
namespace com.keyman.osk.embedded {
/**
* As the subkey popup view is handled by the host app when in embedded mode
* within our Android app, this class represents the fact that KMW has
* "delegated" subkey UI and selection to the host app. Hence, "Delegator",
* rather than "Popup".
*
* The `resolve` method should be triggered, in some fashion, by the host app
* whenever the user has completed their longpress, potentially selecting
* a subkey.
*
* This class will also track the ongoing touch event in case the base key is
* reselected, which _is_ managed by this class, not the host app.
*/
export class SubkeyDelegator implements RealizedGesture {
private resolver: (keyEvent: text.KeyEvent) => void;
private readonly vkbd: VisualKeyboard;
public readonly baseKey: KeyElement;
public readonly promise: Promise<text.KeyEvent>;
private movedFromBaseKey: boolean = false;
private baseKeySelected: boolean = false;
constructor(vkbd: VisualKeyboard, e: KeyElement) {
this.vkbd = vkbd;
let _this = this;
this.promise = new Promise<text.KeyEvent>(function(resolve) {
_this.resolver = resolve;
});
this.baseKey = e;
}
/**
* Resolves the ongoing longpress -> subkey gesture, fulfilling this
* `SubkeyDelegator`'s `promise` of a `KeyEvent`.
*
* If no subkey is selected but the original base key is, `resolve(null)`
* will return a key event corresponding to the base key.
*
* @param keyCoreID {string} The 'core ID' (id + modifier layer) of
* a selected subkey. May be `null`.
*/
public resolve(keyCoreID: string) {
if(this.resolver) {
let keyEvent: text.KeyEvent = null;
if(keyCoreID == null && this.baseKeySelected) {
// Handle selection of base key underneath the subkey array.
keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null);
this.baseKey.key.highlight(false);
} else if(keyCoreID != null) {
// This is set with the base key of our current subkey elsewhere within the engine.
let baseKey: OSKKeySpec = this.baseKey.key.spec;
let selectedKey: OSKKeySpec;
if(baseKey.coreID == keyCoreID) {
selectedKey = baseKey;
} else {
// ... yeah, there are some funky type shenanigans between the two.
// OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type.
selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec;
}
if(!selectedKey) {
// While we can't complete successfully, the subkey operation is done; we
// should still signal that and update related gesture state management.
this.resolver(null);
console.error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!");
return;
}
keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null);
keyEvent.vkCode=keyEvent.Lcode;
} // else /* if(keyCoreID == null) */ keyEvent = null; // As initialized at the top.
this.resolver(keyEvent);
}
this.resolver = null;
}
public isVisible(): boolean {
return true;
}
public clear() {
// no-op; it's fully controlled on the app side.
}
/**
* Allows this class to detect if the user may have changed their mind and
* re-selected the base key.
* @param touch
*/
updateTouch(touch: Touch) {
this.baseKeySelected = this.baseKey.key.isUnderTouch(touch);
// Prevent highlighting & selection before the touch has moved from the base key.
if(this.movedFromBaseKey) {
this.baseKey.key.highlight(this.baseKeySelected);
} else {
this.movedFromBaseKey = !this.baseKeySelected;
}
}
}
}

View file

@ -430,5 +430,19 @@ namespace com.keyman.osk {
return t;
}
public isUnderTouch(touch: Touch): boolean {
let x = touch.clientX;
let y = touch.clientY;
let btn = this.btn;
let x0 = dom.Utils.getAbsoluteX(btn);
let y0 = dom.Utils.getAbsoluteY(btn);//-document.body.scrollTop;
let x1 = x0 + btn.offsetWidth;
let y1 = y0 + btn.offsetHeight;
return (x > x0 && x < x1 && y > y0 && y < y1);
}
}
}
}

View file

@ -0,0 +1,45 @@
namespace com.keyman.osk {
/**
* Used for evaluating potential gestures. Classes adhering to this interface
* should be instantiated whenever the (implied) state-machine allows a new
* touch event to mark the start of a potential new gesture.
*
* For example, whenever a user touches a base key and there are no "realized"
* (fully-completed, but as-of-yet unresolved) gestures, that state allows the
* start of a potential new longpress event.
*
* The role of the `PendingGesture` is complete whenever all touch-events and
* conditions necessary for a modeled gesture have been met. As this point,
* it should be `resolve`d, fulfilling its `promise`. This results in a
* `RealizedGesture` appropriate for the gesture type that is used to obtain
* the final `KeyEvent` for the overall gesture sequence.
*
* For example, a "longpress" is considered resolved once the user has maintained
* an active, stationary touch point on the same key for a sufficiently long
* period without releasing it.
* * Were it released earlier, that would result in selection of a base key.
*
* Alternatively, a "flick" might be considered resolved if:
* * a user has rapidly moved a touch point in a consistent direction
* * for a long enough distance
* * and _then_ releases that touch point within a short timeframe.
*
* The pending gesture should only `resolve` to a realized gesture once
* _all_ such conditions are met, confirming that this specific gesture,
* and _only_ this specific gesture, could have resulted from the active
* touch sequence.
*
* The `RealizedGesture` that results and is 'returned' via the Promise will
* be handled by the `VisualKeyboard` class, which will retrieve and forward
* any `KeyEvent` that results from the overall gesture input sequence.
*
* @see `RealizedGesture`
*/
export interface PendingGesture {
readonly baseKey: KeyElement;
readonly promise: Promise<RealizedGesture>;
cancel(): void;
resolve?(): void;
}
}

View file

@ -0,0 +1,34 @@
namespace com.keyman.osk {
/**
* Implementations of this interface allow individual types of gestures to
* specify any additional user interaction and functionality (which may
* include UI elements) appropriate for obtaining a key event that may be
* produced by the modeled gesture type. These should only be instantiated
* once the associated `PendingLongpress` is no longer 'pending' - once it
* has become clear that the input touch-event sequence could only correspond
* to the modeled gesture.
*
* For example, when a longpress gesture completes - and hence, the user has
* kept their finger stationary on the same key for a long enough period -
* we display a popup view presenting subkeys corresponding to the gesture's
* underlying element. This popup view accepts touch input and completes only
* upon release of the ongoing touch sequence.
*
* Gestures are events that occur over intervals of time, and since some of them
* will require time and user interaction after becoming 'realized', these cases
* will be inherently async. The simplest way to model this is with `Promise`s.
*
* If appropriate for the modeled gesture type, an implementation may supply an
* instantly-resolving `Promise`. This may be appropriate for modeling "flick"
* or "swipe" gestures in the future, which may require no additional input once
* such a gesture is fully realized.
*/
export interface RealizedGesture {
readonly baseKey: KeyElement;
readonly promise: Promise<text.KeyEvent>;
clear(): void;
isVisible(): boolean;
updateTouch(touch: Touch): void;
}
}

View file

@ -3,7 +3,7 @@
/// <reference path="oskBaseKey.ts" />
/// <reference path="keytip.interface.ts" />
/// <reference path="browser/keytip.ts" />
/// <reference path="browser/subkeyPopup.ts" />
/// <reference path="browser/pendingLongpress.ts" />
namespace com.keyman.osk {
@ -41,7 +41,6 @@ namespace com.keyman.osk {
// State-related properties
ddOSK: boolean = false;
popupVisible: boolean;
keyPending: KeyElement;
touchPending: Touch;
deleteKey: KeyElement;
@ -60,14 +59,13 @@ namespace com.keyman.osk {
touchCount: number;
currentTarget: KeyElement;
// Popup key management
popupBaseKey: KeyElement;
popupPending: boolean = false;
subkeyDelayTimer: number;
popupDelay: number = 500;
// Used by embedded-mode's globe key
menuEvent: KeyElement; // Used by embedded-mode.
// Popup key management
keytip: KeyTip;
subkeyPopup: browser.SubkeyPopup;
pendingSubkey: PendingGesture;
subkeyGesture: RealizedGesture;
get layerId(): string {
return this._layerId;
@ -426,7 +424,7 @@ namespace com.keyman.osk {
let kbdAspectRatio = layerGroup.offsetWidth / this.kbdDiv.offsetHeight;
let baseKeyProbabilities = this.layout.getLayer(this.layerId).getTouchProbabilities(touchKbdPos, kbdAspectRatio);
if(!this.popupBaseKey || !this.popupBaseKey.key) {
if(!this.subkeyGesture || !this.subkeyGesture.baseKey.key) {
return baseKeyProbabilities;
} else {
// A temp-hack, as this was noted just before 14.0's release.
@ -438,7 +436,7 @@ namespace com.keyman.osk {
let baseMass = 1.0;
let baseKeyMass = 1.0;
let baseKeyID = this.popupBaseKey.key.spec.coreID;
let baseKeyID = this.subkeyGesture.baseKey.key.spec.coreID;
let popupKeyMass = 0.0;
let popupKeyID: string = null;
@ -514,8 +512,7 @@ namespace com.keyman.osk {
this.cancelDelete();
// Prevent multi-touch if popup displayed
var sk = document.getElementById('kmw-popup-keys');
if((sk && sk.style.visibility == 'visible') || this.popupVisible) {
if(this.subkeyGesture && this.subkeyGesture.isVisible()) {
return;
}
@ -561,14 +558,20 @@ namespace com.keyman.osk {
} else {
if(this.keyPending) {
this.highlightKey(this.keyPending, false);
this.modelKeyClick(this.keyPending, this.touchPending);
this.clearPopup();
if(this.subkeyGesture && this.subkeyGesture instanceof browser.SubkeyPopup) {
let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup;
subkeyPopup.updateTouch(e.changedTouches[0]);
subkeyPopup.finalize(e.changedTouches[0]);
} else {
this.modelKeyClick(this.keyPending, this.touchPending);
}
// Decrement the number of unreleased touch points to prevent
// sending the keystroke again when the key is actually released
this.touchCount--;
} else {
// If this key has subkey, start timer to display subkeys after delay, set up release
this.touchHold(key);
this.initGestures(key, e.changedTouches[0]);
}
this.keyPending = key;
this.touchPending = e.changedTouches[0];
@ -583,32 +586,25 @@ namespace com.keyman.osk {
**/
release: (e: TouchEvent) => void = function(this: VisualKeyboard, e: TouchEvent) {
// Prevent incorrect multi-touch behaviour if native or device popup visible
var sk = document.getElementById('kmw-popup-keys'), t = this.currentTarget;
var t = this.currentTarget;
// Clear repeated backspace if active, preventing 'sticky' behavior.
this.cancelDelete();
if((sk && sk.style.visibility == 'visible')) {
if((this.subkeyGesture && this.subkeyGesture.isVisible())) {
// Ignore release if a multiple touch
if(e.touches.length > 0) {
return;
}
// Cancel (but do not execute) pending key if neither a popup key or the base key
if((t == null) || ((t.id.indexOf('popup') < 0) && (t.id != this.popupBaseKey.id))) {
this.highlightKey(this.keyPending,false);
this.clearPopup();
this.keyPending = null;
this.touchPending = null;
if(this.subkeyGesture instanceof browser.SubkeyPopup) {
let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup;
subkeyPopup.finalize(e.changedTouches[0]);
}
}
this.highlightKey(this.keyPending,false);
this.keyPending = null;
this.touchPending = null;
// Only set when embedded in our Android/iOS app. Signals that the device is handling
// subkeys, so we shouldn't allow output for the base key.
//
// Note that on iOS (at least), this.release() will trigger before kmwembedded.ts's
// executePopupKey() function.
if(this.popupVisible) {
return;
}
@ -631,7 +627,6 @@ namespace com.keyman.osk {
// Process and clear highlighting of pending target
if(this.keyPending) {
this.highlightKey(this.keyPending,false);
// Output character unless moved off key
if(this.keyPending.className.indexOf('hidden') < 0 && tc > 0 && !beyondEdge) {
this.modelKeyClick(this.keyPending, e.changedTouches[0]);
@ -702,77 +697,29 @@ namespace com.keyman.osk {
return;
}
// Do not move over keys if device popup visible
if(this.popupVisible) {
if(key1 == null) {
if(key0) {
this.highlightKey(key0,false);
}
this.keyPending=null;
this.touchPending=null;
} else {
if(key1 == this.popupBaseKey) {
if(!key1.classList.contains('kmw-key-touched')) {
this.highlightKey(key1,true);
}
this.keyPending = key1;
this.touchPending = e.touches[0];
} else {
if(key0) {
this.highlightKey(key0,false);
}
this.keyPending = null;
this.touchPending = null;
}
}
// Update all gesture tracking. The function returns true if further input processing
// should be blocked.
if(this.updateGestures(key1, key0, e.changedTouches[0])) {
return;
}
var sk=document.getElementById('kmw-popup-keys');
// Use the popup duplicate of the base key if a phone with a visible popup array
if(sk && sk.style.visibility == 'visible' && this.device.formFactor == 'phone' && key1 == this.popupBaseKey) {
key1 = <KeyElement> sk.childNodes[0].firstChild;
}
// Identify current touch position (to manage off-key release)
this.currentTarget = key1;
// Clear previous key highlighting
if(key0 && key1 && key1 !== key0) {
// _Box has (most of) the useful client values.
let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box;
let height = this.kbdDiv.offsetHeight;
// We need to adjust the offset properties by any offsets related to the active banner.
// Determine the y-threshold at which touch-cancellation should automatically occur.
let rowCount = this.layers[this.layerIndex].row.length;
let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row.
var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5;
if(key0 && e.touches[0].pageY < yMin) {
this.highlightKey(key0,false);
}
// If popup is visible, need to move over popup, not over main keyboard
this.highlightSubKeys(key1,x,y);
if(sk && sk.style.visibility == 'visible') {
// Once a subkey array is displayed, do not allow changing the base key.
// Keep that array visible and accept no other options until the touch ends.
if(key1 && key1.id.indexOf('popup') < 0 && key1 != this.popupBaseKey) {
return;
}
// Highlight the base key on devices that do not append it to the subkey array.
if(key1 && key1 == this.popupBaseKey && key1.className.indexOf('kmw-key-touched') < 0) {
this.highlightKey(key1,true);
}
// Cancel touch if moved up and off keyboard, unless popup keys visible
} else {
// _Box has (most of) the useful client values.
let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box;
let height = this.kbdDiv.offsetHeight;
// Determine the y-threshold at which touch-cancellation should automatically occur.
let rowCount = this.layers[this.layerIndex].row.length;
let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row.
var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5;
if(key0 && e.touches[0].pageY < yMin) {
this.highlightKey(key0,false);
this.showKeyTip(null,false);
this.keyPending = null;
this.touchPending = null;
}
this.showKeyTip(null,false);
this.keyPending = null;
this.touchPending = null;
}
// Replace the target key, if any, by the new target key
@ -787,29 +734,6 @@ namespace com.keyman.osk {
this.highlightKey(key1,true);
}
}
if(key0 && key1 && (key1 != key0) && (key1.id != '')) {
// Display the touch-hold keys (after a pause)
this.touchHold(key1);
/*
// Clear and restart the popup timer
if(this.subkeyDelayTimer)
{
window.clearTimeout(this.subkeyDelayTimer);
this.subkeyDelayTimer = null;
}
if(key1.subKeys != null)
{
this.subkeyDelayTimer = window.setTimeout(
function()
{
this.clearPopup();
this.showSubKeys(key1);
}.bind(this),
this.popupDelay);
}
*/
}
}.bind(this);
/**
@ -945,8 +869,6 @@ namespace com.keyman.osk {
// Turn off key highlighting (or preview)
this.highlightKey(e,false);
let core = com.keyman.singleton.core; // only singleton-based ref currently needed here.
// Future note: we need to refactor osk.OSKKeySpec to instead be a 'tag field' for
// keyboards.ActiveKey. (Prob with generics, allowing the Web-only parts to
// be fully specified within the tag.)
@ -958,7 +880,13 @@ namespace com.keyman.osk {
console.error("OSK key with ID '" + e.id + "', keyID '" + e.keyId + "' missing needed specification");
return null;
}
// Return the event object.
return this.keyEventFromSpec(keySpec, touch);
}
keyEventFromSpec(keySpec: keyboards.ActiveKey, touch?: Touch) {
let core = com.keyman.singleton.core; // only singleton-based ref currently needed here.
// Start: mirrors _GetKeyEventProperties
@ -1074,42 +1002,15 @@ namespace com.keyman.osk {
clearPopup() {
// Remove the displayed subkey array, if any, and cancel popup request
if(this.subkeyPopup) {
this.subkeyPopup.clear();
this.subkeyPopup = null;
if(this.subkeyGesture) {
this.subkeyGesture.clear();
this.subkeyGesture = null;
}
if(this.subkeyDelayTimer) {
window.clearTimeout(this.subkeyDelayTimer);
this.subkeyDelayTimer = null;
if(this.pendingSubkey) {
this.pendingSubkey.cancel();
this.pendingSubkey = null;
}
this.popupBaseKey = null;
}
//#region 'native'-mode subkey handling
/**
* Display touch-hold array of 'sub-keys' above the currently touched key
* @param {Object} e primary key element
*/
showSubKeys(e: KeyElement) {
// Do not show subkeys if key already released
if(this.keyPending == null) {
return;
}
// Clear key preview if any
this.showKeyTip(null,false);
this.popupBaseKey = e;
let subKeys = this.subkeyPopup = new browser.SubkeyPopup(this, e);
// Otherwise append the touch-hold (subkey) array to the OSK
let keyman = com.keyman.singleton;
keyman.osk._Box.appendChild(subKeys.element);
keyman.osk._Box.appendChild(subKeys.shim);
// Must be placed after its `.element` has been inserted into the DOM.
subKeys.reposition(this);
}
//#endregion
@ -1692,26 +1593,123 @@ namespace com.keyman.osk {
}
}
/**
* Touch hold key display management
*
* @param {Object} key base key object
*/
touchHold(key: KeyElement) {
// Clear and restart the popup timer
if(this.subkeyDelayTimer) {
window.clearTimeout(this.subkeyDelayTimer);
this.subkeyDelayTimer = null;
/**
* Starts an implementation-specific longpress gesture. Separately implemented for
* in-browser and embedded modes.
* @param key The base key of the longpress.
* @returns
*/
startLongpress(key: KeyElement): PendingGesture {
let _this = this;
// First-level object/Promise: will produce a subkey popup when the longpress gesture completes.
// 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled.
let pendingLongpress = new browser.PendingLongpress(this, key);
pendingLongpress.promise.then(function(subkeyPopup) {
// In-browser-specific handling.
if(subkeyPopup) {
// Append the touch-hold (subkey) array to the OSK
let keyman = com.keyman.singleton;
keyman.osk._Box.appendChild(subkeyPopup.element);
keyman.osk._Box.appendChild(subkeyPopup.shim);
// Must be placed after its `.element` has been inserted into the DOM.
subkeyPopup.reposition(_this);
}
});
return pendingLongpress;
}
if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) {
this.subkeyDelayTimer = window.setTimeout(
function(this: VisualKeyboard) {
this.clearPopup();
this.showSubKeys(key);
}.bind(this), this.popupDelay);
/**
* Initializes all supported gestures given a base key and the triggering touch coordinates.
* @param key The gesture's base key
* @param touch The starting touch coordinates for the gesture
* @returns
*/
initGestures(key: KeyElement, touch: Touch) {
if(key['subKeys']) {
let _this = this;
let pendingLongpress = this.startLongpress(key);
if(pendingLongpress == null) {
return;
}
this.pendingSubkey = pendingLongpress;
pendingLongpress.promise.then(function(subkeyPopup) {
if(_this.pendingSubkey == pendingLongpress) {
_this.pendingSubkey = null;
}
if(subkeyPopup) {
// Clear key preview if any
_this.showKeyTip(null,false);
_this.subkeyGesture = subkeyPopup;
subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) {
// Allow active cancellation, even if the source should allow passive.
// It's an easy and cheap null guard.
if(keyEvent) {
PreProcessor.raiseKeyEvent(keyEvent);
}
_this.clearPopup();
});
}
});
}
}
/**
* Updates all currently-pending and activated gestures.
*
* @param currentKey The key currently underneath the most recent touch coordinate
* @param previousKey The previously-selected key
* @param touch The current touch-coordinate for the gesture
* @returns true if should fully capture input, false if input should 'fall through'.
*/
updateGestures(currentKey: KeyElement, previousKey: KeyElement, touch: Touch): boolean {
let key0 = previousKey;
let key1 = currentKey;
// Clear previous key highlighting, allow subkey controller to highlight as appropriate.
if(this.subkeyGesture) {
if(key0) {
key0.key.highlight(false);
}
this.subkeyGesture.updateTouch(touch);
this.keyPending = null;
this.touchPending = null;
return true;
}
this.currentTarget = null;
// If popup is visible, need to move over popup, not over main keyboard
// Could be turned into a browser-longpress specific implementation within browser.PendingLongpress?
if(key1 && key1['subKeys'] != null) {
// Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353)
if((this.touchY - touch.pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) {
this.pendingSubkey.resolve();
}
}
// If there is an active popup menu (which can occur from the previous block),
// a subkey popup exists; do not allow base key output.
if(this.subkeyGesture) {
return true;
}
if(key0 && key1 && (key1 != key0) && (key1.id != '')) {
// While there may not be an active subkey menu, we should probably update which base key
// is being highlighted by the current touch & start a pending longpress for it.
this.clearPopup();
this.initGestures(key1, touch);
}
return false;
}
};
optionKey(e: KeyElement, keyName: string, keyDown: boolean) {
let keyman = com.keyman.singleton;
@ -1731,32 +1729,6 @@ namespace com.keyman.osk {
}
};
// Manage popup key highlighting
highlightSubKeys(k: KeyElement, x: number, y: number) {
// Test for subkey array, return if none
// Issue: if `k` is itself a subkey, this won't do subkey highlighting correctly.
// That "common case" is actually handled through _standard_ key highlighting.
if(k == null || k['subKeys'] == null) {
return;
}
// Highlight key at touch position (and clear other highlighting)
var skBox=document.getElementById('kmw-popup-keys');
//#region This section fills a different role than the method name would suggest.
// Might correspond better to a 'checkInstantSubkeys' or something.
// Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353)
if((this.touchY-y > 5) && skBox == null) {
if(this.subkeyDelayTimer) {
window.clearTimeout(this.subkeyDelayTimer);
}
this.showSubKeys(k);
}
//#endregion
};
/**
* Add (or remove) the keytip preview (if KeymanWeb on a phone device)
*
@ -1771,8 +1743,8 @@ namespace com.keyman.osk {
return;
}
var sk=this.subkeyPopup,
popup = (sk && sk.element.style.visibility == 'visible')
let sk = this.subkeyGesture;
let popup = (sk && sk.isVisible());
// If popup keys are active, do not show the key tip.
on = popup ? false : on;

View file

@ -205,8 +205,6 @@ WIXHEAT=$(WIXPATH)\heat.exe
LINKPATH=link.exe
BACKUPDEFAULTS=*.map *.tds *.rsm *.dbg *.pdb *.cod
TORTOISEPROC=C:\Progra~1\TortoiseSVN\bin\TortoiseProc.exe
#

View file

@ -19,7 +19,7 @@ ROOT=c:\keyman\windows
!include $(ROOT)\src\Defines.mak
#
# The targets build, signcode, symbols, backup are standard
# The targets build, signcode, symbols are standard
# targets for all projects
#
@ -32,18 +32,12 @@ signcode:
symbols:
$(MAKE) -DTARGET=symbols $(TARGETS)
backup:
$(MAKE) -DTARGET=backup $(TARGETS)
!ELSE
signcode:
rem no signcode required
symbols:
rem no symbols required
backup:
rem no backup required
!ENDIF
build-release:

View file

@ -244,7 +244,6 @@ build-release-wrapper: clean-output global-versions
$(MAKE) -DSIGNCODE_BUILD build-release
$(MAKE) signcode
$(MAKE) -DTARGET=test test
$(MAKE) backup
!endif
cd $(ROOT)\src\engine\inst
@ -260,9 +259,6 @@ build-release-wrapper: clean-output global-versions
cd $(ROOT)\src\developer\inst
$(MAKE)
cd $(ROOT)\src\buildtools\inst
$(MAKE)
!endif
build-release-wrapper-oem:

View file

@ -6,7 +6,7 @@ NOTARGET_SIGNCODE=yes
TARGETS=devtools sentrytool tds2dbg mkver \
buildunidata build_standards_data buildpkg test-klog delphiprojectmanager
CLEANS=inst clean-buildtools
CLEANS=clean-buildtools
!include ..\Header.mak
@ -40,10 +40,6 @@ mkver:
cd $(ROOT)\src\buildtools\mkver
$(MAKE) $(TARGET)
inst:
cd $(ROOT)\src\buildtools\inst
$(MAKE) $(TARGET)
buildpkg:
cd $(ROOT)\src\buildtools\buildpkg
$(MAKE) $(TARGET)

View file

@ -17,7 +17,4 @@ clean: def-clean
signcode:
$(SIGNCODE) /d "Package Installer Creator for Server" $(PROGRAM)\online\Buildpkg.exe
backup:
rem #$(WZZIP) $(BUILD)\buildtools\Buildpkg.zip $(BACKUPDEFAULTS) Buildpkg.exe
!include ..\..\Target.mak

View file

@ -1,2 +0,0 @@
# These files generated during release build should not be committed
copydebug.mak

View file

@ -1,37 +0,0 @@
#
# Install tools Makefile
#
!include ..\..\Defines.mak
build: copydebug
clean:
cd $(ROOT)\src\buildtools\inst
-del copydebug.mak
-del srcbackup.mak
copydebug:
cd $(ROOT)\src
-del $(ROOT)\src\tds_file.txt
dir /b/s desktop\*.dbg desktop\*.pdb > $(ROOT)\src\tds_file.txt
dir /b/s developer\*.dbg developer\*.pdb >> $(ROOT)\src\tds_file.txt
dir /b/s engine\*.dbg engine\*.pdb >> $(ROOT)\src\tds_file.txt
$(WZZIP) -x!vc*.pdb tds.zip @tds_file.txt
-del $(ROOT)\src\tds_file.txt
cd $(ROOT)\src\buildtools\inst
$(MKVER_U) copydebug.in copydebug.mak
$(MAKE) -fcopydebug.mak
uploadsymbols:
cd $(ROOT)\src\buildtools\inst
$(MKVER_U) copydebug.in copydebug.mak
$(MAKE) -fcopydebug.mak uploadsymbols
srcbackup:
cd $(ROOT)\src\buildtools\inst
$(MKVER_U) $(ROOT)\src\buildtools\inst\srcbackup.in srcbackup.mak
make -fsrcbackup.mak
!include ..\..\Target.mak

View file

@ -1,42 +0,0 @@
#
# Copy debug information to the RELEASE directory
#
SIGNCODE_BUILD=1
!include ..\..\Defines.mak
copydebug:
-mkdir $(ROOT)\release\$Version
echo mkdir c:\kmsymbols > $(ROOT)\src\install.bat
echo mkdir c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
echo copy *.tds c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
echo copy *.dbg c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
echo copy *.pdb c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
echo regedit /s tds.reg >> $(ROOT)\src\install.bat
$(WZZIP) $(ROOT)\src\tds.zip $(ROOT)\src\install.bat $(ROOT)\src\buildtools\inst\tds.reg
rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin)
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
# ping localhost # delay for a few seconds
# $(WZSE) $(ROOT)\src\tds.zip -setup -t $(ROOT)\src\buildtools\inst\tds_dialog.txt -c .\install.bat
copy $(ROOT)\src\tds.zip $(ROOT)\release\$Version\tds-$Version.zip
-del $(ROOT)\src\tds.zip
del $(ROOT)\src\install.bat
# wait 1 second -- gives winzip time to finish
# ping -n 2 127.0.0.1
# copy $(ROOT)\src\tds.exe $(ROOT)\release\$Version\tds-$Version.exe
cd $(BUILD)
$(WZZIP) -r $(ROOT)\release\$Version\debug-$Version.zip *.zip
uploadsymbols:
rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin)
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"

View file

@ -1,10 +0,0 @@
#
# Copy debug information to the RELEASE directory
#
!include ..\..\Defines.mak
srcbackup:
-mkdir $(ROOT)\release\$Version
cd $(ROOT)\src
$(WZZIP) -r -x!*.ipch -x!*.sdf -x!*.db $(ROOT)\release\$Version\src-$Version.zip *.*

View file

@ -1,5 +0,0 @@
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Tavultesoft\Exception]
"SymbolPath"="c:\\kmsymbols\\"

View file

@ -1 +0,0 @@
Install Tavultesoft Keyman Desktop and Keyman Developer Symbol Files for Exception Reports

View file

@ -32,9 +32,6 @@ web:
signcode:
@rem
backup:
@rem
clean:
@rem

View file

@ -24,9 +24,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\desktop\insthelp.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\desktop\insthelp.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\desktop\insthelp.zip $(BACKUPDEFAULTS) insthelp.exe
install:
rem

View file

@ -27,7 +27,4 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\desktop\kmbrowserhost.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\desktop\kmbrowserhost.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\desktop\kmbrowserhost.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmbrowserhost.exe $(WIN32_TARGET_PATH)\kmbrowserhost.dbg
!include ..\..\Target.mak

View file

@ -23,9 +23,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\desktop\kmconfig.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\desktop\kmconfig.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\desktop\kmconfig.zip $(BACKUPDEFAULTS) kmconfig.exe
test-manifest:
# test that linked manifest exists and correct
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmconfig.exe -validate_manifest

View file

@ -38,9 +38,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\desktop\kmshell.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\desktop\kmshell.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\desktop\kmshell.zip $(BACKUPDEFAULTS) kmshell.exe
test-manifest:
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmshell.exe -validate_manifest

View file

@ -33,9 +33,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\desktop\setup.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\desktop\setup.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\desktop\setup.zip $(BACKUPDEFAULTS) setup.exe
test-manifest:
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\setup.exe -validate_manifest

View file

@ -76,10 +76,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\tike.exe /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\tike.dbg /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\tike.zip $(BACKUPDEFAULTS) tike.exe
install:
$(COPY) $(PROGRAM)\developer\tike.exe "$(INSTALLPATH_KEYMANDEVELOPER)\tike.exe"

View file

@ -44,9 +44,6 @@ build:
signcode:
@rem
backup:
@rem
wrap-symbols:
@rem

View file

@ -19,9 +19,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\kmanalyze.exe /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\kmanalyze.pdb /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\kmanalyze.exe $(BACKUPDEFAULTS) kmanalyze.exe
test-manifest:
@rem

View file

@ -39,9 +39,6 @@ wrap-symbols:
$(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.pdb /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.x64.pdb /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\kmcmpdll.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcmpdll.dll $(PROGRAM)\developer\kmcmpdll.x64.dll
test-manifest:
@rem This target needed as dependency for TIKE and KMCMPDLL

View file

@ -45,9 +45,6 @@ wrap-symbols:
$(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.dbg /t keyman-developer
#TODO: $(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.x64.dbg /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\kmcomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcomp.exe $(PROGRAM)\developer\kmcomp.x64.exe
test-manifest:
# test that (a) linked manifest exists and correct
$(MT) -nologo -inputresource:$(PROGRAM)\developer\kmcomp.exe -validate_manifest

View file

@ -30,9 +30,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\kmconvert.exe /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\kmconvert.dbg /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\kmconvert.zip $(BACKUPDEFAULTS) kmconvert.exe
test-manifest:
# test that (a) linked manifest exists and correct
$(MT) -nologo -inputresource:$(PROGRAM)\developer\kmconvert.exe -validate_manifest

View file

@ -20,9 +20,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\kmdecomp.exe /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\kmdecomp.pdb /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\kmdecomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmdecomp.exe
test-manifest:
@rem This target needed as dependency for KMDECOMP

View file

@ -18,9 +18,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\imsample.dll /t keyman-developer
$(SYMSTORE) $(DEBUGPATH)\developer\imsample.pdb /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\imsample.zip $(BACKUPDEFAULTS) imsample.dll
clean: def-clean
$(MSBUILD) imsample.sln $(MSBUILD_CLEAN)

View file

@ -32,9 +32,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\developer\setup.exe /t keyman-developer
$(SYMSTORE) $(ROOT)\src\developer\setup\setup.dbg /t keyman-developer
backup:
$(WZZIP) $(BUILD)\developer\devsetup.zip $(BACKUPDEFAULTS) setup.exe
test-manifest:
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
$(MT) -nologo -inputresource:$(PROGRAM)\developer\setup.exe -validate_manifest

View file

@ -18,10 +18,6 @@ signcode:
cd $(ROOT)\src\engine\inst\insthelper
$(MAKE) signcode
backup:
cd $(ROOT)\src\engine\inst\insthelper
$(MAKE) backup
wrap-symbols:
cd $(ROOT)\src\engine\inst\insthelper
$(MAKE) symbols

View file

@ -23,7 +23,4 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\inst\insthelper.dll /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\inst\insthelper.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\inst\insthelper.zip $(BACKUPDEFAULTS) insthelper.dll
!include ..\..\..\Target.mak

View file

@ -22,9 +22,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\keyman.dll /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\keyman.dbg /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\keyman.zip $(BACKUPDEFAULTS) keyman.exe
keymanmenuitem.res:
rc keymanmenuitem.rc

View file

@ -20,9 +20,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\keyman32.dll /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\keyman32.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\keyman32.zip $(BACKUPDEFAULTS) keyman32.dll
install:
$(COPY) $(PROGRAM)\engine\keyman32.dll "$(INSTALLPATH_KEYMANENGINE)\keyman32.dll"
echo You may want to manually tweak keyman-debug-etw.man and fill in $(INSTALLPATH_KEYMANENGINE)\keyman32.dll

View file

@ -20,9 +20,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\keyman64.dll /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\keyman64.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\keyman64.zip $(BACKUPDEFAULTS) keyman64.dll
install:
$(COPY) $(PROGRAM)\engine\keyman64.dll "$(INSTALLPATH_KEYMANENGINE)\keyman64.dll"

View file

@ -23,9 +23,6 @@ signcode:
wrap-symbols:
rem no symbols
backup:
$(WZZIP) $(BUILD)\engine\keymanmc.zip $(BACKUPDEFAULTS) keymanmc.dll
install:
$(COPY) $(PROGRAM)\engine\keymanmc.dll "$(INSTALLPATH_KEYMANENGINE)\keymanmc.dll"
reg add HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman /v CategoryCount /t REG_DWORD /d 1 /f

View file

@ -26,9 +26,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\keymanx64.exe /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\keymanx64.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\keymanx64.exe $(BACKUPDEFAULTS) keymanx64.exe
install:
$(COPY) $(PROGRAM)\engine\keymanx64.exe "$(INSTALLPATH_KEYMANENGINE)"

View file

@ -50,7 +50,4 @@ wrap-symbols:
$(SYMSTORE) $(DEBUGPATH)\engine\kmcomapi.dbg /t keyman-engine-windows
#TODO: $(SYMSTORE) $(PROGRAM)\engine\kmcomapi.x64.dbg /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\kmcomapi.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmcomapi.dll $(WIN64_TARGET_PATH)\kmcomapi.x64.dll $(WIN32_TARGET_PATH)\kmcomapi.dbg
!include ..\..\Target.mak

View file

@ -29,10 +29,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\kmrefresh.x64.exe /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\kmrefresh.x64.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\kmrefresh.x86.exe $(BACKUPDEFAULTS) kmrefresh.x86.exe
$(WZZIP) $(BUILD)\engine\kmrefresh.x64.exe $(BACKUPDEFAULTS) kmrefresh.x64.exe
install:
$(COPY) $(PROGRAM)\engine\kmrefresh.x86.exe "$(INSTALLPATH_KEYMANENGINE)"
$(COPY) $(PROGRAM)\engine\kmrefresh.x64.exe "$(INSTALLPATH_KEYMANENGINE)"

View file

@ -26,10 +26,6 @@ wrap-symbols:
$(SYMSTORE) $(DEBUGPATH)\engine\kmtip.pdb /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\kmtip64.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\kmtip.zip x86\kmtip.pdb $(BACKUPDEFAULTS) x86\kmtip.dll
$(WZZIP) $(BUILD)\engine\kmtip64.zip x64\kmtip64.pdb $(BACKUPDEFAULTS) x64\kmtip64.dll
install:
$(COPY) $(PROGRAM)\engine\kmtip.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip.dll"
$(COPY) $(PROGRAM)\engine\kmtip64.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip64.dll"

View file

@ -22,9 +22,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\mcompile.exe /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\mcompile.pdb /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\mcompile.exe $(BACKUPDEFAULTS) mcompile.exe
install:
$(COPY) $(PROGRAM)\engine\mcompile.exe "$(INSTALLPATH_KEYMANENGINE)"

View file

@ -32,9 +32,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\tsysinfo.exe /t keyman-engine-windows
$(SYMSTORE) $(DEBUGPATH)\engine\tsysinfo.dbg /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\tsysinfo.zip $(BACKUPDEFAULTS) tsysinfo.exe
install:
$(COPY) $(PROGRAM)\engine\tsysinfo.exe "$(INSTALLPATH_KEYMANENGINE)"
$(COPY) $(ROOT)\src\engine\tsysinfo\*.xsl "$(INSTALLPATH_KEYMANENGINE)"

View file

@ -18,9 +18,6 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\engine\tsysinfox64.exe /t keyman-engine-windows
#$(SYMSTORE) $(DEBUGPATH)\engine\tsysinfox64.dbg /t keyman-engine-windows
backup:
$(WZZIP) $(BUILD)\engine\tsysinfox64.zip $(BACKUPDEFAULTS) $(WIN64_TARGET_PATH)\*.map $(WIN64_TARGET_PATH)\tsysinfox64.exe
install:
echo Nothing to do.

View file

@ -36,9 +36,6 @@ clean:
signcode:
@rem no signcode
backup:
@rem
test-manifest:
@rem

View file

@ -17,7 +17,4 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\support\charident.exe /t keyman-windows
#TODO: $(SYMSTORE) $(DEBUGPATH)\support\charident.dbg /t keyman-windows
backup:
$(WZZIP) $(BUILD)\support\charident.zip $(BACKUPDEFAULTS) charident.exe
!include ..\..\Target.mak

View file

@ -19,7 +19,4 @@ wrap-symbols:
$(SYMSTORE) $(PROGRAM)\support\etl2log.exe /t keyman-windows
$(SYMSTORE) $(DEBUGPATH)\support\etl2log.pdb /t keyman-windows
backup:
$(WZZIP) $(BUILD)\support\etl2log.zip $(BACKUPDEFAULTS) etl2log.exe
!include ..\..\Target.mak

Some files were not shown because too many files have changed in this diff Show more