Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d37d763
Add seat-select navigator overlay view (versus / co-op / solo)
petegordon Jul 5, 2026
4449ba0
core: elect one lizard-mode owner per Steam Controller Puck
petegordon Jul 5, 2026
3eca6bb
core: harden DualSense/DS4 Bluetooth full-report handshake
petegordon Jul 5, 2026
09e2235
core: retire electronAutoRequestDevice (unfixable at boot) to a no-op
petegordon Jul 5, 2026
a40a5c0
overlay: add root start:lobby passthrough to the overlay workspace
petegordon Jul 5, 2026
a16c492
overlay(lobby): always-visible Pair button + live controller count
petegordon Jul 5, 2026
70e0c93
overlay: WebHID-first controller selection + identity (Phase 3a)
petegordon Jul 6, 2026
b5f500f
core: expose last touchpad/grips on HidEntry for pooled viz reads (Ph…
petegordon Jul 6, 2026
342eeed
overlay: single-owner WebHID pool drives the viz (Phase 3b/2)
petegordon Jul 6, 2026
59e3b0c
overlay: fix duplicate _preferredGyroDevice/_deviceIds declaration (3b)
petegordon Jul 6, 2026
84edcdd
overlay: Steam back paddles + calibrate-on-select (Phase 3b fixes)
petegordon Jul 6, 2026
5978e15
overlay: fix USB DS4 "SELECTED but dead" + calibrate-on-select reset …
petegordon Jul 6, 2026
ef971f3
overlay: calibrate re-zeros orientation + DS4 pool diagnostics (3b)
petegordon Jul 6, 2026
f34b712
overlay: evict phantom HID handles (fixes USB DS4 stale grant) (3b)
petegordon Jul 6, 2026
ae40091
overlay: recover a SELECTED controller whose HID stream stalls mid-se…
petegordon Jul 6, 2026
e5f630c
overlay: input-driven auto-adopt — no idle controller ever auto-SELEC…
petegordon Jul 6, 2026
49115ad
overlay: trim the [designate] diagnostic to a single line (3b)
petegordon Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions apps/overlay/electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let clickThrough = false;
// default for the single-controller window is 720×540 — a clean capture size.
const DEFAULT_WINDOW_SIZE = { width: 720, height: 540 };
const MULTI_WINDOW_SIZE = { width: 1100, height: 520 };
const LOBBY_WINDOW_SIZE = { width: 820, height: 660 };
let windowStatePath = null; // resolved once app is ready (needs userData path)
let saveWindowSizeTimer = null;

Expand Down Expand Up @@ -114,8 +115,11 @@ function upsertHidController(d, connected) {
function hidControllerList() { return [...hidControllers.values()]; }

function broadcastHidControllers() {
if (inventoryWindow && !inventoryWindow.isDestroyed()) {
inventoryWindow.webContents.send('hid-controllers-snapshot', hidControllerList());
const list = hidControllerList();
// The inventory window and the lobby both render this (the lobby shows each
// seat's per-unit serial/MAC — see its Controllers panel).
for (const w of [inventoryWindow, mainWindow]) {
if (w && !w.isDestroyed()) w.webContents.send('hid-controllers-snapshot', list);
}
}

Expand All @@ -139,6 +143,8 @@ function openInventoryWindow() {
// Which size profile this launch uses. Single source of truth so the window
// creation and the "reset to default" handler agree on key + default size.
function getSizeMode() {
const useLobby = process.env.OVERLAY_LOBBY === '1' || process.argv.includes('--lobby');
if (useLobby) return { stateKey: 'lobby', defaultSize: LOBBY_WINDOW_SIZE };
const useMulti = process.env.OVERLAY_MULTI === '1' || process.argv.includes('--multi');
return {
stateKey: useMulti ? 'multi' : 'main',
Expand Down Expand Up @@ -173,7 +179,7 @@ function createWindow() {
},
});

const entry = stateKey === 'multi' ? 'multi.html' : 'index.html';
const entry = stateKey === 'lobby' ? 'lobby.html' : stateKey === 'multi' ? 'multi.html' : 'index.html';
mainWindow.loadFile(path.join(__dirname, '..', 'src', entry));

mainWindow.once('ready-to-show', () => {
Expand Down Expand Up @@ -354,6 +360,7 @@ app.whenReady().then(() => {
gyro: { file: 'gyro-window.html', width: 260, height: 260 },
axis: { file: 'axis-window.html', width: 280, height: 90 },
roll: { file: 'roll-window.html', width: 240, height: 170 },
controllers: { file: 'controllers-window.html', width: 320, height: 460 },
};

ipcMain.handle('open-hud-window', async (_event, { kind, profile, greenScreen }) => {
Expand Down Expand Up @@ -420,6 +427,11 @@ app.whenReady().then(() => {
if (w && !w.isDestroyed()) w.webContents.send('hud-state-update', state);
});

// Controllers window → main overlay: a row was clicked; switch the shown pad.
ipcMain.on('controller-select', (_event, payload) => {
if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.send('controller-select', payload);
});

// A detached window's close button uses this to close itself (frameless
// windows have no titlebar close affordance).
ipcMain.on('close-this-window', (event) => {
Expand Down
7 changes: 7 additions & 0 deletions apps/overlay/electron/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
// Periodic snapshot of currently-present HID controllers (with serials).
onHidControllersSnapshot: (callback) =>
ipcRenderer.on('hid-controllers-snapshot', (_, list) => callback(list)),

// ── Controllers window → overlay selection ──
// The detached Controllers list sends the clicked controller's key; the main
// overlay receives it and switches which controller drives the visualization.
selectController: (payload) => ipcRenderer.send('controller-select', payload),
onControllerSelect: (callback) =>
ipcRenderer.on('controller-select', (_, payload) => callback(payload)),
});
2 changes: 2 additions & 0 deletions apps/overlay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
"scripts": {
"prestart": "node scripts/copy-three.js && node scripts/copy-workspace.js",
"prestart:multi": "node scripts/copy-three.js && node scripts/copy-workspace.js",
"prestart:lobby": "node scripts/copy-three.js && node scripts/copy-workspace.js",
"prepackage": "node scripts/copy-three.js && node scripts/copy-workspace.js",
"premake": "node scripts/copy-three.js && node scripts/copy-workspace.js",
"start": "electron .",
"start:multi": "electron . --multi",
"start:lobby": "electron . --lobby",
"make": "electron-forge make",
"make:dmg": "npm run package && bash scripts/make-dmg.sh",
"package": "electron-forge package"
Expand Down
49 changes: 49 additions & 0 deletions apps/overlay/src/controllers-window.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Controllers</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: transparent; user-select: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #e7ecf5; }
/* Opaque panel filling the (transparent) window, rounded corners. */
#panel { position: fixed; inset: 0; display: flex; flex-direction: column;
background: rgba(12,15,20,0.98); border: 1px solid rgba(255,255,255,0.12); border-radius: 12px; overflow: hidden; }
#titlebar { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 9px 12px;
border-bottom: 1px solid rgba(255,255,255,0.08); font: 700 11px/1 ui-monospace, monospace; letter-spacing: 1.4px; color: #9fb0c6; text-transform: uppercase; }
#count { font-weight: 400; color: #7d879a; letter-spacing: .3px; }
#list { flex: 1 1 auto; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.row { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 9px;
background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.09); cursor: pointer; }
.row:hover { border-color: rgba(255,255,255,0.28); background: rgba(255,255,255,0.09); }
.row.sel { border-color: #6ce08f; box-shadow: 0 0 0 1px rgba(108,224,143,.4); }
.dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: #39404e; transition: background .08s, box-shadow .08s; }
.dot.on { background: #6ce08f; box-shadow: 0 0 8px 1px #6ce08f; }
.main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.name { font-size: 13px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sub { font: 10px/1.3 ui-monospace, monospace; color: #8894a8; }
.serial { font: 9px/1.3 ui-monospace, monospace; color: #9fb0c6; word-break: break-all; margin-top: 1px; }
.tag { flex: 0 0 auto; font: 800 8px/1 ui-monospace, monospace; letter-spacing: .8px; padding: 3px 6px; border-radius: 5px; }
.tag.selected { color: #06140c; background: #6ce08f; box-shadow: 0 0 8px -2px #6ce08f; }
.tag.active { color: #1a1205; background: #e0b453; }
.tag.available { color: #c9d6ff; background: rgba(110,168,255,0.18); border: 1px solid rgba(110,168,255,0.4); }
.empty { font: 11px/1.5 ui-monospace, monospace; color: #6a7184; text-align: center; padding: 16px 8px; }
.hint { flex: 0 0 auto; padding: 7px 12px; border-top: 1px solid rgba(255,255,255,0.08); font: 9px/1.4 ui-monospace, monospace; color: #6a7184; }
#close-btn { position: fixed; top: 7px; right: 8px; width: 20px; height: 20px; background: rgba(255,255,255,0.12);
border: none; border-radius: 50%; color: #ccc; font-size: 13px; line-height: 1; cursor: pointer; z-index: 51;
display: flex; align-items: center; justify-content: center; }
#close-btn:hover { background: rgba(255,255,255,0.25); color: #fff; }
</style>
</head>
<body>
<div id="panel">
<div id="titlebar">CONTROLLERS <span id="count"></span></div>
<div id="list"></div>
<div class="hint">click a controller to show it in the overlay · ● in use now · drag to move</div>
</div>
<button id="close-btn" title="Close">&times;</button>
<script src="js/hud-window-chrome.js"></script>
<script type="module" src="js/controllers-window.js"></script>
</body>
</html>
22 changes: 21 additions & 1 deletion apps/overlay/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@
letter-spacing: 0.5px;
}

/* Controllers list (which controller drives the overlay) */
#ovl-ctrl-list { display: flex; flex-direction: column; gap: 5px; max-height: 210px; overflow-y: auto; }
.ovl-ctrl-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 8px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.09); cursor: pointer; }
.ovl-ctrl-row:hover { border-color: rgba(255,255,255,0.28); }
.ovl-ctrl-row.sel { border-color: #6ce08f; box-shadow: 0 0 0 1px rgba(108,224,143,.4); }
.ovl-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: #39404e; transition: background .08s, box-shadow .08s; }
.ovl-dot.on { background: #6ce08f; box-shadow: 0 0 7px 1px #6ce08f; }
.ovl-ctrl-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.ovl-ctrl-name { font-size: 12px; font-weight: 600; color: #e7ecf5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ovl-ctrl-sub { font: 9px/1.3 ui-monospace, monospace; color: #8894a8; }
.ovl-tag { flex: 0 0 auto; font: 800 8px/1 ui-monospace, monospace; letter-spacing: .6px; padding: 3px 5px; border-radius: 4px; }
.ovl-tag.active { color: #06140c; background: #6ce08f; }
.ovl-tag.available { color: #c9d6ff; background: rgba(110,168,255,0.18); border: 1px solid rgba(110,168,255,0.4); }
.ovl-ctrl-empty { font: 10px/1.4 ui-monospace, monospace; color: #6a7184; text-align: center; padding: 8px; }

.setting-row {
display: flex;
align-items: center;
Expand Down Expand Up @@ -919,7 +934,12 @@
<h3>Controller Overlay</h3>

<div class="setting-row">
<label>Controller</label>
<label>Controllers</label>
<button id="ovl-ctrl-popout" title="Open a movable window listing every connected controller with live activity">⧉ Show list…</button>
</div>

<div class="setting-row">
<label>Model</label>
<select id="controller-type">
<option value="auto">Auto-detect</option>
<option value="dualsense">DualSense</option>
Expand Down
Loading
Loading