Output window can no longer be closed by accident
Cmd+W / the red button on the output window are ignored while the control panel is open: the audio engine and all visual state live in that window, so an accidental close killed the whole show with no way to reopen it. If the window dies anyway (renderer crash) it is recreated automatically. Quitting normally (closing the control panel or Cmd+Q) still works. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e745743c5
commit
134af5775c
1 file changed
+49
-21
@@ -11,6 +11,51 @@ function externalDisplay() {
|
|||||||
return screen.getAllDisplays().find(d => d.id !== primary.id) || primary;
|
return screen.getAllDisplays().find(d => d.id !== primary.id) || primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let quitting = false;
|
||||||
|
|
||||||
|
function createOutputWindow() {
|
||||||
|
const primary = screen.getPrimaryDisplay();
|
||||||
|
const ext = externalDisplay();
|
||||||
|
const hasExternal = ext.id !== primary.id;
|
||||||
|
|
||||||
|
outputWin = new BrowserWindow({
|
||||||
|
width: hasExternal ? ext.bounds.width : 960,
|
||||||
|
height: hasExternal ? ext.bounds.height : 600,
|
||||||
|
x: hasExternal ? ext.bounds.x : primary.bounds.x + 520,
|
||||||
|
y: hasExternal ? ext.bounds.y : primary.bounds.y + 60,
|
||||||
|
title: 'DJ Visualizer — Output',
|
||||||
|
backgroundColor: '#000000',
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
additionalArguments: ['--role=output'],
|
||||||
|
// Let audio loaded via IPC play without a click inside the output window.
|
||||||
|
autoplayPolicy: 'no-user-gesture-required'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
outputWin.loadFile(path.join(__dirname, 'src', 'output.html'));
|
||||||
|
if (hasExternal) {
|
||||||
|
outputWin.once('ready-to-show', () => placeOutputOnDisplay(ext, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The output window hosts the audio engine and all visual state: closing it
|
||||||
|
// by accident (Cmd+W, red button) would kill the show. Ignore close requests
|
||||||
|
// while the control panel is open; only app quit may close it.
|
||||||
|
outputWin.on('close', (e) => {
|
||||||
|
if (!quitting && controlWin && !controlWin.isDestroyed()) e.preventDefault();
|
||||||
|
});
|
||||||
|
// Safety net: if it dies anyway (e.g. a renderer crash), bring it back.
|
||||||
|
outputWin.on('closed', () => {
|
||||||
|
outputWin = null;
|
||||||
|
if (!quitting && controlWin && !controlWin.isDestroyed()) createOutputWindow();
|
||||||
|
});
|
||||||
|
outputWin.webContents.on('render-process-gone', () => {
|
||||||
|
if (quitting) return;
|
||||||
|
if (outputWin && !outputWin.isDestroyed()) outputWin.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function createWindows() {
|
function createWindows() {
|
||||||
const primary = screen.getPrimaryDisplay();
|
const primary = screen.getPrimaryDisplay();
|
||||||
const ext = externalDisplay();
|
const ext = externalDisplay();
|
||||||
@@ -38,33 +83,14 @@ function createWindows() {
|
|||||||
controlWin.loadFile(path.join(__dirname, 'src', 'control.html'));
|
controlWin.loadFile(path.join(__dirname, 'src', 'control.html'));
|
||||||
|
|
||||||
// --- Output window (the visuals) ---
|
// --- Output window (the visuals) ---
|
||||||
outputWin = new BrowserWindow({
|
createOutputWindow();
|
||||||
width: hasExternal ? ext.bounds.width : 960,
|
|
||||||
height: hasExternal ? ext.bounds.height : 600,
|
|
||||||
x: hasExternal ? ext.bounds.x : primary.bounds.x + 520,
|
|
||||||
y: hasExternal ? ext.bounds.y : primary.bounds.y + 60,
|
|
||||||
title: 'DJ Visualizer — Output',
|
|
||||||
backgroundColor: '#000000',
|
|
||||||
webPreferences: {
|
|
||||||
preload: path.join(__dirname, 'preload.js'),
|
|
||||||
contextIsolation: true,
|
|
||||||
nodeIntegration: false,
|
|
||||||
additionalArguments: ['--role=output'],
|
|
||||||
// Let audio loaded via IPC play without a click inside the output window.
|
|
||||||
autoplayPolicy: 'no-user-gesture-required'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
outputWin.loadFile(path.join(__dirname, 'src', 'output.html'));
|
|
||||||
if (hasExternal) {
|
|
||||||
outputWin.once('ready-to-show', () => placeOutputOnDisplay(ext, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
controlWin.on('closed', () => {
|
controlWin.on('closed', () => {
|
||||||
|
quitting = true; // closing the panel ends the show: let the output close
|
||||||
controlWin = null;
|
controlWin = null;
|
||||||
if (outputWin) outputWin.close();
|
if (outputWin) outputWin.close();
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
outputWin.on('closed', () => { outputWin = null; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureMicAccess() {
|
async function ensureMicAccess() {
|
||||||
@@ -86,6 +112,8 @@ ipcMain.handle('cam:ensure', async () => {
|
|||||||
} catch (e) { return true; }
|
} catch (e) { return true; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.on('before-quit', () => { quitting = true; });
|
||||||
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
if (process.platform === 'darwin' && app.dock) {
|
if (process.platform === 'darwin' && app.dock) {
|
||||||
try { app.dock.setIcon(path.join(__dirname, 'build', 'icon_1024.png')); } catch (e) { /* non-fatal */ }
|
try { app.dock.setIcon(path.join(__dirname, 'build', 'icon_1024.png')); } catch (e) { /* non-fatal */ }
|
||||||
|
|||||||
Reference in new issue
Block a user