diff --git a/hosts/symbian/runtime/main.cpp b/hosts/symbian/runtime/main.cpp index e069937a..b8f44b35 100644 --- a/hosts/symbian/runtime/main.cpp +++ b/hosts/symbian/runtime/main.cpp @@ -32,6 +32,7 @@ extern "C" { #include "pocketjs_symbian_core.h" #include "pocketjs_symbian_extension.h" +#include "pocketjs_symbian_keys.h" typedef char PocketJsQuickJsValueMustBeEightBytes[ sizeof(JSValue) == 8 ? 1 : -1 @@ -2339,8 +2340,9 @@ bool PocketJsRuntime::event(QEvent *event) void PocketJsRuntime::keyPressEvent(QKeyEvent *event) { - const int button = buttonForKey(event->key()); - const uint32_t nativeKey = nativeKeyForKey(event->key()); + const int key = pocketjsSymbianNormalizeKey(event->key()); + const int button = buttonForKey(key); + const uint32_t nativeKey = nativeKeyForKey(key); if (button != 0 || nativeKey != 0) { if (event->isAutoRepeat()) { event->accept(); @@ -2356,8 +2358,9 @@ void PocketJsRuntime::keyPressEvent(QKeyEvent *event) void PocketJsRuntime::keyReleaseEvent(QKeyEvent *event) { - const int button = buttonForKey(event->key()); - const uint32_t nativeKey = nativeKeyForKey(event->key()); + const int key = pocketjsSymbianNormalizeKey(event->key()); + const int button = buttonForKey(key); + const uint32_t nativeKey = nativeKeyForKey(key); if (button != 0 || nativeKey != 0) { if (event->isAutoRepeat()) { event->accept(); diff --git a/hosts/symbian/runtime/pocketjs-e7-runtime.pro b/hosts/symbian/runtime/pocketjs-e7-runtime.pro index 61cbdceb..33f761a8 100644 --- a/hosts/symbian/runtime/pocketjs-e7-runtime.pro +++ b/hosts/symbian/runtime/pocketjs-e7-runtime.pro @@ -8,7 +8,7 @@ CONFIG += release CONFIG -= debug app_bundle SOURCES += main.cpp -HEADERS += pocketjs_symbian_core.h pocketjs_symbian_extension.h +HEADERS += pocketjs_symbian_core.h pocketjs_symbian_extension.h pocketjs_symbian_keys.h RESOURCES += pocketjs-runtime.qrc isEmpty(POCKETJS_QUICKJS_INCLUDE): error(POCKETJS_QUICKJS_INCLUDE is required) diff --git a/hosts/symbian/runtime/pocketjs_symbian_keys.h b/hosts/symbian/runtime/pocketjs_symbian_keys.h new file mode 100644 index 00000000..6081757c --- /dev/null +++ b/hosts/symbian/runtime/pocketjs_symbian_keys.h @@ -0,0 +1,18 @@ +#ifndef POCKETJS_SYMBIAN_KEYS_H +#define POCKETJS_SYMBIAN_KEYS_H + +/* + * Qt 4.7's Symbian backend forwards ordinary character keysyms directly. + * The E7 therefore reports an unshifted physical W as 'w', while Qt::Key_W + * is the uppercase ASCII value. Keep semantic game controls independent of + * Shift and Caps Lock without changing punctuation or platform control keys. + */ +#define POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY(key) \ + (((key) >= 'a' && (key) <= 'z') ? ((key) - ('a' - 'A')) : (key)) + +static inline int pocketjsSymbianNormalizeKey(int key) +{ + return POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY(key); +} + +#endif diff --git a/tests/fixtures/symbian-key-normalization-test.c b/tests/fixtures/symbian-key-normalization-test.c new file mode 100644 index 00000000..3159dd03 --- /dev/null +++ b/tests/fixtures/symbian-key-normalization-test.c @@ -0,0 +1,22 @@ +#include "../../hosts/symbian/runtime/pocketjs_symbian_keys.h" + +#if POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY('w') != 'W' +#error unshifted W must normalize to Qt::Key_W +#endif + +#if POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY('r') != 'R' +#error unshifted R must normalize to Qt::Key_R +#endif + +#if POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY('A') != 'A' +#error uppercase letters must remain unchanged +#endif + +#if POCKETJS_SYMBIAN_NORMALIZED_ASCII_KEY(0x01000013) != 0x01000013 +#error Qt special keys must remain unchanged +#endif + +int main(void) +{ + return pocketjsSymbianNormalizeKey('d') == 'D' ? 0 : 1; +} diff --git a/tests/npm-package.test.ts b/tests/npm-package.test.ts index ce9a8051..9a39d3fc 100644 --- a/tests/npm-package.test.ts +++ b/tests/npm-package.test.ts @@ -91,6 +91,7 @@ describe("published npm artifacts", () => { "hosts/symbian/probe/pocketjs-e7-probe.pro", "hosts/symbian/runtime/main.cpp", "hosts/symbian/runtime/pocketjs-e7-runtime.pro", + "hosts/symbian/runtime/pocketjs_symbian_keys.h", "engine/symbian/Cargo.toml", "engine/symbian/rust-toolchain.toml", "engine/symbian/src/lib.rs", diff --git a/tests/symbian-runtime.test.ts b/tests/symbian-runtime.test.ts index 7cab3871..96824472 100644 --- a/tests/symbian-runtime.test.ts +++ b/tests/symbian-runtime.test.ts @@ -29,6 +29,28 @@ import { validateAndResolveBuildPlan } from "../framework/src/manifest/resolve.t const repository = new URL("..", import.meta.url).pathname; describe("experimental Nokia E7 runtime profile", () => { + test("normalizes unshifted S60 letter keysyms without changing special keys", () => { + const compiler = Bun.which("cc"); + expect( + compiler, + "cc is required to validate the Symbian key normalizer", + ).toBeTruthy(); + if (!compiler) return; + const compiled = Bun.spawnSync([ + compiler, + "-std=c11", + "-Wall", + "-Wextra", + "-Werror", + "-fsyntax-only", + join( + repository, + "tests/fixtures/symbian-key-normalization-test.c", + ), + ], { cwd: repository }); + expect(compiled.exitCode, compiled.stderr.toString()).toBe(0); + }); + test("serializes the shared launcher source tree across targets", async () => { const root = mkdtempSync(join(tmpdir(), "pocketjs-launcher-source-lock-")); try { @@ -354,6 +376,10 @@ describe("experimental Nokia E7 runtime profile", () => { join(repository, "hosts/symbian/runtime/pocketjs_symbian_extension.h"), "utf8", ); + const keyHeader = readFileSync( + join(repository, "hosts/symbian/runtime/pocketjs_symbian_keys.h"), + "utf8", + ); const orchestrator = readFileSync( join(repository, "tools/symbian.ts"), "utf8", @@ -431,8 +457,14 @@ describe("experimental Nokia E7 runtime profile", () => { expect(runtime).toContain("POCKETJS_SYMBIAN_KEY_MOVE_FORWARD"); expect(runtime).toContain("POCKETJS_SYMBIAN_KEY_LOOK_LEFT"); expect(runtime).toContain("POCKETJS_SYMBIAN_KEY_RELOAD"); + expect(runtime.match(/pocketjsSymbianNormalizeKey\(event->key\(\)\)/g)) + .toHaveLength(2); expect(runtime).toContain("nativeKeys_ |= nativeKey"); expect(runtime).toContain("nativeKeys_ &= ~nativeKey"); + expect(keyHeader).toContain( + "((key) >= 'a' && (key) <= 'z') ? ((key) - ('a' - 'A')) : (key)", + ); + expect(keyHeader).toContain("pocketjsSymbianNormalizeKey"); expect(runtime).toContain("setAttribute(Qt::WA_AutoOrientation, true)"); expect(runtime).not.toContain("WA_LockLandscapeOrientation"); expect(runtime).toContain('"__pocketResizeViewport"');