feat(core): ldml: make hardware be an enum 🙀

For: #7986
This commit is contained in:
Steven R. Loomis 2023-01-19 12:24:47 -06:00
parent 5cf1a1fad3
commit d7ce2191bf
10 changed files with 95 additions and 49 deletions

View file

@ -13,8 +13,7 @@ import { BUILDER_SECTION } from "./builder-section.js";
* List of layers, the <layers> 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) {

View file

@ -225,8 +225,7 @@ export class Disp extends Section {
* In-memory `<layers>`
*/
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

View file

@ -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

View file

@ -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<String, number> = 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
*/

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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 # 'ថា'

View file

@ -10,7 +10,7 @@
<key id="grave" to="🪦" />
</keys>
<layers form="hardware">
<layers form="hardware" hardware="us">
<layer id="base">
<row keys="grave" />
</layer>

View file

@ -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);