diff --git a/apps/overlay/electron/main.js b/apps/overlay/electron/main.js index b6f512c..cbfa9d8 100644 --- a/apps/overlay/electron/main.js +++ b/apps/overlay/electron/main.js @@ -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; @@ -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); } } @@ -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', @@ -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', () => { @@ -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 }) => { @@ -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) => { diff --git a/apps/overlay/electron/preload.js b/apps/overlay/electron/preload.js index b5b4d4e..269d218 100644 --- a/apps/overlay/electron/preload.js +++ b/apps/overlay/electron/preload.js @@ -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)), }); diff --git a/apps/overlay/package.json b/apps/overlay/package.json index d75abd3..26011be 100644 --- a/apps/overlay/package.json +++ b/apps/overlay/package.json @@ -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" diff --git a/apps/overlay/src/controllers-window.html b/apps/overlay/src/controllers-window.html new file mode 100644 index 0000000..a84c48a --- /dev/null +++ b/apps/overlay/src/controllers-window.html @@ -0,0 +1,49 @@ + + + + +Controllers + + + +
+
CONTROLLERS
+
+
click a controller to show it in the overlay · ● in use now · drag to move
+
+ + + + + diff --git a/apps/overlay/src/index.html b/apps/overlay/src/index.html index ff6af63..f8a63c3 100644 --- a/apps/overlay/src/index.html +++ b/apps/overlay/src/index.html @@ -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; @@ -919,7 +934,12 @@

Controller Overlay

- + + +
+ +
+