diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index c025be418e..43a35da936 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -13,8 +13,7 @@ import { BUILDER_SECTION } from "./builder-section.js"; * List of layers, the element */ interface BUILDER_LAYR_LIST { - flags: number; - hardware: number; // str - hardware name, see #7986 + hardware: number; // hardware indicator layer: number; // index of first layer in the list, in the count: number; // number of layer entries in the list minDeviceWidth: number; // width in millimeters @@ -83,8 +82,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l layr.lists = kmxplus.layr.lists.map((list) => { const blist: BUILDER_LAYR_LIST = { - flags: list.flags, - hardware: build_strs_index(sect_strs, list.hardware), + hardware: list.hardware, layer: null, // to be set below _layers: list.layers, count: list.layers.length, @@ -94,11 +92,9 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l }); // now sort the lists layr.lists.sort((a, b) => { - const aform = a.flags & constants.layr_list_flags_mask_form; - const bform = b.flags & constants.layr_list_flags_mask_form; - if (aform < bform) { + if (a.hardware < b.hardware) { return -1; - } else if (aform > bform) { + } else if (a.hardware > b.hardware) { return 1; } if (a.minDeviceWidth < b.minDeviceWidth) { diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index e9779826c1..9f488555f0 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -225,8 +225,7 @@ export class Disp extends Section { * In-memory `` */ export class LayrList { - flags: number; - hardware: StrsItem; + hardware: number; layers: LayrEntry[] = []; minDeviceWidth: number; // millimeters }; @@ -507,8 +506,7 @@ export class KMXPlusFile extends KMXFile { }); this.COMP_PLUS_LAYR_LIST = new r.Struct({ - flags: r.uint32le, - hardware: r.uint32le, //str + hardware: r.uint32le, //enum layer: r.uint32le, // index into layers count: r.uint32le, minDeviceWidth: r.uint32le, // integer: millimeters diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index c44c3102d8..5bab7018f4 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -29,9 +29,11 @@ #define LDML_KEY2_KEY_FLAGS_GAP 0x2 #define LDML_KEY2_KEY_FLAGS_NOTRANSFORM 0x4 #define LDML_KEYS_FLAGS_EXTEND 0x1 -#define LDML_LAYR_LIST_FLAGS_HARDWARE 0x0 -#define LDML_LAYR_LIST_FLAGS_MASK_FORM 0x1 -#define LDML_LAYR_LIST_FLAGS_TOUCH 0x1 +#define LDML_LAYR_LIST_HARDWARE_ABNT2 0x1 +#define LDML_LAYR_LIST_HARDWARE_ISO 0x2 +#define LDML_LAYR_LIST_HARDWARE_JIS 0x3 +#define LDML_LAYR_LIST_HARDWARE_TOUCH 0x0 +#define LDML_LAYR_LIST_HARDWARE_US 0x4 #define LDML_LENGTH_BKSP 0xC #define LDML_LENGTH_BKSP_ITEM 0x10 #define LDML_LENGTH_DISP 0x10 @@ -51,7 +53,7 @@ #define LDML_LENGTH_LAYR 0x18 #define LDML_LENGTH_LAYR_ENTRY 0x10 #define LDML_LENGTH_LAYR_KEY 0x4 -#define LDML_LENGTH_LAYR_LIST 0x14 +#define LDML_LENGTH_LAYR_LIST 0x10 #define LDML_LENGTH_LAYR_ROW 0x8 #define LDML_LENGTH_LIST 0x10 #define LDML_LENGTH_LIST_INDEX 0x4 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index 49e210fcfe..08fc02036e 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -281,19 +281,39 @@ class Constants { /** * Length of each layer list in the 'layr' section variable part */ - readonly length_layr_list = 20; + readonly length_layr_list = 16; /** - * bitmask for the 'form' field of the layr.list[].flags bitfield + * for the 'hardware' field indicating a touch keyboard, non-hardware */ - readonly layr_list_flags_mask_form = 1; + readonly layr_list_hardware_touch = 0; /** - * hardware layout: value for the 'form' field of the layr.list[].flags + * for the 'hardware' field indicating an abnt2 layout */ - readonly layr_list_flags_hardware = 0; + readonly layr_list_hardware_abnt2 = 1; /** - * touch layout: value for the 'form' field of the layr.list[].flags + * for the 'hardware' field indicating an iso layout */ - readonly layr_list_flags_touch = 1; + readonly layr_list_hardware_iso = 2; + /** + * for the 'hardware' field indicating a jis layout + */ + readonly layr_list_hardware_jis = 3; + /** + * for the 'hardware' field indicating a us layout + */ + readonly layr_list_hardware_us = 4; + /** + * Convenience map of layr_list_hardware field values + */ + readonly layr_list_hardware_map: Map = new Map( + [ + ["touch", this.layr_list_hardware_touch], + ["abnt2", this.layr_list_hardware_abnt2], + ["iso", this.layr_list_hardware_iso], + ["jis", this.layr_list_hardware_jis], + ["us", this.layr_list_hardware_us], + ] + ); /** * Length of each layer entry in the 'layr' section variable part */ diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 9f064f503b..349e6be045 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -369,8 +369,7 @@ static_assert(sizeof(struct COMP_KMXPLUS_DISP) == LDML_LENGTH_DISP, "mismatched ------------------------------------------------------------------ */ struct COMP_KMXPLUS_LAYR_LIST { - KMX_DWORD flags; - KMXPLUS_STR hardware; + KMX_DWORD hardware; KMX_DWORD layer; KMX_DWORD count; KMX_DWORD minDeviceWidth; diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-keyboard/src/compiler/layr.ts index b143a86c3c..beb0dba6f1 100644 --- a/developer/src/kmc-keyboard/src/compiler/layr.ts +++ b/developer/src/kmc-keyboard/src/compiler/layr.ts @@ -8,7 +8,6 @@ import Layr = KMXPlus.Layr; import LayrEntry = KMXPlus.LayrEntry; import LayrList = KMXPlus.LayrList; import LayrRow = KMXPlus.LayrRow; -// import USVirtualKeyMap = Constants.USVirtualKeyMap; export class LayrCompiler extends SectionCompiler { @@ -18,11 +17,48 @@ export class LayrCompiler extends SectionCompiler { public validate() { let valid = true; - if(!this.keyboard.layers?.[0]?.layer?.length) { + if (!this.keyboard.layers?.[0]?.layer?.length) { valid = false; this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement()); } - // TODO-LDML + let hardwareLayers = 0; + let touchLayers = 0; + this.keyboard.layers.forEach(({ hardware, form }) => { + // TODO-LDML: in the future >1 hardware layer may be allowed, check for duplicates + if (form === 'touch') { + touchLayers++; + if (hardware) { + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({ + errorText: `Not allowed: hardware="${hardware}" with layers form="touch"` + })); + } else if (touchLayers > 1) { // TODO-LDML: revisit if spec changes + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form })); + } + } else if (form === 'hardware') { + hardwareLayers++; + if (!hardware) { + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({ + errorText: `on layers form="hardware", missing required hardware= attribute.` + })); + } else if (!constants.layr_list_hardware_map.get(hardware)) { + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({ + errorText: `Unknown hardware layout id: hardware="${hardware}"` + })); + } else if (hardwareLayers > 1) { // TODO-LDML: revisit if spec changes + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form })); + } + } else { + valid = false; + this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({ + errorText: `Invalid form="${form}" on layers element` + })); + } + }); return valid; } @@ -30,16 +66,17 @@ export class LayrCompiler extends SectionCompiler { const sect = new Layr(); sect.lists = this.keyboard.layers.map((layers) => { - const list : LayrList = { - flags: 0, - hardware: sections.strs.allocString(layers.hardware), + const hardware = constants.layr_list_hardware_map.get(layers.hardware || 'touch'); + // Don't need to check 'form' because it is checked in validate + const list: LayrList = { + hardware, minDeviceWidth: layers.minDeviceWidth || 0, layers: layers.layer.map((layer) => { - const entry : LayrEntry = { + const entry: LayrEntry = { id: sections.strs.allocString(layer.id), modifier: sections.strs.allocString(layer.modifier), rows: layer.row.map((row) => { - const erow : LayrRow = { + const erow: LayrRow = { keys: row.keys.split(' ').map((id) => sections.strs.allocString(id)), }; return erow; @@ -49,9 +86,6 @@ export class LayrCompiler extends SectionCompiler { return entry; }), }; - if (layers.form === 'touch') { - list.flags |= constants.layr_list_flags_touch; - } return list; }); return sect; diff --git a/developer/src/kmc-keyboard/src/compiler/messages.ts b/developer/src/kmc-keyboard/src/compiler/messages.ts index 0a6801f9d2..ba6006d96d 100644 --- a/developer/src/kmc-keyboard/src/compiler/messages.ts +++ b/developer/src/kmc-keyboard/src/compiler/messages.ts @@ -79,7 +79,11 @@ export class CompilerMessages { static Error_KeyMissingToGapOrSwitch = (o:{keyId: string}) => m(this.ERROR_KeyMissingToGapOrSwitch, `key id='${o.keyId}' must have either to=, gap=, or switch=.`); -static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011; + static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011; + + static Error_MustHaveAtMostOneLayersElementPerForm = (o:{form: string}) => m(this.ERROR_MustHaveAtMostOneLayersElementPerForm, + `Must have at most one layers element with form=${o.form}`); + static ERROR_MustHaveAtMostOneLayersElementPerForm = SevError | 0x0012; static severityName(code: number): string { let severity = code & CompilerErrorSeverity.Severity_Mask; diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-keyboard/test/fixtures/basic.txt index 2ab7bee4b5..5e81d4c360 100644 --- a/developer/src/kmc-keyboard/test/fixtures/basic.txt +++ b/developer/src/kmc-keyboard/test/fixtures/basic.txt @@ -329,8 +329,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR { 01 00 00 00 # KMX_DWORD rowCount 02 00 00 00 # KMX_DWORD keyCount # list 0 - 00 00 00 00 # KMX_DWORD flags - index(strNull,strUs,2) # KMXPLUS_STR hardware; + 04 00 00 00 # KMX_DWORD hardware = 'us' 00 00 00 00 # KMX_DWORD layer; 01 00 00 00 # count 7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123 @@ -453,7 +452,6 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) diff(strs,strThat) sizeof(strThat,2) - diff(strs,strUs) sizeof(strUs,2) diff(strs,strTranTo) sizeof(strTranTo,2) diff(strs,strKey1) sizeof(strKey1,2) diff(strs,strKey2) sizeof(strKey2,2) @@ -482,7 +480,6 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' - block(strUs) 75 00 73 00 block(x) 00 00 block(strTranTo) E2 00 block(x) 00 00 # 'â' block(strKey1) 27 01 block(x) 00 00 # 'ħ' block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml b/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml index de051c02ca..c29bfa1fd9 100644 --- a/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml +++ b/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml @@ -10,7 +10,7 @@ - + diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-keyboard/test/test-layr.ts index 2828fdd1b6..4709f5855b 100644 --- a/developer/src/kmc-keyboard/test/test-layr.ts +++ b/developer/src/kmc-keyboard/test/test-layr.ts @@ -29,8 +29,7 @@ describe('layr', function () { const list0 = layr.lists[0]; assert.ok(list0); assert.equal(list0.layers.length, 1); - assert.equal(list0.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware); - assert.equal(list0.hardware?.value, ''); + assert.equal(list0.hardware, constants.layr_list_hardware_us); const layer0 = list0.layers[0]; assert.ok(layer0); assert.equal(layer0.rows.length, 1); @@ -51,12 +50,10 @@ describe('layr', function () { assert.equal(layr.lists?.length, 2); - const listHardware = layr.lists.find(v => v.hardware.value === 'abnt2'); + const listHardware = layr.lists.find(v => v.hardware === constants.layr_list_hardware_abnt2); assert.ok(listHardware); assert.equal(listHardware.minDeviceWidth, 0); assert.equal(listHardware.layers.length, 2); - assert.equal(listHardware.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware); - assert.equal(listHardware.hardware?.value, 'abnt2'); const hardware0 = listHardware.layers[0]; assert.ok(hardware0); assert.equal(hardware0.id.value, 'base'); @@ -75,11 +72,10 @@ describe('layr', function () { assert.equal(hardware1row0.keys.length, 2); allKeysOk(hardware1row0,'q w', 'hardware1row0'); - const listTouch = layr.lists.find(v => v.hardware.value !== 'abnt2'); // TODO-LDML: need to add some more fields!!! + const listTouch = layr.lists.find(v => v.hardware === constants.layr_list_hardware_touch); assert.ok(listTouch); assert.equal(listTouch.minDeviceWidth, 300); assert.equal(listTouch.layers.length, 1); - assert.equal(listTouch.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_touch); const touch0 = listTouch.layers[0]; assert.ok(touch0); assert.equal(touch0.rows.length, 1);