From 2479cbd41b42f25832d6385dbd181916e3582f5a Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:03:50 +0900 Subject: [PATCH 1/7] change: parse repository root --- scripts/concat_licenses.py | 4 +++- scripts/copy_changelog.py | 9 +++++---- scripts/parse_toml.py | 5 ++--- scripts/prebuild.py | 4 +++- scripts/release.py | 3 ++- scripts/scrot.py | 10 +--------- scripts/shared/__init__.py | 11 +++++++++++ 7 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 scripts/shared/__init__.py diff --git a/scripts/concat_licenses.py b/scripts/concat_licenses.py index 56f80a6..5008b5e 100644 --- a/scripts/concat_licenses.py +++ b/scripts/concat_licenses.py @@ -1,5 +1,7 @@ from pathlib import Path +from shared import get_git_root + BOILERPLATE = """ This file includes the combined licenses to the following components: @@ -40,7 +42,7 @@ class CombinedLicense: def concat_license() -> str: combined = CombinedLicense() - root = Path(__file__).resolve().parents[1] + root = get_git_root() licenses = root.joinpath("docs/licenses") main_license = root.joinpath(LICENSE_FILE) diff --git a/scripts/copy_changelog.py b/scripts/copy_changelog.py index c32a4cb..e900469 100644 --- a/scripts/copy_changelog.py +++ b/scripts/copy_changelog.py @@ -1,10 +1,11 @@ import shutil import subprocess -from pathlib import Path -par = Path(__file__).parent.parent -clog = par / "CHANGELOG.md" -target = par / "dzgui/data/CHANGELOG.md" +from shared import get_git_root + +root = get_git_root() +clog = root.joinpath("CHANGELOG.md") +target = root.joinpath("dzgui/data/CHANGELOG.md") shutil.copy(clog, target) diff --git a/scripts/parse_toml.py b/scripts/parse_toml.py index dc0afe0..e4b5210 100644 --- a/scripts/parse_toml.py +++ b/scripts/parse_toml.py @@ -1,10 +1,9 @@ import tomllib -from pathlib import Path - -root = Path(__file__).resolve().parents[1] +from shared import get_git_root def get_entrypoint() -> None: + root = get_git_root() p = root.joinpath("pyproject.toml") with open(p, "rb") as f: t = tomllib.load(f) diff --git a/scripts/prebuild.py b/scripts/prebuild.py index 8700b62..162f500 100644 --- a/scripts/prebuild.py +++ b/scripts/prebuild.py @@ -6,11 +6,13 @@ import tarfile from pathlib import Path from urllib.parse import unquote +from shared import get_git_root + BUILD = "3.13.9" DATE = "20251014" CPYTHON_BUILD = f"cpython-{BUILD}" -root = Path(__file__).resolve().parents[1] +root = get_git_root() build_dir = root.joinpath("build") cpython_dir = build_dir.joinpath(CPYTHON_BUILD) diff --git a/scripts/release.py b/scripts/release.py index 2eb8f02..2afaf47 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -9,6 +9,7 @@ from pathlib import Path from concat_licenses import concat_license from parse_toml import get_entrypoint from prebuild import get_pyapp, rebuild_cpython +from shared import get_git_root def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo: @@ -17,7 +18,7 @@ def update_uname(info: tarfile.TarInfo) -> tarfile.TarInfo: return info -root = Path(__file__).resolve().parents[1] +root = get_git_root() dist_dir = root.joinpath("dist") build_dir = root.joinpath("build") diff --git a/scripts/scrot.py b/scripts/scrot.py index 9dbf04e..2209b9a 100644 --- a/scripts/scrot.py +++ b/scripts/scrot.py @@ -2,15 +2,7 @@ import subprocess from pathlib import Path from dzgui.app_init import build_gui - - -def get_git_root() -> Path: - proc = subprocess.run( - ["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True - ) - if proc.returncode != 0: - raise OSError("Failed to parse git root") - return Path(proc.stdout.rstrip()) +from shared import get_git_root def get_image_path() -> Path: diff --git a/scripts/shared/__init__.py b/scripts/shared/__init__.py new file mode 100644 index 0000000..f638b7b --- /dev/null +++ b/scripts/shared/__init__.py @@ -0,0 +1,11 @@ +import subprocess +from pathlib import Path + + +def get_git_root() -> Path: + proc = subprocess.run( + ["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True + ) + if proc.returncode != 0: + raise OSError("Failed to parse git root") + return Path(proc.stdout.rstrip()) From 03fa707305a070934140fbb9dde99d1dd2b1d5aa Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:04:56 +0900 Subject: [PATCH 2/7] chore: remove unused imports --- scripts/concat_licenses.py | 2 -- scripts/prebuild.py | 1 - scripts/scrot.py | 1 - 3 files changed, 4 deletions(-) diff --git a/scripts/concat_licenses.py b/scripts/concat_licenses.py index 5008b5e..c1f149c 100644 --- a/scripts/concat_licenses.py +++ b/scripts/concat_licenses.py @@ -1,5 +1,3 @@ -from pathlib import Path - from shared import get_git_root BOILERPLATE = """ diff --git a/scripts/prebuild.py b/scripts/prebuild.py index 162f500..143bc56 100644 --- a/scripts/prebuild.py +++ b/scripts/prebuild.py @@ -3,7 +3,6 @@ import requests import subprocess import tarfile -from pathlib import Path from urllib.parse import unquote from shared import get_git_root diff --git a/scripts/scrot.py b/scripts/scrot.py index 2209b9a..3800197 100644 --- a/scripts/scrot.py +++ b/scripts/scrot.py @@ -1,4 +1,3 @@ -import subprocess from pathlib import Path from dzgui.app_init import build_gui From cc708c64b299e82ca52143da72bea5626dc87414 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:19:32 +0900 Subject: [PATCH 3/7] docs: add history file --- HISTORY.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 HISTORY.md diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..019b55e --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,119 @@ +# History of the project +This file collects notable milestones and events related to DZGUI, mainly around DZGUI as a framework and the history of development. +A chronological summary of features can be found in CHANGELOG.md. + +## 2021 +### 2021-12-03: DayZ adds support for Battleye over Proton +DayZ was among a handful of games with whitelisted anti-cheat support under Valve's emerging WINE-based Proton compatibility layer. +While adding anti-cheat compatibility at a technical level, this change did not actually allow the client to connect to servers running complex modlists. + +Development of the precursor to DZGUI began at around this time. + +### 2021-12-12: initial version of DZTUI is created +DZTUI, a Bash script and terminal user interface, was the precursor to DZGUI. +This was an initial attempt at bootstrapping the Steam client to handle mods correctly when launching DayZ. +Due to user interest, features continued to be added. This early tool is archived under the 'dztui' branch. + +## 2022 +### 2022-05-09: addition of zenity GUI and project name change (DZGUI) +With the introduction of the Steam Deck, more users wanted a graphical user interface +to interact with DZTUI. Being that DZTUI was purely a Bash script, a method of +drawing menus was needed, so the 'zenity' package was used to create prefabricated dialogs. +While this met the minimum requirement to show prompts and selectable options, no rich customization was possible. + +The introduction of zenity as a dependency marked the shift from "DZTUI" to "DZGUI". + +Throughout 2022, the limits of zenity for DZGUI's purposes were tested, particularly in the context of SteamOS. +This led to the discovery of a number of bugs in zenity, GTK, and SteamOS, which were reported up to their respective channels. +While this was improving the SteamOS user experience, it started to become clear that a fully custom UI was needed. + +This barebones version of DZGUI sufficed for some time, with the Bash script and some standalone Python modules doing the heavy lifting, +and this period served to surface a number of bugs and interesting interactions with the Steam client. + +## 2023 +### 2023-11-23: Python 3.10 dependency +Prior to this time, DZGUI open-endedly allowed you to use any Python version for its modules, without any significant handling for +the idiosyncrasies of different versions. A minimum requirement of Python 3.10 was thus instituted to normalize expected behavior. + +Throughout 2023, various features related to server browsing were added, such as filters and more granular search parameters. + +There were still a lot of unknowns at this time, so things were being resolved through trial and error. + +## 2024 +### 2024-01-31: DZGUI 5 API introduces GTK UI +Using zenity as a simple GUI had exposed some bugs and limitations in that package, particularly when trying to push huge lists of server records +into tables and sorting them in a performant manner. + +Around this time it became clear that a more robust UI framework was needed. While keeping the original Bash script and Python modules for +background processing, PyGObject (GTK) was added to the project in order to ship a custom UI that could be designed from the ground up. + +However, it should be noted that at this point, the DZGUI logic itself was still a (increasingly large) Bash script to which a GTK UI had been stitched on. +These were totally separate components, with UI actions on the GTK side triggering calls to Bash functions in an overly complex helper file. +The UI was basically a thin client around the old Bash logic, but this was still a far cry from an integrated application with proper signals and dedicated components. + +Effectively, DZGUI 5 through 6 were a stepping stone that set the groundwork to go from DZGUI 4 (pure Bash script) to +DZGUI 7 (standalone Python application). Today, little of DZGUI 5's original logic remains outside of the basic UI grid. + +### 2024-10-15: Frostline DLC released, widespread parsing problems +DayZ's first DLC introduced a new format for server responses that packed extra metadata into the A2S_RULES format. +Upon its release, all third-party DayZ launchers and server listing web sites were unable to parse the mod information being returned from Frostline servers. + +For a period of two weeks, this remained unsolved and various attempts were made to understand the updated specification. + +### 2024-10-30: DZGUI hotfix adds support for Frostline servers +DZGUI was the first third-party tool to fix parsing for Frostline servers and restore functionality. +This was a small milestone, because it meant DZGUI was evolving from a "dumb" launcher to a featureful tool +touching upon and improving all aspects of the end-to-end DayZ experience. + +Particularly around this time, the sheer diversity of needs of modders, server operators, and players started to become clear. +Users had highly specific and diverse use cases for setting up their game environment, and requested a number of features to support that on +the DZGUI side. + +## 2025 +### 2025-01-17: DZGUI 6 API +With DZGUI 6, the difficult decision to enforce a Python 3.13 dependency was made. +While the previous dependency was Python 3.10+, it was deemed better to develop against and target identical versions. + +However, due to the vagaries of managing Python environments on end-user systems, this change added unnecessary complexity and frustration. +Users had to properly set up virtual environments, install the relevant dependencies, and create a wrapper script to run this version, whereas +previous versions had essentially been point and click. + +### 2025-10-23: DZGUI 7 API and the "Ship of Theseus" +It had become clear that the situation above was untenable and the legacy Bash script was unmaintainable given the project's enlarged scope. +The lack of a robust testing or typehinting framework made it difficult to diagnose rare bugs. + +Shipping a standalone, turnkey application with zero or minimal dependencies was the most urgent priority. +DZGUI was still carrying around small bits and pieces of the old Bash script logic, so the decision was made to +refactor the application into a pure Python + GTK tool. This would effectively be a total rewrite of all logic, +only preserving the most minimal skeleton of the original UI layout as a reference. + +Over the course of a year, a wholesale "Ship of Theseus" rewrite took place. +Nearly every component of DZGUI 6, both API and UI side, was taken apart and replaced. + +A soft alpha and beta phase were used to iterate on this concept further. DZGUI 7 was increasingly recommended to users +as the preferred choice. This allowed for keeping the established userbase on the (relatively) stable DZGUI 6 spec, while building up +the new spec and making it available to more users as features were elaborated. + +Through late 2025, several LLM-generated applications that recycled significant parts of DZGUI's code without the applicable GPL license were found. +The maintainers of those projects were advised to excise the offending segments or apply the necessary GPL licenses. + +## 2026 +### 2026-05-17: Switch to standalone releases +The DZGUI 7 beta starts shipping standalone glibc builds with an integrated setup wizard and installer. + +# TODO: remarks on DZGUI 6 stable vs testing branch logic and "over the air" updates vs standalone builds +# TODO: note archived branch names + +### 2026-10-27: 500 tickets closed +The milestone of 500 tickets/PRs being closed was reached. + +2026 was by far DZGUI's most intensive year by commits, perhaps suggesting the scope of the rewrite: + +- 2021: 16 +- 2022: 434 +- 2023: 322 +- 2024: 347 +- 2025: 426 +- 2026: 1300 + +DZGUI 7 is scheduled to release as a stable version within the year. From 72e3d95808fe10b65e3f3e643426c3dccc87e3b7 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:48:10 +0900 Subject: [PATCH 4/7] change: normalize icon names --- dzgui/const/constants.py | 22 ---------------- dzgui/const/icons.py | 20 +++++++++++++++ dzgui/views/components/buttons.py | 34 ++++++++++++------------- dzgui/views/components/connect_panel.py | 9 ++++--- dzgui/views/components/entry.py | 8 +++--- dzgui/views/components/eventbox.py | 4 +-- dzgui/views/components/filter_panel.py | 4 +-- dzgui/views/components/misc.py | 5 ++-- dzgui/views/dialogs/calc.py | 6 ++--- dzgui/views/dialogs/servers.py | 4 +-- dzgui/views/pages/offline.py | 10 +++----- dzgui/views/pages/options.py | 9 +++---- dzgui/views/pages/preconnect.py | 10 ++++---- 13 files changed, 71 insertions(+), 74 deletions(-) create mode 100644 dzgui/const/icons.py diff --git a/dzgui/const/constants.py b/dzgui/const/constants.py index aba522f..18affdb 100644 --- a/dzgui/const/constants.py +++ b/dzgui/const/constants.py @@ -37,28 +37,6 @@ HEX_GREEN = "#32CD32" HEX_RED = "#FF0000" HEX_ORANGE = "#FFAC1C" -# TODO: normalize icon names -CALCULATOR = "accessories-calculator-symbolic" -CARET_DOWN = "go-down-symbolic" -CARET_UP = "go-up-symbolic" -CLIPBOARD = "edit-copy-symbolic" -CLOSE = "window-close-symbolic" -FOLDER = "folder-symbolic" -EDIT_DELETE = "edit-delete-symbolic" -ERROR = "dialog-error-symbolic" -HELP_BUBBLE = "help-about-symbolic" -INPUT_KEYBOARD = "input-keyboard-symbolic" -LIST_ADD = "list-add-symbolic" -REFRESH_ICON = "view-refresh-symbolic" -SEARCH_ICON = "system-search-symbolic" -STEAM_ICON = "steam-tray-symbolic" -VIEW_CONCEAL = "view-conceal-symbolic" -VIEW_REVEAL = "view-reveal-symbolic" -WARNING = "dialog-warning-symbolic" -WEB_BROWSER = "web-browser-symbolic" -DAY_ICON = "weather-clear-symbolic" -NIGHT_ICON = "weather-clear-night-symbolic" - SEPARATOR = "SEPARATOR" # TODO: drop diff --git a/dzgui/const/icons.py b/dzgui/const/icons.py new file mode 100644 index 0000000..176e73e --- /dev/null +++ b/dzgui/const/icons.py @@ -0,0 +1,20 @@ +ICON_CALCULATOR = "accessories-calculator-symbolic" +ICON_CARET_DOWN = "go-down-symbolic" +ICON_CARET_UP = "go-up-symbolic" +ICON_CLIPBOARD = "edit-copy-symbolic" +ICON_CLOSE = "window-close-symbolic" +ICON_DAY = "weather-clear-symbolic" +ICON_EDIT_DELETE = "edit-delete-symbolic" +ICON_ERROR = "dialog-error-symbolic" +ICON_FOLDER = "folder-symbolic" +ICON_HELP_BUBBLE = "help-about-symbolic" +ICON_INPUT_KEYBOARD = "input-keyboard-symbolic" +ICON_LIST_ADD = "list-add-symbolic" +ICON_NIGHT = "weather-clear-night-symbolic" +ICON_REFRESH = "view-refresh-symbolic" +ICON_SEARCH = "system-search-symbolic" +ICON_STEAM = "steam-tray-symbolic" +ICON_VIEW_CONCEAL = "view-conceal-symbolic" +ICON_VIEW_REVEAL = "view-reveal-symbolic" +ICON_WARNING = "dialog-warning-symbolic" +ICON_WEB_BROWSER = "web-browser-symbolic" diff --git a/dzgui/views/components/buttons.py b/dzgui/views/components/buttons.py index bb3d45d..2135892 100644 --- a/dzgui/views/components/buttons.py +++ b/dzgui/views/components/buttons.py @@ -7,15 +7,15 @@ from dzgui.util.strings import ( alert_button_tooltip, atomic_buttons, ) -from dzgui.const.constants import ( - CLIPBOARD, - CLOSE, - INPUT_KEYBOARD, - LIST_ADD, - REFRESH_ICON, - STEAM_ICON, - WARNING, - WEB_BROWSER, +from dzgui.const.icons import ( + ICON_CLIPBOARD, + ICON_CLOSE, + ICON_INPUT_KEYBOARD, + ICON_LIST_ADD, + ICON_REFRESH, + ICON_STEAM, + ICON_WARNING, + ICON_WEB_BROWSER, ) from dzgui.strings import connect_panel @@ -102,7 +102,7 @@ class LargeIconTextButton(IconButton): class ClipboardButton(IconTextButton): def __init__(self, controller: Union["Controller", None], func: Callable) -> None: - super().__init__(CLIPBOARD, atomic_buttons.copy) + super().__init__(ICON_CLIPBOARD, atomic_buttons.copy) self.controller = controller self.connect("clicked", self._on_button_clicked, func) @@ -131,7 +131,7 @@ class CopyIpButton(ClipboardButton): class CloseButton(IconTextButton): def __init__(self, label: str) -> None: - super().__init__(icon=CLOSE, label=label) + super().__init__(icon=ICON_CLOSE, label=label) # TODO: strings self.set_tooltip_text("Unset this server as favorite") @@ -139,14 +139,14 @@ class CloseButton(IconTextButton): class WebButton(IconTextButton): def __init__(self, label: str) -> None: - super().__init__(icon=WEB_BROWSER, label=label) + super().__init__(icon=ICON_WEB_BROWSER, label=label) pass class RefreshButton(IconTextButton): def __init__(self, controller: "Controller") -> None: super().__init__( - icon=REFRESH_ICON, + icon=ICON_REFRESH, label=atomic_buttons.refresh, ) @@ -211,7 +211,7 @@ class RefreshButton(IconTextButton): class KeysButton(IconTextButton): def __init__(self, controller: "Controller") -> None: - super().__init__(icon=INPUT_KEYBOARD, label=atomic_buttons.keys) + super().__init__(icon=ICON_INPUT_KEYBOARD, label=atomic_buttons.keys) self.controller = controller self.set_margin_top(10) @@ -228,7 +228,7 @@ class KeysButton(IconTextButton): class SteamConnectButton(LargeIconTextButton): def __init__(self) -> None: - super().__init__(icon=STEAM_ICON, label=connect_panel.connect_button) + super().__init__(icon=ICON_STEAM, label=connect_panel.connect_button) self.set_tooltip_text(connect_panel.connect_tooltip) @@ -248,7 +248,7 @@ class SteamWorkshopButton(SteamTextButton): class AddButton(IconTextButton): def __init__(self) -> None: - super().__init__(icon=LIST_ADD, label=connect_panel.add_button) + super().__init__(icon=ICON_LIST_ADD, label=connect_panel.add_button) self.set_tooltip_text(connect_panel.add_tooltip) @@ -267,7 +267,7 @@ class LoggerAlertsButton(IconTextButton): separator = "," concat = warnings_text + separator + errors_text super().__init__( - icon=WARNING, + icon=ICON_WARNING, label=f"Loaded with{concat}", ) self.set_halign(Gtk.Align.END) diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index 17d0a71..2884baa 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -1,6 +1,7 @@ from typing import Self, TYPE_CHECKING -from dzgui.const.constants import CARET_DOWN, CARET_UP, UDP_PORT +from dzgui.const.icons import ICON_CARET_DOWN, ICON_CARET_UP +from dzgui.const.constants import UDP_PORT from dzgui.model.servers import ServerModelManager from dzgui.strings import connect_panel from dzgui.util.keys import is_ctrl_mask @@ -30,7 +31,7 @@ ROWS = 1 class DockButton(IconButton): def __init__(self, child: Gtk.Widget) -> None: - super().__init__(CARET_DOWN) + super().__init__(ICON_CARET_DOWN) self.expanded = True self.child = child @@ -43,12 +44,12 @@ class DockButton(IconButton): self.set_tooltip_text(self.collapse_text) def collapse(self) -> None: - self.set_image(Icon(CARET_UP)) + self.set_image(Icon(ICON_CARET_UP)) self.child.hide() self.set_tooltip_text(self.expand_text) def expand(self) -> None: - self.set_image(Icon(CARET_DOWN)) + self.set_image(Icon(ICON_CARET_DOWN)) self.child.show() self.set_tooltip_text(self.collapse_text) diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index e1b3e7e..1f7fe0f 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -1,7 +1,7 @@ from typing import Any, Callable, TYPE_CHECKING from dzgui.api.servers import validate_ip -from dzgui.const.constants import VIEW_CONCEAL, VIEW_REVEAL +from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.util.css import add_class, remove_class from dzgui.strings import connect_panel from dzgui.strings.errors import api_popover @@ -147,7 +147,7 @@ class APIEntry(Gtk.Box): self.entry = Gtk.Entry( width_chars=60, hexpand=True, placeholder_text="Enter API key" ) - self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL) + self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, ICON_VIEW_REVEAL) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.set_visibility(False) self.entry.connect("changed", self._on_text_changed) @@ -206,9 +206,9 @@ class APIEntry(Gtk.Box): ) -> None: visible = widget.get_visibility() if visible: - icon, state = VIEW_REVEAL, False + icon, state = ICON_VIEW_REVEAL, False else: - icon, state = VIEW_CONCEAL, True + icon, state = ICON_VIEW_CONCEAL, True widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_visibility(state) diff --git a/dzgui/views/components/eventbox.py b/dzgui/views/components/eventbox.py index 5e06127..5ef530d 100644 --- a/dzgui/views/components/eventbox.py +++ b/dzgui/views/components/eventbox.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING from dzgui.const.enum import NotebookPage -from dzgui.const.constants import HELP_BUBBLE +from dzgui.const.icons import ICON_HELP_BUBBLE import gi @@ -20,7 +20,7 @@ class InfoEventBox(Gtk.EventBox): self.text = text self.icon = Gtk.Image.new_from_icon_name( - HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR + ICON_HELP_BUBBLE, Gtk.IconSize.LARGE_TOOLBAR ) self.icon.set_opacity(0.8) box = Gtk.Box() diff --git a/dzgui/views/components/filter_panel.py b/dzgui/views/components/filter_panel.py index 75c51e9..9eae88e 100644 --- a/dzgui/views/components/filter_panel.py +++ b/dzgui/views/components/filter_panel.py @@ -7,8 +7,8 @@ from dzgui.const.constants import ( NO_EXPAND, NO_FILL, NO_PADDING, - SEARCH_ICON, ) +from dzgui.const.icons import ICON_SEARCH from dzgui.util.format import format_exception from dzgui.util.strings import all_maps from dzgui.views.components.maps_combo import MapsCombo @@ -109,7 +109,7 @@ class KeywordEntry(Gtk.Entry): self.connect("key-press-event", self._on_keypress) self.connect("icon-release", lambda *args: self.activate()) - self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, SEARCH_ICON) + self.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, ICON_SEARCH) self.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.emitter.connect("load_maps", self._on_maps_loaded) diff --git a/dzgui/views/components/misc.py b/dzgui/views/components/misc.py index cdc3f49..80e694e 100644 --- a/dzgui/views/components/misc.py +++ b/dzgui/views/components/misc.py @@ -1,7 +1,8 @@ from typing import Self from warnings import deprecated -from dzgui.const.constants import ERROR, FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD +from dzgui.const.constants import FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD +from dzgui.const.icons import ICON_ERROR from dzgui.init.prereqs import has_steam_client, has_flatpak_client from dzgui.strings import options from dzgui.views.components.box import HBox @@ -73,7 +74,7 @@ class ErrorPopover(Gtk.Popover): self.hbox = HBox() self.label = Gtk.Label(label="", margin_start=10, margin_end=10) - error_icon = Icon(ERROR, margin_start=10) + error_icon = Icon(ICON_ERROR, margin_start=10) self.hbox.extend([error_icon, self.label]) self.add(self.hbox) self.show_all() diff --git a/dzgui/views/dialogs/calc.py b/dzgui/views/dialogs/calc.py index 979cb0a..0b6501e 100644 --- a/dzgui/views/dialogs/calc.py +++ b/dzgui/views/dialogs/calc.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from datetime import datetime, time, timedelta from typing import Literal -from dzgui.const.constants import DAY_ICON, NIGHT_ICON +from dzgui.const.icons import ICON_DAY, ICON_NIGHT from dzgui.strings import calc from dzgui.views.components.icon import Icon from dzgui.views.components.labels import BoldLabel @@ -385,7 +385,7 @@ class TimePickerFrame(Gtk.Frame): h1 = BoldLabel(calc.heading_server_time) h2 = BoldLabel(calc.heading_target_time) - self.icon_indicator = Icon(DAY_ICON) + self.icon_indicator = Icon(ICON_DAY) self.update_server_icon() server_time_box = HBox(10) @@ -411,7 +411,7 @@ class TimePickerFrame(Gtk.Frame): self.add(flowbox) def update_server_icon(self) -> None: - icon = DAY_ICON if self.server_time.is_day() else NIGHT_ICON + icon = ICON_DAY if self.server_time.is_day() else ICON_NIGHT self.icon_indicator.set_icon_name(icon) def _on_server_clock_increment(self, emitter: Emitter, time_now: datetime) -> None: diff --git a/dzgui/views/dialogs/servers.py b/dzgui/views/dialogs/servers.py index 6166aee..b58d352 100644 --- a/dzgui/views/dialogs/servers.py +++ b/dzgui/views/dialogs/servers.py @@ -2,7 +2,7 @@ import gi import html from typing import Self, TYPE_CHECKING -from dzgui.const.constants import CALCULATOR +from dzgui.const.icons import ICON_CALCULATOR from dzgui.const.enum import ContextMenuGroup from dzgui.model.model_factory import ModelFactory from dzgui.util import css @@ -110,7 +110,7 @@ class ServerDetailsDialog(ServerDialog): text = format_hyperlinks(text) self.description.set_markup(html.escape(text)) - calc_button = IconTextButton(CALCULATOR, dialogs.toggle_calc) + calc_button = IconTextButton(ICON_CALCULATOR, dialogs.toggle_calc) calc_button.connect("clicked", self._on_calc_pressed) calc_button.set_halign(Gtk.Align.END) calc_button.set_valign(Gtk.Align.START) diff --git a/dzgui/views/pages/offline.py b/dzgui/views/pages/offline.py index b0200b0..ceb5686 100644 --- a/dzgui/views/pages/offline.py +++ b/dzgui/views/pages/offline.py @@ -3,14 +3,12 @@ from enum import Enum from typing import Self, TYPE_CHECKING, Union from dzgui.util import css +from dzgui.const.icons import ICON_EDIT_DELETE, ICON_FOLDER, ICON_WARNING from dzgui.const.constants import ( APPID_DAYZ, APPID_DAYZ_EXP, APPNAME_DAYZ, APPNAME_DAYZ_EXP_HUMAN, - EDIT_DELETE, - FOLDER, - WARNING, ) from dzgui.const.enum import ContextMenuGroup, NotebookPage from dzgui.managers.offline import OfflineManager @@ -87,7 +85,7 @@ class FolderHBox(HBox): self.emitter = controller.get_emitter() self.eb = InfoEventBox(eb_text, controller) - self.button = IconTextButton(FOLDER, btn_label, Gtk.PositionType.LEFT) + self.button = IconTextButton(ICON_FOLDER, btn_label, Gtk.PositionType.LEFT) self.button.set_halign(Gtk.Align.START) self.button.set_image_position(Gtk.PositionType.LEFT) @@ -99,7 +97,7 @@ class FolderHBox(HBox): self.label = Gtk.Label() self.scrolled_label.add(self.label) - self.unset_button = IconTextButton(EDIT_DELETE, offline.unset_button) + self.unset_button = IconTextButton(ICON_EDIT_DELETE, offline.unset_button) self.unset_button.connect("clicked", self._on_unset_clicked) self.spinner = Gtk.Spinner() @@ -195,7 +193,7 @@ class ModFrame(HeadingFrame): self.tree_vbox.extend([self.scrolled, self.status]) self.no_mods = HBox(spacing=10) - no_mods_icon = Icon(WARNING, margin_start=10) + no_mods_icon = Icon(ICON_WARNING, margin_start=10) no_mods_label = Gtk.Label(label=offline.no_local_mods, halign=Gtk.Align.START) self.no_mods.extend([no_mods_icon, no_mods_label]) diff --git a/dzgui/views/pages/options.py b/dzgui/views/pages/options.py index b5633ca..e4d5f18 100644 --- a/dzgui/views/pages/options.py +++ b/dzgui/views/pages/options.py @@ -16,9 +16,8 @@ from dzgui.const.constants import ( NO_EXPAND, NO_FILL, NO_PADDING, - VIEW_CONCEAL, - VIEW_REVEAL, ) +from dzgui.const.icons import ICON_VIEW_CONCEAL, ICON_VIEW_REVEAL from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.enum import Preferences, ServerTab from dzgui.strings import options @@ -67,7 +66,7 @@ class SubmitField(Gtk.Box): # TODO: audit for all applicable callbacks if private: self.entry.set_icon_from_icon_name( - Gtk.EntryIconPosition.SECONDARY, VIEW_REVEAL + Gtk.EntryIconPosition.SECONDARY, ICON_VIEW_REVEAL ) self.entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, True) self.entry.connect("icon-release", self._on_icon_release) @@ -131,9 +130,9 @@ class SubmitField(Gtk.Box): ) -> None: visible = widget.get_visibility() if visible: - icon, state = VIEW_REVEAL, False + icon, state = ICON_VIEW_REVEAL, False else: - icon, state = VIEW_CONCEAL, True + icon, state = ICON_VIEW_CONCEAL, True widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.set_visibility(state) diff --git a/dzgui/views/pages/preconnect.py b/dzgui/views/pages/preconnect.py index b737ff0..a094fc7 100644 --- a/dzgui/views/pages/preconnect.py +++ b/dzgui/views/pages/preconnect.py @@ -1,9 +1,9 @@ from dataclasses import dataclass from typing import Self, Sequence, TYPE_CHECKING -from dzgui.const.constants import ( - ERROR, - WARNING, +from dzgui.const.icons import ( + ICON_ERROR, + ICON_WARNING, ) from dzgui.const.enum import NotebookPage from dzgui.util.css import add_class @@ -166,7 +166,7 @@ class PreConnectionAssistant(Gtk.Box): # TODO: abstract into components self.warning_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.warning_tree = MaskedTree(WARNING) + self.warning_tree = MaskedTree(ICON_WARNING) self.warning_placeholder = Placeholder(preconnect.placeholder_no_warnings) self.warning_box.add(self.warning_tree) self.warning_box.add(self.warning_placeholder) @@ -175,7 +175,7 @@ class PreConnectionAssistant(Gtk.Box): ) self.error_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.error_tree = MaskedTree(ERROR) + self.error_tree = MaskedTree(ICON_ERROR) self.error_placeholder = Placeholder(preconnect.no_errors) self.error_box.add(self.error_tree) self.error_box.add(self.error_placeholder) From 49298d99430d06311a32e14acb2526f30a2ca23e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Mon, 9 Nov 2026 23:51:19 +0900 Subject: [PATCH 5/7] change: normalize endpoint names --- dzgui/api/steam.py | 12 ++++++------ dzgui/const/endpoints.py | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dzgui/api/steam.py b/dzgui/api/steam.py index 4a7d1e8..23357f2 100644 --- a/dzgui/api/steam.py +++ b/dzgui/api/steam.py @@ -31,10 +31,10 @@ from dzgui.const.constants import ( VDF_PATH, ) from dzgui.const.endpoints import ( - APP_DETAILS, - SUB_ENDPOINT, + STEAM_APP_DETAILS, STEAM_PUBLISHED_FILES, - UNSUB_ENDPOINT, + STEAM_SUB, + STEAM_UNSUB, ) from dzgui.strings import wizard from dzgui.strings.errors import api_validation_error, workshop_endpoint_error @@ -310,11 +310,11 @@ def update_workshop(key: str, mod: int, endpoint: str) -> None: def subscribe(key: str, mod: int) -> None: - update_workshop(key, mod, SUB_ENDPOINT) + update_workshop(key, mod, STEAM_SUB) def unsubscribe(key: str, mod: int) -> None: - update_workshop(key, mod, UNSUB_ENDPOINT) + update_workshop(key, mod, STEAM_UNSUB) def validate_mod(client: str, mod: str) -> None: @@ -512,7 +512,7 @@ def get_app_path(folders_path: Path, appid: int) -> Path: def get_app_name(appid: int) -> str | None: payload = {"appids": [appid]} - res = requests.get(APP_DETAILS, params=payload) + res = requests.get(STEAM_APP_DETAILS, params=payload) if res.status_code != 200: return None try: diff --git a/dzgui/const/endpoints.py b/dzgui/const/endpoints.py index 7616ff0..ed6239d 100644 --- a/dzgui/const/endpoints.py +++ b/dzgui/const/endpoints.py @@ -3,9 +3,9 @@ STEAM_PUBLISHED_FILES = ( "https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1" ) STEAM_SERVERS = "https://api.steampowered.com/IGameServersService/GetServerList/v1" -SUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1" -UNSUB_ENDPOINT = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1" -APP_DETAILS = "https://store.steampowered.com/api/appdetails?" +STEAM_SUB = "https://api.steampowered.com/IPublishedFileService/Subscribe/v1" +STEAM_UNSUB = "https://api.steampowered.com/IPublishedFileService/Unsubscribe/v1" +STEAM_APP_DETAILS = "https://store.steampowered.com/api/appdetails?" GITHUB = "https://github.com/aclist" GITHUB_RELEASES = "https://api.github.com/repos/aclist/dztui/releases/latest" From 16d3df618a737ca862727a20e05816d1ce9bc646 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:07:30 +0900 Subject: [PATCH 6/7] chore: add remarks to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c3079a8..d062444 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Users wishing to review the licenses to these components can inspect the `LICENS Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries. +DayZ is a trademark and brand of Bohemia Interactive a.s. + ## Disclaimer DZGUI is beta-quality software and is provided as-is. From 06c85fdfd8183b87c784278fa2fea6cca45d516e Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:12:51 +0900 Subject: [PATCH 7/7] chore: add remarks about release structure --- HISTORY.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 019b55e..63a5323 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,7 +18,7 @@ Due to user interest, features continued to be added. This early tool is archive ### 2022-05-09: addition of zenity GUI and project name change (DZGUI) With the introduction of the Steam Deck, more users wanted a graphical user interface to interact with DZTUI. Being that DZTUI was purely a Bash script, a method of -drawing menus was needed, so the 'zenity' package was used to create prefabricated dialogs. +drawing menus was needed, so the "zenity" package was used to create prefabricated dialogs. While this met the minimum requirement to show prompts and selectable options, no rich customization was possible. The introduction of zenity as a dependency marked the shift from "DZTUI" to "DZGUI". @@ -101,8 +101,13 @@ The maintainers of those projects were advised to excise the offending segments ### 2026-05-17: Switch to standalone releases The DZGUI 7 beta starts shipping standalone glibc builds with an integrated setup wizard and installer. -# TODO: remarks on DZGUI 6 stable vs testing branch logic and "over the air" updates vs standalone builds -# TODO: note archived branch names +DZGUI 6 had relied on "over the air" updates delivered via either a "stable" or "testing" branch +(archived respectively under the "dzgui" and "testing" branches). Effectively, the script would bootstrap itself and +fetch raw files directly from the repository. This was prone to problems because updates were monolithic, not giving +an easy escape hatch if the user needed to rollback in the event of a regression. + +This was changed to atomic, tagged releases. In addition to updating directly through DZGUI, these prebuilt releases can be +downloaded manually as needed, and rollbacks are available for all prior versions under the DZGUI 7 specification. ### 2026-10-27: 500 tickets closed The milestone of 500 tickets/PRs being closed was reached.