fix: Added keyboard focus shortcut
This commit is contained in:
parent
ccc5a05ed7
commit
b4e9112989
7 changed files with 141 additions and 15 deletions
|
|
@ -5,9 +5,23 @@ import { GamepadManager } from './manager';
|
|||
export default async function Initialize ()
|
||||
{
|
||||
let startSelectPressed = false;
|
||||
let endPressed = false;
|
||||
|
||||
const manager = new GamepadManager();
|
||||
|
||||
function handleFocus ()
|
||||
{
|
||||
const launchGameTask = taskQueue.findJob(LaunchGameJob.id, LaunchGameJob);
|
||||
if (launchGameTask)
|
||||
{
|
||||
launchGameTask.abort('exit');
|
||||
taskQueue.waitForJob(LaunchGameJob.id).then(() => setTimeout(() => events.emit('focus'), 300));
|
||||
} else
|
||||
{
|
||||
events.emit('focus');
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(() =>
|
||||
{
|
||||
for (const pad of manager.getGamepads())
|
||||
|
|
@ -20,21 +34,26 @@ export default async function Initialize ()
|
|||
if (!startSelectPressed)
|
||||
{
|
||||
startSelectPressed = true;
|
||||
console.log("Focus");
|
||||
const launchGameTask = taskQueue.findJob(LaunchGameJob.id, LaunchGameJob);
|
||||
if (launchGameTask)
|
||||
{
|
||||
launchGameTask.abort('exit');
|
||||
taskQueue.waitForJob(LaunchGameJob.id).then(() => setTimeout(() => events.emit('focus'), 300));
|
||||
} else
|
||||
{
|
||||
events.emit('focus');
|
||||
}
|
||||
handleFocus();
|
||||
}
|
||||
} else
|
||||
{
|
||||
startSelectPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
const keyboard = manager.getKeyboard();
|
||||
const keyState = keyboard.update();
|
||||
if (keyState?.keys.End && keyState?.keys.LeftControl)
|
||||
{
|
||||
if (!endPressed)
|
||||
{
|
||||
endPressed = true;
|
||||
handleFocus();
|
||||
}
|
||||
} else
|
||||
{
|
||||
endPressed = false;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
22
src/bun/api/controls/keyboard.ts
Normal file
22
src/bun/api/controls/keyboard.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import type { IKeyboardBackend, KeyboardState } from "./types";
|
||||
|
||||
export class Keybaord
|
||||
{
|
||||
private backend: IKeyboardBackend | undefined;
|
||||
|
||||
async init ()
|
||||
{
|
||||
if (process.platform === "win32")
|
||||
{
|
||||
const { KeyboardWindows } = await import("./windows");
|
||||
this.backend = new KeyboardWindows();
|
||||
} else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
update (): KeyboardState | null
|
||||
{
|
||||
return this.backend?.update() ?? null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import { Gamepad } from "./gamepad";
|
||||
import { platform } from "os";
|
||||
import { Keybaord } from "./keyboard";
|
||||
|
||||
export class GamepadManager
|
||||
{
|
||||
private gamepads: Gamepad[] = [];
|
||||
private keyboard: Keybaord;
|
||||
private scanInterval: any;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.scanGamepads();
|
||||
this.keyboard = new Keybaord();
|
||||
this.keyboard.init();
|
||||
// scan every second for new/disconnected devices
|
||||
this.scanInterval = setInterval(() => this.scanGamepads(), 1000);
|
||||
this.scanInterval = setInterval(async () => this.scanGamepads(), 1000);
|
||||
}
|
||||
|
||||
private scanGamepads ()
|
||||
private async scanGamepads ()
|
||||
{
|
||||
const max = platform() === "win32" ? 4 : 8; // max controllers
|
||||
for (let i = 0; i < max; i++)
|
||||
|
|
@ -23,6 +27,7 @@ export class GamepadManager
|
|||
try
|
||||
{
|
||||
const pad = new Gamepad(i);
|
||||
await pad.init();
|
||||
if (pad.update())
|
||||
{
|
||||
this.gamepads[i] = pad;
|
||||
|
|
@ -42,6 +47,11 @@ export class GamepadManager
|
|||
}
|
||||
}
|
||||
|
||||
getKeyboard ()
|
||||
{
|
||||
return this.keyboard;
|
||||
}
|
||||
|
||||
getGamepads ()
|
||||
{
|
||||
return this.gamepads.filter(Boolean);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,21 @@ export type ButtonName =
|
|||
| "START" | "SELECT"
|
||||
| "L3" | "R3";
|
||||
|
||||
export type KeyCode =
|
||||
| "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight"
|
||||
| "KeyW" | "KeyA" | "KeyS" | "KeyD"
|
||||
| "Enter" | "Escape" | "Space" | "End" | "LeftShift" | "RightShift" | "LeftControl" | "RightControl" | "LeftAlt" | "RightAlt";
|
||||
|
||||
export interface KeyboardState
|
||||
{
|
||||
keys: Record<KeyCode, boolean>;
|
||||
}
|
||||
|
||||
export interface IKeyboardBackend
|
||||
{
|
||||
update (): KeyboardState;
|
||||
}
|
||||
|
||||
export interface Stick
|
||||
{
|
||||
x: number; // -1 → 1
|
||||
|
|
|
|||
|
|
@ -1,11 +1,72 @@
|
|||
import { IGamepadBackend, GamepadState, ButtonName } from "./types";
|
||||
import { IGamepadBackend, GamepadState, ButtonName, IKeyboardBackend, KeyboardState, KeyCode } from "./types";
|
||||
import { dlopen, FFIType } from "bun:ffi";
|
||||
|
||||
const xinput = dlopen("xinput1_4.dll", {
|
||||
XInputGetState: { args: [FFIType.u32, FFIType.ptr], returns: FFIType.u32 },
|
||||
});
|
||||
|
||||
const user32 = dlopen("user32.dll", {
|
||||
GetAsyncKeyState: {
|
||||
args: [FFIType.i32],
|
||||
returns: FFIType.i16,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Virtual key codes
|
||||
const VK: Record<KeyCode, number> = {
|
||||
ArrowUp: 0x26,
|
||||
ArrowDown: 0x28,
|
||||
ArrowLeft: 0x25,
|
||||
ArrowRight: 0x27,
|
||||
KeyW: 0x57,
|
||||
KeyA: 0x41,
|
||||
KeyS: 0x53,
|
||||
KeyD: 0x44,
|
||||
Enter: 0x0D,
|
||||
Escape: 0x1B,
|
||||
Space: 0x20,
|
||||
End: 0x23,
|
||||
LeftShift: 0xA0,
|
||||
RightShift: 0xA1,
|
||||
LeftControl: 0xA2,
|
||||
RightControl: 0xA3,
|
||||
LeftAlt: 0xA4,
|
||||
RightAlt: 0xA5,
|
||||
};
|
||||
|
||||
const ERROR_SUCCESS = 0;
|
||||
|
||||
export class KeyboardWindows implements IKeyboardBackend
|
||||
{
|
||||
private keys: Record<KeyCode, boolean> = {} as any;
|
||||
|
||||
update (): KeyboardState
|
||||
{
|
||||
const next: Record<KeyCode, boolean> = {} as any;
|
||||
|
||||
// default all keys to false
|
||||
|
||||
// poll keys globally
|
||||
for (const vkStr in VK)
|
||||
{
|
||||
const vk = Number(VK[vkStr as KeyCode]);
|
||||
const key = vkStr;
|
||||
|
||||
const state = user32.symbols.GetAsyncKeyState(vk);
|
||||
|
||||
if ((state & 0x8000) !== 0)
|
||||
{
|
||||
next[key as KeyCode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.keys = next;
|
||||
|
||||
return { keys: this.keys };
|
||||
}
|
||||
}
|
||||
|
||||
export class GamepadWindows implements IGamepadBackend
|
||||
{
|
||||
private index: number;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSche
|
|||
// We have full control over launching integrated emulators better to use bun spawn
|
||||
const bunGame = Bun.spawn([this.validCommand.metadata.emulatorBin, ...commandArgs], {
|
||||
cwd: this.validCommand.startDir,
|
||||
windowsVerbatimArguments: true,
|
||||
signal: context.abortSignal
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ const assets = new Set<string>([
|
|||
]);
|
||||
|
||||
// Store basePath resolved from Vite config
|
||||
const BASE_PATH = "./";
|
||||
const BASE_PATH = "/";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue