From 33869158ded708bf5de82de9cfad227e7e7f94ea Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:07:59 +0900
Subject: [PATCH 01/22] change: explicitly disable NSG creation on Flatpak
---
dzgui/strings/wizard.py | 3 ++-
dzgui/views/dialogs/wizard.py | 7 +++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index fd9f834..9f499b0 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -70,7 +70,8 @@ checkbox_steam_shortcut = (
(
"This creates a standalone copy of DZGUI and adds it to Steam. "
"You must restart Steam for the shortcut to appear. This will not replace or update existing shortcuts. "
- "If you update DZGUI when it is launched via Steam, updates will be applied to the Steam version."
+ "If you update DZGUI when it is launched via Steam, updates will be applied to the Steam version. "
+ "Not available if you selected the Flatpak client as your Steam default."
),
)
checkbox_start_menu = (
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index 3aa6aa3..13d2341 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -12,6 +12,7 @@ from dzgui.const.constants import (
APP_NAME,
APP_NAME_LOWER,
HERO_PATH,
+ FLATPAK_STEAM_PATH,
LEGACY_CONFIG_PATH,
)
from dzgui.const.boilerplate import config_boilerplate
@@ -624,6 +625,12 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
self.add_start(self.checks_area)
self.show_all()
self.connect("map", self._on_map)
+ self.connect("map", self._toggle_checkbox)
+
+ def _toggle_checkbox(self, page: "EnumeratedWizardPage") -> None:
+ if FLATPAK_STEAM_PATH in self.steam_path:
+ self.steam_checkbox.set_active(False)
+ self.steam_checkbox.set_sensitive(False)
def _on_start_menu_toggled(self, button: Gtk.CheckButton) -> None:
state = button.get_active()
From da98b1d5b13cb63efe11d0336c4470a146ef6417 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:08:44 +0900
Subject: [PATCH 02/22] feat: toggle flatpak client dynamically
---
dzgui/strings/options.py | 7 +++++++
dzgui/views/pages/options.py | 31 +++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/dzgui/strings/options.py b/dzgui/strings/options.py
index fd2aaaf..a105f07 100644
--- a/dzgui/strings/options.py
+++ b/dzgui/strings/options.py
@@ -17,3 +17,10 @@ fullscreen_eventbox = "This option is not available on Steam Deck."
save_button = "Save"
api_failed = "API validation failed"
+
+flatpak_label = "Flatpak client"
+flatpak_standard = "Standard"
+flatpak_container = "Containerized (flatpak-spawn --host)"
+flatpak_info = (
+ "Containerized mode is used on certain distributions. May have side effects."
+)
diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py
index 91e0b72..64c5d51 100644
--- a/dzgui/views/pages/options.py
+++ b/dzgui/views/pages/options.py
@@ -9,6 +9,9 @@ from dzgui.const.constants import (
APPID_DAYZ_EXP,
APPNAME_DAYZ,
APPNAME_DAYZ_EXP_HUMAN,
+ FLATPAK_RUN_CMD,
+ FLATPAK_SANDBOX,
+ FLATPAK_STEAM_PATH,
NO_EXPAND,
NO_FILL,
NO_PADDING,
@@ -246,6 +249,27 @@ class ToggleField(Gtk.Box):
el.set_sensitive(state)
+class FlatpakToggleField(ToggleField):
+ def __init__(
+ self,
+ controller: "Controller",
+ ) -> None:
+ super().__init__(
+ controller,
+ first_option=options.flatpak_standard,
+ second_option=options.flatpak_container,
+ context=Preferences.CLIENT,
+ )
+ self.add(InfoEventBox(options.flatpak_info, self.controller))
+ self.controller = controller
+ self.radio1.connect("toggled", self._on_radio_toggled, Preferences.CLIENT)
+
+ def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
+ cmd = FLATPAK_RUN_CMD if self.radio1.get_active() else FLATPAK_SANDBOX
+ print(context)
+ self.controller.update_config(context, cmd)
+
+
class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
def __init__(self, controller: "Controller"):
super().__init__(
@@ -274,6 +298,7 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
self.player_box.set_halign(Gtk.Align.START)
self.player_box.get_children()[0].set_width_chars(30) # type: ignore
+ self.flatpak_toggle = FlatpakToggleField(self.controller)
self.fullscreen_toggle = ToggleField(
self.controller,
strings.options.last_used,
@@ -309,6 +334,12 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
[LeftLabel(options.start_tab), start_tab_hbox],
[LeftLabel(strings.options.name), self.player_box],
]
+ default = self.controller.query_config(Preferences.DEFAULT)
+ if FLATPAK_STEAM_PATH in default:
+ pref_rows.insert(
+ 0,
+ [LeftLabel(options.flatpak_label), self.flatpak_toggle],
+ )
self.dayz_version_label = Gtk.Label(label=strings.null)
self.dayz_exp_version_label = Gtk.Label(label=strings.null)
From 80e127889e9e901f6c1ba41582e83e8572936c0c Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:09:07 +0900
Subject: [PATCH 03/22] chore: drop unused import
---
tests/test_process_detection.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/test_process_detection.py b/tests/test_process_detection.py
index 16909fa..23f0956 100644
--- a/tests/test_process_detection.py
+++ b/tests/test_process_detection.py
@@ -6,7 +6,6 @@ from dzgui.const.constants import (
FLATPAK_ROOT_PATH,
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
- STEAM_CMD,
)
from dzgui.init import proc
From 0359aacd5463cb8661399ce2ef9222b5acee31f5 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:09:40 +0900
Subject: [PATCH 04/22] chore: clear typehinting errors
---
dzgui/views/dialogs/wizard.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index 13d2341..b3e54f4 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -628,7 +628,7 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
self.connect("map", self._toggle_checkbox)
def _toggle_checkbox(self, page: "EnumeratedWizardPage") -> None:
- if FLATPAK_STEAM_PATH in self.steam_path:
+ if FLATPAK_STEAM_PATH in str(self.steam_path):
self.steam_checkbox.set_active(False)
self.steam_checkbox.set_sensitive(False)
From 9f3c39fdd67790a6287795808dab5c3db4544312 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:35:56 +0900
Subject: [PATCH 05/22] chore: drop unused print statement
---
dzgui/views/pages/options.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py
index 64c5d51..ebce129 100644
--- a/dzgui/views/pages/options.py
+++ b/dzgui/views/pages/options.py
@@ -266,7 +266,6 @@ class FlatpakToggleField(ToggleField):
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
cmd = FLATPAK_RUN_CMD if self.radio1.get_active() else FLATPAK_SANDBOX
- print(context)
self.controller.update_config(context, cmd)
From e286f02342b22dc5a8349a60599d428a99eb34d3 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Thu, 22 Oct 2026 12:36:23 +0900
Subject: [PATCH 06/22] chore: add flatpak toggle test
---
tests/test_flatpak_toggle.py | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 tests/test_flatpak_toggle.py
diff --git a/tests/test_flatpak_toggle.py b/tests/test_flatpak_toggle.py
new file mode 100644
index 0000000..eec56e7
--- /dev/null
+++ b/tests/test_flatpak_toggle.py
@@ -0,0 +1,30 @@
+import pytest
+
+from dzgui.const.constants import FLATPAK_RUN_CMD, FLATPAK_SANDBOX
+from dzgui.const.enum import Preferences
+from dzgui.views.pages.options import FlatpakToggleField
+
+
+class MockController:
+ def __init__(self) -> None:
+ self.command: str
+
+ def update_config(self, context: Preferences, cmd: str) -> None:
+ self.command = cmd
+
+ def get_command(self) -> str:
+ return self.command
+
+
+@pytest.mark.parametrize("index, expect", [(0, FLATPAK_RUN_CMD), (1, FLATPAK_SANDBOX)])
+def test_toggle(index: int, expect: str) -> None:
+ controller = MockController()
+ toggle = FlatpakToggleField(controller)
+ match index:
+ case 0:
+ toggle.radio2.set_active(True)
+ toggle.radio1.set_active(True)
+ case 1:
+ toggle.radio1.set_active(True)
+ toggle.radio2.set_active(True)
+ assert controller.get_command() == expect
From 01b3babdf5ea78e0bf244248856b844de4d131d5 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 11:23:28 +0900
Subject: [PATCH 07/22] feat: FlatpakOverridePage WIP
---
dzgui/api/steam.py | 1 +
dzgui/config/xdg.py | 12 ++++-
dzgui/init/proc.py | 14 ++++--
dzgui/strings/wizard.py | 2 +
dzgui/util/bash.py | 7 +++
dzgui/views/dialogs/wizard.py | 91 +++++++++++++++++++++++++++++------
6 files changed, 108 insertions(+), 19 deletions(-)
diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py
index 7253d1c..ab87025 100644
--- a/dzgui/api/steam.py
+++ b/dzgui/api/steam.py
@@ -198,6 +198,7 @@ def load_to_menu(client: str, appid: int, name: str, mods: list[str]) -> int:
"""Loads to the menu screen with the selected mods; used for AFK/pre-joining"""
concat = concat_mods(mods)
client_args = concat_bash_args(client)
+ logger.debug(f"Setting client arguments to: {client_args}")
params = [
"-applaunch",
str(appid),
diff --git a/dzgui/config/xdg.py b/dzgui/config/xdg.py
index 47f747a..77919f0 100644
--- a/dzgui/config/xdg.py
+++ b/dzgui/config/xdg.py
@@ -3,7 +3,13 @@ import os
from pathlib import Path
from dataclasses import dataclass
-from dzgui.const.constants import APP_NAME_LOWER, DEBUG_LOG, SYSTEM_LOG
+from dzgui.const.constants import (
+ APP_NAME_LOWER,
+ DEBUG_LOG,
+ FLATPAK_ROOT_PATH,
+ SYSTEM_LOG,
+)
+from dzgui.init.proc import is_sandboxed
@dataclass
@@ -53,10 +59,12 @@ def get_xdg_paths() -> dict:
"XDG_DATA_HOME": home / ".local" / "share",
"XDG_CACHE_HOME": home / ".cache",
}
-
resolved_paths = {}
for path in xdg_paths:
real_path = os.environ.get(path)
+ if is_sandboxed():
+ search_string = FLATPAK_ROOT_PATH + "/"
+ real_path = real_path.replace(search_string, "")
if real_path is not None and is_writeable(real_path):
new_path = Path(real_path)
else:
diff --git a/dzgui/init/proc.py b/dzgui/init/proc.py
index 3d76516..e10a049 100644
--- a/dzgui/init/proc.py
+++ b/dzgui/init/proc.py
@@ -1,7 +1,8 @@
-import psutil
-import subprocess
-import shutil
import logging
+import os
+import psutil
+import shutil
+import subprocess
from dzgui.const.constants import (
APP_NAME,
@@ -26,7 +27,14 @@ def is_steam_running(cmd: str) -> bool:
raise TypeError("Not a valid Steam client selection")
+def is_sandboxed() -> bool:
+ logger.debug("Inside Flatpak sandbox")
+ return bool(os.environ.get("FLATPAK_ID"))
+
+
def is_flatpak_steam_running() -> bool:
+ if is_sandboxed():
+ return True
if has_cmd(FLATPAK_CMD) is False:
return False
proc = subprocess.check_output(
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index 9f499b0..8a9baf4 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -3,6 +3,8 @@ title_intro = "Welcome"
blurb_intro = """
This wizard will help you set up some common config options before launching the application.
"""
+error_sandbox = "Setup cannot be run from inside Flatpak sandbox.\nPlease launch the application directly."
+welcome = "Welcome to {0}!"
### SteamPathPage
heading_steam_path = "Steam path"
diff --git a/dzgui/util/bash.py b/dzgui/util/bash.py
index 9c2a73d..d612b94 100644
--- a/dzgui/util/bash.py
+++ b/dzgui/util/bash.py
@@ -1,5 +1,12 @@
import shlex
+from dzgui.const.constants import STEAM_CMD
+from dzgui.init.proc import is_sandboxed
+
def concat_bash_args(command: str) -> list[str]:
+ # NOTE: NSGs spawned from sandbox cannot access host application commands,
+ # so fallback to Steam internal URIs
+ if is_sandboxed():
+ return [STEAM_CMD]
return shlex.split(command)
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index b3e54f4..b1d779b 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -12,13 +12,13 @@ from dzgui.const.constants import (
APP_NAME,
APP_NAME_LOWER,
HERO_PATH,
- FLATPAK_STEAM_PATH,
+ FLATPAK_ROOT_PATH,
LEGACY_CONFIG_PATH,
)
from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.config import freedesktop
-from dzgui.init.proc import path_to_steam_cmd
+from dzgui.init.proc import is_sandboxed, path_to_steam_cmd
from dzgui.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard
@@ -45,7 +45,8 @@ class PageNum(Enum):
STEAM_API = 4
USER_PREFS = 5
SHORTCUTS = 6
- FINAL = 7
+ FLATPAK = 7
+ FINAL = 8
class OptionalPageMixin:
@@ -144,7 +145,7 @@ class NotificationFrame(Gtk.Frame):
margin_start=50,
margin_end=50,
)
- wrapped = textwrap.fill(label, width=80)
+ wrapped = textwrap.fill(label, width=60)
self.label = Gtk.Label(
label=wrapped,
justify=Gtk.Justification.CENTER,
@@ -158,7 +159,7 @@ class NotificationFrame(Gtk.Frame):
add_class(self, "error-frame")
def set_text(self, text: str) -> None:
- wrapped = textwrap.fill(text, width=80)
+ wrapped = textwrap.fill(text, width=60)
self.label.set_markup(wrapped)
@@ -243,12 +244,23 @@ class IntroductionPage(EnumeratedWizardPage):
def __init__(self) -> None:
super().__init__(
enum=PageNum.INTRO,
- heading=f"Welcome to {APP_NAME}!",
+ heading=wizard.welcome.format(APP_NAME),
description=wizard.blurb_intro,
)
self.page_type = Gtk.AssistantPageType.INTRO
self.set_title(wizard.title_intro)
+ self.error_box = NotificationFrame(wizard.error_sandbox, error=True)
+ self.add_start(self.error_box)
+
+ self.connect("map", self._on_map)
+
+ def _on_map(self, page: "EnumeratedWizardPage") -> None:
+ self.error_box.hide()
+ if is_sandboxed():
+ self.error_box.show()
+ EMITTER.emit("step_pending")
+
class Heading(Gtk.Label):
def __init__(self, label: str):
@@ -405,6 +417,43 @@ class PreferencesPage(EnumeratedWizardPage):
EMITTER.emit("step_complete")
+class FlatpakOverridePage(EnumeratedWizardPage):
+ def __init__(self, xdg: "Xdg") -> None:
+ # TODO: strings
+ super().__init__(
+ enum=PageNum.FLATPAK,
+ heading="Flatpak overrides",
+ description="Enable overrides",
+ )
+ self.xdg = xdg
+ self.page_type = Gtk.AssistantPageType.INTRO
+
+ # TODO: labels, blurbs
+ # You chose to add a DZGUI shortcut directly to Steam.
+ # Your Steam client selection is Flatpak Steam.
+ # To launch DZGUI from Flatpak's sandbox, you must
+ # grant permission for the sandbox to read from and write to the
+ # following locations.
+
+ # Click below to whitelist these paths and enable Steam to register the DZGUI application.
+
+ # XDG.config, XDG.share, XDG.state, /tmp?
+
+ self.label = Gtk.Label(label="Whitelist")
+ self.authorize_button = Gtk.Button(
+ label="Grant permissions", halign=Gtk.Align.CENTER
+ )
+ self.authorize_button.connect("clicked", self._on_authorize)
+
+ for el in self.label, self.authorize_button:
+ self.add_start(el)
+
+ def _on_authorize(self, button: Gtk.Button) -> None:
+ # TODO: apply flatpak overrides via module
+ print(self.xdg)
+ EMITTER.emit("step_complete")
+
+
class CompletionPage(EnumeratedWizardPage):
def __init__(self) -> None:
super().__init__(
@@ -437,6 +486,7 @@ class Assistant(Gtk.Assistant):
self.page_api = SteamValidationPage()
self.page_prefs = PreferencesPage()
self.page_shortcuts = ShortcutCreationPage(XDG.shortcut)
+ self.page_flatpak = FlatpakOverridePage(XDG)
self.page_completion = CompletionPage()
self.set_forward_page_func(self._advance_page)
@@ -456,6 +506,7 @@ class Assistant(Gtk.Assistant):
self.page_api,
self.page_prefs,
self.page_shortcuts,
+ self.page_flatpak,
self.page_completion,
):
# NOTE: skip config migration page if no legacy config file
@@ -476,7 +527,10 @@ class Assistant(Gtk.Assistant):
def write_config(self, config: dict[str, Any]) -> None:
write_json(config, self.config_path)
- def get_final_page(self) -> int:
+ def get_completion_page(self) -> int:
+ return self.pages[self.page_completion]
+
+ def get_shortcut_page(self) -> int:
return self.pages[self.page_shortcuts]
def _advance_page(self, index: int) -> int:
@@ -489,7 +543,7 @@ class Assistant(Gtk.Assistant):
sp = page.migrated_conf["default_steam_path"]
self.page_shortcuts.set_steam_path(sp)
self.config_values = self.page_migration.migrated_conf
- return self.get_final_page()
+ return self.get_shortcuts_page()
case SteamPathPage():
path = page.get_path_from_radio()
self.config_values["default_steam_path"] = path
@@ -506,10 +560,22 @@ class Assistant(Gtk.Assistant):
)
case ShortcutCreationPage():
page.create_shortcuts()
+ if not self.needs_flatpak_override():
+ return self.get_completion_page()
+ case FlatpakOverridePage():
+ pass
case _:
raise AttributeError("Trying to advance a non-canonical page")
return index + 1
+ def needs_flatpak_override(self) -> None:
+ if (
+ self.page_shortcuts.is_steam_checkbox_active()
+ and FLATPAK_ROOT_PATH in self.config_values["default_steam_path"]
+ ):
+ return True
+ return False
+
def is_setup_complete(self) -> bool:
return self.setup_complete
@@ -625,12 +691,6 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
self.add_start(self.checks_area)
self.show_all()
self.connect("map", self._on_map)
- self.connect("map", self._toggle_checkbox)
-
- def _toggle_checkbox(self, page: "EnumeratedWizardPage") -> None:
- if FLATPAK_STEAM_PATH in str(self.steam_path):
- self.steam_checkbox.set_active(False)
- self.steam_checkbox.set_sensitive(False)
def _on_start_menu_toggled(self, button: Gtk.CheckButton) -> None:
state = button.get_active()
@@ -641,6 +701,9 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
def set_steam_path(self, path: Path) -> None:
self.steam_path = path
+ def is_steam_checkbox_active(self) -> None:
+ return self.steam_checkbox.get_active()
+
def create_shortcuts(self) -> None:
# NOTE: best-effort, permissive even on failure (page is already marked as complete)
if self.steam_checkbox.get_active():
From 519e6c354d08a8c440a087478af7ad7c4a953297 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 11:42:08 +0900
Subject: [PATCH 08/22] chore: patch VDF changes from main
---
dzgui/api/shortcuts.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/dzgui/api/shortcuts.py b/dzgui/api/shortcuts.py
index 594d1a1..144bb44 100644
--- a/dzgui/api/shortcuts.py
+++ b/dzgui/api/shortcuts.py
@@ -10,8 +10,8 @@ from pathlib import Path
from typing import Any
from dzgui.api.steam import find_user_id_32
-from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
-from dzgui.util.dirs import copy_dzgui_to_xdg_data
+from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, GTK_APP_ID, IMAGES_PATH
+from dzgui.util.dirs import copy_dzgui_to_xdg_data, make_parents
from dzgui.util.strings import unknown
logger = logging.getLogger(APP_NAME)
@@ -75,6 +75,8 @@ class Shortcuts:
shortcuts = vdf.binary_load(f)
self.shortcuts = shortcuts
logger.debug("Loaded shortcuts file")
+ except FileNotFoundError:
+ self.shortcuts = {"shortcuts": {}}
except Exception as e:
logger.critical(e)
raise e
@@ -109,6 +111,7 @@ class Shortcuts:
for img in ("_hero", "p", "_logo"):
filename = str(self.bpid) + img
dest = grid_path.joinpath(filename).with_suffix(".png")
+ make_parents(dest)
b = images.joinpath(img).with_suffix(".png").read_bytes()
dest.write_bytes(b)
@@ -155,7 +158,8 @@ class Shortcuts:
NEW_ENTRY["DevkitGameID"] = ""
NEW_ENTRY["DevkitOverrideAppID"] = 0
NEW_ENTRY["LastPlayTime"] = 0
- NEW_ENTRY["FlatpakAppID"] = ""
+ NEW_ENTRY["FlatpakAppID"] = GTK_APP_ID
+
NEW_ENTRY["sortas"] = ""
NEW_ENTRY["tags"] = {}
return NEW_ENTRY
@@ -172,6 +176,9 @@ class Shortcuts:
try:
backup = self.shortcuts_path.with_suffix(".vdf.bak")
shutil.copy(self.shortcuts_path, backup)
+ except Exception as e:
+ logger.critical(e)
+ try:
with open(self.shortcuts_path, "wb") as f:
vdf.binary_dump(self.shortcuts, f)
except Exception as e:
From edb87893f9be6cf00175f60c5a0ed4b78b62afd5 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 11:46:05 +0900
Subject: [PATCH 09/22] chore: clear typehinting errors
---
dzgui/config/xdg.py | 2 +-
dzgui/views/dialogs/wizard.py | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/dzgui/config/xdg.py b/dzgui/config/xdg.py
index 77919f0..1d7f5b3 100644
--- a/dzgui/config/xdg.py
+++ b/dzgui/config/xdg.py
@@ -62,7 +62,7 @@ def get_xdg_paths() -> dict:
resolved_paths = {}
for path in xdg_paths:
real_path = os.environ.get(path)
- if is_sandboxed():
+ if real_path is not None and is_sandboxed():
search_string = FLATPAK_ROOT_PATH + "/"
real_path = real_path.replace(search_string, "")
if real_path is not None and is_writeable(real_path):
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index b1d779b..2873eb9 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -543,7 +543,7 @@ class Assistant(Gtk.Assistant):
sp = page.migrated_conf["default_steam_path"]
self.page_shortcuts.set_steam_path(sp)
self.config_values = self.page_migration.migrated_conf
- return self.get_shortcuts_page()
+ return self.get_shortcut_page()
case SteamPathPage():
path = page.get_path_from_radio()
self.config_values["default_steam_path"] = path
@@ -568,7 +568,7 @@ class Assistant(Gtk.Assistant):
raise AttributeError("Trying to advance a non-canonical page")
return index + 1
- def needs_flatpak_override(self) -> None:
+ def needs_flatpak_override(self) -> bool:
if (
self.page_shortcuts.is_steam_checkbox_active()
and FLATPAK_ROOT_PATH in self.config_values["default_steam_path"]
@@ -701,7 +701,7 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
def set_steam_path(self, path: Path) -> None:
self.steam_path = path
- def is_steam_checkbox_active(self) -> None:
+ def is_steam_checkbox_active(self) -> bool:
return self.steam_checkbox.get_active()
def create_shortcuts(self) -> None:
From c2993ad289d2543bf13e9859fffd5661e692d864 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 12:11:19 +0900
Subject: [PATCH 10/22] change: revert prior VDF change, no observable effect
on D-Bus
---
dzgui/api/shortcuts.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dzgui/api/shortcuts.py b/dzgui/api/shortcuts.py
index 144bb44..2ecb076 100644
--- a/dzgui/api/shortcuts.py
+++ b/dzgui/api/shortcuts.py
@@ -10,7 +10,7 @@ from pathlib import Path
from typing import Any
from dzgui.api.steam import find_user_id_32
-from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, GTK_APP_ID, IMAGES_PATH
+from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
from dzgui.util.dirs import copy_dzgui_to_xdg_data, make_parents
from dzgui.util.strings import unknown
@@ -158,7 +158,7 @@ class Shortcuts:
NEW_ENTRY["DevkitGameID"] = ""
NEW_ENTRY["DevkitOverrideAppID"] = 0
NEW_ENTRY["LastPlayTime"] = 0
- NEW_ENTRY["FlatpakAppID"] = GTK_APP_ID
+ NEW_ENTRY["FlatpakAppID"] = ""
NEW_ENTRY["sortas"] = ""
NEW_ENTRY["tags"] = {}
From d577ba09707eedc268b2ac90a8f1b7c4dd8cdf8f Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 12:20:10 +0900
Subject: [PATCH 11/22] change: remove Flatpak option if binary is not
installed
---
dzgui/api/steam.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py
index ab87025..7efff23 100644
--- a/dzgui/api/steam.py
+++ b/dzgui/api/steam.py
@@ -13,6 +13,7 @@ from warnings import deprecated
from dzgui.api.acf import ACF
from dzgui.init.prereqs import has_any_client
+from dzgui.init.proc import has_cmd
from dzgui.const.constants import (
APPID_DAYZ,
APPID_DAYZ_EXP,
@@ -22,6 +23,7 @@ from dzgui.const.constants import (
DEBIAN_STEAM_PATH,
DEFAULT_STEAM_PATH,
DOWNLOADS_PATH,
+ FLATPAK_CMD,
FLATPAK_STEAM_PATH,
LIBRARYFOLDERS_PATH,
UBUNTU_STEAM_PATH,
@@ -82,6 +84,8 @@ def get_steam_paths() -> list[tuple[Path, str]]:
}
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
+ if path is FLATPAK_PATH and has_cmd(FLATPAK_CMD) is False:
+ continue
if path.joinpath(VDF_PATH).is_file():
paths.append((path, human[path]))
return paths
From 682532a867e5262ed97fbbe2c7c1f22afc073407 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 12:34:04 +0900
Subject: [PATCH 12/22] chore: remove erroneous debug string
---
dzgui/init/proc.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dzgui/init/proc.py b/dzgui/init/proc.py
index e10a049..0c1a2a0 100644
--- a/dzgui/init/proc.py
+++ b/dzgui/init/proc.py
@@ -28,7 +28,6 @@ def is_steam_running(cmd: str) -> bool:
def is_sandboxed() -> bool:
- logger.debug("Inside Flatpak sandbox")
return bool(os.environ.get("FLATPAK_ID"))
From 761988d8c2df913e0dad30072cd500556f3e60a1 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 14:14:30 +0900
Subject: [PATCH 13/22] feat: add override module
---
dzgui/app_init.py | 2 +-
dzgui/views/dialogs/wizard.py | 43 ++++++++++++++++-------------------
2 files changed, 20 insertions(+), 25 deletions(-)
diff --git a/dzgui/app_init.py b/dzgui/app_init.py
index 95da728..728a495 100644
--- a/dzgui/app_init.py
+++ b/dzgui/app_init.py
@@ -148,7 +148,7 @@ def load_gui(version: str, is_debug: bool) -> None:
migrate_cols_file(XDG.columns)
copy_state_files(xdg_paths["XDG_STATE_HOME"])
# TODO: add logging inside wizard
- wizard = SetupWizard(_is_steam_deck, XDG)
+ wizard = SetupWizard(_is_steam_deck, XDG, xdg_paths)
if not wizard.is_setup_complete():
return
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index 2873eb9..2639093 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -23,11 +23,13 @@ from dzgui.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard
from dzgui.util._json import write_json
+from dzgui.util.flatpak import apply_overrides
from dzgui.util.open_links import open_link_by_url
from dzgui.util.css import add_class, load_css
from dzgui.views.components.buttons import WebButton
from dzgui.views.components.entry import APIEntry
from dzgui.views.mixins.colorscheme import ColorAwareApp
+from dzgui.views.trees.tree_simple import SimpleTree
import gi
@@ -418,39 +420,32 @@ class PreferencesPage(EnumeratedWizardPage):
class FlatpakOverridePage(EnumeratedWizardPage):
- def __init__(self, xdg: "Xdg") -> None:
- # TODO: strings
+ def __init__(self, xdg_paths: dict[str, str]) -> None:
super().__init__(
enum=PageNum.FLATPAK,
- heading="Flatpak overrides",
- description="Enable overrides",
+ heading=wizard.heading_flatpak,
+ description=wizard.blurb_flatpak,
)
- self.xdg = xdg
+ self.xdg_paths = xdg_paths
self.page_type = Gtk.AssistantPageType.INTRO
- # TODO: labels, blurbs
- # You chose to add a DZGUI shortcut directly to Steam.
- # Your Steam client selection is Flatpak Steam.
- # To launch DZGUI from Flatpak's sandbox, you must
- # grant permission for the sandbox to read from and write to the
- # following locations.
- # Click below to whitelist these paths and enable Steam to register the DZGUI application.
+ store = Gtk.ListStore(str)
+ for path in xdg_paths.values():
+ store.append((str(path),))
+ tree = SimpleTree(store, wizard.tree_column)
+ tree.set_halign(Gtk.Align.CENTER)
- # XDG.config, XDG.share, XDG.state, /tmp?
-
- self.label = Gtk.Label(label="Whitelist")
self.authorize_button = Gtk.Button(
label="Grant permissions", halign=Gtk.Align.CENTER
)
self.authorize_button.connect("clicked", self._on_authorize)
- for el in self.label, self.authorize_button:
+ for el in tree, self.authorize_button:
self.add_start(el)
def _on_authorize(self, button: Gtk.Button) -> None:
- # TODO: apply flatpak overrides via module
- print(self.xdg)
+ apply_overrides(self.xdg_paths)
EMITTER.emit("step_complete")
@@ -467,7 +462,7 @@ class CompletionPage(EnumeratedWizardPage):
class Assistant(Gtk.Assistant):
- def __init__(self, is_deck: bool, XDG: "Xdg"):
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]):
super().__init__()
if is_deck:
# NOTE: deemed to be "safe" dimensions that exclude taskbar size
@@ -486,7 +481,7 @@ class Assistant(Gtk.Assistant):
self.page_api = SteamValidationPage()
self.page_prefs = PreferencesPage()
self.page_shortcuts = ShortcutCreationPage(XDG.shortcut)
- self.page_flatpak = FlatpakOverridePage(XDG)
+ self.page_flatpak = FlatpakOverridePage(xdg_paths)
self.page_completion = CompletionPage()
self.set_forward_page_func(self._advance_page)
@@ -772,10 +767,10 @@ class SteamPathPage(EnumeratedWizardPage):
class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
- def __init__(self, is_deck: bool, XDG: "Xdg") -> None:
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None:
super().__init__()
GLib.set_prgname(APP_NAME)
- self.win = Window(is_deck, XDG)
+ self.win = Window(is_deck, XDG, xdg_paths)
Gtk.main()
def is_setup_complete(self) -> int:
@@ -783,9 +778,9 @@ class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
class Window(Gtk.Window):
- def __init__(self, is_deck: bool, XDG: "Xdg") -> None:
+ def __init__(self, is_deck: bool, XDG: "Xdg", xdg_paths: dict[str, str]) -> None:
super().__init__(title=APP_NAME, icon_name=APP_NAME)
- self.assistant = Assistant(is_deck, XDG)
+ self.assistant = Assistant(is_deck, XDG, xdg_paths)
class Emitter(GObject.GObject):
From 1440871819fd17ca5f76810d432c75b6fe3f850f Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 14:14:44 +0900
Subject: [PATCH 14/22] chore: update strings
---
dzgui/strings/wizard.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index 8a9baf4..9662256 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -82,6 +82,15 @@ checkbox_start_menu = (
)
checkbox_desktop_shortcut = ("Add a desktop shortcut", "Depends on the option above.")
+### Flatpak page
+heading_flatpak = "Flatpak sandbox overrides"
+blurb_flatpak = (
+ "You chose to add a shortcut directly to Flatpak Steam. "
+ "To launch DZGUI from Flatpak's sandbox, you must grant "
+ "permission to read from and write to the following locations."
+)
+tree_column = "Paths"
+
### Completion page
heading_completion = "Setup complete"
blurb_completion = "Configuration completed successfully. Click 'Close' to close this dialog and launch DZGUI."
From d9d3f747bbe6bc72ff23557757a4de1e4d18b828 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 15:10:24 +0900
Subject: [PATCH 15/22] feat: add override module
---
dzgui/util/flatpak.py | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 dzgui/util/flatpak.py
diff --git a/dzgui/util/flatpak.py b/dzgui/util/flatpak.py
new file mode 100644
index 0000000..21cd062
--- /dev/null
+++ b/dzgui/util/flatpak.py
@@ -0,0 +1,29 @@
+import logging
+import subprocess
+
+from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID
+
+logger = logging.getLogger(APP_NAME)
+
+
+def apply_overrides(xdg: dict[str, str]) -> None:
+ try:
+ # NOTE: D-Bus registration required for sandbox
+ args = [
+ "flatpak",
+ "--user",
+ "override" f"--own-name={GTK_APP_ID}",
+ FLATPAK_APPID,
+ ]
+ subprocess.run([*args], capture_output=True)
+
+ for path in xdg.values():
+ args = [
+ "flatpak",
+ "--user",
+ "override" f"--filesystem={path}",
+ FLATPAK_APPID,
+ ]
+ subprocess.run([*args], capture_output=True)
+ except Exception as e:
+ logger.critical(e)
From 67c19fb90dee8757e76847198d65e62df19297ee Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 15:11:05 +0900
Subject: [PATCH 16/22] feat: add SimpleTree
---
dzgui/views/trees/tree_simple.py | 35 ++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 dzgui/views/trees/tree_simple.py
diff --git a/dzgui/views/trees/tree_simple.py b/dzgui/views/trees/tree_simple.py
new file mode 100644
index 0000000..be96668
--- /dev/null
+++ b/dzgui/views/trees/tree_simple.py
@@ -0,0 +1,35 @@
+from typing import TYPE_CHECKING, Union
+
+if TYPE_CHECKING:
+ from dzgui.model.model_factory import FastInsertListStore
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk # noqa
+
+
+class SimpleTree(Gtk.ScrolledWindow):
+ """Simple scrollable 1-column tree"""
+
+ def __init__(
+ self, model: Union[Gtk.ListStore, "FastInsertListStore"], title: str
+ ) -> None:
+ super().__init__(
+ overlay_scrolling=False,
+ propagate_natural_width=True,
+ propagate_natural_height=True,
+ )
+
+ self.tree = Gtk.TreeView(model=model, enable_search=False)
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn(title, renderer, text=0)
+ column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
+ column.set_resizable(False)
+ column.set_sort_column_id(0)
+ self.tree.append_column(column)
+
+ self.add(self.tree)
+
+ def get_tree(self) -> Gtk.TreeView:
+ return self.tree
From 97b5c0e6ee40d6c0fe6a3b2f1eaac65e43b50cbb Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 15:19:37 +0900
Subject: [PATCH 17/22] fix: split arguments
---
dzgui/util/flatpak.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dzgui/util/flatpak.py b/dzgui/util/flatpak.py
index 21cd062..a432970 100644
--- a/dzgui/util/flatpak.py
+++ b/dzgui/util/flatpak.py
@@ -12,7 +12,8 @@ def apply_overrides(xdg: dict[str, str]) -> None:
args = [
"flatpak",
"--user",
- "override" f"--own-name={GTK_APP_ID}",
+ "override",
+ f"--own-name={GTK_APP_ID}",
FLATPAK_APPID,
]
subprocess.run([*args], capture_output=True)
@@ -21,7 +22,8 @@ def apply_overrides(xdg: dict[str, str]) -> None:
args = [
"flatpak",
"--user",
- "override" f"--filesystem={path}",
+ "override",
+ f"--filesystem={path}",
FLATPAK_APPID,
]
subprocess.run([*args], capture_output=True)
From cdec81071d4fcb55bd7852cafa57651b49a5b2d0 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Fri, 23 Oct 2026 15:39:56 +0900
Subject: [PATCH 18/22] feat: show notice on override
---
dzgui/strings/wizard.py | 2 ++
dzgui/views/dialogs/wizard.py | 12 +++++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index 9662256..a495de8 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -90,6 +90,8 @@ blurb_flatpak = (
"permission to read from and write to the following locations."
)
tree_column = "Paths"
+overrides_applied = "Overrides applied."
+button_flatpak = "Grant permissions"
### Completion page
heading_completion = "Setup complete"
diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py
index 2639093..1c12d40 100644
--- a/dzgui/views/dialogs/wizard.py
+++ b/dzgui/views/dialogs/wizard.py
@@ -429,7 +429,6 @@ class FlatpakOverridePage(EnumeratedWizardPage):
self.xdg_paths = xdg_paths
self.page_type = Gtk.AssistantPageType.INTRO
-
store = Gtk.ListStore(str)
for path in xdg_paths.values():
store.append((str(path),))
@@ -437,15 +436,22 @@ class FlatpakOverridePage(EnumeratedWizardPage):
tree.set_halign(Gtk.Align.CENTER)
self.authorize_button = Gtk.Button(
- label="Grant permissions", halign=Gtk.Align.CENTER
+ label=wizard.button_flatpak, halign=Gtk.Align.CENTER
)
self.authorize_button.connect("clicked", self._on_authorize)
- for el in tree, self.authorize_button:
+ self.notice = NotificationFrame(wizard.overrides_applied)
+ self.connect("map", self._on_map)
+
+ for el in tree, self.authorize_button, self.notice:
self.add_start(el)
+ def _on_map(self, page: "EnumeratedWizardPage") -> None:
+ self.notice.hide()
+
def _on_authorize(self, button: Gtk.Button) -> None:
apply_overrides(self.xdg_paths)
+ self.notice.show()
EMITTER.emit("step_complete")
From a9ae5f59b519ddc5a0a7c6c7420156420f4aaf46 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Sat, 24 Oct 2026 09:21:50 +0900
Subject: [PATCH 19/22] chore: add flatpak tests
---
pyproject.toml | 3 ++-
tests/test_flatpak_override.py | 43 +++++++++++++++++++++++++++++++++
tests/test_process_detection.py | 3 +++
3 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 tests/test_flatpak_override.py
diff --git a/pyproject.toml b/pyproject.toml
index 1d52faa..b7a9729 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -110,10 +110,11 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } }
markers = [
"apitest: checks remote endpoints",
"config: config file keys/values",
- "realconfig: depends on actual generated local config file",
+ "flatpak: depends on local flatpak installation",
"mods: tests mod metadata/link creation",
"pefile: validate PE files",
"post_install: requires a completed installation",
+ "realconfig: depends on actual generated local config file",
"redact: log redaction mechanisms",
"slow: long-running tests",
"webtest: depends on remote endpoint",
diff --git a/tests/test_flatpak_override.py b/tests/test_flatpak_override.py
new file mode 100644
index 0000000..d34307d
--- /dev/null
+++ b/tests/test_flatpak_override.py
@@ -0,0 +1,43 @@
+import configparser
+import pytest
+
+from dzgui.const.constants import FLATPAK_APPID, GTK_APP_ID
+from dzgui.config.xdg import get_xdg_paths
+from dzgui.util.flatpak import apply_overrides
+
+PATHS_KEY = "Context"
+PATHS_SUBKEY = "filesystems"
+BUS_KEY = "Session Bus Policy"
+
+pytestmark = pytest.mark.flatpak
+
+
+@pytest.fixture
+def xdg_paths() -> dict[str, str]:
+ return get_xdg_paths()
+
+
+@pytest.fixture
+def override(xdg_paths) -> None:
+ apply_overrides(xdg_paths)
+
+
+@pytest.fixture
+def config(xdg_paths) -> configparser.ConfigParser:
+ conf_path = xdg_paths["XDG_DATA_HOME"].parent.joinpath(
+ f"flatpak/overrides/{FLATPAK_APPID}"
+ )
+ config = configparser.ConfigParser()
+ config.read(conf_path)
+ return config
+
+
+def test_session_bus(override, config) -> None:
+ bus = config[BUS_KEY][GTK_APP_ID]
+ assert bus == "own"
+
+
+def test_paths(override, xdg_paths, config) -> None:
+ expect = xdg_paths
+ paths = config[PATHS_KEY][PATHS_SUBKEY].split(";")
+ assert all(str(path) in paths for path in expect.values())
diff --git a/tests/test_process_detection.py b/tests/test_process_detection.py
index 23f0956..8b25c9b 100644
--- a/tests/test_process_detection.py
+++ b/tests/test_process_detection.py
@@ -1,3 +1,4 @@
+import pytest
import unittest
from unittest.mock import patch
@@ -9,6 +10,8 @@ from dzgui.const.constants import (
)
from dzgui.init import proc
+pytestmark = pytest.mark.flatpak
+
class TestSteamDetection(unittest.TestCase):
def test_flatpak_detection_with_multiple_running_apps(self):
From a6282e4401a5cac339fa5a04107a679478d14856 Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Sat, 24 Oct 2026 10:58:21 +0900
Subject: [PATCH 20/22] chore: clean up test blockage
---
tests/test_html_escaping.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/tests/test_html_escaping.py b/tests/test_html_escaping.py
index 471bb1a..904e5b2 100644
--- a/tests/test_html_escaping.py
+++ b/tests/test_html_escaping.py
@@ -1,3 +1,5 @@
+import pytest
+
from dzgui.api.servers import Details
from dzgui.views.dialogs.servers import ServerDetailsDialog
@@ -21,6 +23,7 @@ class MockController:
return None
+@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_html_escaping() -> None:
name = "Test server"
desc = " Game & mods"
@@ -31,5 +34,7 @@ def test_html_escaping() -> None:
controller = MockController()
dialog = ServerDetailsDialog(controller, details)
dialog.destroy()
+ while Gtk.events_pending():
+ Gtk.main_iteration()
assert dialog.description.get_text() == desc
From f4be9a31e90c8545e8b5facc94a9377cc548742f Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Sun, 25 Oct 2026 15:56:06 +0900
Subject: [PATCH 21/22] change: check actual client preference
---
dzgui/app_init.py | 15 ++++++++++++---
dzgui/util/strings.py | 2 +-
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/dzgui/app_init.py b/dzgui/app_init.py
index 728a495..77d076f 100644
--- a/dzgui/app_init.py
+++ b/dzgui/app_init.py
@@ -7,7 +7,12 @@ from typing import TYPE_CHECKING
from warnings import deprecated
-from dzgui.const.constants import APP_NAME, STEAM_ICON, STEAM_ICONS_PATH
+from dzgui.const.constants import (
+ APP_NAME,
+ FLATPAK_ROOT_PATH,
+ STEAM_ICON,
+ STEAM_ICONS_PATH,
+)
from dzgui.const.enum import Preferences
from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs
@@ -18,7 +23,7 @@ from dzgui.init.migrate import (
migrate_cols_file,
)
from dzgui.init.flock import lock_acquire
-from dzgui.init.prereqs import has_any_client
+from dzgui.init.prereqs import has_flatpak_client, has_steam_client
from dzgui.strings import boot
from dzgui.util.deck import is_steam_deck, is_game_mode
@@ -161,7 +166,11 @@ def load_gui(version: str, is_debug: bool) -> None:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
- if has_any_client() is False:
+ if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT):
+ has_client = has_flatpak_client()
+ else:
+ has_client = has_steam_client()
+ if has_client is False:
EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version)
diff --git a/dzgui/util/strings.py b/dzgui/util/strings.py
index 30a7fc6..91c41ea 100644
--- a/dzgui/util/strings.py
+++ b/dzgui/util/strings.py
@@ -297,7 +297,7 @@ class Options:
init = Init(
is_steam_running="Is Steam running? DZGUI must be run on top of Steam.",
is_dayz_running="Is DayZ already running? DZGUI cannot launch DayZ if another process is using it.",
- requires_steam="DZGUI requires that Steam or Flatpak Steam be installed on the system.",
+ requires_steam="Your chosen client (Steam/Flatpak Steam) must be installed on the system.",
)
mod_panel = ModPanelStrings(
From 3e6bd95285beaab7bc47ddc3abe854e91f14938b Mon Sep 17 00:00:00 2001
From: aclist <92275929+aclist@users.noreply.github.com>
Date: Mon, 26 Oct 2026 15:47:45 +0900
Subject: [PATCH 22/22] change: require explicit Steam launch command
---
dzgui/api/steam.py | 5 ++++-
dzgui/strings/wizard.py | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py
index 7efff23..2580bd0 100644
--- a/dzgui/api/steam.py
+++ b/dzgui/api/steam.py
@@ -26,8 +26,9 @@ from dzgui.const.constants import (
FLATPAK_CMD,
FLATPAK_STEAM_PATH,
LIBRARYFOLDERS_PATH,
- UBUNTU_STEAM_PATH,
REQUEST_TIMEOUT,
+ STEAM_CMD,
+ UBUNTU_STEAM_PATH,
VDF_PATH,
)
from dzgui.const.endpoints import (
@@ -86,6 +87,8 @@ def get_steam_paths() -> list[tuple[Path, str]]:
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
if path is FLATPAK_PATH and has_cmd(FLATPAK_CMD) is False:
continue
+ if path is not FLATPAK_PATH and has_cmd(STEAM_CMD) is False:
+ continue
if path.joinpath(VDF_PATH).is_file():
paths.append((path, human[path]))
return paths
diff --git a/dzgui/strings/wizard.py b/dzgui/strings/wizard.py
index a495de8..de83762 100644
--- a/dzgui/strings/wizard.py
+++ b/dzgui/strings/wizard.py
@@ -19,7 +19,7 @@ desc_flatpak_path = (
desc_ubuntu_path = "This is the default Steam path on Ubuntu-based systems."
desc_debian_path = "This is the default Steam path on Debian-based systems."
no_valid_paths = (
- "No valid Steam paths found on system. Please install Steam to continue."
+ "No valid Steam path or Steam command found on system. Please install Steam to continue."
)
button_scan = "Scan for Steam"