Skip to content

Commit ab59f32

Browse files
authored
Merge pull request #3878 from rommapp/fix/v2-hotkeys-emulator-keyboard-owner
fix(v2): take the hotkey-mute playing flag from EmulatorJS' start hook
2 parents f7701e2 + 27b243c commit ab59f32

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { mount } from "@vue/test-utils";
2+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3+
import { defineComponent, h } from "vue";
4+
5+
const push = vi.fn();
6+
const playingStore = { playing: false };
7+
8+
vi.mock("vue-router", () => ({
9+
useRouter: () => ({ push }),
10+
}));
11+
vi.mock("@/stores/playing", () => ({
12+
default: () => playingStore,
13+
}));
14+
// The real module instantiates a router at import time.
15+
vi.mock("@/plugins/router", () => ({
16+
ROUTES: {
17+
SEARCH: "search",
18+
HOME: "home",
19+
PLATFORMS_INDEX: "platforms",
20+
COLLECTIONS_INDEX: "collections",
21+
},
22+
}));
23+
24+
// `installed` is module state, so each test gets a fresh module and a fresh
25+
// window listener via a throwaway host component.
26+
async function install() {
27+
vi.resetModules();
28+
const { useGlobalHotkeys } = await import("./index");
29+
const Host = defineComponent({
30+
setup() {
31+
useGlobalHotkeys().install();
32+
return () => h("div");
33+
},
34+
});
35+
return mount(Host);
36+
}
37+
38+
function press(key: string, target: EventTarget = document.body) {
39+
target.dispatchEvent(
40+
new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true }),
41+
);
42+
}
43+
44+
beforeEach(() => {
45+
push.mockClear();
46+
playingStore.playing = false;
47+
document.body.innerHTML = "";
48+
});
49+
50+
afterEach(() => {
51+
document.body.innerHTML = "";
52+
});
53+
54+
describe("useGlobalHotkeys", () => {
55+
it("routes to search on '/'", async () => {
56+
const host = await install();
57+
press("/");
58+
expect(push).toHaveBeenCalledWith({ name: "search" });
59+
host.unmount();
60+
});
61+
62+
it("ignores keys while the playing flag is set", async () => {
63+
const host = await install();
64+
playingStore.playing = true;
65+
press("/");
66+
press("g");
67+
press("h");
68+
expect(push).not.toHaveBeenCalled();
69+
host.unmount();
70+
});
71+
72+
// The DOSBox case: keys land on <body>, not on a form control, because the
73+
// emulator canvas reads them off window. Only the playing flag tells them
74+
// apart from a plain "/" pressed in the gallery.
75+
it("ignores '/' pressed on the body while a game is running", async () => {
76+
const host = await install();
77+
playingStore.playing = true;
78+
const stage = document.createElement("div");
79+
stage.id = "game";
80+
document.body.appendChild(stage);
81+
82+
press("/");
83+
expect(push).not.toHaveBeenCalled();
84+
host.unmount();
85+
});
86+
87+
it("ignores keys typed into form fields", async () => {
88+
const host = await install();
89+
const input = document.createElement("input");
90+
document.body.appendChild(input);
91+
92+
press("/", input);
93+
expect(push).not.toHaveBeenCalled();
94+
host.unmount();
95+
});
96+
97+
it("still routes 'g h' outside a session", async () => {
98+
const host = await install();
99+
press("g");
100+
press("h");
101+
expect(push).toHaveBeenCalledWith({ name: "home" });
102+
host.unmount();
103+
});
104+
});

frontend/src/views/Player/EmulatorJS/Player.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,12 @@ window.EJS_onSaveState = async function ({
383383
};
384384
385385
window.EJS_onGameStart = async () => {
386+
// The emulator now owns the keyboard: every key, "/" included, belongs to
387+
// the game (a DOS prompt typing "mount A / -t floppy" must not reach the
388+
// global hotkeys). Callers flag this at launch too, but taking it from the
389+
// emulator's own start hook keeps the flag true for any entry point.
390+
playing.value = true;
391+
386392
// Install netplay overrides synchronously, before any await below, so they
387393
// are in place before room polling or a Create/Join action can start.
388394
const netplay = window.EJS_emulator?.netplay;

0 commit comments

Comments
 (0)