guard prefix and wine edits while their apps run

This commit is contained in:
olcxja 2026-10-02 14:46:21 +02:00
commit 38eea05789
9 changed files with 170 additions and 39 deletions

View file

@ -85,7 +85,15 @@ function registerIpc({ getWindow, onGamesChanged }) {
ipcMain.handle('wines:setDisabled', (e, id, disabled) => wines.setWineDisabled(id, disabled))
ipcMain.handle('wines:setDisplayName', (e, id, name) => wines.setWineDisplayName(id, name))
ipcMain.handle('wines:setDefault', (e, id) => wines.setDefaultWine(id))
ipcMain.handle('wines:delete', (e, id) => wines.deleteWine(id))
ipcMain.handle('wines:delete', (e, id) => {
// the build's files back the running prefix (dlls, stub exes) - deleting
// it under a running game breaks that game
if (library.isWineInUse(id, games.runningInfo())) {
const apps = games.runningInfo().filter(r => r.wine === id).map(r => r.name)
return { ok: false, error: 'inUse', apps }
}
return wines.deleteWine(id)
})
ipcMain.handle('wines:import', async (e, srcDir) => {
try {
return await wines.importWineFolder(srcDir)
@ -95,11 +103,37 @@ function registerIpc({ getWindow, onGamesChanged }) {
})
// prefixes ---------------------------------------------------------------------
// A prefix folder must not move or disappear while a game runs in it: wine
// keeps the registry and user files open and would write into a folder that
// no longer exists (or, for a delete, lose them mid-run). The renderer shows
// these as a toast naming the blocking app.
const prefixInUse = name => {
const apps = library.appsUsingPrefix(name, games.runningInfo())
return apps.length ? { ok: false, error: 'inUse', apps: apps.map(r => r.name) } : null
}
ipcMain.handle('prefixes:list', () => prefixes.listPrefixes())
ipcMain.handle('prefixes:create', (e, name) => prefixes.createPrefix(name))
ipcMain.handle('prefixes:delete', (e, name) => prefixes.deletePrefix(name))
ipcMain.handle('prefixes:rename', (e, oldName, newName) => prefixes.renamePrefix(oldName, newName))
ipcMain.handle('prefixes:setDefault', (e, name) => prefixes.setDefaultPrefix(name))
ipcMain.handle('prefixes:delete', (e, name) => {
const busy = prefixInUse(name)
if (busy) return busy
// deleting a prefix that still has apps registered in it would orphan
// them (they fall back to the default prefix and lose their files)
const users = library.gamesUsingPrefix(name).map(g => g.name)
if (users.length) return { ok: false, error: 'hasApps', apps: users }
return prefixes.deletePrefix(name)
})
ipcMain.handle('prefixes:rename', (e, oldName, newName) => prefixInUse(oldName) || prefixes.renamePrefix(oldName, newName))
ipcMain.handle('prefixes:setDefault', (e, name) => {
// apps without a prefix field of their own resolve the default at launch;
// switching it now would silently move their next run (a prefix import is
// pinned to its own name)
const defs = prefixes.listPrefixes()
if (name !== defs.default) {
const affected = library.gamesFollowingDefault(games.runningInfo())
if (affected.length) return { ok: false, error: 'inUse', apps: affected.map(r => r.name) }
}
return prefixes.setDefaultPrefix(name)
})
// games: library -----------------------------------------------------------------
ipcMain.handle('games:list', () => library.loadGames())