Skip to content
Open
Changes from all commits
Commits
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
61 changes: 61 additions & 0 deletions electron/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,10 @@ function refreshTrayMenu() {
if (trayPopupWindow && !trayPopupWindow.isDestroyed() && trayPopupWindow.isVisible()) {
trayPopupWindow.webContents.send('tray-popup:data', await buildTrayPopupData())
}
// Keep the native Linux context menu in sync with running-game state
if (tray && process.platform === 'linux') {
try { tray.setContextMenu(buildTrayContextMenu()) } catch { /* ignore */ }
}
} catch (err) {
ucLog(`refreshTrayMenu failed: ${err?.message || err}`, 'warn')
}
Expand Down Expand Up @@ -2689,6 +2693,52 @@ function showTrayPopup() {
})()
}

// Native tray context menu, attached only on Linux. The StatusNotifierItem and
// libappindicator tray used by most Linux desktops does not deliver click or
// right-click events to Electron, so the custom styled popup never opens there
// and a tray icon is only interactive when a native menu is attached via
// setContextMenu. Windows and macOS keep the styled right-click popup, so this
// stays Linux only where there would otherwise be no buttons at all
function buildTrayContextMenu() {
const running = pickCurrentRunningGame()
const navTo = (targetPath) => {
showMainWindow()
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('uc:navigation-action', { path: targetPath })
}
}
const template = [
{ label: `Open ${WINDOW_DISPLAY_NAME}`, click: () => showMainWindow() },
{ type: 'separator' },
{ label: 'Browse', click: () => navTo('/') },
{ label: 'Library', click: () => navTo('/library') },
{ label: 'Downloads', click: () => navTo('/downloads') },
{ label: 'Settings', click: () => navTo('/settings') },
]
if (running && running.appid) {
template.push({ type: 'separator' })
template.push({
label: `Stop ${running.name || 'game'}`,
click: () => {
const gp = runningGames.get(running.appid)
if (gp) {
gp.userQuitRequested = true
if (typeof killProcessTree === 'function' && gp.pid) void killProcessTree(gp.pid)
}
},
})
}
template.push({ type: 'separator' })
template.push({
label: `Quit ${WINDOW_DISPLAY_NAME}`,
click: () => {
app.isQuitting = true
app.quit()
},
})
return Menu.buildFromTemplate(template)
}

function createTray() {
if (tray) return
const iconImage = resolveTrayIcon()
Expand Down Expand Up @@ -2716,6 +2766,17 @@ function createTray() {
destroyTrayPopupWindow()
showMainWindow()
})

// On Linux the click and right-click events above generally never fire, so the
// tray needs a real native context menu to be interactive. Rebuilt by
// refreshTrayMenu when a game starts or stops so the Stop entry stays accurate
if (process.platform === 'linux') {
try {
tray.setContextMenu(buildTrayContextMenu())
} catch (err) {
ucLog(`tray.setContextMenu failed: ${err?.message || err}`, 'warn')
}
}
}

ipcMain.on('tray-popup:ready', (event, { height }) => {
Expand Down