Add wine support back for Linux

This commit is contained in:
2026-01-08 11:28:25 -07:00
parent 2777e6dac0
commit f3fdf76817
8 changed files with 143 additions and 22 deletions

View File

@@ -255,7 +255,14 @@ async fn download(
#[allow(unused_variables)] #[allow(unused_variables)]
#[tauri::command] #[tauri::command]
fn launch_game(app: AppHandle, name: String, executable: String, display_name: String) { fn launch_game(
app: AppHandle,
name: String,
executable: String,
display_name: String,
use_wine: bool,
wine_command: String,
) {
let game_folder = app let game_folder = app
.path() .path()
.app_local_data_dir() .app_local_data_dir()
@@ -266,10 +273,12 @@ fn launch_game(app: AppHandle, name: String, executable: String, display_name: S
return; return;
} }
let exe_path = game_folder.join(&executable);
//if already running on macos, it'll auto take the user to that proccess //if already running on macos, it'll auto take the user to that proccess
#[cfg(any(target_os = "windows", target_os = "linux"))] #[cfg(any(target_os = "windows", target_os = "linux"))]
{ {
if is_running_by_path(&game_folder.join(&executable)) { if !use_wine && is_running_by_path(&exe_path) {
app.dialog() app.dialog()
.message(format!( .message(format!(
"{} is already running, if this doesn't seem true, try to kill the proccess.", "{} is already running, if this doesn't seem true, try to kill the proccess.",
@@ -282,6 +291,23 @@ fn launch_game(app: AppHandle, name: String, executable: String, display_name: S
} }
} }
#[cfg(target_os = "linux")]
{
if use_wine {
let quoted_path = format!("\"{}\"", exe_path.to_string_lossy());
let cmd = wine_command.replace("%path%", &quoted_path);
Command::new("bash")
.arg("-c")
.arg(cmd)
.current_dir(&game_folder)
.spawn()
.unwrap();
return;
}
}
if platform() == "macos" { if platform() == "macos" {
Command::new("open") Command::new("open")
.arg(&executable) .arg(&executable)
@@ -289,7 +315,7 @@ fn launch_game(app: AppHandle, name: String, executable: String, display_name: S
.spawn() .spawn()
.unwrap(); .unwrap();
} else { } else {
Command::new(&game_folder.join(&executable)) Command::new(&exe_path)
.current_dir(&game_folder) .current_dir(&game_folder)
.spawn() .spawn()
.unwrap(); .unwrap();

View File

@@ -155,7 +155,7 @@ body {
} }
.input-field { .input-field {
@apply border-2 border-(--col4) rounded-md bg-(--col2) p-2 px-4 focus:border-blue-600 transition-colors; @apply border-2 border-(--col4) rounded-md bg-(--col2) py-1 px-2 focus:bg-(--col3) focus:border-(--col6) transition-colors w-full;
} }
.entry-info-item { .entry-info-item {

View File

@@ -7,6 +7,8 @@ import { invoke } from '@tauri-apps/api/core'
import { useGlobal } from '../GlobalProvider' import { useGlobal } from '../GlobalProvider'
import { useSearchParams } from 'next/navigation' import { useSearchParams } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os' import { platform } from '@tauri-apps/plugin-os'
import { faWarning } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
export default function Installs () { export default function Installs () {
const { const {
@@ -71,6 +73,12 @@ export default function Installs () {
.filter(v => { .filter(v => {
const info = getVersionInfo(v) const info = getVersionInfo(v)
if (!info) return false if (!info) return false
if (
platform() === 'linux' &&
info.wine &&
!normalConfig?.settings.useWineOnUnixWhenNeeded
)
return false
return info.game === Number(params.get('id') || 0) return info.game === Number(params.get('id') || 0)
}) })
.map((entry, i) => ( .map((entry, i) => (
@@ -95,11 +103,19 @@ export default function Installs () {
invoke('launch_game', { invoke('launch_game', {
name: verInfo.id, name: verInfo.id,
executable: verInfo.executable, executable: verInfo.executable,
displayName: `${gameInfo.name} v${verInfo.versionName}` displayName: `${gameInfo.name} v${verInfo.versionName}`,
useWine: !!(
platform() === 'linux' &&
verInfo.wine &&
normalConfig?.settings.useWineOnUnixWhenNeeded
),
wineCommand: normalConfig?.settings.wineOnUnixCommand
}) })
}} }}
onContextMenu={e => { onContextMenu={e => {
e.preventDefault() e.preventDefault()
if (normalConfig?.settings.useLegacyInteractButtons) return
setManagingVersion(entry) setManagingVersion(entry)
setPopupMode(2) setPopupMode(2)
setShowPopup(true) setShowPopup(true)
@@ -112,18 +128,37 @@ export default function Installs () {
{getVersionInfo(entry)?.versionName} {getVersionInfo(entry)?.versionName}
</p> </p>
<div className='flex gap-2 absolute left-0 bottom-0'>
<div <div
className='entry-info-item absolute left-0 bottom-0' className='entry-info-item'
title='The date the game was installed in MM/dd/yyyy format' title='The date the game was installed in MM/dd/yyyy format'
onClick={e => e.stopPropagation()}
> >
<p> <p>
Installed{' '} Installed{' '}
{format( {format(
new Date(downloadedVersionsConfig.timestamps[entry]), new Date(
downloadedVersionsConfig.timestamps[entry]
),
'MM/dd/yyyy' 'MM/dd/yyyy'
)} )}
</p> </p>
</div> </div>
<div
className='entry-info-item'
title='This version is using wine. It cannot be guarenteed to work fully and might not work at all.'
hidden={
!(
platform() === 'linux' &&
getVersionInfo(entry)?.wine
)
}
onClick={e => e.stopPropagation()}
>
<FontAwesomeIcon icon={faWarning} color='#ffc800' />
<p>Uses wine</p>
</div>
</div>
<div className='flex gap-2 absolute right-0 bottom-0'> <div className='flex gap-2 absolute right-0 bottom-0'>
<button <button

View File

@@ -89,6 +89,13 @@ export default function RootLayout ({
return serverVersionList.versions return serverVersionList.versions
.filter(v => !downloadedVersionsConfig?.list.includes(v.id)) .filter(v => !downloadedVersionsConfig?.list.includes(v.id))
.filter(v => { .filter(v => {
if (
platform() === 'linux' &&
v.wine &&
!normalConfig.settings.useWineOnUnixWhenNeeded
)
return false
if (game && v.game != game) return false if (game && v.game != game) return false
if (downloadProgress.length != 0) { if (downloadProgress.length != 0) {
return !downloadProgress.some(d => d.version === v.id) return !downloadProgress.some(d => d.version === v.id)
@@ -133,13 +140,21 @@ export default function RootLayout ({
} | null { } | null {
if (!downloadedVersionsConfig || !serverVersionList) return null if (!downloadedVersionsConfig || !serverVersionList) return null
const installed = downloadedVersionsConfig.list.filter( const allowWine =
v => getGameInfo(getVersionInfo(v)?.game)?.id === gameId platform() !== 'linux' || normalConfig?.settings.useWineOnUnixWhenNeeded
).length
const total = serverVersionList.versions.filter( const installed = downloadedVersionsConfig.list.filter(v => {
v => getGameInfo(v?.game)?.id === gameId const info = getVersionInfo(v)
).length if (!info) return false
if (info.wine && !allowWine) return false
return getGameInfo(info.game)?.id === gameId
}).length
const total = serverVersionList.versions.filter(v => {
if (!v) return false
if (v.wine && !allowWine) return false
return getGameInfo(v.game)?.id === gameId
}).length
return { installed, total } return { installed, total }
} }

View File

@@ -89,6 +89,7 @@ export default function Installs () {
<div <div
className='entry-info-item' className='entry-info-item'
title='The amount of versions installed of this game in installed/installable format.' title='The amount of versions installed of this game in installed/installable format.'
onClick={e => e.stopPropagation()}
> >
<p> <p>
{(() => { {(() => {
@@ -103,6 +104,7 @@ export default function Installs () {
className='entry-info-item' className='entry-info-item'
hidden={!i.official} hidden={!i.official}
title='This game is official.' title='This game is official.'
onClick={e => e.stopPropagation()}
> >
<FontAwesomeIcon icon={faCheck} color='#19c84b' /> <FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p> <p>Official</p>
@@ -115,6 +117,7 @@ export default function Installs () {
? 'This game is verified to be safe' ? 'This game is verified to be safe'
: 'This game is not verified to be save. Proceed with caution.' : 'This game is not verified to be save. Proceed with caution.'
} }
onClick={e => e.stopPropagation()}
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={i.verified ? faShieldHalved : faWarning} icon={i.verified ? faShieldHalved : faWarning}

View File

@@ -5,6 +5,7 @@ import { Setting } from '../componets/Setting'
import { writeNormalConfig } from '../util/BazookaManager' import { writeNormalConfig } from '../util/BazookaManager'
import { useGlobal } from '../GlobalProvider' import { useGlobal } from '../GlobalProvider'
import { copyToClipboard } from '../util/Clipboard' import { copyToClipboard } from '../util/Clipboard'
import { platform } from '@tauri-apps/plugin-os'
export default function Settings () { export default function Settings () {
const [allowNotifications, setAllowNotifications] = useState(false) const [allowNotifications, setAllowNotifications] = useState(false)
@@ -12,6 +13,8 @@ export default function Settings () {
useState(false) useState(false)
const [useLegacyInteractButtons, setUseLegacyInteractButtons] = const [useLegacyInteractButtons, setUseLegacyInteractButtons] =
useState(false) useState(false)
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
const [wineOnUnixCommand, setWineOnUnixCommand] = useState('wine %path%')
const [theme, setTheme] = useState(0) const [theme, setTheme] = useState(0)
const [loaded, setLoaded] = useState(false) const [loaded, setLoaded] = useState(false)
@@ -27,6 +30,10 @@ export default function Settings () {
setUseLegacyInteractButtons( setUseLegacyInteractButtons(
normalConfig.settings.useLegacyInteractButtons normalConfig.settings.useLegacyInteractButtons
) )
setUseWineOnUnixWhenNeeded(
normalConfig.settings.useWineOnUnixWhenNeeded
)
setWineOnUnixCommand(normalConfig.settings.wineOnUnixCommand)
setTheme(normalConfig.settings.theme) setTheme(normalConfig.settings.theme)
setLoaded(true) setLoaded(true)
break break
@@ -114,6 +121,38 @@ export default function Settings () {
}} }}
title='Enable the legacy method of using the installs/launch/manage buttons. In the future this setting may be removed so try and get used to the new method.' title='Enable the legacy method of using the installs/launch/manage buttons. In the future this setting may be removed so try and get used to the new method.'
/> />
<Setting
label='Use wine when needed to launch games'
value={useWineOnUnixWhenNeeded}
onChange={async () => {
while (normalConfig != null) {
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
normalConfig.settings.useWineOnUnixWhenNeeded =
!useWineOnUnixWhenNeeded
await writeNormalConfig(normalConfig)
break
}
}}
className={platform() == 'linux' ? '' : 'hidden'}
/>
<p hidden={!(platform() == 'linux' && useWineOnUnixWhenNeeded)}>
Wine Command:
</p>
<input
type='text'
value={wineOnUnixCommand}
onChange={async e => {
while (normalConfig != null) {
setWineOnUnixCommand(e.target.value)
normalConfig.settings.wineOnUnixCommand = e.target.value
await writeNormalConfig(normalConfig)
break
}
}}
className={`input-field my-1 ${
platform() == 'linux' && useWineOnUnixWhenNeeded ? '' : 'hidden'
}`}
></input>
<div title='The theme you want the launcher to use.'> <div title='The theme you want the launcher to use.'>
<label className='text-lg'>Theme:</label> <label className='text-lg'>Theme:</label>
<select <select

View File

@@ -9,4 +9,5 @@ export interface GameVersion {
size: number size: number
place: number place: number
changelog: string changelog: string
wine: number | undefined
} }

View File

@@ -3,6 +3,8 @@ export class SettingsType {
public allowNotifications: boolean = true, public allowNotifications: boolean = true,
public alwaysShowGamesInSidebar: boolean = true, public alwaysShowGamesInSidebar: boolean = true,
public useLegacyInteractButtons: boolean = false, public useLegacyInteractButtons: boolean = false,
public useWineOnUnixWhenNeeded: boolean = false,
public wineOnUnixCommand: string = 'wine %path%',
public theme: number = 0 public theme: number = 0
) {} ) {}
} }