fix: Fixed issues on windows

feat: Implemented mouse and gamepad automatic switching
fix: Made touch screen work better on the steam deck
This commit is contained in:
Simeon Radivoev 2026-02-24 18:58:48 +02:00
parent e4df8fb9fb
commit b4a89385d0
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
24 changed files with 334 additions and 137 deletions

View file

@ -1,7 +1,13 @@
import { getCurrentFocusKey, navigateByDirection, SpatialNavigation } from "@noriginmedia/norigin-spatial-navigation";
import { dispatchFocusedEvent, GetFocusedElement } from "./spatialNavigation";
import { getCurrentFocusKey, navigateByDirection } from "@noriginmedia/norigin-spatial-navigation";
import { GetFocusedElement } from "./spatialNavigation";
import { useEffect, useState } from "react";
let loopStarted = false;
let isTouching = false;
type ActiveControlType = 'keyboard' | 'gamepad' | 'mouse' | 'touch' | undefined;
let activeControls: ActiveControlType = undefined;
let mouseUpdateTimeout: any | undefined = undefined;
let touchStopTimeout: any | undefined = undefined;
const handleLoop = () =>
{
@ -11,8 +17,70 @@ const handleLoop = () =>
loopStarted = true;
}
};
// Mouse needs to be delayed so that touch events can cancel it.
// This is to prevent both touch and mouse events triggering as they do on the steam deck.
const handleMouseMove = (e: MouseEvent) =>
{
if (!mouseUpdateTimeout && !isTouching)
{
mouseUpdateTimeout = setTimeout(() =>
{
focusControl('mouse');
mouseUpdateTimeout = undefined;
}, 300);
}
};
function clearMouseUpdate ()
{
if (mouseUpdateTimeout)
clearTimeout(mouseUpdateTimeout);
mouseUpdateTimeout = undefined;
};
const handleKeyDown = () =>
{
focusControl('keyboard');
};
const handleTouchStart = (e: TouchEvent) =>
{
isTouching = true;
focusControl('touch');
clearMouseUpdate();
};
const handleTouchEnd = (e: TouchEvent) =>
{
setTimeout(() => isTouching = false, 10);
};
window.addEventListener('touchstart', handleTouchStart);
window.addEventListener('touchend', handleTouchEnd);
window.addEventListener('touchcancel', handleTouchEnd);
window.addEventListener("gamepadconnected", handleLoop);
import.meta.hot.dispose(() => window.addEventListener('gamepaddisconnected', handleLoop));
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('keydown', handleKeyDown);
import.meta.hot.dispose(() => window.removeEventListener('gamepaddisconnected', handleLoop));
import.meta.hot.dispose(() => window.removeEventListener('mousemove', handleMouseMove));
import.meta.hot.dispose(() => window.removeEventListener('keydown', handleKeyDown));
import.meta.hot.dispose(() => window.removeEventListener('touchstart', handleTouchStart));
import.meta.hot.dispose(() => window.removeEventListener('touchend', handleTouchEnd));
import.meta.hot.dispose(() => window.removeEventListener('touchcancel', handleTouchEnd));
export default function useActiveControl ()
{
const [c, setC] = useState<typeof activeControls>(activeControls);
useEffect(() =>
{
const handler = (e: Event) => setC((e as CustomEvent).detail);
window.addEventListener('activecontrolschange', handler);
return () => window.removeEventListener('activecontrolschange', handler);
});
return { isMouse: c === 'mouse', isPointer: c === 'mouse' || c === 'touch', control: c };
}
const throttleMap = new Map<string, number>();
const throttleAcceleration = new Map<string, number>();
@ -32,6 +100,19 @@ function throttleNav (key: string, dir: string, event: Event)
}
}
function focusControl (control: typeof activeControls)
{
if (activeControls != control)
{
activeControls = control;
window.dispatchEvent(new CustomEvent('activecontrolschange', { detail: control }));
if (control !== 'mouse')
{
clearMouseUpdate();
}
}
}
/*window.addEventListener('keydown', e =>
{
if (e.key === 'Escape')
@ -74,6 +155,7 @@ function updateStatus ()
if (!throttleMap.has(key))
{
window.dispatchEvent(new GamepadButtonEvent('gamepadbuttondown', { button: i, gamepad: gamepad }));
focusControl('gamepad');
throttleMap.set(key, 0);
}
} else
@ -81,6 +163,7 @@ function updateStatus ()
if (throttleMap.delete(key))
{
window.dispatchEvent(new GamepadButtonEvent('gamepadbuttonup', { button: i, gamepad: gamepad }));
focusControl('gamepad');
}
}
}