105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
'use strict'
|
|
|
|
// "Open with PLWL" (file manager -> Open With -> PLWL): a file handed in via
|
|
// `--open <path>` (or the Exec %f plumbing) is parked here until the renderer
|
|
// picks it up, then launched ad hoc with the wine version + prefix chosen in
|
|
// the picker popup. Nothing is imported into the library.
|
|
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
let pending = null // file path waiting for the picker (service not ready yet)
|
|
|
|
function setPending(p) {
|
|
pending = p
|
|
}
|
|
|
|
function getPending() {
|
|
return pending
|
|
}
|
|
|
|
function hasPending() {
|
|
return !!pending
|
|
}
|
|
|
|
function clearPending() {
|
|
pending = null
|
|
}
|
|
|
|
// The launcher's own translations may not cover openwith texts in older
|
|
// installs - fall back to English keys so the UI never shows raw key names.
|
|
function textsFor(code) {
|
|
let texts = {}
|
|
try {
|
|
texts = require('./i18n').getLang(code).texts || {}
|
|
} catch {}
|
|
const fallback = {
|
|
'openwith.title': 'Open with Wine',
|
|
'openwith.file': 'File',
|
|
'openwith.fileMissing': 'This file no longer exists.',
|
|
'openwith.wine': 'Wine / Proton version',
|
|
'openwith.wineDefault': 'Default ({0})',
|
|
'openwith.prefix': 'Wine prefix',
|
|
'openwith.prefixDefault': 'Default prefix ({0})',
|
|
'openwith.run': 'Run',
|
|
'openwith.startFailed': 'Could not start {0}: {1}',
|
|
'openwith.notWindows': 'This is not a Windows executable (exe/msi/bat/cmd/lnk).'
|
|
}
|
|
return { ...fallback, ...texts }
|
|
}
|
|
|
|
function resolveWine(id) {
|
|
return require('./wines').resolveWine(id)
|
|
}
|
|
|
|
function resolvePrefix(name) {
|
|
const defs = require('./prefixes').listPrefixes()
|
|
const known = name && defs.prefixes.some(p => p.name === name) ? name : ''
|
|
return known || defs.default || 'Main'
|
|
}
|
|
|
|
// Ad-hoc run target: shaped exactly like a library game (runner.buildEnv and
|
|
// the playtime/log paths expect those fields) but nothing is persisted.
|
|
function makeTarget(openPath, wineId, prefixName) {
|
|
const dir = path.dirname(openPath)
|
|
return {
|
|
id: 'openwith',
|
|
name: path.basename(openPath),
|
|
mode: 'open',
|
|
path: dir,
|
|
exePath: openPath,
|
|
workdir: dir,
|
|
wine: wineId || 'default',
|
|
prefix: prefixName || '',
|
|
args: '',
|
|
dllOverrides: [],
|
|
env: {},
|
|
iconSource: '',
|
|
playSeconds: 0,
|
|
created: 0,
|
|
lastPlayed: 0
|
|
}
|
|
}
|
|
|
|
// Launch without importing: resolves the chosen wine/prefix, builds the ad
|
|
// hoc target and runs it exactly like a library app (same env, same logging,
|
|
// same detached process group).
|
|
async function run({ openPath, wine: wineId, prefix: prefixName0, lang, onStarted }) {
|
|
if (!openPath || !fs.existsSync(openPath)) return { ok: false, error: 'file-missing' }
|
|
if (!/\.(exe|msi|bat|cmd|lnk)$/i.test(openPath)) return { ok: false, error: 'not-windows' }
|
|
const wine = resolveWine(wineId)
|
|
const prefixName = resolvePrefix(prefixName0)
|
|
const runner = require('./runner')
|
|
try {
|
|
// runAdHoc builds the synthetic target internally (shared `openwith` id,
|
|
// so logs/playtime never collide with a library app id); the resolved
|
|
// prefix is passed through so buildEnv uses the chosen one
|
|
const res = await runner.runAdHoc({ openPath, wine, systemWine: wine.type === 'system', prefix: prefixName })
|
|
if (onStarted) onStarted({ name: path.basename(openPath), pid: res.pid })
|
|
return { ok: true, pid: res.pid || null, logFile: res.logFile || null }
|
|
} catch (err) {
|
|
return { ok: false, error: String((err && err.message) || err) }
|
|
}
|
|
}
|
|
|
|
module.exports = { setPending, getPending, hasPending, clearPending, run, makeTarget, resolveWine, resolvePrefix, textsFor }
|