diff --git a/HISTORY.md b/HISTORY.md index 0c1646e28e..f3ba952cd4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,9 @@ # Keyman Version History +## 16.0.50 alpha 2022-08-23 + +* chore(core): Remove obsolete python keyboardprocessor (#7094) + ## 16.0.49 alpha 2022-08-22 * fix: remove saving and restoring context kbd options (#7077) diff --git a/VERSION.md b/VERSION.md index 245e8fdca3..251a25b345 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -16.0.50 \ No newline at end of file +16.0.51 \ No newline at end of file diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java index 7cff5b5e80..fe217c57e4 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KMPBrowserActivity.java @@ -18,6 +18,7 @@ import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.tavultesoft.kmea.BaseActivity; @@ -113,7 +114,11 @@ public class KMPBrowserActivity extends BaseActivity { // All links that aren't internal Keyman keyboard links open in user's browser Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } return true; } if (lowerURL.startsWith("keyman:")) { diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java index 8aee2a6acb..dc5519095a 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/KeyboardSettingsActivity.java @@ -26,6 +26,7 @@ import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; +import android.widget.Toast; import com.tavultesoft.kmea.ConfirmDialogFragment; import com.tavultesoft.kmea.KMHelpFileActivity; @@ -160,7 +161,11 @@ public final class KeyboardSettingsActivity extends AppCompatActivity { Bundle args = kbd.buildDownloadBundle(); Intent i = new Intent(getApplicationContext(), KMKeyboardDownloaderActivity.class); i.putExtras(args); - startActivity(i); + if (i.resolveActivity(getPackageManager()) != null) { + startActivity(i); + } else { + Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } finish(); // "Help" link clicked diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 6f3affe4fb..2a9626984a 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -762,10 +762,24 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene // Launch PlayStore to update Chrome try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.android.chrome")); - startActivity(intent); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } + } } catch (android.content.ActivityNotFoundException e) { // Link to Chrome if user is not signed in to Play Store - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"))); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } } } }); diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml index 09a91b5f60..ed4e5b85a1 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml @@ -247,4 +247,7 @@ Keyboard requires a newer version of Keyman + + + Unable to launch web browser diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardInfoActivity.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardInfoActivity.java index d9b15f0ea8..4b818acf7c 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardInfoActivity.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KeyboardInfoActivity.java @@ -136,7 +136,11 @@ public final class KeyboardInfoActivity extends BaseActivity { } else { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(customHelpLink)); - startActivity(i); + if (i.resolveActivity(getPackageManager()) != null) { + startActivity(i); + } else { + Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } } } } diff --git a/android/KMEA/app/src/main/res/values/strings.xml b/android/KMEA/app/src/main/res/values/strings.xml index 8f7d20150d..50f52c5ef3 100644 --- a/android/KMEA/app/src/main/res/values/strings.xml +++ b/android/KMEA/app/src/main/res/values/strings.xml @@ -3,10 +3,10 @@ AndroidMobile - + KMEA - + Keyboard @@ -267,4 +267,6 @@ Tap here to change keyboard + + Unable to launch web browser diff --git a/common/include/km_types.h b/common/include/km_types.h index 113647d3a0..bf25b354b5 100644 --- a/common/include/km_types.h +++ b/common/include/km_types.h @@ -1,5 +1,7 @@ #pragma once +#include + /* #if defined(_WIN32) || defined(_WIN64) #define snprintf _snprintf diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index a6e623debb..5d2cab3050 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -14,27 +14,28 @@ // #pragma once -#define LDML_KEYS_FLAGS_EXTEND 1 -#define LDML_LENGTH_HEADER 8 -#define LDML_LENGTH_KEYS 16 -#define LDML_LENGTH_LOCA 12 -#define LDML_LENGTH_META 36 -#define LDML_LENGTH_SECT 16 -#define LDML_LENGTH_STRS 16 -#define LDML_LENGTH_VKEY 12 -#define LDML_META_SETTINGS_FALLBACK_OMIT 1 -#define LDML_META_SETTINGS_TRANSFORMFAILURE_OMIT 2 -#define LDML_META_SETTINGS_TRANSFORMPARTIAL_HIDE 4 -// Section ID -#define LDML_SECTION_KEYS ((uint32_t)'keys') -// Section ID -#define LDML_SECTION_LOCA ((uint32_t)'loca') -// Section ID -#define LDML_SECTION_META ((uint32_t)'meta') -// Section ID -#define LDML_SECTION_SECT ((uint32_t)'sect') -// Section ID -#define LDML_SECTION_STRS ((uint32_t)'strs') -// Section ID -#define LDML_SECTION_VKEY ((uint32_t)'vkey') + +#define LDML_KEYS_FLAGS_EXTEND 0x1 +#define LDML_LENGTH_HEADER 0x8 +#define LDML_LENGTH_KEYS 0x10 +#define LDML_LENGTH_LOCA 0xC +#define LDML_LENGTH_META 0x24 +#define LDML_LENGTH_SECT 0x10 +#define LDML_LENGTH_STRS 0x10 +#define LDML_LENGTH_VKEY 0xC +#define LDML_META_SETTINGS_FALLBACK_OMIT 0x1 +#define LDML_META_SETTINGS_TRANSFORMFAILURE_OMIT 0x2 +#define LDML_META_SETTINGS_TRANSFORMPARTIAL_HIDE 0x4 +#define LDML_SECTIONID_KEYS 0x7379656B /* "keys" */ +#define LDML_SECTIONNAME_KEYS "keys" +#define LDML_SECTIONID_LOCA 0x61636F6C /* "loca" */ +#define LDML_SECTIONNAME_LOCA "loca" +#define LDML_SECTIONID_META 0x6174656D /* "meta" */ +#define LDML_SECTIONNAME_META "meta" +#define LDML_SECTIONID_SECT 0x74636573 /* "sect" */ +#define LDML_SECTIONNAME_SECT "sect" +#define LDML_SECTIONID_STRS 0x73727473 /* "strs" */ +#define LDML_SECTIONNAME_STRS "strs" +#define LDML_SECTIONID_VKEY 0x79656B76 /* "vkey" */ +#define LDML_SECTIONNAME_VKEY "vkey" #define LDML_VERSION "1.0" diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index e4a5a78346..35a0e01731 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -19,7 +19,10 @@ // TODO-LDML: namespace com.keyman.core.ldml { /** * Constants for the KMXPlus data format - * These are shared between the data access layer and the compiler + * These are shared between the data access layer and the compiler. + * Note that the section IDs (section_keys etc.) are 32 bit hex + * values that are designed to appear as text when written in little endian + * format, so 0x7379656b = 'keys' */ export const constants = { /** @@ -30,28 +33,16 @@ export const constants = { * Length of a raw section header, in bytes */ length_header: 8, - /** - * Section ID for the keybag - */ - section_keys: 'keys', /** * Minimum length of the 'keys' section * not including variable parts */ length_keys: 16, - /** - * Section ID for the locale list - */ - section_loca: 'loca', /** * Minimum length of the 'loca' section * not including variable parts */ length_loca: 12, - /** - * Section ID for the metadata - */ - section_meta: 'meta', /** * length of the 'meta' section */ @@ -68,18 +59,10 @@ export const constants = { * bitwise or value for transformPartial=hide in meta.settings */ meta_settings_transformPartial_hide: 4, - /** - * Section ID for the section header - */ - section_sect: 'sect', /** * Minimum length of the 'sect' section, not including entries */ length_sect: 16, - /** - * Section ID for the string table - */ - section_strs: 'strs', /** * Minimum length of the 'strs' section * not including variable parts @@ -93,14 +76,39 @@ export const constants = { * `extend = flags & keys_flags_extend` */ keys_flags_extend: 1, - /** - * Section ID for the vkeys map - */ - section_vkey: 'vkey', /** * Minimum length of the 'vkey' section * not including variable parts */ length_vkey: 12, + + /** + * All section IDs. + */ + section: { + keys: 'keys', + loca: 'loca', + meta: 'meta', + sect: 'sect', + strs: 'strs', + vkey: 'vkey', + }, + + /** + * Use to convert 4-char string into hex + * @param id section id such as 'sect' + * @returns hex ID such as 0x74636573 + */ + hex_section_id: function(id:string) { + if(!id || typeof id !== 'string' || !id.match(/[a-z][a-z][a-z][a-z]/)) { + throw Error(`hex_section_id(${id}) - need a 4-character string`); + } + let r = 0; + for (let i = 3; i>=0; i--) { + r = (r << 8 | id.charCodeAt(i)); + } + return r; + }, + }; // } diff --git a/core/include/ldml/ldml-const-builder.ts b/core/include/ldml/ldml-const-builder.ts index d8a398b72e..e028dc4b70 100644 --- a/core/include/ldml/ldml-const-builder.ts +++ b/core/include/ldml/ldml-const-builder.ts @@ -23,19 +23,29 @@ console.log(` // based on core/include/ldml/keyboardprocessor_ldml.ts // -#pragma once`); +#pragma once +`); for (const key of keys) { const value = constants[key]; const upkey = key.toUpperCase(); const type = typeof value; - if ((key.indexOf('section_') === 0) && type === 'string' && value.length === 4) { - // the 4-char section ID strings get handled specially - console.log('// Section ID'); - console.log(`#define LDML_${upkey} ((uint32_t)'${value}')`); - } else if (type === 'number') { - console.log(`#define LDML_${upkey} ${value}`); + if (type === 'number') { + console.log(`#define LDML_${upkey} 0x${value.toString(16).toUpperCase()}`); } else if (type === 'string') { console.log(`#define LDML_${upkey} "${value}"`); + } else if (key === 'section') { + // handle section table + const subkeys = Object.keys(value); + subkeys.sort(); + for (const subkey of subkeys) { + const upsubkey = subkey.toUpperCase(); + const subvalue = subkeys[subkey]; + const asnum = constants.hex_section_id(subkey); + console.log(`#define LDML_${upkey}ID_${upsubkey} 0x${asnum.toString(16).toUpperCase()} /* "${subkey}" */`); + console.log(`#define LDML_${upkey}NAME_${upsubkey} "${subkey}"`); + } + } else if (type !== 'function') { + console.error(`Unrecognized key ${key}`); } } diff --git a/core/src/kmx/kmx_plus.cpp b/core/src/kmx/kmx_plus.cpp index 7949cb030f..fa47279364 100644 --- a/core/src/kmx/kmx_plus.cpp +++ b/core/src/kmx/kmx_plus.cpp @@ -4,12 +4,14 @@ Implementation for the KMX Plus utilities */ +#include +#include #include /** * @def KMXPLUS_DEBUG Set to 1 to enable debug output */ -#define KMXPLUS_DEBUG 1 +#define KMXPLUS_DEBUG 0 #if KMXPLUS_DEBUG #include @@ -32,8 +34,9 @@ dump_section_name(KMX_DWORD ident) { static void dump_kmxplus_header(const COMP_KMXPLUS_HEADER* hdr) { - if (hdr == NULL) { + if (hdr == nullptr) { printf("! dump_kmxplus_header: NULL header\n"); + return; } dump_section_name(hdr->ident); printf(": (%X) size 0x%X\n", hdr->ident, hdr->size); @@ -41,7 +44,7 @@ dump_kmxplus_header(const COMP_KMXPLUS_HEADER* hdr) { static void dump_kmxplus_keys(const uint8_t* /*data*/, const COMP_KMXPLUS_KEYS* keys) { - if(keys == NULL) { + if(keys == nullptr) { printf("! could not load 'keys' section\n"); return; } @@ -63,7 +66,7 @@ dump_kmxplus_keys(const uint8_t* /*data*/, const COMP_KMXPLUS_KEYS* keys) { static void dump_kmxplus_loca(const uint8_t* /*data*/, const COMP_KMXPLUS_LOCA* loca) { - if(loca == NULL) { + if(loca == nullptr) { printf("! could not load 'loca' section\n"); return; } @@ -76,7 +79,7 @@ dump_kmxplus_loca(const uint8_t* /*data*/, const COMP_KMXPLUS_LOCA* loca) { static void dump_kmxplus_meta(const uint8_t* /*data*/, const COMP_KMXPLUS_META* meta) { - if(meta == NULL) { + if(meta == nullptr) { printf("! could not load 'meta' section\n"); return; } @@ -92,7 +95,7 @@ dump_kmxplus_meta(const uint8_t* /*data*/, const COMP_KMXPLUS_META* meta) { static void dump_kmxplus_vkey(const uint8_t* /*data*/, const COMP_KMXPLUS_VKEY* vkey) { - if(vkey == NULL) { + if (vkey == nullptr) { printf("! could not load 'vkey' section\n"); return; } @@ -102,6 +105,10 @@ dump_kmxplus_vkey(const uint8_t* /*data*/, const COMP_KMXPLUS_VKEY* vkey) { static void dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) { + if (strs == nullptr) { + printf("! could not load 'strs' section\n"); + return; + } dump_kmxplus_header((const COMP_KMXPLUS_HEADER*)strs); printf("strs: count 0x%X\n", strs->count); for (KMX_DWORD i=0; icount; i++) { @@ -113,10 +120,10 @@ dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) { continue; } for(int j=0; str[j] && j<0x30; j++) { - if (str[j] < 0x7F && str[j] != 0x0020 && str[j] > 0x20) { + if (str[j] < 0x7F && str[j] > 0x20) { putchar(str[j]); } else { - printf("U+%04X ", str[j]); + printf(" U+%04X ", str[j]); } } printf("\n"); @@ -125,6 +132,9 @@ dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) { static void dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) { + if (sect == nullptr) { + printf("! could not load 'sect' section\n"); + } dump_kmxplus_header((const COMP_KMXPLUS_HEADER*)sect); printf("sect: total 0x%X\n", sect->total); printf("sect: count 0x%X\n", sect->count); @@ -135,22 +145,22 @@ dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) { printf(" sect#%d: %X @ %X\n", i, entry.sect, entry.offset); const uint8_t* entrydata = (data+entry.offset); switch(entry.sect) { - case LDML_SECTION_KEYS: + case LDML_SECTIONID_KEYS: dump_kmxplus_keys(data, as_kmxplus_keys(entrydata)); break; - case LDML_SECTION_LOCA: + case LDML_SECTIONID_LOCA: dump_kmxplus_loca(data, as_kmxplus_loca(entrydata)); break; - case LDML_SECTION_META: + case LDML_SECTIONID_META: dump_kmxplus_meta(data, as_kmxplus_meta(entrydata)); break; - case LDML_SECTION_SECT: - printf("! Cowardly refusing to dump nested 'sect' section.\n"); + case LDML_SECTIONID_SECT: + printf("! Cowardly refusing to dump invalid nested 'sect' section.\n"); break; - case LDML_SECTION_STRS: + case LDML_SECTIONID_STRS: dump_kmxplus_strs(data, as_kmxplus_strs(entrydata)); break; - case LDML_SECTION_VKEY: + case LDML_SECTIONID_VKEY: dump_kmxplus_vkey(data, as_kmxplus_vkey(entrydata)); break; default: @@ -160,21 +170,31 @@ dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) { } #endif +#if !KMXPLUS_DEBUG +void +dump_kmxplus_data(const uint8_t* ) { + // no op +} + +void +dump_kmxplus_data(kmx::PCOMP_KEYBOARD) { + // no op +} + +#else + void dump_kmxplus_data(const uint8_t* data) { -#if KMXPLUS_DEBUG const COMP_KMXPLUS_SECT* sect = as_kmxplus_sect(data); - if (sect == NULL) { - printf("Err: 'sect' null from %p\n", data); + if (sect == nullptr) { + printf("Err: 'sect' NULL from %p\n", data); return; } dump_kmxplus_sect(data, sect); -#endif } void dump_kmxplus_data(kmx::PCOMP_KEYBOARD keyboard) { -#if KMXPLUS_DEBUG printf("dump_kmxplus_data(): Got a PCOMP_KEYBOARD at %p\n", keyboard); if (!(keyboard->dwFlags & KF_KMXPLUS)) { printf("Err: flags KF_KMXPLUS not set\n"); @@ -185,8 +205,8 @@ dump_kmxplus_data(kmx::PCOMP_KEYBOARD keyboard) { printf("KMXPlus offset 0x%X, KMXPlus size 0x%X\n", ex->kmxplus.dpKMXPlus, ex->kmxplus.dwKMXPlusSize); const uint8_t* rawdata = reinterpret_cast(keyboard); dump_kmxplus_data(rawdata + ex->kmxplus.dpKMXPlus); -#endif } +#endif const COMP_KMXPLUS_KEYS_ENTRY *COMP_KMXPLUS_KEYS::find(KMX_DWORD vkey, KMX_DWORD mod) const { // TODO-LDML: eventually, assume sorted order & binary search @@ -195,7 +215,7 @@ const COMP_KMXPLUS_KEYS_ENTRY *COMP_KMXPLUS_KEYS::find(KMX_DWORD vkey, KMX_DWORD return &entries[i]; } } - return NULL; + return nullptr; } KMX_DWORD COMP_KMXPLUS_SECT::find(KMX_DWORD ident) const { @@ -211,7 +231,7 @@ PKMX_WCHAR COMP_KMXPLUS_STRS::get(KMX_DWORD entry, PKMX_WCHAR buf, KMX_DWORD bufsiz) const { assert(entry < count); if (entry >= count) { - return NULL; + return nullptr; } KMX_DWORD offset = entries[entry].offset; KMX_DWORD length = entries[entry].length; diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 89c3421d7f..98d091036b 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -7,13 +7,23 @@ #pragma once #include -#include +#include +#include +#include #include namespace km { namespace kbp { namespace kmx { +/** + * Using C99 flexible array initializers: entries[] + * https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-levels-2-and-4-c4200 + */ +#if defined(_WIN32) +#pragma warning ( disable : 4200 ) +#endif + struct COMP_KMXPLUS_HEADER { KMX_DWORD ident; // 0000 Section name KMX_DWORD size; // 0004 Section length @@ -31,7 +41,7 @@ struct COMP_KMXPLUS_SECT { COMP_KMXPLUS_HEADER header; KMX_DWORD total; // 0008 KMXPlus entire length KMX_DWORD count; // 000B number of section headers - COMP_KMXPLUS_SECT_ENTRY entries[0]; // 0010 section entries + COMP_KMXPLUS_SECT_ENTRY entries[]; // 0010 section entries /** * @brief Get the offset of a section, or 0 * @@ -53,7 +63,7 @@ struct COMP_KMXPLUS_STRS { COMP_KMXPLUS_HEADER header; KMX_DWORD count; // 0008 count of str entries KMX_DWORD reserved; // 000C padding - COMP_KMXPLUS_STRS_ENTRY entries[0]; // 0010+ entries + COMP_KMXPLUS_STRS_ENTRY entries[]; // 0010+ entries /** * @brief Get a string entry @@ -61,7 +71,7 @@ struct COMP_KMXPLUS_STRS { * @param entry entry number * @param buf output buffer * @param bufsiz buffer size in bytes - * @return NULL or a pointer to the output buffer + * @return nullptr or a pointer to the output buffer */ PKMX_WCHAR get(KMX_DWORD entry, PKMX_WCHAR buf, KMX_DWORD bufsiz) const; }; @@ -88,7 +98,7 @@ struct COMP_KMXPLUS_LOCA_ENTRY { struct COMP_KMXPLUS_LOCA { COMP_KMXPLUS_HEADER header; KMX_DWORD count; // 0008 number of locales - COMP_KMXPLUS_LOCA_ENTRY entries[0]; + COMP_KMXPLUS_LOCA_ENTRY entries[]; }; static_assert(sizeof(struct COMP_KMXPLUS_LOCA) == LDML_LENGTH_LOCA, "mismatched size of section loca"); @@ -104,7 +114,7 @@ struct COMP_KMXPLUS_KEYS { COMP_KMXPLUS_HEADER header; KMX_DWORD count; // number of keys KMX_DWORD reserved; // padding - COMP_KMXPLUS_KEYS_ENTRY entries[0]; + COMP_KMXPLUS_KEYS_ENTRY entries[]; const COMP_KMXPLUS_KEYS_ENTRY *find(KMX_DWORD vkey, KMX_DWORD mod) const; }; @@ -118,11 +128,19 @@ struct COMP_KMXPLUS_VKEY_ENTRY { struct COMP_KMXPLUS_VKEY { COMP_KMXPLUS_HEADER header; KMX_DWORD count; - COMP_KMXPLUS_VKEY_ENTRY entries[0]; + COMP_KMXPLUS_VKEY_ENTRY entries[]; }; static_assert(sizeof(struct COMP_KMXPLUS_VKEY) == LDML_LENGTH_VKEY, "mismatched size of section vkey"); +/** + * See above + */ +#if defined(_WIN32) +#pragma warning ( default : 4200 ) +#endif + + /** * @brief Validate that this data is the named section. * @@ -133,14 +151,14 @@ static_assert(sizeof(struct COMP_KMXPLUS_VKEY) == LDML_LENGTH_VKEY, "mismatched static inline const COMP_KMXPLUS_HEADER * validate_as_section(const uint8_t *data, uint32_t ident) { if (!data) { - return NULL; + return nullptr; } const COMP_KMXPLUS_HEADER *all = reinterpret_cast(data); // TODO-LDML these fail on 000null .. assert(all->size >= LDML_LENGTH_HEADER); assert(ident == all->ident); if (ident != all->ident || (all->size < LDML_LENGTH_HEADER)) { - return NULL; // invalid header or wrong section + return nullptr; // invalid header or wrong section } return all; } @@ -151,7 +169,7 @@ validate_as_section(const uint8_t *data, uint32_t ident) { */ static inline const COMP_KMXPLUS_SECT * as_kmxplus_sect(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_SECT); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_SECT); return reinterpret_cast(all); } @@ -161,7 +179,7 @@ as_kmxplus_sect(const uint8_t *data) { */ static inline const COMP_KMXPLUS_STRS * as_kmxplus_strs(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_STRS); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_STRS); return reinterpret_cast(all); } @@ -171,7 +189,7 @@ as_kmxplus_strs(const uint8_t *data) { */ static inline const COMP_KMXPLUS_KEYS * as_kmxplus_keys(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_KEYS); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_KEYS); return reinterpret_cast(all); } /** @@ -180,7 +198,7 @@ as_kmxplus_keys(const uint8_t *data) { */ static inline const COMP_KMXPLUS_LOCA * as_kmxplus_loca(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_LOCA); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_LOCA); return reinterpret_cast(all); } /** @@ -189,7 +207,7 @@ as_kmxplus_loca(const uint8_t *data) { */ static inline const COMP_KMXPLUS_META * as_kmxplus_meta(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_META); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_META); return reinterpret_cast(all); } /** @@ -198,7 +216,7 @@ as_kmxplus_meta(const uint8_t *data) { */ static inline const COMP_KMXPLUS_VKEY * as_kmxplus_vkey(const uint8_t *data) { - const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_VKEY); + const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_VKEY); return reinterpret_cast(all); } diff --git a/core/src/ldml/ldml_processor.cpp b/core/src/ldml/ldml_processor.cpp index d20caf44a3..595dc6abf6 100644 --- a/core/src/ldml/ldml_processor.cpp +++ b/core/src/ldml/ldml_processor.cpp @@ -8,8 +8,8 @@ #include #include "ldml/ldml_processor.hpp" #include "state.hpp" -#include "../kmx/kmx_file.h" -#include "../kmx/kmx_plus.h" +#include "kmx_file.h" +#include "kmx/kmx_plus.h" #include "ldml/keyboardprocessor_ldml.h" // extern "C" { @@ -28,6 +28,10 @@ enum ConversionFlags { typedef KMX_WCHAR UTF16; typedef KMX_DWORD UTF32; +/** + * This is a temporary patch for now. + * API surface somewhat modelled after ConvertUTF.h + */ ConversionResult ConvertUTF32toUTF16( UTF32** sourceStart, const UTF32* sourceEnd, UTF16** targetStart, const UTF16* targetEnd, const ConversionFlags /*flags*/) { @@ -170,21 +174,21 @@ ldml_processor::process_event( const uint8_t* kmxplusdata = rawdata.data() + ex->kmxplus.dpKMXPlus; // Get out the SECT header const kmx::COMP_KMXPLUS_SECT *sect = kmx::as_kmxplus_sect(kmxplusdata); - assert(sect != NULL); - assert(sect->header.ident == LDML_SECTION_SECT); + assert(sect != nullptr); + assert(sect->header.ident == LDML_SECTIONID_SECT); KMX_DWORD offset; // Fill out the other sections we need. - offset = sect->find(LDML_SECTION_STRS); + offset = sect->find(LDML_SECTIONID_STRS); assert(offset != 0); // or else section not found const kmx::COMP_KMXPLUS_STRS *strs = kmx::as_kmxplus_strs(kmxplusdata+offset); - assert(strs->header.ident == LDML_SECTION_STRS); - offset = sect->find(LDML_SECTION_KEYS); + assert(strs->header.ident == LDML_SECTIONID_STRS); + offset = sect->find(LDML_SECTIONID_KEYS); assert(offset != 0); // or else section not found const kmx::COMP_KMXPLUS_KEYS *keys = kmx::as_kmxplus_keys(kmxplusdata+offset); - assert(keys->header.ident == LDML_SECTION_KEYS); + assert(keys->header.ident == LDML_SECTIONID_KEYS); // Look up the key const kmx::COMP_KMXPLUS_KEYS_ENTRY *key = keys->find(vk, modifier_state); - assert(key != NULL); + assert(key != nullptr); if (!key) { return KM_KBP_STATUS_KEY_ERROR; } @@ -193,7 +197,7 @@ ldml_processor::process_event( KMX_WCHAR out[BUFSIZ]; if (key->flags && LDML_KEYS_FLAGS_EXTEND) { // It's a string. - assert(NULL != strs->get(key->to, out, BUFSIZ)); + assert(nullptr != strs->get(key->to, out, BUFSIZ)); // u_strlen() for(len=0; len=1 && len <= 2); } - assert(len>=0); + assert(len>0); assert(len #include -#include +#include "kmx/kmx_plus.h" using namespace km::kbp::kmx; diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index eff7811f5e..021a6fc8bc 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -3,32 +3,31 @@ # # @darcywong00 @mcdurdin @ermshiperete @rc-swag @SabineSIL @sgschantz -/android/ @darcywong00 @rc-swag +/android/ @darcywong00 @mcdurdin -/common/ @mcdurdin @jahorton -/core/ @mcdurdin @jahorton +/common/ @mcdurdin @rc-swag +/core/ @mcdurdin @rc-swag /common/lexical-model-types/ @jahorton @mcdurdin /common/models/ @jahorton @mcdurdin /common/predictive-text/ @jahorton @mcdurdin /common/schemas/ @mcdurdin @jahorton /common/test/ @mcdurdin @ermshiperete -/common/web/ @jahorton @ermshiperete @mcdurdin +/common/web/ @jahorton @sgschantz @mcdurdin /developer/ @mcdurdin @darcywong00 /docs/ @mcdurdin @jahorton -/ios/ @sgschantz @mcdurdin +/ios/ @sgschantz @jahorton /linux/ @ermshiperete @darcywong00 /mac/ @sgschantz @SabineSIL -/oem/firstvoices/android/ @darcywong00 @rc-swag -/oem/firstvoices/common/ @mcdurdin @jahorton -/oem/firstvoices/ios/ @sgschantz @mcdurdin -/oem/firstvoices/windows/ @rc-swag @sgschantz +/oem/firstvoices/android/ @darcywong00 @mcdurdin +/oem/firstvoices/common/ @mcdurdin @rc-swag +/oem/firstvoices/ios/ @sgschantz @jahorton +/oem/firstvoices/windows/ @rc-swag @ermshiperete /resources/ @mcdurdin @jahorton # Web is currently shared between Marc and Joshua: -/web/ @jahorton @ermshiperete @mcdurdin +/web/ @jahorton @sgschantz @mcdurdin + +/windows/ @rc-swag @ermshiperete -/windows/ @rc-swag @sgschantz -# Override for windows/src: -/windows/src/developer/ @mcdurdin @darcywong00 diff --git a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/FVShared.java b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/FVShared.java index 854175e4cc..d308049417 100644 --- a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/FVShared.java +++ b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/FVShared.java @@ -6,6 +6,7 @@ import android.content.SharedPreferences; import android.content.res.AssetManager; import android.net.Uri; import android.util.Log; +import android.widget.Toast; import com.tavultesoft.kmea.KMManager; import com.tavultesoft.kmea.data.Keyboard; import com.tavultesoft.kmea.packages.PackageProcessor; @@ -252,7 +253,11 @@ final class FVShared { String helpUrl = String.format("%s%s", FVKeyboardHelpLink, id); Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(helpUrl)); - localContext.startActivity(i); + if (i.resolveActivity(localContext.getPackageManager()) != null) { + localContext.startActivity(i); + } else { + Toast.makeText(localContext, localContext.getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } } private void updateActiveKeyboardsList() { diff --git a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java index 5d3e5c5939..95309efe48 100644 --- a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java +++ b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/MainActivity.java @@ -105,7 +105,11 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardDownloa } else { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(i); + if (i.resolveActivity(getPackageManager()) != null) { + startActivity(i); + } else { + Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show(); + } } return true; diff --git a/oem/firstvoices/android/app/src/main/res/values/strings.xml b/oem/firstvoices/android/app/src/main/res/values/strings.xml index 83edf8f86b..e443e0f717 100644 --- a/oem/firstvoices/android/app/src/main/res/values/strings.xml +++ b/oem/firstvoices/android/app/src/main/res/values/strings.xml @@ -42,4 +42,6 @@ Invalid/Missing metadata in package + + Unable to launch web browser diff --git a/windows/src/engine/keyman32/kmprocess.cpp b/windows/src/engine/keyman32/kmprocess.cpp index 03a047cc63..7faac081c2 100644 --- a/windows/src/engine/keyman32/kmprocess.cpp +++ b/windows/src/engine/keyman32/kmprocess.cpp @@ -190,7 +190,7 @@ BOOL ProcessHook() } if (!_td->TIPFUpdateable) { - ProcessActionsTestParse(&fOutputKeystroke); + ProcessActionsNonUpdatableParse(&fOutputKeystroke); } else { ProcessActions(&fOutputKeystroke); } diff --git a/windows/src/engine/keyman32/kmprocessactions.cpp b/windows/src/engine/keyman32/kmprocessactions.cpp index f0aa36256d..6a5e03c123 100644 --- a/windows/src/engine/keyman32/kmprocessactions.cpp +++ b/windows/src/engine/keyman32/kmprocessactions.cpp @@ -167,7 +167,7 @@ BOOL ProcessActions(BOOL* emitKeyStroke) } BOOL -ProcessActionsTestParse(BOOL* emitKeyStroke) { +ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) { PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) { return FALSE; @@ -184,13 +184,16 @@ ProcessActionsTestParse(BOOL* emitKeyStroke) { switch (act->type) { case KM_KBP_IT_EMIT_KEYSTROKE: *emitKeyStroke = TRUE; - SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsTestParse EMIT_KEYSTROKE: act->type=%d", act->type); + SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE: act->type=[%d]", act->type); continueProcessingActions = TRUE; _td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse break; case KM_KBP_IT_CAPSLOCK: continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable); break; + case KM_KBP_IT_INVALIDATE_CONTEXT: + continueProcessingActions = processInvalidateContext(_td->app, _td->lpActiveKeyboard->lpCoreKeyboardState); + break; } if (!continueProcessingActions) { return FALSE; diff --git a/windows/src/engine/keyman32/kmprocessactions.h b/windows/src/engine/keyman32/kmprocessactions.h index e96f2226d1..80827ea623 100644 --- a/windows/src/engine/keyman32/kmprocessactions.h +++ b/windows/src/engine/keyman32/kmprocessactions.h @@ -18,12 +18,12 @@ BOOL ProcessActions(BOOL* emitKeyStroke); /** * This function process the actions queued in the core processor in - * the non updateable parse of a keystroke. - * Emit keystroke and capslock are required to be processed in this phase. + * the non-updateable parse of a keystroke. + * Emit keystroke, capslock and possibly invalidate key stroke are required to be processed in this parse. * * @param [in, out] emitKeyStroke is set to true if requested by the core action queue * @return BOOL True if actions were successfully processed */ -BOOL ProcessActionsTestParse(BOOL* emitKeyStroke); +BOOL ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke); #endif