Compare commits

...
Author SHA1 Message Date
aclist 30119fec46 fix: add path out of loop 2026-10-26 19:24:42 +09:00
aclist ac01818549 Merge pull request #506 from aclist/prerelease/7.0.0b29-patch-2
fix: add /tmp to wizard
2026-10-26 19:21:40 +09:00
aclist d486712a4c fix: add /tmp to wizard 2026-10-26 19:20:17 +09:00
aclist d116702145 Merge pull request #505 from aclist/prerelease/7.0.0b29-patch-1
Prerelease/7.0.0b29 patch 1
2026-10-26 19:11:15 +09:00
aclist 5bee3614bd fix: whitelist /tmp 2026-10-26 19:09:50 +09:00
aclist c73b07991f change: abstract lock file path 2026-10-26 19:09:32 +09:00
aclist 199cd07982 Merge pull request #504 from aclist/prerelease/7.0.0b29
chore: bump version
2026-10-26 18:05:52 +09:00
aclist e3d62c6749 chore: bump version 2026-10-26 17:59:18 +09:00
aclist 5134bf02fb Merge pull request #503 from aclist/fix/flatpak-fixes
fix: flatpak fixes
2026-10-26 17:22:58 +09:00
aclist f97be9352d fix: typo 2026-10-26 17:21:10 +09:00
aclist b092a809f4 fix: capitalization 2026-10-26 17:20:28 +09:00
aclist 2cc7710807 fix: handle sandboxed mode 2026-10-26 17:20:14 +09:00
aclist 5fb10d9758 change: suppress missing shortcuts message 2026-10-26 17:19:56 +09:00
aclist 100cb43afc Merge pull request #502 from aclist/fix/check-granular-client
fix: check granular client
2026-10-26 16:15:47 +09:00
aclist 7e2a585812 chore: drop unused imports 2026-10-26 16:14:57 +09:00
aclist c1181435b3 chore: formatting 2026-10-26 16:13:56 +09:00
aclist 22a7814ce4 fix: check actual client command 2026-10-26 16:13:42 +09:00
aclist b030da5c65 Merge pull request #498 from aclist/fix/refresh-on-timeout
fix: refresh on timeout
2026-10-26 15:56:51 +09:00
aclist 3c0acdd068 Merge pull request #501 from aclist/fix/symbolic-svg
fix: symbolic svg
2026-10-26 15:56:16 +09:00
aclist 25ae2052e4 Merge branch 'dzgui7' into fix/symbolic-svg
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-10-26 15:55:37 +09:00
aclist 1b1777117f Merge pull request #497 from aclist/feat/dynamic-flatpak-toggle
feat: dynamic flatpak toggle
2026-10-26 15:51:18 +09:00
aclist 3e6bd95285 change: require explicit Steam launch command
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-10-26 15:47:45 +09:00
aclist f4be9a31e9 change: check actual client preference
Mirror to Codeberg / mirror-to-codeberg (push) Canceled after 0s
2026-10-25 15:56:06 +09:00
aclist a6282e4401 chore: clean up test blockage
Mirror to Codeberg / mirror-to-codeberg (push) Canceled after 0s
2026-10-24 10:58:21 +09:00
aclist a9ae5f59b5 chore: add flatpak tests 2026-10-24 09:21:50 +09:00
aclist cdec81071d feat: show notice on override
Mirror to Codeberg / mirror-to-codeberg (push) Canceled after 0s
2026-10-23 15:39:56 +09:00
aclist 97b5c0e6ee fix: split arguments 2026-10-23 15:19:37 +09:00
aclist 67c19fb90d feat: add SimpleTree 2026-10-23 15:11:05 +09:00
aclist d9d3f747bb feat: add override module 2026-10-23 15:10:24 +09:00
aclist 1440871819 chore: update strings 2026-10-23 14:14:44 +09:00
aclist 761988d8c2 feat: add override module 2026-10-23 14:14:30 +09:00
aclist 97ee2b7307 chore: update attribution 2026-10-23 13:07:38 +09:00
aclist ff6edb6181 fix: light mode symbolic icon 2026-10-23 13:05:20 +09:00
aclist 4c22263fb5 Merge branch 'dzgui7' of github.com:aclist/dztui into dzgui7 2026-10-23 13:04:09 +09:00
aclist ab5637ae3b Merge branch 'dzgui7' into feat/dynamic-flatpak-toggle 2026-10-23 12:35:53 +09:00
aclist 682532a867 chore: remove erroneous debug string 2026-10-23 12:34:04 +09:00
aclist d577ba0970 change: remove Flatpak option if binary is not installed 2026-10-23 12:20:10 +09:00
aclist c2993ad289 change: revert prior VDF change, no observable effect on D-Bus 2026-10-23 12:11:19 +09:00
aclist edb87893f9 chore: clear typehinting errors 2026-10-23 11:46:05 +09:00
aclist 519e6c354d chore: patch VDF changes from main 2026-10-23 11:42:08 +09:00
aclist e3d1d62b4d Merge pull request #499 from aclist/fix/vdf-missing
Mirror to Codeberg / mirror-to-codeberg (push) Canceled after 0s
fix: missing shortcuts file
2026-10-23 11:38:48 +09:00
aclist 01b3babdf5 feat: FlatpakOverridePage WIP 2026-10-23 11:23:28 +09:00
aclist 141f96fb9f fix: allow using refresh button after timeout events (#484) 2026-10-22 15:59:05 +09:00
aclist e286f02342 chore: add flatpak toggle test 2026-10-22 12:36:23 +09:00
aclist 9f3c39fdd6 chore: drop unused print statement 2026-10-22 12:35:56 +09:00
aclist 0359aacd54 chore: clear typehinting errors 2026-10-22 12:09:40 +09:00
aclist 80e127889e chore: drop unused import 2026-10-22 12:09:07 +09:00
aclist da98b1d5b1 feat: toggle flatpak client dynamically 2026-10-22 12:08:44 +09:00
aclist 33869158de change: explicitly disable NSG creation on Flatpak 2026-10-22 12:07:59 +09:00
26 changed files with 388 additions and 57 deletions

No files matched your search

+3 -1
View File
@@ -17,7 +17,7 @@ DZGUI follows a specific set of guidelines, enumerated below:
- Provide a free and open-source tool under GPL - Provide a free and open-source tool under GPL
- Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server - Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server
- All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints - All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints
- Target Linux desktop and Steam deck specifically; not a cross-platform tool - Target Linux desktop and Steam Deck specifically; not a cross-platform tool
## Setup and usage ## Setup and usage
@@ -35,6 +35,8 @@ Geolocation records from [DB-IP](https://db-ip.com) under [CC 4.0 license](https
Executable versions of DZGUI published as release binaries ship with various runtime dependencies and the Python interpreter built in. Executable versions of DZGUI published as release binaries ship with various runtime dependencies and the Python interpreter built in.
Users wishing to review the licenses to these components can inspect the `LICENSE` file located in the release tarball. Users wishing to review the licenses to these components can inspect the `LICENSE` file located in the release tarball.
Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries.
## Disclaimer ## Disclaimer
DZGUI is beta-quality software and is provided as-is. DZGUI is beta-quality software and is provided as-is.
+8 -7
View File
@@ -10,7 +10,7 @@ from pathlib import Path
from typing import Any from typing import Any
from dzgui.api.steam import find_user_id_32 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.dirs import copy_dzgui_to_xdg_data, make_parents
from dzgui.util.strings import unknown from dzgui.util.strings import unknown
@@ -158,7 +158,7 @@ class Shortcuts:
NEW_ENTRY["DevkitGameID"] = "" NEW_ENTRY["DevkitGameID"] = ""
NEW_ENTRY["DevkitOverrideAppID"] = 0 NEW_ENTRY["DevkitOverrideAppID"] = 0
NEW_ENTRY["LastPlayTime"] = 0 NEW_ENTRY["LastPlayTime"] = 0
NEW_ENTRY["FlatpakAppID"] = GTK_APP_ID NEW_ENTRY["FlatpakAppID"] = ""
NEW_ENTRY["sortas"] = "" NEW_ENTRY["sortas"] = ""
NEW_ENTRY["tags"] = {} NEW_ENTRY["tags"] = {}
@@ -173,11 +173,12 @@ class Shortcuts:
self.save_shortcuts() self.save_shortcuts()
def save_shortcuts(self) -> None: def save_shortcuts(self) -> None:
try: if self.shortcuts_path.is_file():
backup = self.shortcuts_path.with_suffix(".vdf.bak") try:
shutil.copy(self.shortcuts_path, backup) backup = self.shortcuts_path.with_suffix(".vdf.bak")
except Exception as e: shutil.copy(self.shortcuts_path, backup)
logger.critical(e) except Exception as e:
logger.critical(e)
try: try:
with open(self.shortcuts_path, "wb") as f: with open(self.shortcuts_path, "wb") as f:
vdf.binary_dump(self.shortcuts, f) vdf.binary_dump(self.shortcuts, f)
+7 -1
View File
@@ -13,6 +13,7 @@ from warnings import deprecated
from dzgui.api.acf import ACF from dzgui.api.acf import ACF
from dzgui.init.prereqs import has_any_client from dzgui.init.prereqs import has_any_client
from dzgui.init.prereqs import has_flatpak_client, has_steam_client
from dzgui.const.constants import ( from dzgui.const.constants import (
APPID_DAYZ, APPID_DAYZ,
APPID_DAYZ_EXP, APPID_DAYZ_EXP,
@@ -24,8 +25,8 @@ from dzgui.const.constants import (
DOWNLOADS_PATH, DOWNLOADS_PATH,
FLATPAK_STEAM_PATH, FLATPAK_STEAM_PATH,
LIBRARYFOLDERS_PATH, LIBRARYFOLDERS_PATH,
UBUNTU_STEAM_PATH,
REQUEST_TIMEOUT, REQUEST_TIMEOUT,
UBUNTU_STEAM_PATH,
VDF_PATH, VDF_PATH,
) )
from dzgui.const.endpoints import ( from dzgui.const.endpoints import (
@@ -82,6 +83,10 @@ def get_steam_paths() -> list[tuple[Path, str]]:
} }
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH: for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
if path is FLATPAK_PATH and has_flatpak_client() is False:
continue
if path is not FLATPAK_PATH and has_steam_client() is False:
continue
if path.joinpath(VDF_PATH).is_file(): if path.joinpath(VDF_PATH).is_file():
paths.append((path, human[path])) paths.append((path, human[path]))
return paths return paths
@@ -198,6 +203,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""" """Loads to the menu screen with the selected mods; used for AFK/pre-joining"""
concat = concat_mods(mods) concat = concat_mods(mods)
client_args = concat_bash_args(client) client_args = concat_bash_args(client)
logger.debug(f"Setting client arguments to: {client_args}")
params = [ params = [
"-applaunch", "-applaunch",
str(appid), str(appid),
+22 -17
View File
@@ -2,12 +2,17 @@ import logging
import os import os
import shutil import shutil
from importlib import resources
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING 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,
APP_NAME_LOWER,
FLATPAK_ROOT_PATH,
ICONS_PATH,
)
from dzgui.const.enum import Preferences from dzgui.const.enum import Preferences
from dzgui.config.query import lookup from dzgui.config.query import lookup
from dzgui.config.userprefs import UserPrefs from dzgui.config.userprefs import UserPrefs
@@ -18,7 +23,7 @@ from dzgui.init.migrate import (
migrate_cols_file, migrate_cols_file,
) )
from dzgui.init.flock import lock_acquire 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.strings import boot
from dzgui.util.deck import is_steam_deck, is_game_mode from dzgui.util.deck import is_steam_deck, is_game_mode
@@ -26,7 +31,7 @@ from dzgui.util.dirs import make_parents
from dzgui.util.localize import set_locale from dzgui.util.localize import set_locale
from dzgui.util.map_count import test_map_count from dzgui.util.map_count import test_map_count
from dzgui.util.redact import RedactionFilter, REDACTION_PATTERNS from dzgui.util.redact import RedactionFilter, REDACTION_PATTERNS
from dzgui.util.strings import init, steam_icon_missing from dzgui.util.strings import init
from dzgui.views.base import App from dzgui.views.base import App
from dzgui.views.dialogs.boot import BootWindow from dzgui.views.dialogs.boot import BootWindow
@@ -99,26 +104,21 @@ def copy_bare_configs(config: "Path", resolution: "Path") -> tuple[bool, bool]:
return (config_changed, state_changed) return (config_changed, state_changed)
@deprecated("Currently unused") def load_steam_icon() -> None:
def load_steam_icon(config: Path) -> None:
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa from gi.repository import Gtk # noqa
default = lookup(config, Preferences.DEFAULT)
icon_path = Path(default).joinpath(STEAM_ICONS_PATH)
try: try:
traversable = resources.files(APP_NAME_LOWER).joinpath(ICONS_PATH)
icons = Path(str(traversable))
theme = Gtk.IconTheme.get_default() theme = Gtk.IconTheme.get_default()
paths = theme.get_search_path() paths = theme.get_search_path()
theme.set_search_path(paths + [str(icon_path)]) theme.set_search_path(paths + [str(icons)])
paths = theme.get_search_path()
icons = theme.list_icons(None)
if STEAM_ICON not in icons:
logger.warning(steam_icon_missing)
except Exception as e: except Exception as e:
print(e) logger.critical(e)
logger.warning(e)
def load_gui(version: str, is_debug: bool) -> None: def load_gui(version: str, is_debug: bool) -> None:
@@ -148,7 +148,7 @@ def load_gui(version: str, is_debug: bool) -> None:
migrate_cols_file(XDG.columns) migrate_cols_file(XDG.columns)
copy_state_files(xdg_paths["XDG_STATE_HOME"]) copy_state_files(xdg_paths["XDG_STATE_HOME"])
# TODO: add logging inside wizard # 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(): if not wizard.is_setup_complete():
return return
@@ -161,7 +161,11 @@ def load_gui(version: str, is_debug: bool) -> None:
# TODO: sudo escalation dialog # TODO: sudo escalation dialog
# count = get_map_count() # count = get_map_count()
# TODO: move into module # 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) EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version) bootwin = BootWindow(XDG, version)
@@ -180,5 +184,6 @@ def load_gui(version: str, is_debug: bool) -> None:
use_miles=use_miles, use_miles=use_miles,
) )
print(boot.all_ok) print(boot.all_ok)
load_steam_icon()
app = App(prefs) app = App(prefs)
app.run(None) app.run(None)
+10 -2
View File
@@ -3,7 +3,13 @@ import os
from pathlib import Path from pathlib import Path
from dataclasses import dataclass 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 @dataclass
@@ -53,10 +59,12 @@ def get_xdg_paths() -> dict:
"XDG_DATA_HOME": home / ".local" / "share", "XDG_DATA_HOME": home / ".local" / "share",
"XDG_CACHE_HOME": home / ".cache", "XDG_CACHE_HOME": home / ".cache",
} }
resolved_paths = {} resolved_paths = {}
for path in xdg_paths: for path in xdg_paths:
real_path = os.environ.get(path) real_path = os.environ.get(path)
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): if real_path is not None and is_writeable(real_path):
new_path = Path(real_path) new_path = Path(real_path)
else: else:
+3 -2
View File
@@ -51,7 +51,7 @@ INPUT_KEYBOARD = "input-keyboard-symbolic"
LIST_ADD = "list-add-symbolic" LIST_ADD = "list-add-symbolic"
REFRESH_ICON = "view-refresh-symbolic" REFRESH_ICON = "view-refresh-symbolic"
SEARCH_ICON = "system-search-symbolic" SEARCH_ICON = "system-search-symbolic"
STEAM_ICON = "steam_tray_mono" STEAM_ICON = "steam-tray-symbolic"
VIEW_CONCEAL = "view-conceal-symbolic" VIEW_CONCEAL = "view-conceal-symbolic"
VIEW_REVEAL = "view-reveal-symbolic" VIEW_REVEAL = "view-reveal-symbolic"
WARNING = "dialog-warning-symbolic" WARNING = "dialog-warning-symbolic"
@@ -80,6 +80,7 @@ DEBUG_LOG = f"{APP_NAME}_DEBUG.LOG"
SYSTEM_LOG = f"{APP_NAME}_SYSTEM.LOG" SYSTEM_LOG = f"{APP_NAME}_SYSTEM.LOG"
CHANGELOG_PATH = "data/CHANGELOG.md" CHANGELOG_PATH = "data/CHANGELOG.md"
IMAGES_PATH = "data/images" IMAGES_PATH = "data/images"
ICONS_PATH = "data/images/gtk"
HERO_PATH = "data/images/_hero.png" HERO_PATH = "data/images/_hero.png"
CSS_PATH = "data/app.css" CSS_PATH = "data/app.css"
@@ -89,13 +90,13 @@ FLATPAK_ROOT_PATH = ".var/app/com.valvesoftware.Steam"
FLATPAK_STEAM_PATH = FLATPAK_ROOT_PATH + "/data/Steam" FLATPAK_STEAM_PATH = FLATPAK_ROOT_PATH + "/data/Steam"
UBUNTU_STEAM_PATH = ".steam/steam" UBUNTU_STEAM_PATH = ".steam/steam"
DEBIAN_STEAM_PATH = ".steam/debian-installation" DEBIAN_STEAM_PATH = ".steam/debian-installation"
STEAM_ICONS_PATH = "public"
LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG") LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG")
TMP_PATH = "/tmp" TMP_PATH = "/tmp"
TMP_TARBALL = "/tmp/dzgui.tar.gz" TMP_TARBALL = "/tmp/dzgui.tar.gz"
TMP_EXE = "/tmp/dzgui/dzgui" TMP_EXE = "/tmp/dzgui/dzgui"
LOCK_FILE = "/tmp/dzgui.flock"
DAYZ_COMMUNITY_ROOT = "Missions" DAYZ_COMMUNITY_ROOT = "Missions"
+4
View File
@@ -111,6 +111,10 @@ class Emitter(GObject.GObject):
def check_toggled(self, label: str, state: bool) -> None: def check_toggled(self, label: str, state: bool) -> None:
pass pass
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=())
def servers_timed_out(self) -> None:
pass
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,)) @GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,))
def servers_loaded(self, tag: "ServerTab") -> None: def servers_loaded(self, tag: "ServerTab") -> None:
pass pass
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.3.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M568 320C568 457 456.8 568 319.6 568C205.8 568 110 491.7 80.6 387.6L175.8 426.9C182.2 459 210.7 483.3 244.7 483.3C283.9 483.3 316.6 450.9 314.9 409.8L399.4 349.6C451.5 350.9 495.2 308.7 495.2 256.1C495.2 204.5 453.2 162.6 401.5 162.6C349.8 162.6 307.8 204.6 307.8 256.1L307.8 257.3L248.6 343C233.1 342.1 217.9 346.4 205.1 355.1L72 300.1C82.2 172.4 189.1 72 319.6 72C456.8 72 568 183 568 320zM227.7 448.3L197.2 435.7C202.8 447.3 212.5 456.5 224.4 461.5C251.3 472.7 282.2 459.9 293.4 433.1C298.8 420.1 298.9 405.8 293.5 392.8C288.1 379.8 278 369.6 265 364.2C252.1 358.8 238.3 359 226.1 363.6L257.6 376.6C277.4 384.8 286.8 407.5 278.5 427.3C270.2 447.2 247.5 456.5 227.7 448.3zM401.5 193.8C435.9 193.8 463.8 221.7 463.8 256.1C463.8 290.5 435.9 318.4 401.5 318.4C367.1 318.4 339.2 290.5 339.2 256.1C339.2 221.7 367.1 193.8 401.5 193.8zM401.6 302.8C427.4 302.8 448.4 281.8 448.4 256C448.4 230.2 427.4 209.2 401.6 209.2C375.8 209.2 354.8 230.2 354.8 256C354.8 281.8 375.8 302.8 401.6 302.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+3 -2
View File
@@ -5,8 +5,9 @@ import sys
from pathlib import Path from pathlib import Path
from typing import TextIO from typing import TextIO
from dzgui.views.dialogs.early_alert import EarlyAlertDialog from dzgui.const.constants import LOCK_FILE
from dzgui.util.strings import cannot_acquire_lock from dzgui.util.strings import cannot_acquire_lock
from dzgui.views.dialogs.early_alert import EarlyAlertDialog
def lock_release(lock: TextIO) -> None: def lock_release(lock: TextIO) -> None:
@@ -15,7 +16,7 @@ def lock_release(lock: TextIO) -> None:
def lock_acquire() -> TextIO: def lock_acquire() -> TextIO:
lockfile = Path("/tmp/dzgui.flock") lockfile = Path(LOCK_FILE)
lock = open(lockfile, "w") lock = open(lockfile, "w")
try: try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
+3
View File
@@ -3,11 +3,14 @@ import shutil
import subprocess import subprocess
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, FLATPAK_CMD, STEAM_CMD from dzgui.const.constants import APP_NAME, FLATPAK_APPID, FLATPAK_CMD, STEAM_CMD
from dzgui.init.proc import is_sandboxed
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
def has_flatpak_client() -> bool: def has_flatpak_client() -> bool:
if is_sandboxed():
return True
try: try:
res = subprocess.run( res = subprocess.run(
[FLATPAK_CMD, "list", "--columns=application"], [FLATPAK_CMD, "list", "--columns=application"],
+10 -3
View File
@@ -1,7 +1,8 @@
import psutil
import subprocess
import shutil
import logging import logging
import os
import psutil
import shutil
import subprocess
from dzgui.const.constants import ( from dzgui.const.constants import (
APP_NAME, APP_NAME,
@@ -26,7 +27,13 @@ def is_steam_running(cmd: str) -> bool:
raise TypeError("Not a valid Steam client selection") raise TypeError("Not a valid Steam client selection")
def is_sandboxed() -> bool:
return bool(os.environ.get("FLATPAK_ID"))
def is_flatpak_steam_running() -> bool: def is_flatpak_steam_running() -> bool:
if is_sandboxed():
return True
if has_cmd(FLATPAK_CMD) is False: if has_cmd(FLATPAK_CMD) is False:
return False return False
proc = subprocess.check_output( proc = subprocess.check_output(
+3
View File
@@ -92,6 +92,7 @@ class ServerModelManager:
job = Servers.query_api job = Servers.query_api
params = Servers.params params = Servers.params
servers = [] servers = []
with ThreadPoolExecutor() as executor: with ThreadPoolExecutor() as executor:
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params] futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
for future in as_completed(futures): for future in as_completed(futures):
@@ -414,6 +415,8 @@ class ServerModelManager:
# TODO: disable map, keyword, and filter widgets if model is None # TODO: disable map, keyword, and filter widgets if model is None
# signal driven (servers_empty, servers_failed_to_load) # signal driven (servers_empty, servers_failed_to_load)
self.emitter.emit("servers_timed_out")
if self.preserve_on_fail is False: if self.preserve_on_fail is False:
self.tv.set_model(None) self.tv.set_model(None)
filter_man = self.tv.get_filter_man() filter_man = self.tv.get_filter_man()
+7
View File
@@ -17,3 +17,10 @@ fullscreen_eventbox = "This option is not available on Steam Deck."
save_button = "Save" save_button = "Save"
api_failed = "API validation failed" 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."
)
+16 -2
View File
@@ -3,6 +3,8 @@ title_intro = "Welcome"
blurb_intro = """ blurb_intro = """
This wizard will help you set up some common config options before launching the application. 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 ### SteamPathPage
heading_steam_path = "Steam path" heading_steam_path = "Steam path"
@@ -17,7 +19,7 @@ desc_flatpak_path = (
desc_ubuntu_path = "This is the default Steam path on <b>Ubuntu-based systems</b>." desc_ubuntu_path = "This is the default Steam path on <b>Ubuntu-based systems</b>."
desc_debian_path = "This is the default Steam path on <b>Debian-based systems</b>." desc_debian_path = "This is the default Steam path on <b>Debian-based systems</b>."
no_valid_paths = ( 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" button_scan = "Scan for Steam"
@@ -70,7 +72,8 @@ checkbox_steam_shortcut = (
( (
"This creates a standalone copy of DZGUI and adds it to Steam. " "This creates a standalone copy of DZGUI and adds it to Steam. "
"You <b>must restart Steam</b> for the shortcut to appear. This will not replace or update existing shortcuts. " "You <b>must restart Steam</b> 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 = ( checkbox_start_menu = (
@@ -79,6 +82,17 @@ checkbox_start_menu = (
) )
checkbox_desktop_shortcut = ("Add a desktop shortcut", "Depends on the option above.") 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"
overrides_applied = "Overrides applied."
button_flatpak = "Grant permissions"
### Completion page ### Completion page
heading_completion = "Setup complete" heading_completion = "Setup complete"
blurb_completion = "Configuration completed successfully. Click 'Close' to close this dialog and launch DZGUI." blurb_completion = "Configuration completed successfully. Click 'Close' to close this dialog and launch DZGUI."
+7
View File
@@ -1,5 +1,12 @@
import shlex import shlex
from dzgui.const.constants import STEAM_CMD
from dzgui.init.proc import is_sandboxed
def concat_bash_args(command: str) -> list[str]: 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) return shlex.split(command)
+35
View File
@@ -0,0 +1,35 @@
import logging
import subprocess
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID, TMP_PATH
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)
paths: list[str] = []
paths.extend([*xdg.values()])
paths.append(TMP_PATH)
for path in paths:
args = [
"flatpak",
"--user",
"override",
f"--filesystem={path}",
FLATPAK_APPID,
]
subprocess.run([*args], capture_output=True)
except Exception as e:
logger.critical(e)
+1 -1
View File
@@ -297,7 +297,7 @@ class Options:
init = Init( init = Init(
is_steam_running="Is Steam running? DZGUI must be run on top of Steam.", 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.", 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( mod_panel = ModPanelStrings(
+10 -4
View File
@@ -163,18 +163,24 @@ class RefreshButton(IconTextButton):
self.set_tooltip_text(atomic_buttons.refresh_tooltip) self.set_tooltip_text(atomic_buttons.refresh_tooltip)
self.connect("clicked", self._on_refresh_clicked) self.connect("clicked", self._on_refresh_clicked)
emitter.connect("servers_loaded", self.start_decrement) emitter.connect("servers_loaded", self._on_load)
emitter.connect("servers_timed_out", self._on_timeout)
def _on_timeout(self, emitter: "Emitter") -> None:
self.is_clicked = True
self.start_decrement()
def _on_load(self, emitter: "Emitter", tab: "ServerTab") -> None:
self.start_decrement()
def _on_refresh_clicked(self, button: Self) -> None: def _on_refresh_clicked(self, button: Self) -> None:
"""Spawned in a thread""" """Spawned in a thread"""
if self.controller.get_active_treeview().is_loaded() is False:
return
self.is_clicked = True self.is_clicked = True
self.controller.refresh_tree() self.controller.refresh_tree()
# TODO: get server tab enum # TODO: get server tab enum
# if LAN tab, reload existing entries in place # if LAN tab, reload existing entries in place
def start_decrement(self, emitter: "Emitter", tab: "ServerTab") -> None: def start_decrement(self) -> None:
if self.is_clicked is False: if self.is_clicked is False:
return return
if self.loading: if self.loading:
+86 -12
View File
@@ -12,21 +12,25 @@ from dzgui.const.constants import (
APP_NAME, APP_NAME,
APP_NAME_LOWER, APP_NAME_LOWER,
HERO_PATH, HERO_PATH,
FLATPAK_ROOT_PATH,
LEGACY_CONFIG_PATH, LEGACY_CONFIG_PATH,
TMP_PATH
) )
from dzgui.const.boilerplate import config_boilerplate from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.config import freedesktop 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.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard from dzgui.strings import wizard
from dzgui.util._json import write_json 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.open_links import open_link_by_url
from dzgui.util.css import add_class, load_css from dzgui.util.css import add_class, load_css
from dzgui.views.components.buttons import WebButton from dzgui.views.components.buttons import WebButton
from dzgui.views.components.entry import APIEntry from dzgui.views.components.entry import APIEntry
from dzgui.views.mixins.colorscheme import ColorAwareApp from dzgui.views.mixins.colorscheme import ColorAwareApp
from dzgui.views.trees.tree_simple import SimpleTree
import gi import gi
@@ -44,7 +48,8 @@ class PageNum(Enum):
STEAM_API = 4 STEAM_API = 4
USER_PREFS = 5 USER_PREFS = 5
SHORTCUTS = 6 SHORTCUTS = 6
FINAL = 7 FLATPAK = 7
FINAL = 8
class OptionalPageMixin: class OptionalPageMixin:
@@ -143,7 +148,7 @@ class NotificationFrame(Gtk.Frame):
margin_start=50, margin_start=50,
margin_end=50, margin_end=50,
) )
wrapped = textwrap.fill(label, width=80) wrapped = textwrap.fill(label, width=60)
self.label = Gtk.Label( self.label = Gtk.Label(
label=wrapped, label=wrapped,
justify=Gtk.Justification.CENTER, justify=Gtk.Justification.CENTER,
@@ -157,7 +162,7 @@ class NotificationFrame(Gtk.Frame):
add_class(self, "error-frame") add_class(self, "error-frame")
def set_text(self, text: str) -> None: def set_text(self, text: str) -> None:
wrapped = textwrap.fill(text, width=80) wrapped = textwrap.fill(text, width=60)
self.label.set_markup(wrapped) self.label.set_markup(wrapped)
@@ -242,12 +247,23 @@ class IntroductionPage(EnumeratedWizardPage):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__( super().__init__(
enum=PageNum.INTRO, enum=PageNum.INTRO,
heading=f"Welcome to {APP_NAME}!", heading=wizard.welcome.format(APP_NAME),
description=wizard.blurb_intro, description=wizard.blurb_intro,
) )
self.page_type = Gtk.AssistantPageType.INTRO self.page_type = Gtk.AssistantPageType.INTRO
self.set_title(wizard.title_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): class Heading(Gtk.Label):
def __init__(self, label: str): def __init__(self, label: str):
@@ -404,6 +420,44 @@ class PreferencesPage(EnumeratedWizardPage):
EMITTER.emit("step_complete") EMITTER.emit("step_complete")
class FlatpakOverridePage(EnumeratedWizardPage):
def __init__(self, xdg_paths: dict[str, str]) -> None:
super().__init__(
enum=PageNum.FLATPAK,
heading=wizard.heading_flatpak,
description=wizard.blurb_flatpak,
)
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),))
store.append((TMP_PATH,))
tree = SimpleTree(store, wizard.tree_column)
tree.set_halign(Gtk.Align.CENTER)
self.authorize_button = Gtk.Button(
label=wizard.button_flatpak, halign=Gtk.Align.CENTER
)
self.authorize_button.connect("clicked", self._on_authorize)
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")
class CompletionPage(EnumeratedWizardPage): class CompletionPage(EnumeratedWizardPage):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__( super().__init__(
@@ -417,7 +471,7 @@ class CompletionPage(EnumeratedWizardPage):
class Assistant(Gtk.Assistant): 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__() super().__init__()
if is_deck: if is_deck:
# NOTE: deemed to be "safe" dimensions that exclude taskbar size # NOTE: deemed to be "safe" dimensions that exclude taskbar size
@@ -436,6 +490,7 @@ class Assistant(Gtk.Assistant):
self.page_api = SteamValidationPage() self.page_api = SteamValidationPage()
self.page_prefs = PreferencesPage() self.page_prefs = PreferencesPage()
self.page_shortcuts = ShortcutCreationPage(XDG.shortcut) self.page_shortcuts = ShortcutCreationPage(XDG.shortcut)
self.page_flatpak = FlatpakOverridePage(xdg_paths)
self.page_completion = CompletionPage() self.page_completion = CompletionPage()
self.set_forward_page_func(self._advance_page) self.set_forward_page_func(self._advance_page)
@@ -455,6 +510,7 @@ class Assistant(Gtk.Assistant):
self.page_api, self.page_api,
self.page_prefs, self.page_prefs,
self.page_shortcuts, self.page_shortcuts,
self.page_flatpak,
self.page_completion, self.page_completion,
): ):
# NOTE: skip config migration page if no legacy config file # NOTE: skip config migration page if no legacy config file
@@ -475,7 +531,10 @@ class Assistant(Gtk.Assistant):
def write_config(self, config: dict[str, Any]) -> None: def write_config(self, config: dict[str, Any]) -> None:
write_json(config, self.config_path) 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] return self.pages[self.page_shortcuts]
def _advance_page(self, index: int) -> int: def _advance_page(self, index: int) -> int:
@@ -488,7 +547,7 @@ class Assistant(Gtk.Assistant):
sp = page.migrated_conf["default_steam_path"] sp = page.migrated_conf["default_steam_path"]
self.page_shortcuts.set_steam_path(sp) self.page_shortcuts.set_steam_path(sp)
self.config_values = self.page_migration.migrated_conf self.config_values = self.page_migration.migrated_conf
return self.get_final_page() return self.get_shortcut_page()
case SteamPathPage(): case SteamPathPage():
path = page.get_path_from_radio() path = page.get_path_from_radio()
self.config_values["default_steam_path"] = path self.config_values["default_steam_path"] = path
@@ -505,10 +564,22 @@ class Assistant(Gtk.Assistant):
) )
case ShortcutCreationPage(): case ShortcutCreationPage():
page.create_shortcuts() page.create_shortcuts()
if not self.needs_flatpak_override():
return self.get_completion_page()
case FlatpakOverridePage():
pass
case _: case _:
raise AttributeError("Trying to advance a non-canonical page") raise AttributeError("Trying to advance a non-canonical page")
return index + 1 return index + 1
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"]
):
return True
return False
def is_setup_complete(self) -> bool: def is_setup_complete(self) -> bool:
return self.setup_complete return self.setup_complete
@@ -634,6 +705,9 @@ class ShortcutCreationPage(OptionalPageMixin, EnumeratedWizardPage): # type: ig
def set_steam_path(self, path: Path) -> None: def set_steam_path(self, path: Path) -> None:
self.steam_path = path self.steam_path = path
def is_steam_checkbox_active(self) -> bool:
return self.steam_checkbox.get_active()
def create_shortcuts(self) -> None: def create_shortcuts(self) -> None:
# NOTE: best-effort, permissive even on failure (page is already marked as complete) # NOTE: best-effort, permissive even on failure (page is already marked as complete)
if self.steam_checkbox.get_active(): if self.steam_checkbox.get_active():
@@ -702,10 +776,10 @@ class SteamPathPage(EnumeratedWizardPage):
class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore 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__() super().__init__()
GLib.set_prgname(APP_NAME) GLib.set_prgname(APP_NAME)
self.win = Window(is_deck, XDG) self.win = Window(is_deck, XDG, xdg_paths)
Gtk.main() Gtk.main()
def is_setup_complete(self) -> int: def is_setup_complete(self) -> int:
@@ -713,9 +787,9 @@ class SetupWizard(ColorAwareApp, Gtk.Application): # type: ignore
class Window(Gtk.Window): 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) 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): class Emitter(GObject.GObject):
+30
View File
@@ -9,6 +9,9 @@ from dzgui.const.constants import (
APPID_DAYZ_EXP, APPID_DAYZ_EXP,
APPNAME_DAYZ, APPNAME_DAYZ,
APPNAME_DAYZ_EXP_HUMAN, APPNAME_DAYZ_EXP_HUMAN,
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
FLATPAK_STEAM_PATH,
NO_EXPAND, NO_EXPAND,
NO_FILL, NO_FILL,
NO_PADDING, NO_PADDING,
@@ -246,6 +249,26 @@ class ToggleField(Gtk.Box):
el.set_sensitive(state) 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
self.controller.update_config(context, cmd)
class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
def __init__(self, controller: "Controller"): def __init__(self, controller: "Controller"):
super().__init__( super().__init__(
@@ -274,6 +297,7 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
self.player_box.set_halign(Gtk.Align.START) self.player_box.set_halign(Gtk.Align.START)
self.player_box.get_children()[0].set_width_chars(30) # type: ignore self.player_box.get_children()[0].set_width_chars(30) # type: ignore
self.flatpak_toggle = FlatpakToggleField(self.controller)
self.fullscreen_toggle = ToggleField( self.fullscreen_toggle = ToggleField(
self.controller, self.controller,
strings.options.last_used, strings.options.last_used,
@@ -309,6 +333,12 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
[LeftLabel(options.start_tab), start_tab_hbox], [LeftLabel(options.start_tab), start_tab_hbox],
[LeftLabel(strings.options.name), self.player_box], [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_version_label = Gtk.Label(label=strings.null)
self.dayz_exp_version_label = Gtk.Label(label=strings.null) self.dayz_exp_version_label = Gtk.Label(label=strings.null)
+35
View File
@@ -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
+3 -2
View File
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [ authors = [
{name = "aclist"} {name = "aclist"}
] ]
version = "7.0.0b28" version = "7.0.0b29"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
license-files = ["LICENSE"] license-files = ["LICENSE"]
readme = "README.md" readme = "README.md"
@@ -110,10 +110,11 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } }
markers = [ markers = [
"apitest: checks remote endpoints", "apitest: checks remote endpoints",
"config: config file keys/values", "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", "mods: tests mod metadata/link creation",
"pefile: validate PE files", "pefile: validate PE files",
"post_install: requires a completed installation", "post_install: requires a completed installation",
"realconfig: depends on actual generated local config file",
"redact: log redaction mechanisms", "redact: log redaction mechanisms",
"slow: long-running tests", "slow: long-running tests",
"webtest: depends on remote endpoint", "webtest: depends on remote endpoint",
+43
View File
@@ -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())
+30
View File
@@ -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
+5
View File
@@ -1,3 +1,5 @@
import pytest
from dzgui.api.servers import Details from dzgui.api.servers import Details
from dzgui.views.dialogs.servers import ServerDetailsDialog from dzgui.views.dialogs.servers import ServerDetailsDialog
@@ -21,6 +23,7 @@ class MockController:
return None return None
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_html_escaping() -> None: def test_html_escaping() -> None:
name = "Test server" name = "Test server"
desc = "<Server description> Game & mods" desc = "<Server description> Game & mods"
@@ -31,5 +34,7 @@ def test_html_escaping() -> None:
controller = MockController() controller = MockController()
dialog = ServerDetailsDialog(controller, details) dialog = ServerDetailsDialog(controller, details)
dialog.destroy() dialog.destroy()
while Gtk.events_pending():
Gtk.main_iteration()
assert dialog.description.get_text() == desc assert dialog.description.get_text() == desc
+3 -1
View File
@@ -1,3 +1,4 @@
import pytest
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
@@ -6,10 +7,11 @@ from dzgui.const.constants import (
FLATPAK_ROOT_PATH, FLATPAK_ROOT_PATH,
FLATPAK_RUN_CMD, FLATPAK_RUN_CMD,
FLATPAK_SANDBOX, FLATPAK_SANDBOX,
STEAM_CMD,
) )
from dzgui.init import proc from dzgui.init import proc
pytestmark = pytest.mark.flatpak
class TestSteamDetection(unittest.TestCase): class TestSteamDetection(unittest.TestCase):
def test_flatpak_detection_with_multiple_running_apps(self): def test_flatpak_detection_with_multiple_running_apps(self):