Merge branch 'beta' into web-64-touch-alias-api

This commit is contained in:
Joshua Horton 2019-01-03 08:28:25 +07:00 committed by GitHub
commit 06713c2b0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 540 additions and 468 deletions

View file

@ -1,20 +1,28 @@
# Keyman for Android
## 2019-01-02 11.0.2050 beta
* Initial beta release of Keyman for Android 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Aandroid+-label%3Acherry-pick+-label%3Astable)
## 11.0 alpha
* Move to 11.0
* Add round launcher icons (#1077)
* Change default keyboard from `european` to `sil_euro_latin` (#1112)
* Remove deprecated ad-hoc distribution of keyboards via `keyman://` protocol (#1109)
* Add splash screen (#1151)
* Update app to use Material Design theme (#681)
* Fix globe button when pausing WebBrowser (#1213)
* Change system keyboard to "numeric" layer for digit/phone number text fields (#1218)
* Add feature to vibrate device when Keyman Web calls `beep` (#1227)
* Add support for extra key found on European hardware keyboards (#1291)
* Add feature for keyboard picker to switch to next keyboard (#1283)
* Update to Cloud API 4.0 for downloading keyboards (#1320)
* Fixes issue where file extensions are upper-case, e.g. ".TTF" (#1333)
* New Features:
* System keyboard changes to "numeric" layer for digit/phone number text fields (#1218)
* Device vibrates when Keyman Web calls `beep` -- when invalid combinations are pressed (#1227)
* Added support for 102nd key found on European hardware keyboards (#1291)
* Keyboard picker can now switch to next system keyboard (#1283)
* Changes:
* Added round launcher icons (#1077)
* Added splash screen (#1151)
* Updated app to use Material Design theme (#681, #1378, #1303)
* Updated to Cloud API 4.0 for downloading keyboards (#1320)
* Removed deprecated ad-hoc distribution of keyboards via `keyman://` protocol (#1109)
* Changed default keyboard from `european` to `sil_euro_latin` (#1112, #1400)
* Bug fixes:
* Diacritics now display more consistently on key caps (#1407)
* Fixed globe button when pausing WebBrowser (#1213)
* Fixed issue where file extensions are upper-case, e.g. ".TTF" (#1333)
* Fixed various crashes (#1108, #1057)
## 2018-11-14 10.0.508 stable
* Fix crash that can occur when text selection ends before the starting position (#1313)

View file

@ -42,9 +42,9 @@ km_kbp_state_option_lookup(km_kbp_state const *state,
if (scope == KM_KBP_OPT_UNKNOWN || scope > KM_KBP_OPT_MAX_SCOPES)
return KM_KBP_STATUS_INVALID_ARGUMENT;
auto & opts = state->options();
auto & processor = state->processor();
*value_out = opts.lookup(km_kbp_option_scope(scope), key);
*value_out = processor.lookup_option(km_kbp_option_scope(scope), key);
if (!*value_out) return KM_KBP_STATUS_KEY_ERROR;
return KM_KBP_STATUS_OK;
@ -57,7 +57,7 @@ km_kbp_state_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
assert(state); assert(opt);
if (!state|| !opt) return KM_KBP_STATUS_INVALID_ARGUMENT;
auto & opts = state->options();
auto & processor = state->processor();
try
{
@ -66,7 +66,10 @@ km_kbp_state_options_update(km_kbp_state *state, km_kbp_option_item const *opt)
if (opt->scope == KM_KBP_OPT_UNKNOWN || opt->scope > KM_KBP_OPT_MAX_SCOPES)
return KM_KBP_STATUS_INVALID_ARGUMENT;
if (!opts.assign(state, km_kbp_option_scope(opt->scope), opt->key, opt->value))
if (processor.update_option(
km_kbp_option_scope(opt->scope),
opt->key,
opt->value).empty())
return KM_KBP_STATUS_KEY_ERROR;
}
}
@ -92,7 +95,8 @@ km_kbp_state_options_to_json(km_kbp_state const *state, char *buf, size_t *space
try
{
jo << state->options();
// TODO: Fix
// jo << state->options();
}
catch (std::bad_alloc)
{

View file

@ -176,7 +176,7 @@ km_kbp_status km_kbp_state_to_json(km_kbp_state const *state,
jo << json::object
<< "$schema" << "keyman/keyboardprocessor/doc/introspection.schema"
<< "keyboard" << state->processor().keyboard()
<< "options" << state->options()
// << "options" << state->options() TODO: Fix
<< "context" << state->context()
<< "actions" << state->actions()
<< json::close;

View file

@ -4,7 +4,8 @@
*/
#include <kmx/kmx_processor.h>
using namespace km::kbp::kmx;
using namespace km::kbp;
using namespace kmx;
void KMX_Actions::ResetQueue()
{
@ -31,7 +32,7 @@ KMX_BOOL KMX_Actions::QueueAction(int ItemType, KMX_DWORD dwData)
QueueSize++;
int result = TRUE;
switch(ItemType)
{
case QIT_VKEYDOWN:

View file

@ -21,23 +21,41 @@ namespace {
}
KMX_Environment::KMX_Environment() {
Load(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
Load(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
Load(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
Load(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
Load(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
Load(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
}
void KMX_Environment::InitOption(std::vector<option> &default_env, km_kbp_cp const *key, km_kbp_cp const *default_value) {
default_env.emplace_back(KM_KBP_OPT_ENVIRONMENT, key, default_value);
Load(key, default_value);
}
void KMX_Environment::Init(std::vector<option> &default_env) {
InitOption(default_env, KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
InitOption(default_env, KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
InitOption(default_env, KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
InitOption(default_env, KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
char16_t const * KMX_Environment::LookUp(std::u16string const & key) const {
assert(!key.empty());
if (!key.empty()) return nullptr;
// TODO refactor this and the keyboard option terminator into state.cpp and keyboard.cpp respectively
default_env.emplace_back();
if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) {
return _platform.c_str();
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUT)) {
return _baseLayout.c_str();
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTALT)) {
return _baseLayoutAlt.c_str();
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_SIMULATEALTGR)) {
return _simulateAltGr ? u"1" : u"0";
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_CAPSLOCK)) {
return _capsLock ? u"1" : u"0";
}
else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) {
return _baseLayoutGivesCtrlRAltForRAlt ? u"1" : u"0";
}
else {
// Unsupported key
return nullptr;
}
}

View file

@ -21,7 +21,7 @@ private:
public:
KMX_Environment();
void Load(std::u16string const & key, std::u16string const & value);
void Init(std::vector<option> &default_env);
char16_t const * LookUp(std::u16string const & key) const;
KMX_BOOL capsLock() const noexcept { return _capsLock; }
KMX_BOOL simulateAltGr() const noexcept { return _simulateAltGr; }

View file

@ -2,6 +2,7 @@
Copyright: Copyright (C) 2003-2018 SIL International.
Authors: mcdurdin
*/
#include "processor.hpp"
#include "kmx_processor.h"
#include <option.hpp>
#include <state.hpp>
@ -15,7 +16,7 @@ int KMX_Options::_GetIndex(std::u16string const &key) const {
for (auto sp = _kp->Keyboard->dpStoreArray;
i != _kp->Keyboard->cxStoreArray; ++i, ++sp)
{
if (sp->dpName && sp->dpName == key) break;
if (sp->dpName && sp->dpName == key) return i;
}
return -1;
@ -41,20 +42,19 @@ void KMX_Options::AddOptionsStoresFromXString(PKMX_WCHAR s) {
}
}
void KMX_Options::Load(options *options, std::u16string const &key) {
void KMX_Options::Load(abstract_processor & ap, std::u16string const &key) {
LPSTORE sp;
auto i = 0U;
assert(options != nullptr);
assert(!key.empty());
if (options == nullptr || key.empty()) return;
if (key.empty()) return;
for (i = 0, sp = _kp->Keyboard->dpStoreArray; i < _kp->Keyboard->cxStoreArray; i++, sp++) {
if (_kp->KeyboardOptions[i].OriginalStore != NULL
&& sp->dpName != NULL
&& u16icmp(sp->dpName, key.c_str()) == 0) {
Reset(options, i);
Reset(ap, i);
return;
}
}
@ -124,6 +124,16 @@ KMX_Options::~KMX_Options()
_kp->KeyboardOptions = NULL;
}
char16_t const * KMX_Options::LookUp(std::u16string const &key) const
{
auto idx = _GetIndex(key);
if (idx < 0) return nullptr;
return _kp->Keyboard->dpStoreArray[idx].dpString;
}
void KMX_Options::Set(int nStoreToSet, std::u16string const & rValueToSet)
{
assert(_kp != NULL);
@ -158,8 +168,7 @@ void KMX_Options::Set(int nStoreToSet, int nStoreToRead)
Set(nStoreToSet, rStoreToReadValue);
}
void KMX_Options::Reset(options *options, int nStoreToReset)
void KMX_Options::Reset(abstract_processor & ap, int nStoreToReset)
{
assert(_kp != NULL);
assert(_kp->Keyboard != NULL);
@ -179,17 +188,17 @@ void KMX_Options::Reset(options *options, int nStoreToReset)
if(rStoreToReset.dpName == nullptr) return;
// Now we need to go back and get any saved value from KPAPI. internal_value is owned by options api
km_kbp_cp const *internal_value = options->lookup(km_kbp_option_scope(KM_KBP_OPT_KEYBOARD), _kp->Keyboard->dpStoreArray[nStoreToReset].dpName);
if(internal_value) {
auto i = ap.persisted_store().find(rStoreToReset.dpName);
if(i != ap.persisted_store().end()) {
// Copy the value from KPAPI
_kp->KeyboardOptions[nStoreToReset].Value = new KMX_WCHAR[u16len(internal_value) + 1];
u16cpy(_kp->KeyboardOptions[nStoreToReset].Value, /*u16len(val) + 1,*/ internal_value);
_kp->Keyboard->dpStoreArray[nStoreToReset].dpString = _kp->KeyboardOptions[nStoreToReset].Value;
rOptionToReset.Value = new KMX_WCHAR[i->second.size() + 1];
u16cpy(rOptionToReset.Value, /*u16len(val) + 1,*/ i->second.c_str());
rStoreToReset.dpString = rOptionToReset.Value;
}
}
void KMX_Options::Save(km_kbp_state *state, int nStoreToSave)
void KMX_Options::Save(state & state, int nStoreToSave)
{
assert(_kp != NULL);
assert(_kp->Keyboard != NULL);
@ -200,6 +209,7 @@ void KMX_Options::Save(km_kbp_state *state, int nStoreToSave)
auto const & rStoreToSave = _kp->Keyboard->dpStoreArray[nStoreToSave];
if (rStoreToSave.dpName == nullptr) return;
auto opt_ = state->options().assign(state, KM_KBP_OPT_KEYBOARD, rStoreToSave.dpName, rStoreToSave.dpString);
state->actions().push_persist(*static_cast<option const *>(opt_));
state.processor().persisted_store()[rStoreToSave.dpName] = rStoreToSave.dpString;
state.actions().push_persist(
option{KM_KBP_OPT_KEYBOARD, rStoreToSave.dpName, rStoreToSave.dpString});
}

View file

@ -11,6 +11,10 @@
namespace km {
namespace kbp {
class abstract_processor;
class state;
namespace kmx {
class KMX_Options
@ -27,12 +31,13 @@ public:
~KMX_Options();
void Init(std::vector<option> &opts);
void Load(options *options, std::u16string const &key);
void Load(abstract_processor &, std::u16string const &key);
char16_t const * LookUp(std::u16string const &key) const;
void Set(int nStoreToSet, int nStoreToRead);
void Set(int nStoreToSet, std::u16string const &value);
void Set(std::u16string const &key, std::u16string const &value);
void Reset(options *options, int nStoreToReset);
void Save(km_kbp_state *state, int nStoreToSave);
void Reset(abstract_processor &, int nStoreToReset);
void Save(state & state, int nStoreToSave);
STORE const * begin() const;
STORE const * end() const;
@ -41,7 +46,7 @@ public:
inline
void KMX_Options::Set(std::u16string const &key, std::u16string const &value) {
auto i = _GetIndex(key);
if (i != signed(_kp->Keyboard->cxStoreArray)) Set(i, value);
if (i >= 0) Set(i, value);
}
inline

View file

@ -19,6 +19,11 @@ namespace km {
if (_valid)
_kmx.GetOptions()->Init(defaults);
for (auto const & opt: defaults)
{
if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
persisted_store()[opt.key] = opt.value;
}
// Fill out attributes
auto v = _kmx.GetKeyboard()->Keyboard->version;
auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff);
@ -27,21 +32,41 @@ namespace km {
std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
}
void kmx_processor::init_state(std::vector<option> &default_env) {
_kmx.GetEnvironment()->Init(default_env);
char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
{
char16_t const * pValue = nullptr;
switch(scope)
{
case KM_KBP_OPT_KEYBOARD:
pValue = _kmx.GetOptions()->LookUp(key);
break;
case KM_KBP_OPT_ENVIRONMENT:
pValue = _kmx.GetEnvironment()->LookUp(key);
break;
default:
break;
}
return pValue ? pValue : nullptr;
}
void kmx_processor::update_option(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value) {
option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
{
switch(scope) {
case KM_KBP_OPT_KEYBOARD:
_kmx.GetOptions()->Load(&state->options(), key);
_kmx.GetOptions()->Set(key, value);
persisted_store()[key] = value;
break;
case KM_KBP_OPT_ENVIRONMENT:
_kmx.GetEnvironment()->Load(key, value);
break;
default:
return option();
break;
}
return option(scope, key, value);
}
km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) {

View file

@ -30,12 +30,11 @@ namespace kbp
km_kbp_attr const & attributes() const override;
km_kbp_status validate() const override;
void update_option(km_kbp_state *state,
km_kbp_option_scope scope,
char16_t const * lookup_option(km_kbp_option_scope,
std::u16string const & key) const override;
option update_option(km_kbp_option_scope scope,
std::u16string const & key,
std::u16string const & value) override;
void init_state(std::vector<option> &) override;
};
} // namespace kbp

View file

@ -403,12 +403,12 @@ int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr
case CODE_RESETOPT:
p++;
n1 = *p - 1;
GetOptions()->Reset(&m_kbp_state->options(), n1);
GetOptions()->Reset(m_kbp_state->processor(), n1);
break;
case CODE_SAVEOPT:
p++;
n1 = *p - 1;
GetOptions()->Save(m_kbp_state, n1);
GetOptions()->Save(*m_kbp_state, n1);
break;
case CODE_IFSYSTEMSTORE:
p+=3;

View file

@ -76,11 +76,33 @@ namespace km {
: abstract_processor(
keyboard_attributes(path.stem(), u"3.145", path.parent(), {
option{KM_KBP_OPT_KEYBOARD, u"__test_point", u"not tiggered"},
option{KM_KBP_OPT_KEYBOARD, u"hello", u"-"}
}))
})),
_options({
{u"\x01__test_point", u"not tiggered"},
{u"\x02hello", u"-"}
})
{
}
char16_t const * mock_processor::lookup_option(km_kbp_option_scope scope,
std::u16string const & key) const
{
auto i = _options.find(char16_t(scope) + key);
return i != _options.end() ? i->second.c_str() : nullptr;
}
option mock_processor::update_option(km_kbp_option_scope scope,
std::u16string const & key,
std::u16string const & value)
{
auto i = _options.find(char16_t(scope) + key);
if (i == _options.end()) return option();
i->second = value;
persisted_store()[key] = value;
return option(scope, key, i->second);
}
km_kbp_status mock_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state)
{
@ -102,12 +124,10 @@ namespace km {
case KM_KBP_VKEY_F2:
{
auto & opts = state->options();
auto opt = opts.assign(state,
KM_KBP_OPT_KEYBOARD,
u"__test_point",
u"F2 pressed test save.");
state->actions().push_persist(static_cast<option const &>(*opt));
state->actions().push_persist(
update_option(KM_KBP_OPT_KEYBOARD,
u"__test_point",
u"F2 pressed test save."));
break;
}
@ -153,17 +173,6 @@ namespace km {
}
void mock_processor::update_option(km_kbp_state *,
km_kbp_option_scope,
std::u16string const &,
std::u16string const &)
{};
void mock_processor::init_state(std::vector<option> &default_env) {
default_env.emplace_back(KM_KBP_OPT_ENVIRONMENT, u"hello", u"-");
default_env.emplace_back();
};
km_kbp_attr const & mock_processor::attributes() const {
return engine_attrs;
}

View file

@ -9,7 +9,7 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <keyman/keyboardprocessor.h>
#include "processor.hpp"
@ -20,9 +20,8 @@ namespace kbp
{
class mock_processor : public abstract_processor
{
std::vector<option> _options;
option const * _find_option(km_kbp_option_scope scope,
std::u16string const & key) const;
std::unordered_map<std::u16string, std::u16string> _options;
public:
mock_processor(km::kbp::path const &);
// ~mock_processor() override;
@ -35,12 +34,12 @@ namespace kbp
km_kbp_status validate() const override;
void update_option(km_kbp_state *state,
km_kbp_option_scope scope,
std::u16string const & key,
std::u16string const & value) override;
void init_state(std::vector<option> &) override;
char16_t const * lookup_option(km_kbp_option_scope,
std::u16string const & key) const override;
option update_option(km_kbp_option_scope,
std::u16string const & key,
std::u16string const & value) override;
};
class null_processor : public mock_processor {

View file

@ -10,7 +10,7 @@
#include "option.hpp"
#include "processor.hpp"
#include "state.hpp"
using namespace km::kbp;
@ -43,88 +43,30 @@ option::option(km_kbp_option_scope s, char16_t const *k, char16_t const *v)
}
}
char16_t const * options::lookup(km_kbp_option_scope scope,
std::u16string const & key) const noexcept
{
// Search first in the updated values
for (auto & opt: _saved)
{
if (opt.key == key && opt.scope == scope)
return opt.value;
}
// Then in the pristine copies.
km_kbp_option_item const * opt = _scopes[scope-1];
while (opt->key && key != opt->key) ++opt;
return opt->key ? opt->value : nullptr;
}
km_kbp_option_item const * options::assign(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key,
std::u16string const & value)
{
km_kbp_option_item const * opt = _scopes[scope-1];
while (opt->key && key != opt->key) ++opt;
if (!opt->key) return nullptr;
for (auto & save: _saved)
{
if (save.key == key && save.scope == scope)
{
save = option(scope, key, value);
state->processor().update_option(state, scope, key, value);
return &save;
}
}
_saved.emplace_back(scope, key, value);
state->processor().update_option(state, scope, key, value);
return &_saved.back();
}
void options::reset(km_kbp_option_scope scope, std::u16string const & key)
{
for (auto i = _saved.begin(); i == _saved.end(); ++i)
{
if (i->key == key && i->scope == scope)
{
_saved.erase(i);
break;
}
}
}
json & km::kbp::operator << (json &j, options const &opts)
// TODO: Relocate this and fix it
json & km::kbp::operator << (json &j, abstract_processor const &)
{
j << json::object;
auto n = 0;
for (auto scope: opts._scopes)
{
j << scope_names_lut[n++] << json::object;
for (auto opt = scope; opt->key; ++opt)
{
j << opt->key << opt->value;
}
j << json::close;
}
// auto n = 0;
// for (auto scope: opts._scopes)
// {
// j << scope_names_lut[n++] << json::object;
// for (auto opt = scope; opt->key; ++opt)
// {
// j << opt->key << opt->value;
// }
// j << json::close;
// }
j << "saved" << json::object;
for (auto scope: {KM_KBP_OPT_KEYBOARD, KM_KBP_OPT_ENVIRONMENT})
{
j << scope_names_lut[scope-1] << json::object;
for (auto & opt: opts._saved)
{
if (opt.scope != scope) continue;
j << opt.key << opt.value;
}
// for (auto & opt: opts._saved)
// {
// if (opt.scope != scope) continue;
// j << opt.key << opt.value;
// }
j << json::close;
}
j << json::close;

View file

@ -10,7 +10,6 @@
#pragma once
#include <vector>
#include <string>
#include <keyman/keyboardprocessor.h>
@ -90,38 +89,5 @@ namespace kbp
class options
{
//km_kbp_keyboard_attrs const &_kb;
km_kbp_option_item const * _scopes[KM_KBP_OPT_MAX_SCOPES-1];
std::vector<option> _saved;
public:
options(km_kbp_option_item const * kb_default_options);
void set_default_env(km_kbp_option_item const *env);
char16_t const * lookup(km_kbp_option_scope scope,
std::u16string const & key) const noexcept;
km_kbp_option_item const * assign(km_kbp_state *state, km_kbp_option_scope scope, std::u16string const & key,
std::u16string const & value);
void reset(km_kbp_option_scope scope,
std::u16string const & key);
friend json & operator << (json &j, km::kbp::options const &opts);
};
json & operator << (json &j, km::kbp::options const &opts);
inline
options::options(km_kbp_option_item const * kb_default_options)
: _scopes {kb_default_options, nullptr}
{}
inline void options::set_default_env(km_kbp_option_item const *env) {
_scopes[KM_KBP_OPT_ENVIRONMENT - 1] = env;
}
} // namespace kbp
} // namespace km

View file

@ -9,6 +9,7 @@
#pragma once
#include <string>
#include <unordered_map>
#include <keyman/keyboardprocessor.h>
@ -19,8 +20,9 @@ namespace kbp
{
class abstract_processor
{
std::unordered_map<std::u16string, std::u16string> _persisted;
protected:
keyboard_attributes _attributes;
keyboard_attributes _attributes;
public:
abstract_processor() {}
@ -31,6 +33,9 @@ namespace kbp
return _attributes;
}
auto & persisted_store() const noexcept { return _persisted; }
auto & persisted_store() noexcept { return _persisted; }
virtual km_kbp_status process_event(km_kbp_state *,
km_kbp_virtual_key,
uint16_t modifier_state) = 0;
@ -38,14 +43,17 @@ namespace kbp
virtual km_kbp_attr const & attributes() const = 0;
virtual km_kbp_status validate() const = 0;
virtual void update_option(km_kbp_state *state,
km_kbp_option_scope,
virtual char16_t const * lookup_option(km_kbp_option_scope,
std::u16string const & key) const = 0;
virtual option update_option(km_kbp_option_scope,
std::u16string const & key,
std::u16string const & value) = 0;
virtual void init_state(std::vector<option> &default_env) = 0;
friend json & operator << (json &j, abstract_processor const &opts);
};
json & operator << (json &j, abstract_processor const &opts);
} // namespace kbp
} // namespace km

View file

@ -28,14 +28,14 @@ void actions::push_persist(option const &&opt) {
state::state(km::kbp::abstract_processor & ap, km_kbp_option_item const *env)
: _options(ap.keyboard().default_options),
_processor(ap)
: _processor(ap)
{
ap.init_state(_env);
_options.set_default_env(_env.data());
for (; env && env->key != nullptr; env++) {
//assert(env->scope == KM_KBP_OPT_ENVIRONMENT); // todo do we need scope? or can we find a way to eliminate it?
assert(_options.assign(static_cast<km_kbp_state *>(this), (km_kbp_option_scope) KM_KBP_OPT_ENVIRONMENT, env->key, env->value) != nullptr);
ap.update_option(env->scope
? km_kbp_option_scope(env->scope)
: KM_KBP_OPT_ENVIRONMENT,
env->key,
env->value);
}
}

View file

@ -122,8 +122,6 @@ protected:
kbp::context _ctxt;
kbp::abstract_processor & _processor;
kbp::actions _actions;
kbp::options _options;
std::vector<option> _env;
public:
state(kbp::abstract_processor & kb, km_kbp_option_item const *env);
@ -134,9 +132,6 @@ public:
kbp::context & context() noexcept { return _ctxt; }
kbp::context const & context() const noexcept { return _ctxt; }
kbp::options & options() noexcept { return _options; }
kbp::options const & options() const noexcept { return _options; }
kbp::abstract_processor const & processor() const noexcept { return _processor; }
kbp::abstract_processor & processor() noexcept { return _processor; }

View file

@ -44,23 +44,6 @@ constexpr char const *doc1_expected = u8"\
\"version\" : \"3.145\",\n\
\"rules\" : []\n\
},\n\
\"options\" : {\n\
\"keyboard\" : {\n\
\"__test_point\" : \"not tiggered\",\n\
\"hello\" : \"-\"\n\
},\n\
\"environment\" : {\n\
\"hello\" : \"-\"\n\
},\n\
\"saved\" : {\n\
\"keyboard\" : {\n\
\"__test_point\" : \"F2 pressed test save.\"\n\
},\n\
\"environment\" : {\n\
\"hello\" : \"world\"\n\
}\n\
}\n\
},\n\
\"context\" : [\n\
\"H\",\n\
\"e\",\n\
@ -87,21 +70,6 @@ constexpr char const *doc2_expected = u8"\
\"version\" : \"3.145\",\n\
\"rules\" : []\n\
},\n\
\"options\" : {\n\
\"keyboard\" : {\n\
\"__test_point\" : \"not tiggered\",\n\
\"hello\" : \"-\"\n\
},\n\
\"environment\" : {\n\
\"hello\" : \"-\"\n\
},\n\
\"saved\" : {\n\
\"keyboard\" : {},\n\
\"environment\" : {\n\
\"hello\" : \"globe\"\n\
}\n\
}\n\
},\n\
\"context\" : [],\n\
\"actions\" : []\n\
}\n";

View file

@ -1,9 +1,9 @@
c Description: Tests basic option rules with save reset+set+reset
c keys: [K_A][K_B][K_C][K_B][K_A]
c expected: foo.foo.
c context:
c context:
c option: foo=1
c expected option: foo=1
c expected option: foo=2
store(&version) '10.0'

View file

@ -1,9 +1,9 @@
c Description: Tests basic option rules with save reset+reset
c keys: [K_A][K_B][K_B][K_A]
c expected: foo.foo.
c context:
c context:
c option: foo=1
c expected option: foo=1
c expected option: foo=2
store(&version) '10.0'

View file

@ -1,15 +1,26 @@
# Keyman for iPhone and iPad Version History
## 11.0 alpha
* Replaced deprecated calls to UIAlertView and cleaned up extraneous blank buttons (#1002)
* Bookmark add button is enabled only when title/url fields have text (#1073)
* Removed deprecated code to to support keyman:// scheme for ad-hoc distribution (#1160)
* Updated the default keyboard to SIL EuroLatin (#1288)
* Fixed bug behind some crashes of system keyboard (#1166)
* Added support for keypress error feedback (#257)
* Fixed ongoing issues with keyboard rotation and sizing, including the iPhone X notch. (#444) (#1045)
* Fixed keyboard display/overlap of "Getting Started" info panel, added keyboard hide/display API functions. (#1084)
* Fixes issues with keyboard keycap scaling and diacritic display. (#1070)
## 2019-01-02 11.0.300 beta
* Initial beta release of Keyman for iPhone and iPad 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Aios+-label%3Acherry-pick+-label%3Astable)
* New Features:
* Added support for keypress error feedback with vibration (#1314)
* Changes:
* Replaced deprecated calls to UIAlertView and cleaned up extraneous blank buttons (#1002)
* Removed deprecated code to to support keyman:// scheme for ad-hoc distribution (#1160)
* Updated the default keyboard to `sil_euro_latin` (#1417, #1288)
* Added SIL logo to info page (#1164)
* Bug Fixes:
* Bookmark add button is enabled only when title/url fields have text (#1073)
* Fixed bug behind some crashes of system keyboard (#1166)
* Fixed ongoing issues with keyboard rotation and sizing, including the iPhone X notch. (#1347, #1318, #1089, #1045)
* Fixed keyboard display/overlap of "Getting Started" info panel, added keyboard hide/display API functions (#1084)
* Fixed issues with keyboard keycap scaling and diacritic display (#1445, #1407, #1070)
* Fixed issue with incorrect font on key caps in some situations (#1450)
* Various crashes (#1057, #1301)
## 2018-08-02 10.0.208 stable
* Fixed OSK layout problems (and possible crash) on iOS 11 on certain hardware (#1089, #1159)

View file

@ -1 +1 @@
10.99
11.0

View file

@ -1,6 +1,11 @@
# Keyman for Linux Version History
## 11.0 alpha
## 2019-01-02 11.0.100 beta
* Initial beta release of Keyman for Linux
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Alinux+-label%3Acherry-pick+-label%3Astable)
* Keyman for Linux supports .kmp and .kmx file types from Keyman keyboard distribution repository
* Import KMFL into Keyman
* kmflcomp 0.9.10
* libkmfl 0.9.12

View file

@ -34,6 +34,7 @@
#define MAXCONTEXT_ITEMS 128
#define KEYMAN_BACKSPACE 14
#define KEYMAN_BACKSPACE_KEYSYM 0xff08
#define KEYMAN_LCTRL 29
#define KEYMAN_LALT 56
#define KEYMAN_RCTRL 97
@ -388,8 +389,8 @@ ibus_keyman_engine_constructor (GType type,
g_warning("problem creating km_kbp_state");
}
for (int i =0; i < 3; i++) {
km_kbp_cp_dispose(keyboard_opts[i].key);
km_kbp_cp_dispose(keyboard_opts[i].value);
g_free((km_kbp_cp *)keyboard_opts[i].key);
g_free((km_kbp_cp *)keyboard_opts[i].value);
}
g_free(keyboard_opts);
@ -454,10 +455,10 @@ ibus_keyman_engine_commit_string (IBusKeymanEngine *kmfl,
g_object_unref (text);
}
static void forward_keycode(IBusKeymanEngine *engine, unsigned char keycode, unsigned int state)
static void forward_backspace(IBusKeymanEngine *engine, unsigned int state)
{
g_debug("DAR: forward_keycode no keysym");
ibus_engine_forward_key_event((IBusEngine *)engine, 0, keycode, state);
g_message("DAR: forward_backspace %d no keysym state %d", KEYMAN_BACKSPACE, state);
ibus_engine_forward_key_event((IBusEngine *)engine, KEYMAN_BACKSPACE_KEYSYM, KEYMAN_BACKSPACE, state);
}
// from android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMHardwareKeyboardInterpreter.java
@ -783,7 +784,7 @@ ibus_keyman_engine_process_key_event (IBusEngine *engine,
km_kbp_context_get(km_kbp_state_context(keyman->state),
&context_items);
reset_context(engine);
forward_keycode(keyman, KEYMAN_BACKSPACE, 0);
forward_backspace(keyman, 0);
km_kbp_context_set(km_kbp_state_context(keyman->state),
context_items);
km_kbp_context_items_dispose(context_items);

View file

@ -1,24 +1,52 @@
#!/usr/bin/python3
def try_ibus():
try:
bus = IBus.Bus()
if not bus.is_connected():
print("x", end='')
#print("Can not connect to ibus-daemon")
else:
print(".", end='')
if bus.is_global_engine_enabled():
print("+", end='')
#print("Is global engine enabled:", bus.is_global_engine_enabled())
#print("Is global engine used:", bus.get_use_global_engine())
#for e in bus.list_active_engines():
# print("Active engine:", e.get_name())
#print("Global engine name:", bus.get_global_engine().get_name())
#for e in bus.list_engines():
# print(e.get_name())
bus.destroy()
print("-", end='')
except Exception as e:
print("Failed to connect to iBus")
print(e)
try:
import gi
gi.require_version('IBus', '1.0')
gi.require_version('DBus', '1.0')
from gi.repository import IBus
from gi.repository import DBus
for i in range(10000):
try_ibus()
#import dbus #,vim
bus = IBus.Bus()
if not bus.is_connected():
print("Can not connect to ibus-daemon")
else:
print("Is global engine enabled:", bus.is_global_engine_enabled())
print("Is global engine used:", bus.get_use_global_engine())
for e in bus.list_active_engines():
print("Active engine:", e.get_name())
print("Global engine name:", bus.get_global_engine().get_name())
#for e in bus.list_engines():
# print(e.get_name())
# bus = IBus.Bus()
# if not bus.is_connected():
# print("Can not connect to ibus-daemon")
# else:
# print("Is global engine enabled:", bus.is_global_engine_enabled())
# print("Is global engine used:", bus.get_use_global_engine())
# for e in bus.list_active_engines():
# print("Active engine:", e.get_name())
# print("Global engine name:", bus.get_global_engine().get_name())
# #for e in bus.list_engines():
# # print(e.get_name())
except Exception as e:
print("Failed to connect to iBus")
print("Failed to run test")
print(e)
# dbusconn = bus.get_connection()

View file

@ -10,16 +10,17 @@ import tempfile
import zipfile
from os import listdir, makedirs
from shutil import copy2, rmtree
from ast import literal_eval
# from ast import literal_eval
from enum import Enum
import requests
from keyman_config.get_kmp import get_keyboard_data, get_kmp, user_keyboard_dir, user_keyman_dir, user_keyman_font_dir
from keyman_config.kmpmetadata import parseinfdata, parsemetadata, infmetadata_to_json, KMFileTypes
from keyman_config.kmpmetadata import parseinfdata, parsemetadata, get_metadata, infmetadata_to_json, KMFileTypes
from keyman_config.uninstall_kmp import uninstall_kmp
from keyman_config.convertico import checkandsaveico
from keyman_config.kvk2ldml import convert_kvk_to_ldml, output_ldml
from keyman_config.ibus_util import install_to_ibus, restart_ibus, get_ibus_bus
#TODO userdir install
# special processing for kmn if needed
@ -51,49 +52,6 @@ def extract_kmp(kmpfile, directory):
with zipfile.ZipFile(kmpfile,"r") as zip_ref:
zip_ref.extractall(directory)
def get_metadata(tmpdirname):
"""
Get metadata from kmp.json if it exists.
If it does not exist then will return get_and_convert_infdata
Args:
inputfile (str): path to kmp file
tmpdirname(str): temp directory to extract kmp
Returns:
list[5]: info, system, options, keyboards, files
see kmpmetadata.parsemetadata for details
"""
kmpjson = os.path.join(tmpdirname, "kmp.json")
if os.path.isfile(kmpjson):
return parsemetadata(kmpjson, False)
else:
return get_and_convert_infdata(tmpdirname)
def get_and_convert_infdata(tmpdirname):
"""
Get metadata from kmp.inf if it exists.
Convert it to kmp.json if possible
Args:
inputfile (str): path to kmp file
tmpdirname(str): temp directory to extract kmp
Returns:
list[5]: info, system, options, keyboards, files
see kmpmetadata.parseinfdata for details
"""
kmpinf = os.path.join(tmpdirname, "kmp.inf")
if os.path.isfile(kmpinf):
info, system, options, keyboards, files = parseinfdata(kmpinf, False)
j = infmetadata_to_json(info, system, options, keyboards, files)
kmpjson = os.path.join(tmpdirname, "kmp.json")
with open(kmpjson, "w") as write_file:
print(j, file=write_file)
return info, system, options, keyboards, files
else:
return None, None, None, None, None
def download_source(keyboardID, packageDir, sourcePath):
# just get latest version of kmn unless there turns out to be a way to get the version of a file at a date
base_url = "https://raw.github.com/keymanapp/keyboards/master/" + sourcePath
@ -165,7 +123,6 @@ def install_kmp_shared(inputfile, online=False):
inputfile (str): path to kmp file
online(bool, default=False): whether to attempt to get a source kmn and ico for the keyboard
"""
do_install_to_ibus = False
check_keyman_dir('/usr/local/share', "You do not have permissions to install the keyboard files to the shared area /usr/local/share/keyman")
check_keyman_dir('/usr/local/share/doc', "You do not have permissions to install the documentation to the shared documentation area /usr/local/share/doc/keyman")
check_keyman_dir('/usr/local/share/fonts', "You do not have permissions to install the font files to the shared font area /usr/local/share/fonts")
@ -177,6 +134,9 @@ def install_kmp_shared(inputfile, online=False):
if not os.path.isdir(packageDir):
os.makedirs(packageDir)
extract_kmp(inputfile, packageDir)
#restart IBus so it knows about the keyboards being installed
logging.debug("restarting IBus")
restart_ibus()
info, system, options, keyboards, files = get_metadata(packageDir)
if keyboards:
@ -187,7 +147,6 @@ def install_kmp_shared(inputfile, online=False):
for kb in keyboards:
if kb['id'] != packageID:
process_keyboard_data(kb['id'], packageDir)
# do_install_to_ibus = True # temporarily disable
for f in files:
fpath = os.path.join(packageDir, f['name'])
@ -218,9 +177,10 @@ def install_kmp_shared(inputfile, online=False):
elif ftype == KMFileTypes.KM_ICON:
logging.info("Converting %s to PNG and installing both as keyman files", f['name'])
checkandsaveico(fpath)
if do_install_to_ibus:
# install all keyboards not just packageID
install_to_ibus(kmn_file)
for kb in keyboards:
# install all kmx for first lang not just packageID
kmx_file = os.path.join(packageDir, kb['id'] + ".kmx")
install_to_ibus(lang, kmx_file)
else:
logging.error("install_kmp.py: error: No kmp.json or kmp.inf found in %s", inputfile)
logging.info("Contents of %s:", inputfile)
@ -231,13 +191,14 @@ def install_kmp_shared(inputfile, online=False):
raise InstallError(InstallStatus.Abort, message)
def install_kmp_user(inputfile, online=False):
do_install_to_ibus = False
packageID, ext = os.path.splitext(os.path.basename(inputfile))
packageDir=user_keyboard_dir(packageID)
if not os.path.isdir(packageDir):
os.makedirs(packageDir)
extract_kmp(inputfile, packageDir)
#restart IBus so it knows about the keyboards being installed
restart_ibus()
info, system, options, keyboards, files = get_metadata(packageDir)
if keyboards:
@ -248,7 +209,6 @@ def install_kmp_user(inputfile, online=False):
for kb in keyboards:
if kb['id'] != packageID:
process_keyboard_data(kb['id'], packageDir)
# do_install_to_ibus = True # temporarily disable
for f in files:
fpath = os.path.join(packageDir, f['name'])
@ -274,10 +234,7 @@ def install_kmp_user(inputfile, online=False):
elif ftype == KMFileTypes.KM_SOURCE:
#TODO for the moment just leave it for ibus-kmfl to ignore if it doesn't load
pass
if do_install_to_ibus:
# install all keyboards not just packageID
kmn_file = os.path.join(packageDir, packageID + ".kmn")
install_to_ibus(kmn_file)
install_keyboards_to_ibus(keyboards, packageDir)
else:
logging.error("install_kmp.py: error: No kmp.json or kmp.inf found in %s", inputfile)
logging.info("Contents of %s:", inputfile)
@ -287,40 +244,22 @@ def install_kmp_user(inputfile, online=False):
message = "install_kmp.py: error: No kmp.json or kmp.inf found in %s" % (inputfile)
raise InstallError(InstallStatus.Abort, message)
def install_to_ibus(kmn_file):
if sys.version_info.major == 3 and sys.version_info.minor < 6:
dconfreadresult = subprocess.run(["dconf", "read", "/desktop/ibus/general/preload-engines"],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT)
dconfread = dconfreadresult.stdout.decode("utf-8", "strict")
else:
dconfreadresult = subprocess.run(["dconf", "read", "/desktop/ibus/general/preload-engines"],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT, encoding="UTF8")
dconfread = dconfreadresult.stdout
if (dconfreadresult.returncode == 0) and dconfread:
preload_engines = literal_eval(dconfread)
preload_engines.append(kmn_file)
logging.info("Installing %s into IBus", kmn_file)
if sys.version_info.major == 3 and sys.version_info.minor < 6:
dconfwriteresult = subprocess.run(["dconf", "write", "/desktop/ibus/general/preload-engines", str(preload_engines)],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT)
def install_keyboards_to_ibus(keyboards, packageDir):
bus = get_ibus_bus()
if bus:
# install all kmx for first lang not just packageID
for kb in keyboards:
kmx_file = os.path.join(packageDir, kb['id'] + ".kmx")
if "languages" in kb:
logging.debug(kb["languages"][0])
keyboard_id = "%s:%s" % (kb["languages"][0]['id'], kmx_file)
else:
keyboard_id = kmx_file
install_to_ibus(bus, keyboard_id)
restart_ibus(bus)
bus.destroy()
else:
dconfwriteresult = subprocess.run(["dconf", "write", "/desktop/ibus/general/preload-engines", str(preload_engines)],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT, encoding="UTF8")
if (dconfwriteresult.returncode == 0):
# restart IBus to be sure the keyboard is installed
ibusrestartresult = subprocess.run(["ibus", "restart"])
if (ibusrestartresult.returncode != 0):
message = "install_kmp.py: error %d: Could not restart IBus." % (ibusrestartresult.returncode)
raise InstallError(InstallStatus.Continue, message)
else:
message = "install_kmp.py: error %d: Could not install the keyboad to IBus." % (dconfwriteresult.returncode)
raise InstallError(InstallStatus.Continue, message)
else:
message = "install_kmp.py: error %d: Could not read dconf preload-engines entry so cannot install to IBus" % (dconfreadresult.returncode)
raise InstallError(InstallStatus.Continue, message)
logging.debug("could not install keyboards to IBus")
def install_kmp(inputfile, online=False, sharedarea=False):

View file

@ -459,6 +459,51 @@ def parsemetadata(jsonfile, verbose=False):
print_files(files, extracted_dir)
return info, system, options, keyboards, files
def get_metadata(tmpdirname):
"""
Get metadata from kmp.json if it exists.
If it does not exist then will return get_and_convert_infdata
Args:
inputfile (str): path to kmp file
tmpdirname(str): temp directory to extract kmp
Returns:
list[5]: info, system, options, keyboards, files
see kmpmetadata.parsemetadata for details
"""
kmpjson = os.path.join(tmpdirname, "kmp.json")
if os.path.isfile(kmpjson):
return parsemetadata(kmpjson, False)
else:
return get_and_convert_infdata(tmpdirname)
def get_and_convert_infdata(tmpdirname):
"""
Get metadata from kmp.inf if it exists.
Convert it to kmp.json if possible
Args:
inputfile (str): path to kmp file
tmpdirname(str): temp directory to extract kmp
Returns:
list[5]: info, system, options, keyboards, files
see kmpmetadata.parseinfdata for details
"""
kmpinf = os.path.join(tmpdirname, "kmp.inf")
if os.path.isfile(kmpinf):
info, system, options, keyboards, files = parseinfdata(kmpinf, False)
j = infmetadata_to_json(info, system, options, keyboards, files)
kmpjson = os.path.join(tmpdirname, "kmp.json")
with open(kmpjson, "w") as write_file:
print(j, file=write_file)
return info, system, options, keyboards, files
else:
return None, None, None, None, None
def infmetadata_to_json(info, system, options, keyboards, files):
jsonfiles = []
for entry in files:

View file

@ -1,53 +1,31 @@
#!/usr/bin/python3
import ast
import logging
import subprocess
import sys
import os.path
from shutil import rmtree
from keyman_config.get_kmp import user_keyboard_dir, user_keyman_font_dir
from keyman_config.kmpmetadata import get_metadata
from keyman_config.ibus_util import uninstall_from_ibus, get_ibus_bus, restart_ibus
def uninstall_from_ibus(kmnfile):
if sys.version_info.major == 3 and sys.version_info.minor < 6:
result = subprocess.run(["dconf", "read", "/desktop/ibus/general/preload-engines"],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT)
logging.debug(result.stdout.decode("utf-8", "strict"))
dconfread = result.stdout.decode("utf-8", "strict")
else:
result = subprocess.run(["dconf", "read", "/desktop/ibus/general/preload-engines"],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT, encoding="UTF8")
dconfread = result.stdout
if (result.returncode == 0) and dconfread:
preload_engines = ast.literal_eval(dconfread)
if kmnfile not in preload_engines:
logging.info("%s is not installed in IBus", kmnfile)
return
preload_engines.remove(kmnfile)
logging.info("Uninstalling %s from IBus", kmnfile)
if sys.version_info.major == 3 and sys.version_info.minor < 6:
result2 = subprocess.run(["dconf", "write", "/desktop/ibus/general/preload-engines", str(preload_engines)],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT)
else:
result2 = subprocess.run(["dconf", "write", "/desktop/ibus/general/preload-engines", str(preload_engines)],
stdout=subprocess.PIPE, stderr= subprocess.STDOUT, encoding="UTF8")
def uninstall_kmp_shared(keyboardid):
def uninstall_kmp_shared(packageID):
"""
Uninstall a kmp from /usr/local/share/keyman
Args:
keyboardid (str): Keyboard ID
packageID (str): Keyboard package ID
"""
kbdir = os.path.join('/usr/local/share/keyman', keyboardid)
kbdir = os.path.join('/usr/local/share/keyman', packageID)
if not os.path.isdir(kbdir):
logging.error("Keyboard directory for %s does not exist. Aborting", keyboardid)
logging.error("Keyboard directory for %s does not exist. Aborting", packageID)
exit(3)
kbdocdir = os.path.join('/usr/local/share/doc/keyman', keyboardid)
kbfontdir = os.path.join('/usr/local/share/fonts/keyman', keyboardid)
kbdocdir = os.path.join('/usr/local/share/doc/keyman', packageID)
kbfontdir = os.path.join('/usr/local/share/fonts/keyman', packageID)
logging.info("Uninstalling shared keyboard: %s", keyboardid)
logging.info("Uninstalling shared keyboard: %s", packageID)
if not os.access(kbdir, os.X_OK | os.W_OK): # Check for write access of keyman dir
logging.error("You do not have permissions to uninstall the keyboard files. You need to run this with `sudo`")
exit(3)
@ -67,45 +45,70 @@ def uninstall_kmp_shared(keyboardid):
logging.info("Removed font directory: %s", kbfontdir)
else:
logging.info("No font directory")
kmnfile = os.path.join(kbdir, keyboardid+".kmn")
uninstall_from_ibus(kmnfile)
# need to uninstall from ibus for all lang and all kmx in kmp
info, system, options, keyboards, files = get_metadata(kbdir)
if keyboards:
uninstall_keyboards_from_ibus(keyboards, kbdir)
else:
logging.warning("could not uninstall keyboards from IBus")
rmtree(kbdir)
logging.info("Removed keyman directory: %s", kbdir)
logging.info("Finished uninstalling shared keyboard: %s", keyboardid)
logging.info("Finished uninstalling shared keyboard: %s", packageID)
def uninstall_kmp_user(keyboardid):
def uninstall_keyboards_from_ibus(keyboards, packageDir):
bus = get_ibus_bus()
if bus:
# install all kmx for first lang not just packageID
for kb in keyboards:
kmx_file = os.path.join(packageDir, kb['id'] + ".kmx")
if "languages" in kb:
logging.debug(kb["languages"][0])
keyboard_id = "%s:%s" % (kb["languages"][0]['id'], kmx_file)
else:
keyboard_id = kmx_file
uninstall_from_ibus(bus, keyboard_id)
restart_ibus(bus)
else:
logging.warning("could not uninstall keyboards from IBus")
def uninstall_kmp_user(packageID):
"""
Uninstall a kmp from ~/.local/share/keyman
Args:
keyboardid (str): Keyboard ID
packageID (str): Keyboard package ID
"""
kbdir=user_keyboard_dir(keyboardid)
kbdir=user_keyboard_dir(packageID)
if not os.path.isdir(kbdir):
logging.error("Keyboard directory for %s does not exist. Aborting", keyboardid)
logging.error("Keyboard directory for %s does not exist. Aborting", packageID)
exit(3)
logging.info("Uninstalling local keyboard: %s", keyboardid)
kmnfile = os.path.join(kbdir, keyboardid+".kmn")
uninstall_from_ibus(kmnfile)
logging.info("Uninstalling local keyboard: %s", packageID)
info, system, options, keyboards, files = get_metadata(kbdir)
if keyboards:
uninstall_keyboards_from_ibus(keyboards, kbdir)
else:
logging.warning("could not uninstall keyboards from IBus")
rmtree(kbdir)
logging.info("Removed user keyman directory: %s", kbdir)
fontdir=os.path.join(user_keyman_font_dir(), keyboardid)
fontdir=os.path.join(user_keyman_font_dir(), packageID)
if os.path.isdir(fontdir):
rmtree(fontdir)
logging.info("Removed user keyman font directory: %s", fontdir)
logging.info("Finished uninstalling local keyboard: %s", keyboardid)
logging.info("Finished uninstalling local keyboard: %s", packageID)
def uninstall_kmp(keyboardid, sharedarea=False):
def uninstall_kmp(packageID, sharedarea=False):
"""
Uninstall a kmp
Args:
keyboardid (str): Keyboard ID
packageID (str): Keyboard package ID
sharedarea (str): whether to uninstall from shared /usr/local or ~/.local
"""
if sharedarea:
uninstall_kmp_shared(keyboardid)
uninstall_kmp_shared(packageID)
else:
uninstall_kmp_user(keyboardid)
uninstall_kmp_user(packageID)

View file

@ -1,17 +1,25 @@
# Keyman for macOS Version History
## 11.0 alpha
* Refactored target-action call from keyView
* Added SIL logo and copyright information to About box and DMG image (#1163)
## 2019-01-02 11.0.200 beta
* Initial beta release of Keyman for macOS 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Amac+-label%3Acherry-pick+-label%3Astable)
* Changes:
* Code refactoring and cleanup (#1050, #1053)
* Added SIL logo and copyright information to About box and DMG image (#1163)
* Bug Fixes:
* In some situations, Keyman for Mac would use the wrong rule in a keyboard (#1091, #1099)
* Various crashes (#1424, #1066, #1080)
## 2018-08-14 10.0.111 stable
* CORRECLTY fixed bug in engine that caused incorrect rules to be used (#1095, #1099)
## 2018-08-10 10.0.110 stable
* No change
## 2018-08-10 10.0.110 stable
* No change (DO NOT USE; use 10.0.111 or later)
## 2018-08-10 10.0.109 stable
* DO NOT USE - Faulty attempt at bug fix in engine (#1091)
## 2018-08-10 10.0.109 stable
* DO NOT USE (use 10.0.111 or later) - Faulty attempt at bug fix in engine (#1091)
## 2018-07-12 10.0.104 stable
* Removed help button from OSK for versions of macOS < 10.10 to prevent crash (#1080, #1081)
@ -96,14 +104,12 @@
## 2018-03-22 10.0.31 beta
* Initial beta release of Keyman 10 for macOS
## 10.0.29 alpha
* Detection of context changes due to mouse clicks and command keys in "legacy" apps (#394)
## 10.0 alpha
* New feature: install keyboard packages by double-clicking the kmp file (#511)
* Added support for L/R Alt and Ctrl modifiers for keyboards (#178)
* Display the version number in the download dialog
* Added support for Keyman version 10.0 keyboards
* Detection of context changes due to mouse clicks and command keys in "legacy" apps (#394)
## 2017-11-21 1.2.0 beta
* Works around bug in macOS High Sierra so that Configuration dialog can be opened (#368)

View file

@ -109,7 +109,7 @@ validate_history_line() {
fi
elif [ $line_type = "pending-version" ]; then
if [[ ${BASH_REMATCH[2]} = "stable" || ${BASH_REMATCH[2]} = "beta" ]]; then
warn "Error in entry for version ${BASH_REMATCH[1]} ${BASH_REMATCH[2]} - build number missing for ${BASH_REMATCH[3]} tier."
warn "Error in entry for version ${BASH_REMATCH[1]} ${BASH_REMATCH[2]} - build number missing for ${BASH_REMATCH[2]} tier."
validation_error=true
fi
elif [ $line_type = "legacy-version" ]; then
@ -130,11 +130,18 @@ validate_history_line() {
elif [ $line_type = "erroneous-version" ]; then
if [ ${BASH_REMATCH[3]} = "alpha" ]; then
warn "Error in entry for version ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} - alphas should not be dated."
validation_error=true
else
warn "Error in entry for version ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} - build number missing for ${BASH_REMATCH[3]} tier."
version="${BASH_REMATCH[2]}"
tier="${BASH_REMATCH[3]}"
# This branch's conditional lack of error 'grandfathers' Android versions 1.0-2.4 stable, which are dated but lack build numbers.
[[ "${BASH_REMATCH[2]}" =~ $re_v_major ]]
if [ "${BASH_REMATCH[1]}" -ge "10" ]; then
warn "Error in entry for version ${version} ${tier} - build number missing for ${tier} tier."
validation_error=true
fi
fi
validation_error=true
fi
}

View file

@ -11,7 +11,7 @@ verify_on_mac() {
}
# The list of valid projects that our build scripts ought expect.
projects=("android" "ios" "mac" "web" "windows")
projects=("android" "ios" "linux" "mac" "web" "windows")
# Used to validate a specified 'project' parameter.
verify_project() {
@ -29,7 +29,7 @@ verify_project() {
}
# The list of valid platforms that our build scripts ought expect.
platforms=("android" "ios" "mac" "web" "desktop" "developer")
platforms=("android" "ios" "linux" "mac" "web" "desktop" "developer")
# Used to validate a specified 'platform' parameter.
verify_platform() {

View file

@ -0,0 +1,11 @@
#!/bin/bash
FAIL=0
./validateHistory.sh android || FAIL=$(($FAIL + 1))
./validateHistory.sh desktop || FAIL=$(($FAIL + 2))
./validateHistory.sh developer || FAIL=$(($FAIL + 4))
./validateHistory.sh ios || FAIL=$(($FAIL + 8))
./validateHistory.sh linux || FAIL=$(($FAIL + 16))
./validateHistory.sh mac || FAIL=$(($FAIL + 32))
./validateHistory.sh web || FAIL=$(($FAIL + 64))
echo Final status = $FAIL
exit $FAIL

View file

@ -1,13 +1,30 @@
# KeymanWeb Version History
## 11.0 alpha
* Add `setNumericLayer()` for embedded platforms to change OSK to numeric layer.
* Fixes issue where file extensions are upper-case, e.g. ".TTF"
* Fixes keyboard layout issues after mobile device rotations. (#248) (#970)
* Fixes issue with oversized key text on some keyboards. (#382)
* Fixes issue with diacritics not displaying on some keyboards. (#1070)
* Adds support for Promises to init() and setActiveKeyboard(). (#100)
* Adds the alignInputs() API function to facilitate touch-alias element work-arounds in case of future issues. (#69)
## 2019-01-03 11.0.201 beta
* New Feature:
* Adds the alignInputs() API function to facilitate touch-alias element work-arounds in case of future issues. (#69)
## 2019-01-02 11.0.200 beta
* Initial beta release of KeymanWeb 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Aweb+-label%3Acherry-pick+-label%3Astable)
* New Features:
* Added `setNumericLayer()` for embedded platforms to change OSK to numeric layer. (#1218)
* Added support for Promises to `init()` and `setActiveKeyboard()`. (#1432)
* Changes:
* Added OSK bulk rendering script for testing (#1432)
* Code refactors (#1403)
* Bug Fixes:
* Fixed issue where file extensions are upper-case, e.g. ".TTF" (#1333)
* Fixed keyboard layout issues after mobile device rotations. (#1393)
* Fixed issue with oversized key text on some keyboards. (#1421)
* Fixed issue with diacritics not displaying on some keyboards. (#1407)
* Fixed issue with virtual key case sensitivity (#1394)
* Fixed multiple issues on iOS (#1393)
* Fixed issues with language duplication in toolbar UI (#1284)
* Fixed crashes (#1057)
## 2018-07-06 10.0.103 stable
* Fixes issue for embedded Android, iOS apps where a keyboard with varying row counts in different layers could crash (#1055)

View file

@ -41,7 +41,7 @@ namespace com.keyman {
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
* This version has been substantially modified to work for this particular application.
*/
static getTextWidth(text: string, style: CSSStyleDeclaration) {
static getTextWidth(text: string, style: {fontFamily?: string, fontSize: string}) {
// A final fallback - having the right font selected makes a world of difference.
if(!style.fontFamily) {
style.fontFamily = getComputedStyle(document.body).fontFamily;
@ -147,7 +147,6 @@ namespace com.keyman {
// Grab our default for the key's font and font size.
let osk = (<KeymanBase> window['keyman']).osk;
ts.fontFamily = osk.fontFamily; // Helps with style sheet calculations.
ts.fontSize=osk.fontSize; //Build 344, KMEW-90
//Override font spec if set for this key in the layout
@ -161,8 +160,18 @@ namespace com.keyman {
let keyboardManager = (<KeymanBase>window['keyman']).keyboardManager;
// For some reason, fonts will sometimes 'bug out' for the embedded iOS page if we
// instead assign fontFamily to the existing style 'ts'. (Occurs in iOS 12.)
let styleSpec: {fontFamily?: string, fontSize: string} = {fontSize: ts.fontSize};
if(ts.fontFamily) {
styleSpec.fontFamily = ts.fontFamily;
} else {
styleSpec.fontFamily = osk.fontFamily; // Helps with style sheet calculations.
}
// Check the key's display width - does the key visualize well?
var width: number = OSKKey.getTextWidth(keyText, ts);
var width: number = OSKKey.getTextWidth(keyText, styleSpec);
if(width == 0 && keyText != '' && keyText != '\xa0') {
// Add the Unicode 'empty circle' as a base support for needy diacritics.
keyText = '\u25cc' + keyText;
@ -173,7 +182,7 @@ namespace com.keyman {
}
// Recompute the new width for use in autoscaling calculations below, just in case.
width = OSKKey.getTextWidth(keyText, ts);
width = OSKKey.getTextWidth(keyText, styleSpec);
}
let fontSpec = util.getFontSizeStyle(ts.fontSize);

View file

@ -1,13 +1,27 @@
# Keyman Desktop Version History
## 11.0 alpha
* Rework keyboard input to serialize input queue to resolve modifier key stickiness (#1226, #1229, #1236)
* Debug logging now uses Event Tracing for Windows (#1261)
* Introduce support for Metro-style (UWP) applications such as Edge, Skype (#1265)
* Fixed conflicts with Game Bar (#1272)
* Added Kannada localization (#1273)
* Added SIL logo to startup (#1194)
* Removed hard coded version numbers, versioned paths and registry settings for simpler future upgrades (#1171, 1175)
## 2019-01-02 11.0.1300.0 beta
* Initial beta release of Keyman Desktop 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Awindows+-label%3Acherry-pick+-label%3Astable)
* New Features:
* Introduce support for Metro-style (UWP) applications such as Edge, Skype (#1265, #1377)
* Added Kannada localization (#1273)
* Changes:
* Debug logging now uses Event Tracing for Windows (#1261, #1286)
* Added SIL logo to startup (#1194)
* Removed hard coded version numbers, versioned paths and registry settings for simpler future upgrades (#1171, #1175)
* Upgraded to WiX 3.11 to build installers (#1098, #1178)
* Support for Delphi Community Edition (#1104)
* Bug Fixes:
* Rework keyboard input to serialize input queue to resolve modifier key stickiness (#1226, #1229, #1236, #1439, #1300)
* Improved On Screen Keyboard key cap font size for scripts such as Tai Dam (#1434)
* Fixed conflicts with Game Bar (#1272)
* Package names and metadata display always uses Unicode JSON metadata now when available (#1413)
* Various crashes (#1410, #1409)
* Fixed documentation links (#1068)
## 2018-06-28 10.0.1200 stable
* 10.0 stable release

View file

@ -1,16 +1,35 @@
# Keyman Developer Version History
## 11.0 alpha
* Refactor how keyboard_info metadata is generated (#1158)
* New Feature: New Project from template and Import Keyboard (#1210)
* New Feature: kmconvert command line utility (#1207)
* New code editor using open source Monaco component (#1153, #1154, #1252)
* Tidy up of installer to make future upgrades easier (#1175)
* Support Linux targets (#1239)
* Opening or creating a project now closes current editor files (#1242)
* Projects can now include other related files such as history.md (#1243)
* Keyman Developer now treats files as UTF-8 by default (#1244)
* Update kmcomp to add language subtag names to keyboard_info files (#1426)
## 2019-01-02 11.0.1300.0 beta
* Initial beta release of Keyman Developer 11
* [Pull Requests](https://github.com/keymanapp/keyman/pulls?utf8=%E2%9C%93&q=is%3Apr+merged%3A2018-07-01..2019-01-01+label%3Adeveloper+-label%3Acherry-pick+-label%3Astable)
* New Features:
* New Project from template and Import Keyboard (#1210, #1216, #1240)
* kmconvert command line utility (#1207)
* New code editor using open source Monaco component (#1153, #1154, #1155, #1156, #1252, #1274)
* Web views now use Chromium, not MSHTML (IE) (#1078, #1086, #1435, #1374, #1354, #1248)
* Support Linux targets (#1239)
* Added oskbulkrenderer tool for generating sample on screen keyboards (#1428)
* Changes:
* Tidy up of installer to make future upgrades easier (#1175)
* Opening or creating a project now closes current editor files (#1242)
* Projects can now include other related files such as history.md (#1243)
* Keyman Developer now treats files as UTF-8 by default (#1244, #1355)
* kmcomp now adds language subtag names to keyboard_info files (#1426)
* kmcomp now supports `-add-help-link` parameter when bulding .keyboard_info files (#1346)
* Refactored how keyboard_info metadata is generated (#1158)
* Improved performance of web server (#1433)
* Current project is now shown in title bar (#1369)
* Tweaked setup user interface and structure (#1098, #1175, #1178, #1245, #1238)
* Bug Fixes:
* Support for display (avoiding crash) when loading large icons (#1416)
* Importing .kvks into .keyman-touch-layout no longer uses incorrect layer names for some rare modifiers (#1415)
* Various bug fixes for touch layout editor (#1414, #1405)
* Various bug fixes and regressions for project view (#1368, #1247, #1241, #1228, #1157)
* Fixed surrogate pair support in some debugger status views (#1246)
## 2018-11-28 10.0.1206 stable
* Add parameter `-add-help-link` to kmcomp (#1346)