diff --git a/HISTORY.md b/HISTORY.md index 5bc3feb1d6..59feb28784 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -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) diff --git a/VERSION.md b/VERSION.md index 9220ad1e60..e0317f8c26 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -15.0.81 \ No newline at end of file +15.0.84 \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java index 5e3bcb4894..f636fa9c76 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/data/LexicalModel.java @@ -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 diff --git a/common/core/desktop/src/km_kbp_keyboard_api.cpp b/common/core/desktop/src/km_kbp_keyboard_api.cpp index 8a2ef7b3ad..04c8eab956 100644 --- a/common/core/desktop/src/km_kbp_keyboard_api.cpp +++ b/common/core/desktop/src/km_kbp_keyboard_api.cpp @@ -13,7 +13,7 @@ #include #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" diff --git a/common/core/desktop/src/kmx/kmx_actions.cpp b/common/core/desktop/src/kmx/kmx_actions.cpp index 03c70eb134..7eea892b63 100644 --- a/common/core/desktop/src/kmx/kmx_actions.cpp +++ b/common/core/desktop/src/kmx/kmx_actions.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include using namespace km::kbp; using namespace kmx; diff --git a/common/core/desktop/src/kmx/kmx_capslock.cpp b/common/core/desktop/src/kmx/kmx_capslock.cpp index 999fa08f64..cd11290af7 100644 --- a/common/core/desktop/src/kmx/kmx_capslock.cpp +++ b/common/core/desktop/src/kmx/kmx_capslock.cpp @@ -2,19 +2,19 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include 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) { diff --git a/common/core/desktop/src/kmx/kmx_consts.cpp b/common/core/desktop/src/kmx/kmx_consts.cpp index 4e549df995..199e3ff45a 100644 --- a/common/core/desktop/src/kmx/kmx_consts.cpp +++ b/common/core/desktop/src/kmx/kmx_consts.cpp @@ -1,5 +1,5 @@ -#include "kmx_processor.h" +#include "kmx_processevent.h" namespace km { namespace kbp { diff --git a/common/core/desktop/src/kmx/kmx_context.cpp b/common/core/desktop/src/kmx/kmx_context.cpp index 6efa11f94f..79db902d7d 100644 --- a/common/core/desktop/src/kmx/kmx_context.cpp +++ b/common/core/desktop/src/kmx/kmx_context.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include using namespace km::kbp; using namespace kmx; diff --git a/common/core/desktop/src/kmx/kmx_debug.cpp b/common/core/desktop/src/kmx/kmx_debug.cpp index a58fd8e17e..a820c6bc45 100644 --- a/common/core/desktop/src/kmx/kmx_debug.cpp +++ b/common/core/desktop/src/kmx/kmx_debug.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include "kmx_processor.h" +#include "kmx_processevent.h" #include #include diff --git a/common/core/desktop/src/kmx/kmx_environment.cpp b/common/core/desktop/src/kmx/kmx_environment.cpp index 4fac39182f..1e166225c1 100644 --- a/common/core/desktop/src/kmx/kmx_environment.cpp +++ b/common/core/desktop/src/kmx/kmx_environment.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include #include #include #include @@ -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)) { diff --git a/common/core/desktop/src/kmx/kmx_environment.h b/common/core/desktop/src/kmx/kmx_environment.h index c8c0e212df..f1ee70faf6 100644 --- a/common/core/desktop/src/kmx/kmx_environment.h +++ b/common/core/desktop/src/kmx/kmx_environment.h @@ -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; } diff --git a/common/core/desktop/src/kmx/kmx_file.cpp b/common/core/desktop/src/kmx/kmx_file.cpp index 2ce631a01c..1a6bdf64f3 100644 --- a/common/core/desktop/src/kmx/kmx_file.cpp +++ b/common/core/desktop/src/kmx/kmx_file.cpp @@ -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 #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; diff --git a/common/core/desktop/src/kmx/kmx_modifiers.cpp b/common/core/desktop/src/kmx/kmx_modifiers.cpp index 1da7a8a3c9..0de3ce6ed8 100644 --- a/common/core/desktop/src/kmx/kmx_modifiers.cpp +++ b/common/core/desktop/src/kmx/kmx_modifiers.cpp @@ -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 // diff --git a/common/core/desktop/src/kmx/kmx_options.cpp b/common/core/desktop/src/kmx/kmx_options.cpp index 51538d2157..a4417cb755 100644 --- a/common/core/desktop/src/kmx/kmx_options.cpp +++ b/common/core/desktop/src/kmx/kmx_options.cpp @@ -3,7 +3,7 @@ Authors: mcdurdin */ #include "processor.hpp" -#include "kmx_processor.h" +#include "kmx_processevent.h" #include #include diff --git a/common/core/desktop/src/kmx/kmx_processevent.cpp b/common/core/desktop/src/kmx/kmx_processevent.cpp index 58bc29e33e..893ff1d9ba 100644 --- a/common/core/desktop/src/kmx/kmx_processevent.cpp +++ b/common/core/desktop/src/kmx/kmx_processevent.cpp @@ -1,189 +1,682 @@ -#include +/* + 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(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(m_environment.baseLayout().c_str())) == 0 || // I4583 + u16icmp(layoutName, static_cast(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. + 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; +} diff --git a/common/core/desktop/src/kmx/kmx_processor.h b/common/core/desktop/src/kmx/kmx_processevent.h similarity index 98% rename from common/core/desktop/src/kmx/kmx_processor.h rename to common/core/desktop/src/kmx/kmx_processevent.h index 22ca170e84..029d5508ee 100644 --- a/common/core/desktop/src/kmx/kmx_processor.h +++ b/common/core/desktop/src/kmx/kmx_processevent.h @@ -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 diff --git a/common/core/desktop/src/kmx/kmx_processor.cpp b/common/core/desktop/src/kmx/kmx_processor.cpp index 01dfbc6e59..18ec00d067 100644 --- a/common/core/desktop/src/kmx/kmx_processor.cpp +++ b/common/core/desktop/src/kmx/kmx_processor.cpp @@ -1,682 +1,189 @@ -/* - Copyright: Copyright (C) 2003-2018 SIL International. - Authors: mcdurdin -*/ -#include "kmx_processor.h" +#include #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(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(m_environment.baseLayout().c_str())) == 0 || // I4583 - u16icmp(layoutName, static_cast(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. - 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 diff --git a/common/core/desktop/src/kmx/kmx_processevent.hpp b/common/core/desktop/src/kmx/kmx_processor.hpp similarity index 94% rename from common/core/desktop/src/kmx/kmx_processevent.hpp rename to common/core/desktop/src/kmx/kmx_processor.hpp index 29ba66e321..4c71db4922 100644 --- a/common/core/desktop/src/kmx/kmx_processevent.hpp +++ b/common/core/desktop/src/kmx/kmx_processor.hpp @@ -10,7 +10,7 @@ #include #include -#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, diff --git a/common/core/desktop/src/kmx/kmx_xstring.cpp b/common/core/desktop/src/kmx/kmx_xstring.cpp index 87267278a9..47cb7d5d50 100644 --- a/common/core/desktop/src/kmx/kmx_xstring.cpp +++ b/common/core/desktop/src/kmx/kmx_xstring.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "kmx_processor.h" +#include "kmx_processevent.h" #include "utfcodec.hpp" diff --git a/common/core/desktop/tests/unit/kmx/kmx.cpp b/common/core/desktop/tests/unit/kmx/kmx.cpp index 2f4ec39945..f2f1b3b6ee 100644 --- a/common/core/desktop/tests/unit/kmx/kmx.cpp +++ b/common/core/desktop/tests/unit/kmx/kmx.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include "path.hpp" #include "state.hpp" diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json index a2e109e69c..b263172ee1 100644 --- a/common/core/web/input-processor/package-lock.json +++ b/common/core/web/input-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/input-processor", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json index ff8afc5cdc..8664c94d23 100644 --- a/common/core/web/input-processor/package.json +++ b/common/core/web/input-processor/package.json @@ -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" } } diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json index 3a028078b9..147045dae3 100644 --- a/common/core/web/keyboard-processor/package-lock.json +++ b/common/core/web/keyboard-processor/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/keyboard-processor", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json index 8c31643bb6..ab94677948 100644 --- a/common/core/web/keyboard-processor/package.json +++ b/common/core/web/keyboard-processor/package.json @@ -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" } } diff --git a/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts index 6cdddf7d83..442d23de64 100644 --- a/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/core/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -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; } } diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json index 71dd357905..3425b059ea 100644 --- a/common/core/web/tools/recorder/package-lock.json +++ b/common/core/web/tools/recorder/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/recorder-core", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json index a56b6cd528..7bb39e4af8 100644 --- a/common/core/web/tools/recorder/package.json +++ b/common/core/web/tools/recorder/package.json @@ -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": { diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index cb17ad7e4b..87cab704e6 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-sentry-manager", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index 9e8809f51e..d92e9ade3f 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -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" } } diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json index bc628008a3..af4acd9319 100644 --- a/common/core/web/utils/package-lock.json +++ b/common/core/web/utils/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/web-utils", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json index 9f70cd704f..54de85e484 100644 --- a/common/core/web/utils/package.json +++ b/common/core/web/utils/package.json @@ -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" } diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json index 7dd5375e55..4b3f57c598 100644 --- a/common/models/templates/package-lock.json +++ b/common/models/templates/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-templates", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 6199c86dd0..efda44b4a1 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -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" } } diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json index 554c9cc229..2db4fccbf0 100644 --- a/common/models/types/package-lock.json +++ b/common/models/types/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-types", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/common/models/types/package.json b/common/models/types/package.json index 4279d6d966..1dbfa29e1a 100644 --- a/common/models/types/package.json +++ b/common/models/types/package.json @@ -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", diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json index 493ea66c94..aba2382baa 100644 --- a/common/models/wordbreakers/package-lock.json +++ b/common/models/wordbreakers/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/models-wordbreakers", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index 905dc5799a..e379a2a6ff 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -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", diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json index 22ba2e71b6..bacc164e2d 100644 --- a/common/predictive-text/package-lock.json +++ b/common/predictive-text/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-layer", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 6f36d8c55b..a21b30afdd 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -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" diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json index 7ed35172d1..bae7464bbb 100644 --- a/developer/js/package-lock.json +++ b/developer/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/lexical-model-compiler", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/developer/js/package.json b/developer/js/package.json index e6cdd1355f..ca083f2bff 100644 --- a/developer/js/package.json +++ b/developer/js/package.json @@ -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", diff --git a/lerna.json b/lerna.json index 48bf23f036..6ecc7ec258 100644 --- a/lerna.json +++ b/lerna.json @@ -20,5 +20,5 @@ "ts-node", "typescript" ], - "version": "15.0.81" + "version": "15.0.84" } diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json index 639e453c09..cc27ea08bf 100644 --- a/resources/gosh/package-lock.json +++ b/resources/gosh/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 1 } diff --git a/resources/gosh/package.json b/resources/gosh/package.json index 3b4566d713..26cfffb979 100644 --- a/resources/gosh/package.json +++ b/resources/gosh/package.json @@ -1,6 +1,6 @@ { "name": "@keymanapp/resources-gosh", - "version": "15.0.81", + "version": "15.0.84", "description": "Good Ol' Shell", "author": "Marc Durdin (https://github.com/mcdurdin)", "contributors": [ diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json index 1b2f3939b1..ec47e489d8 100644 --- a/resources/web-environment/package-lock.json +++ b/resources/web-environment/package-lock.json @@ -1,5 +1,5 @@ { "name": "@keymanapp/web-environment", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 1 } diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json index 67cd6c2b01..b48d1d379e 100644 --- a/resources/web-environment/package.json +++ b/resources/web-environment/package.json @@ -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 (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" } } diff --git a/web/package-lock.json b/web/package-lock.json index a816876426..60da2d0797 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "keyman", - "version": "15.0.81", + "version": "15.0.84", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/web/package.json b/web/package.json index 6ff8e612fb..f3488d8524 100644 --- a/web/package.json +++ b/web/package.json @@ -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" diff --git a/web/source/kmwembedded.ts b/web/source/kmwembedded.ts index 15a32020da..d548909314 100644 --- a/web/source/kmwembedded.ts +++ b/web/source/kmwembedded.ts @@ -2,6 +2,7 @@ // References the base Keyman object (and consequently, the rest of the core objects). /// /// +/// // 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 = ( 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; }; /** diff --git a/web/source/osk/banner.ts b/web/source/osk/banner.ts index 7d69399876..0ba00a3920 100644 --- a/web/source/osk/banner.ts +++ b/web/source/osk/banner.ts @@ -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 { diff --git a/web/source/osk/browser/pendingLongpress.ts b/web/source/osk/browser/pendingLongpress.ts new file mode 100644 index 0000000000..84b2364dc0 --- /dev/null +++ b/web/source/osk/browser/pendingLongpress.ts @@ -0,0 +1,65 @@ +/// +/// + +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; + + 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(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)); + } + } + } +} diff --git a/web/source/osk/browser/subkeyPopup.ts b/web/source/osk/browser/subkeyPopup.ts index 4879140c13..23c7e24bee 100644 --- a/web/source/osk/browser/subkeyPopup.ts +++ b/web/source/osk/browser/subkeyPopup.ts @@ -1,16 +1,54 @@ /// +/// 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; + + // 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(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; + } + } } -} \ No newline at end of file +} diff --git a/web/source/osk/embedded/pendingLongpress.ts b/web/source/osk/embedded/pendingLongpress.ts new file mode 100644 index 0000000000..504c60f1e3 --- /dev/null +++ b/web/source/osk/embedded/pendingLongpress.ts @@ -0,0 +1,48 @@ +/// +/// + +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; + + constructor(vkbd: VisualKeyboard, e: KeyElement) { + this.vkbd = vkbd; + let _this = this; + + this.promise = new Promise(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; + } + } + } +} \ No newline at end of file diff --git a/web/source/osk/embedded/subkeyDelegator.ts b/web/source/osk/embedded/subkeyDelegator.ts new file mode 100644 index 0000000000..645bb8c6a7 --- /dev/null +++ b/web/source/osk/embedded/subkeyDelegator.ts @@ -0,0 +1,110 @@ +/// + +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; + + private movedFromBaseKey: boolean = false; + private baseKeySelected: boolean = false; + + constructor(vkbd: VisualKeyboard, e: KeyElement) { + this.vkbd = vkbd; + + let _this = this; + this.promise = new Promise(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; + } + } + } +} diff --git a/web/source/osk/oskKey.ts b/web/source/osk/oskKey.ts index f9e4509992..7a0bd2dd32 100644 --- a/web/source/osk/oskKey.ts +++ b/web/source/osk/oskKey.ts @@ -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); + } } -} \ No newline at end of file +} diff --git a/web/source/osk/pendingGesture.interface.ts b/web/source/osk/pendingGesture.interface.ts new file mode 100644 index 0000000000..637b108f58 --- /dev/null +++ b/web/source/osk/pendingGesture.interface.ts @@ -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; + + cancel(): void; + resolve?(): void; + } +} \ No newline at end of file diff --git a/web/source/osk/realizedGesture.interface.ts b/web/source/osk/realizedGesture.interface.ts new file mode 100644 index 0000000000..faabf0a367 --- /dev/null +++ b/web/source/osk/realizedGesture.interface.ts @@ -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; + + clear(): void; + isVisible(): boolean; + updateTouch(touch: Touch): void; + } +} \ No newline at end of file diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts index 7906d61db7..93c967b7de 100644 --- a/web/source/osk/visualKeyboard.ts +++ b/web/source/osk/visualKeyboard.ts @@ -3,7 +3,7 @@ /// /// /// -/// +/// 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 = 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; diff --git a/windows/src/Defines.mak b/windows/src/Defines.mak index f430c09840..47a81c6b19 100644 --- a/windows/src/Defines.mak +++ b/windows/src/Defines.mak @@ -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 # diff --git a/windows/src/Header.mak b/windows/src/Header.mak index c6a0b15eac..dee277efbc 100644 --- a/windows/src/Header.mak +++ b/windows/src/Header.mak @@ -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: diff --git a/windows/src/Makefile b/windows/src/Makefile index 36c9ba9d17..4b35eac954 100644 --- a/windows/src/Makefile +++ b/windows/src/Makefile @@ -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: diff --git a/windows/src/buildtools/Makefile b/windows/src/buildtools/Makefile index fdfa300b52..d84bef5bef 100644 --- a/windows/src/buildtools/Makefile +++ b/windows/src/buildtools/Makefile @@ -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) diff --git a/windows/src/buildtools/buildpkg/Makefile b/windows/src/buildtools/buildpkg/Makefile index 07ff23c4b7..dd567d8b31 100644 --- a/windows/src/buildtools/buildpkg/Makefile +++ b/windows/src/buildtools/buildpkg/Makefile @@ -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 diff --git a/windows/src/buildtools/inst/.gitignore b/windows/src/buildtools/inst/.gitignore deleted file mode 100644 index 5587f1f702..0000000000 --- a/windows/src/buildtools/inst/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# These files generated during release build should not be committed -copydebug.mak diff --git a/windows/src/buildtools/inst/Makefile b/windows/src/buildtools/inst/Makefile deleted file mode 100644 index d5c621084f..0000000000 --- a/windows/src/buildtools/inst/Makefile +++ /dev/null @@ -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 diff --git a/windows/src/buildtools/inst/copydebug.in b/windows/src/buildtools/inst/copydebug.in deleted file mode 100644 index 4f7cb7e761..0000000000 --- a/windows/src/buildtools/inst/copydebug.in +++ /dev/null @@ -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" - diff --git a/windows/src/buildtools/inst/srcbackup.in b/windows/src/buildtools/inst/srcbackup.in deleted file mode 100644 index f957819f7d..0000000000 --- a/windows/src/buildtools/inst/srcbackup.in +++ /dev/null @@ -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 *.* diff --git a/windows/src/buildtools/inst/tds.reg b/windows/src/buildtools/inst/tds.reg deleted file mode 100644 index b7c5ac6dd5..0000000000 --- a/windows/src/buildtools/inst/tds.reg +++ /dev/null @@ -1,5 +0,0 @@ -REGEDIT4 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Tavultesoft\Exception] -"SymbolPath"="c:\\kmsymbols\\" - diff --git a/windows/src/buildtools/inst/tds_dialog.txt b/windows/src/buildtools/inst/tds_dialog.txt deleted file mode 100644 index e4d94c60ac..0000000000 --- a/windows/src/buildtools/inst/tds_dialog.txt +++ /dev/null @@ -1 +0,0 @@ -Install Tavultesoft Keyman Desktop and Keyman Developer Symbol Files for Exception Reports diff --git a/windows/src/desktop/help/Makefile b/windows/src/desktop/help/Makefile index b9aa91645b..6ef32d2b58 100644 --- a/windows/src/desktop/help/Makefile +++ b/windows/src/desktop/help/Makefile @@ -32,9 +32,6 @@ web: signcode: @rem -backup: - @rem - clean: @rem diff --git a/windows/src/desktop/insthelp/Makefile b/windows/src/desktop/insthelp/Makefile index 86796906d0..1c5f77dcc3 100644 --- a/windows/src/desktop/insthelp/Makefile +++ b/windows/src/desktop/insthelp/Makefile @@ -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 diff --git a/windows/src/desktop/kmbrowserhost/Makefile b/windows/src/desktop/kmbrowserhost/Makefile index fa126024c8..32ba89e630 100644 --- a/windows/src/desktop/kmbrowserhost/Makefile +++ b/windows/src/desktop/kmbrowserhost/Makefile @@ -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 diff --git a/windows/src/desktop/kmconfig/Makefile b/windows/src/desktop/kmconfig/Makefile index 75a3587c18..d063237156 100644 --- a/windows/src/desktop/kmconfig/Makefile +++ b/windows/src/desktop/kmconfig/Makefile @@ -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 diff --git a/windows/src/desktop/kmshell/Makefile b/windows/src/desktop/kmshell/Makefile index fd3093041b..65dd5b5297 100644 --- a/windows/src/desktop/kmshell/Makefile +++ b/windows/src/desktop/kmshell/Makefile @@ -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 diff --git a/windows/src/desktop/setup/Makefile b/windows/src/desktop/setup/Makefile index 12a5195e55..499dc2e38e 100644 --- a/windows/src/desktop/setup/Makefile +++ b/windows/src/desktop/setup/Makefile @@ -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 diff --git a/windows/src/developer/TIKE/Makefile b/windows/src/developer/TIKE/Makefile index 857feeed5c..cf466bc2f4 100644 --- a/windows/src/developer/TIKE/Makefile +++ b/windows/src/developer/TIKE/Makefile @@ -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" diff --git a/windows/src/developer/inst/Makefile b/windows/src/developer/inst/Makefile index 3357750dd9..cdf3ac8549 100644 --- a/windows/src/developer/inst/Makefile +++ b/windows/src/developer/inst/Makefile @@ -44,9 +44,6 @@ build: signcode: @rem -backup: - @rem - wrap-symbols: @rem diff --git a/windows/src/developer/kmanalyze/Makefile b/windows/src/developer/kmanalyze/Makefile index 0bbac14a23..adda01501d 100644 --- a/windows/src/developer/kmanalyze/Makefile +++ b/windows/src/developer/kmanalyze/Makefile @@ -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 diff --git a/windows/src/developer/kmcmpdll/Makefile b/windows/src/developer/kmcmpdll/Makefile index 72ba2d861a..dac9020154 100644 --- a/windows/src/developer/kmcmpdll/Makefile +++ b/windows/src/developer/kmcmpdll/Makefile @@ -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 diff --git a/windows/src/developer/kmcomp/Makefile b/windows/src/developer/kmcomp/Makefile index 059810af2c..c866336036 100644 --- a/windows/src/developer/kmcomp/Makefile +++ b/windows/src/developer/kmcomp/Makefile @@ -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 diff --git a/windows/src/developer/kmconvert/Makefile b/windows/src/developer/kmconvert/Makefile index 9264d9f2ae..09eff7e0a8 100644 --- a/windows/src/developer/kmconvert/Makefile +++ b/windows/src/developer/kmconvert/Makefile @@ -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 diff --git a/windows/src/developer/kmdecomp/Makefile b/windows/src/developer/kmdecomp/Makefile index 7bf26de79c..1c221fed1a 100644 --- a/windows/src/developer/kmdecomp/Makefile +++ b/windows/src/developer/kmdecomp/Makefile @@ -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 diff --git a/windows/src/developer/samples/imsample/Makefile b/windows/src/developer/samples/imsample/Makefile index d480d00d93..cb4b0b6786 100644 --- a/windows/src/developer/samples/imsample/Makefile +++ b/windows/src/developer/samples/imsample/Makefile @@ -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) diff --git a/windows/src/developer/setup/Makefile b/windows/src/developer/setup/Makefile index 29abb289c2..3ec095b249 100644 --- a/windows/src/developer/setup/Makefile +++ b/windows/src/developer/setup/Makefile @@ -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 diff --git a/windows/src/engine/inst/Makefile b/windows/src/engine/inst/Makefile index 72bf68b4ab..db08b76ead 100644 --- a/windows/src/engine/inst/Makefile +++ b/windows/src/engine/inst/Makefile @@ -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 diff --git a/windows/src/engine/inst/insthelper/Makefile b/windows/src/engine/inst/insthelper/Makefile index 6af633808e..b71f45a804 100644 --- a/windows/src/engine/inst/insthelper/Makefile +++ b/windows/src/engine/inst/insthelper/Makefile @@ -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 diff --git a/windows/src/engine/keyman/Makefile b/windows/src/engine/keyman/Makefile index 86af3f9214..dcb68ff542 100644 --- a/windows/src/engine/keyman/Makefile +++ b/windows/src/engine/keyman/Makefile @@ -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 diff --git a/windows/src/engine/keyman32/Makefile b/windows/src/engine/keyman32/Makefile index 2caeb17985..9b1372370e 100644 --- a/windows/src/engine/keyman32/Makefile +++ b/windows/src/engine/keyman32/Makefile @@ -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 diff --git a/windows/src/engine/keyman64/Makefile b/windows/src/engine/keyman64/Makefile index e6714f289c..401d72fb6d 100644 --- a/windows/src/engine/keyman64/Makefile +++ b/windows/src/engine/keyman64/Makefile @@ -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" diff --git a/windows/src/engine/keymanmc/Makefile b/windows/src/engine/keymanmc/Makefile index 57b35df8ce..d9405f85cb 100644 --- a/windows/src/engine/keymanmc/Makefile +++ b/windows/src/engine/keymanmc/Makefile @@ -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 diff --git a/windows/src/engine/keymanx64/Makefile b/windows/src/engine/keymanx64/Makefile index 420a6d868a..148d040c4b 100644 --- a/windows/src/engine/keymanx64/Makefile +++ b/windows/src/engine/keymanx64/Makefile @@ -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)" diff --git a/windows/src/engine/kmcomapi/Makefile b/windows/src/engine/kmcomapi/Makefile index d39b830626..b41d3ee724 100644 --- a/windows/src/engine/kmcomapi/Makefile +++ b/windows/src/engine/kmcomapi/Makefile @@ -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 diff --git a/windows/src/engine/kmrefresh/Makefile b/windows/src/engine/kmrefresh/Makefile index a3b676ae52..d0f23daec9 100644 --- a/windows/src/engine/kmrefresh/Makefile +++ b/windows/src/engine/kmrefresh/Makefile @@ -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)" diff --git a/windows/src/engine/kmtip/Makefile b/windows/src/engine/kmtip/Makefile index 1e18252772..2b3ad8467a 100644 --- a/windows/src/engine/kmtip/Makefile +++ b/windows/src/engine/kmtip/Makefile @@ -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" diff --git a/windows/src/engine/mcompile/Makefile b/windows/src/engine/mcompile/Makefile index 0f671c7268..447499576e 100644 --- a/windows/src/engine/mcompile/Makefile +++ b/windows/src/engine/mcompile/Makefile @@ -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)" diff --git a/windows/src/engine/tsysinfo/Makefile b/windows/src/engine/tsysinfo/Makefile index a7f4de01b5..49593cd211 100644 --- a/windows/src/engine/tsysinfo/Makefile +++ b/windows/src/engine/tsysinfo/Makefile @@ -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)" diff --git a/windows/src/engine/tsysinfox64/Makefile b/windows/src/engine/tsysinfox64/Makefile index 9ec8e58049..d5161d0ca1 100644 --- a/windows/src/engine/tsysinfox64/Makefile +++ b/windows/src/engine/tsysinfox64/Makefile @@ -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. diff --git a/windows/src/global/Makefile b/windows/src/global/Makefile index 842f8a178d..f783e31de1 100644 --- a/windows/src/global/Makefile +++ b/windows/src/global/Makefile @@ -36,9 +36,6 @@ clean: signcode: @rem no signcode -backup: - @rem - test-manifest: @rem diff --git a/windows/src/support/charident/Makefile b/windows/src/support/charident/Makefile index d3c91a1ca6..dd6721c5c0 100644 --- a/windows/src/support/charident/Makefile +++ b/windows/src/support/charident/Makefile @@ -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 diff --git a/windows/src/support/etl2log/Makefile b/windows/src/support/etl2log/Makefile index 0e9ae69ffd..48bdb1a592 100644 --- a/windows/src/support/etl2log/Makefile +++ b/windows/src/support/etl2log/Makefile @@ -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 diff --git a/windows/src/support/km_yim/Makefile b/windows/src/support/km_yim/Makefile index 661c17e12a..f0f93e6fe2 100644 --- a/windows/src/support/km_yim/Makefile +++ b/windows/src/support/km_yim/Makefile @@ -19,7 +19,5 @@ signcode: wrap-symbols: rem -backup: - $(WZZIP) $(BUILD)\desktop\km_yim.zip $(BACKUPDEFAULTS) km_yim.exe !include ..\..\Target.mak diff --git a/windows/src/support/kmkb0045/Makefile b/windows/src/support/kmkb0045/Makefile index b521bfd026..3e914c8acd 100644 --- a/windows/src/support/kmkb0045/Makefile +++ b/windows/src/support/kmkb0045/Makefile @@ -15,8 +15,5 @@ clean: def-clean signcode: $(SIGNCODE) /d "Tavultesoft KMKB0045" $(PROGRAM)\support\kmkb0045.exe -backup: - $(WZZIP) $(BUILD)\support\kmkb0045.zip $(BACKUPDEFAULTS) kmkb0045.exe - !include ..\..\Target.mak diff --git a/windows/src/support/oskbulkrenderer/Makefile b/windows/src/support/oskbulkrenderer/Makefile index c008301503..12230ea01a 100644 --- a/windows/src/support/oskbulkrenderer/Makefile +++ b/windows/src/support/oskbulkrenderer/Makefile @@ -18,9 +18,6 @@ clean: def-clean signcode: rem Not signing this utility -backup: - rem No source code backup needed - wrap-symbols: @rem diff --git a/windows/src/test/manual-tests/test_httpuploader/Makefile b/windows/src/test/manual-tests/test_httpuploader/Makefile index 31bcb34c66..b6250f4bfd 100644 --- a/windows/src/test/manual-tests/test_httpuploader/Makefile +++ b/windows/src/test/manual-tests/test_httpuploader/Makefile @@ -15,7 +15,4 @@ clean: def-clean signcode: rem no signcode -backup: - rem no backup - !include ..\..\Target.mak