Skip to content

Commit

Permalink
feat(web): load .kmx keyboard from blob
Browse files Browse the repository at this point in the history
This change adds the web side of loading a .kmx keyboard from a
blob. It also replaces the `CoreProcessor` class with `CoreFactory`
that allows to directly use the methods defined in WASM without
having to add another wrapper for each method.
ermshiperete committed Dec 20, 2024

Verified

This commit was signed with the committer’s verified signature.
ermshiperete Eberhard Beilharz
1 parent 6bef09f commit a072171
Showing 8 changed files with 82 additions and 43 deletions.
5 changes: 4 additions & 1 deletion core/include/keyman/keyman_core_api.h
Original file line number Diff line number Diff line change
@@ -302,11 +302,14 @@ typedef uint8_t (*km_core_keyboard_imx_platform)(km_core_state*, uint32_t, void*
## Description
An error code mechanism similar to COMs `HRESULT` scheme (unlike COM, any
An error code mechanism similar to COM's `HRESULT` scheme (unlike COM, any
non-zero value is an error).
## Specification
-->
// keep in sync with web/src/engine/core-processor/src/core-factory.ts
<!--
```c */
enum km_core_status_codes {
KM_CORE_STATUS_OK = 0,
3 changes: 3 additions & 0 deletions web/build.sh
Original file line number Diff line number Diff line change
@@ -122,6 +122,9 @@ build_action() {

tsc --project "${KEYMAN_ROOT}/web/src/test/auto/tsconfig.json"

mkdir -p "${KEYMAN_ROOT}/web/build/test/dom/cases/core-processor/import/core/"
cp "${KEYMAN_ROOT}/web/src/engine/core-processor/src/import/core/keymancore.d.ts" "${KEYMAN_ROOT}/web/build/test/dom/cases/core-processor/import/core/"

for dir in \
"${KEYMAN_ROOT}/web/build/test/dom/cases"/*/ \
"${KEYMAN_ROOT}/web/build/test/integrated/" \
38 changes: 38 additions & 0 deletions web/src/engine/core-processor/src/core-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Keyman is copyright (C) SIL International. MIT License.

import { type MainModule } from './import/core/keymancore.js';

// Unfortunately embind has an open issue with enums and typescript where it
// only generates a type for the enum, but not the values in a usable way.
// So we have to re-define the enum here.
// See https://github.com/emscripten-core/emscripten/issues/18585
// NOTE: Keep in sync with core/include/keyman/keyman_core_api.h#L311
export enum KM_CORE_STATUS {
OK = 0,
NO_MEM = 1,
IO_ERROR = 2,
INVALID_ARGUMENT = 3,
KEY_ERROR = 4,
INSUFFICENT_BUFFER = 5,
INVALID_UTF = 6,
INVALID_KEYBOARD = 7,
NOT_IMPLEMENTED = 8,
OS_ERROR = 0x80000000
}

export class CoreFactory {
public static async createCoreProcessor(baseurl: string): Promise<MainModule> {
try {
const module = await import(baseurl + '/km-core.js');
const createCoreProcessor = module.default;
return await createCoreProcessor({
locateFile: function (path: string, scriptDirectory: string) {
return baseurl + '/' + path;
}
});
} catch (e: any) {
console.log('got execption in CoreFactory.createCoreProcessor', e);
return null;
}
}
}
30 changes: 0 additions & 30 deletions web/src/engine/core-processor/src/core-processor.ts

This file was deleted.

4 changes: 3 additions & 1 deletion web/src/engine/core-processor/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './core-processor.js';
export { CoreFactory, KM_CORE_STATUS } from './core-factory.js';
import { type MainModule, type km_core_keyboard, type CoreKeyboardReturn } from './import/core/keymancore.js';
export { MainModule, km_core_keyboard, CoreKeyboardReturn };
12 changes: 8 additions & 4 deletions web/src/engine/main/src/headless/inputProcessor.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,8 @@ import { LanguageProcessor } from "./languageProcessor.js";
import type { ModelSpec, PathConfiguration } from "keyman/engine/interfaces";
import { globalObject, DeviceSpec } from "@keymanapp/web-utils";

import { CoreProcessor } from "keyman/engine/core-processor";
import { CoreFactory, MainModule as KmCoreModule } from 'keyman/engine/core-processor';

import { Codes, type Keyboard, type KeyEvent } from "keyman/engine/keyboard";
import {
type Alternate,
@@ -35,7 +36,7 @@ export class InputProcessor {
private contextDevice: DeviceSpec;
private kbdProcessor: KeyboardProcessor;
private lngProcessor: LanguageProcessor;
private coreProcessor: CoreProcessor;
private km_core: KmCoreModule;

private readonly contextCache = new TranscriptionCache();

@@ -51,11 +52,10 @@ export class InputProcessor {
this.contextDevice = device;
this.kbdProcessor = new KeyboardProcessor(device, options);
this.lngProcessor = new LanguageProcessor(predictiveTextWorker, this.contextCache);
this.coreProcessor = new CoreProcessor();
}

public async init(paths: PathConfiguration) {
this.coreProcessor.init(paths.basePath);
this.km_core = await CoreFactory.createCoreProcessor(paths.basePath);
}

public get languageProcessor(): LanguageProcessor {
@@ -70,6 +70,10 @@ export class InputProcessor {
return this.keyboardProcessor.keyboardInterface;
}

public get keymanCore(): KmCoreModule {
return this.km_core;
}

public get activeKeyboard(): Keyboard {
return this.keyboardInterface.activeKeyboard;
}
31 changes: 24 additions & 7 deletions web/src/test/auto/dom/cases/core-processor/basic.tests.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
import { assert } from 'chai';
import { CoreProcessor } from 'keyman/engine/core-processor';
import { CoreFactory, KM_CORE_STATUS } from 'keyman/engine/core-processor';

const coreurl = '/web/build/engine/core-processor/obj/import/core';
const coreurl = '/build/engine/core-processor/obj/import/core';

// Test the CoreProcessor interface.
describe('CoreProcessor', function () {
async function loadKeyboardBlob(uri: string) {
const response = await fetch(uri);
if (!response.ok) {
throw new Error(`HTTP ${response.status} ${response.statusText}`);
}

const buffer = await response.arrayBuffer();
return new Uint8Array(buffer);
}

it('can initialize without errors', async function () {
const kp = new CoreProcessor();
assert.isTrue(await kp.init(coreurl));
assert.isNotNull(await CoreFactory.createCoreProcessor(coreurl));
});

it('can call temp function', async function () {
const kp = new CoreProcessor();
await kp.init(coreurl);
const a = kp.tmp_wasm_attributes();
const km_core = await CoreFactory.createCoreProcessor(coreurl);
const a = km_core.tmp_wasm_attributes();
assert.isNotNull(a);
assert.isNumber(a.max_context);
console.dir(a);
});

it('can load a keyboard from blob', async function () {
const km_core = await CoreFactory.createCoreProcessor(coreurl);
const blob = await loadKeyboardBlob('/common/test/resources/keyboards/test_8568_deadkeys.kmx')
const result = km_core.keyboard_load_from_blob('test', blob);
assert.equal(result.status, KM_CORE_STATUS.OK);
assert.isNotNull(result.object);
result.delete();
});
});
2 changes: 2 additions & 0 deletions web/src/test/auto/dom/web-test-runner.config.mjs
Original file line number Diff line number Diff line change
@@ -92,6 +92,8 @@ export default {
function rewriteResourcePath(context, next) {
if(context.url.startsWith('/resources/')) {
context.url = '/web/src/test/auto' + context.url;
} else if (context.url.startsWith('/build/')) {
context.url = '/web' + context.url;
}

return next();

0 comments on commit a072171

Please sign in to comment.