import { run } from './run'; /** * Let go of everything the display thinks is held. * * ── Why this needs to exist at all ── * * Synthesised input is state, and this side owns it. A press without a matching release stays pressed on * the X server forever, and there is nothing to notice: the panel looks connected, the picture updates, * the game responds. It just responds WRONGLY. A stranded `Shift_L` turned every left click into a * shift-click, which Minesweeper reads as a right click, so the board flagged instead of opening. * * The panel now releases what it holds on blur and on unmount, and no longer sends modifiers at all — so * this should never be needed. "Should never be needed" is exactly the class of thing that wants a button, * because when it IS needed the symptom names nothing and the only other cure is restarting the session * and losing the game in progress. * * ── Why a fixed list rather than asking what is down ── * * `xinput --query-state` would report exactly what is held, which is better. It is also a seventh host * binary this plugin would have to declare and a machine would have to have, for a repair. Releasing a key * that was not pressed is a no-op, so the fixed list costs nothing and needs nothing. * * Modifiers and buttons only, deliberately. They are what DISTORTS input — a stuck letter types, which is * obvious and harmless; a stuck modifier silently changes what every click means. */ const MODIFIERS = [ 'Shift_L', 'Shift_R', 'Control_L', 'Control_R', 'Alt_L', 'Alt_R', 'Super_L', 'Super_R', 'Meta_L', 'Meta_R', 'ISO_Level3_Shift', ]; const BUTTONS = ['1', '2', '3', '4', '5']; export type ResetResult = { ok: boolean; released: { modifiers: number; buttons: number } }; export async function resetInput(display: string): Promise { const keys = await run(['xdotool', 'keyup', ...MODIFIERS], { DISPLAY: display }, 5_000); let buttons = 0; for (const button of BUTTONS) { const up = await run(['xdotool', 'mouseup', button], { DISPLAY: display }, 5_000); if (up.ok) buttons++; } return { ok: keys.ok, released: { modifiers: keys.ok ? MODIFIERS.length : 0, buttons } }; }