Compare commits

...
Author SHA1 Message Date
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 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 141f96fb9f fix: allow using refresh button after timeout events (#484) 2026-10-22 15:59:05 +09:00
16 changed files with 113 additions and 35 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
- 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
- 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
@@ -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.
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
DZGUI is beta-quality software and is provided as-is.
+6 -5
View File
@@ -173,11 +173,12 @@ class Shortcuts:
self.save_shortcuts()
def save_shortcuts(self) -> None:
try:
backup = self.shortcuts_path.with_suffix(".vdf.bak")
shutil.copy(self.shortcuts_path, backup)
except Exception as e:
logger.critical(e)
if self.shortcuts_path.is_file():
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)
+5 -4
View File
@@ -13,7 +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.init.prereqs import has_flatpak_client, has_steam_client
from dzgui.const.constants import (
APPID_DAYZ,
APPID_DAYZ_EXP,
@@ -23,11 +23,10 @@ from dzgui.const.constants import (
DEBIAN_STEAM_PATH,
DEFAULT_STEAM_PATH,
DOWNLOADS_PATH,
FLATPAK_CMD,
FLATPAK_STEAM_PATH,
LIBRARYFOLDERS_PATH,
UBUNTU_STEAM_PATH,
REQUEST_TIMEOUT,
UBUNTU_STEAM_PATH,
VDF_PATH,
)
from dzgui.const.endpoints import (
@@ -84,7 +83,9 @@ 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:
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():
paths.append((path, human[path]))
+21 -16
View File
@@ -2,12 +2,17 @@ import logging
import os
import shutil
from importlib import resources
from pathlib import Path
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.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
@@ -26,7 +31,7 @@ from dzgui.util.dirs import make_parents
from dzgui.util.localize import set_locale
from dzgui.util.map_count import test_map_count
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.dialogs.boot import BootWindow
@@ -99,26 +104,21 @@ def copy_bare_configs(config: "Path", resolution: "Path") -> tuple[bool, bool]:
return (config_changed, state_changed)
@deprecated("Currently unused")
def load_steam_icon(config: Path) -> None:
def load_steam_icon() -> None:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa
default = lookup(config, Preferences.DEFAULT)
icon_path = Path(default).joinpath(STEAM_ICONS_PATH)
try:
traversable = resources.files(APP_NAME_LOWER).joinpath(ICONS_PATH)
icons = Path(str(traversable))
theme = Gtk.IconTheme.get_default()
paths = theme.get_search_path()
theme.set_search_path(paths + [str(icon_path)])
paths = theme.get_search_path()
icons = theme.list_icons(None)
if STEAM_ICON not in icons:
logger.warning(steam_icon_missing)
theme.set_search_path(paths + [str(icons)])
except Exception as e:
print(e)
logger.warning(e)
logger.critical(e)
def load_gui(version: str, is_debug: bool) -> None:
@@ -161,7 +161,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)
@@ -180,5 +184,6 @@ def load_gui(version: str, is_debug: bool) -> None:
use_miles=use_miles,
)
print(boot.all_ok)
load_steam_icon()
app = App(prefs)
app.run(None)
+2 -2
View File
@@ -51,7 +51,7 @@ INPUT_KEYBOARD = "input-keyboard-symbolic"
LIST_ADD = "list-add-symbolic"
REFRESH_ICON = "view-refresh-symbolic"
SEARCH_ICON = "system-search-symbolic"
STEAM_ICON = "steam_tray_mono"
STEAM_ICON = "steam-tray-symbolic"
VIEW_CONCEAL = "view-conceal-symbolic"
VIEW_REVEAL = "view-reveal-symbolic"
WARNING = "dialog-warning-symbolic"
@@ -80,6 +80,7 @@ DEBUG_LOG = f"{APP_NAME}_DEBUG.LOG"
SYSTEM_LOG = f"{APP_NAME}_SYSTEM.LOG"
CHANGELOG_PATH = "data/CHANGELOG.md"
IMAGES_PATH = "data/images"
ICONS_PATH = "data/images/gtk"
HERO_PATH = "data/images/_hero.png"
CSS_PATH = "data/app.css"
@@ -89,7 +90,6 @@ FLATPAK_ROOT_PATH = ".var/app/com.valvesoftware.Steam"
FLATPAK_STEAM_PATH = FLATPAK_ROOT_PATH + "/data/Steam"
UBUNTU_STEAM_PATH = ".steam/steam"
DEBIAN_STEAM_PATH = ".steam/debian-installation"
STEAM_ICONS_PATH = "public"
LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG")
+4
View File
@@ -111,6 +111,10 @@ class Emitter(GObject.GObject):
def check_toggled(self, label: str, state: bool) -> None:
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,))
def servers_loaded(self, tag: "ServerTab") -> None:
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
View File
@@ -3,11 +3,14 @@ import shutil
import subprocess
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)
def has_flatpak_client() -> bool:
if is_sandboxed():
return True
try:
res = subprocess.run(
[FLATPAK_CMD, "list", "--columns=application"],
+3
View File
@@ -92,6 +92,7 @@ class ServerModelManager:
job = Servers.query_api
params = Servers.params
servers = []
with ThreadPoolExecutor() as executor:
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
for future in as_completed(futures):
@@ -414,6 +415,8 @@ class ServerModelManager:
# TODO: disable map, keyword, and filter widgets if model is None
# signal driven (servers_empty, servers_failed_to_load)
self.emitter.emit("servers_timed_out")
if self.preserve_on_fail is False:
self.tv.set_model(None)
filter_man = self.tv.get_filter_man()
+1 -1
View File
@@ -19,7 +19,7 @@ desc_flatpak_path = (
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>."
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"
+1 -1
View File
@@ -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(
+10 -4
View File
@@ -163,18 +163,24 @@ class RefreshButton(IconTextButton):
self.set_tooltip_text(atomic_buttons.refresh_tooltip)
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:
"""Spawned in a thread"""
if self.controller.get_active_treeview().is_loaded() is False:
return
self.is_clicked = True
self.controller.refresh_tree()
# TODO: get server tab enum
# 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:
return
if self.loading:
+2 -1
View File
@@ -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",
+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())
+5
View File
@@ -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 = "<Server description> 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
+3
View File
@@ -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):