Merge pull request #486 from aclist/change/flatpak-sens

change: set flatpak option sensitivity dynamically
This commit is contained in:
aclist authored and GitHub committed 2026-10-22 08:20:09 +09:00
commit 0131ee6f0e
14 files changed
+138 -114

No files matched your search

+4 -4
View File
@@ -12,7 +12,7 @@ from typing import Any, Generator, Union
from warnings import deprecated
from dzgui.api.acf import ACF
from dzgui.init.prereqs import has_steam_client
from dzgui.init.prereqs import has_any_client
from dzgui.const.constants import (
APPID_DAYZ,
APPID_DAYZ_EXP,
@@ -61,7 +61,7 @@ class VDFLoadError(Exception):
def get_steam_paths() -> list[tuple[Path, str]]:
paths = []
if has_steam_client():
if has_any_client():
HOME = Path.home()
env = os.environ.get("XDG_DATA_HOME")
if env is not None:
@@ -267,12 +267,12 @@ def find_user_id_32(path: Path) -> int:
class SteamAPIKeyError(Exception):
def __init__(self, msg: str | None = None):
def __init__(self, msg: str | None = None) -> None:
super().__init__(msg or api_validation_error)
class SteamWorkshopEndpointError(Exception):
def __init__(self, msg: str | None = None):
def __init__(self, msg: str | None = None) -> None:
super().__init__(msg or workshop_endpoint_error)
+2 -2
View File
@@ -15,7 +15,7 @@ from dzgui.init.migrate import (
migrate_cols_file,
)
from dzgui.init.flock import lock_acquire
from dzgui.init.prereqs import has_steam_client
from dzgui.init.prereqs import has_any_client
from dzgui.strings import boot
from dzgui.util.deck import is_steam_deck, is_game_mode
@@ -136,7 +136,7 @@ def load_gui(version: str, is_debug: bool) -> None:
# TODO: sudo escalation dialog
# count = get_map_count()
# TODO: move into module
if has_steam_client() is False:
if has_any_client() is False:
EarlyAlertDialog(init.requires_steam)
bootwin = BootWindow(XDG, version)
-20
View File
@@ -4,8 +4,6 @@ from pathlib import Path
from dzgui.util._json import read_json
from dzgui.const.enum import Preferences
from dzgui.const.constants import STEAM_CMD, FLATPAK_RUN_CMD, FLATPAK_SANDBOX
class ConfigFileError(Exception):
pass
@@ -40,23 +38,5 @@ def get_favorites(path: Path) -> list[str]:
return list(conf["ip_list"])
# def is_in_favs(record: str, path: Path) -> bool:
# favs = get_favorites(path)
# if record in favs:
# return True
# return False
def enum_to_key(enum: Preferences) -> str:
return str(enum.dict["key"])
def get_client_index(client: str) -> int:
if client == STEAM_CMD:
return 0
elif client == FLATPAK_RUN_CMD:
return 1
elif client == FLATPAK_SANDBOX:
return 2
else:
return 0
+2 -1
View File
@@ -85,7 +85,8 @@ HERO_PATH = "data/images/_hero.png"
CSS_PATH = "data/app.css"
VDF_PATH = "steamapps/libraryfolders.vdf"
DEFAULT_STEAM_PATH = ".local/share/Steam"
FLATPAK_STEAM_PATH = ".var/app/com.valvesoftware.Steam/data/Steam"
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"
+4 -2
View File
@@ -541,8 +541,10 @@ class Controller(GObject.GObject):
def update_status(self) -> None:
self.mediator.preconnect.mark_finished()
def get_steam_client_name(self, cmd: str) -> str:
return self.mediator.options.get_client_name(cmd)
def get_client_name(self) -> str:
# NOTE: only possible values are "Steam" and "Flatpak"
cmd = self.query_config(Preferences.CLIENT)
return str(cmd.split()[0].capitalize())
def present_window(self) -> None:
self.mediator.window.present_with_time(Gdk.CURRENT_TIME)
+20 -7
View File
@@ -7,19 +7,32 @@ from dzgui.const.constants import APP_NAME, FLATPAK_APPID, FLATPAK_CMD, STEAM_CM
logger = logging.getLogger(APP_NAME)
def has_steam_client() -> bool:
if shutil.which(STEAM_CMD):
return True
if not shutil.which(FLATPAK_CMD):
return False
def has_flatpak_client() -> bool:
try:
res = subprocess.run(
[FLATPAK_CMD, "list"], capture_output=True, text=True, check=True
[FLATPAK_CMD, "list", "--columns=application"],
capture_output=True,
text=True,
check=True,
)
if FLATPAK_APPID in res.stdout:
return True
else:
return False
except subprocess.CalledProcessError as e:
except Exception as e:
logger.critical(e)
return False
def has_steam_client() -> bool:
if shutil.which(STEAM_CMD):
return True
return False
def has_any_client() -> bool:
if shutil.which(STEAM_CMD):
return True
if not shutil.which(FLATPAK_CMD):
return False
return has_flatpak_client()
+28 -14
View File
@@ -10,27 +10,22 @@ from dzgui.const.constants import (
FLATPAK_CMD,
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
FLATPAK_ROOT_PATH,
FLATPAK_STEAM_PATH,
)
logger = logging.getLogger(APP_NAME)
def is_steam_running(cmd: str) -> bool:
is_flatpak_running = is_flatpak_steam_running()
if cmd in (FLATPAK_RUN_CMD, FLATPAK_SANDBOX):
return is_flatpak_running
return is_flatpak_steam_running()
elif cmd == STEAM_CMD:
if is_flatpak_running:
return False
if has_cmd(STEAM_CMD) is False:
return False
return is_running(STEAM_CMD)
return is_native_steam_running()
else:
raise TypeError("Not a valid Steam client selection")
# CHORE: test alternate clients
# TODO: has_flatpak_steam
def is_flatpak_steam_running() -> bool:
if has_cmd(FLATPAK_CMD) is False:
return False
@@ -43,14 +38,33 @@ def is_flatpak_steam_running() -> bool:
return False
def is_running(proc: str) -> bool:
for process in psutil.process_iter(["pid", "name"]):
if process.info["name"] == proc:
return True
return False
def get_steam_procs() -> list[list[str]]:
return [
p.cmdline()
for p in psutil.process_iter(["pid", "name"])
if STEAM_CMD in p.info["name"]
]
def is_native_steam_running() -> bool:
if has_cmd(STEAM_CMD) is False:
return False
substrings = [FLATPAK_ROOT_PATH, "steam-runtime-launcher-service"]
steam_procs = get_steam_procs()
native_steam_procs = [
proc
for proc in steam_procs
if not any(sub in item for item in proc for sub in substrings)
]
return bool(native_steam_procs)
def has_cmd(cmd: str) -> bool:
if shutil.which(cmd) is not None:
return True
return False
def path_to_steam_cmd(path: str) -> str:
cmd = FLATPAK_RUN_CMD if FLATPAK_STEAM_PATH in path else STEAM_CMD
return cmd
-12
View File
@@ -6,9 +6,6 @@ from typing import Any, TYPE_CHECKING
from dzgui.const.constants import (
APP_NAME,
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
STEAM_CMD,
WINDOW_DEFAULT_X,
WINDOW_DEFAULT_Y,
)
@@ -129,15 +126,6 @@ class ConfigManager:
def enum_to_key(self, enum: Preferences) -> str:
return str(enum.dict["key"])
def get_client_index(self, client: str) -> int:
if client == STEAM_CMD:
return 0
if client == FLATPAK_RUN_CMD:
return 1
if client == FLATPAK_SANDBOX:
return 2
return 0
def toggle_config(self, key: Preferences) -> None:
try:
real_key = self.enum_to_key(key)
+1 -1
View File
@@ -188,7 +188,7 @@ class ConnectionManager:
allows_downloads = (True, "")
client = self.controller.query_config(Preferences.CLIENT)
client_name = self.controller.get_steam_client_name(client)
client_name = self.controller.get_client_name()
running = is_steam_running(client)
steam_proc = SteamProcess(client_name, running)
self.client = client
+26 -3
View File
@@ -1,49 +1,72 @@
from typing import Self
from warnings import deprecated
from dzgui.const.constants import ERROR, FLATPAK_RUN_CMD, FLATPAK_SANDBOX, STEAM_CMD
from dzgui.init.prereqs import has_steam_client, has_flatpak_client
from dzgui.strings import options
from dzgui.views.components.box import HBox
from dzgui.views.components.buttons import Icon
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa
@deprecated("Client choice is determined at setup time")
class ClientCombo(Gtk.ComboBox):
def __init__(self) -> None:
super().__init__()
self.client_store = Gtk.ListStore(str, str)
has_steam = has_steam_client()
has_flatpak = has_flatpak_client()
self.client_store = Gtk.ListStore(str, str, bool)
self.clients = (
(
options.steam_combo,
STEAM_CMD,
has_steam,
),
(
options.flatpak_combo,
FLATPAK_RUN_CMD,
has_flatpak,
),
(
options.flatpak_container_combo,
FLATPAK_SANDBOX,
has_flatpak,
),
)
for client in self.clients:
self.client_store.append(client)
self.set_model(self.client_store)
renderer_text = Gtk.CellRendererText()
self.pack_start(renderer_text, True)
self.add_attribute(renderer_text, "text", 0)
self.set_active(0)
self.set_cell_data_func(self.get_cells()[0], self.data_func)
def data_func(
self,
combo: Self,
cell: Gtk.CellRendererText,
model: Gtk.ListStore,
_iter: Gtk.TreeIter,
) -> None:
sens = model[_iter][2]
cell.set_property("sensitive", sens)
def get_client_human_readable(self, cmd: str) -> str:
for client in self.clients:
human, command = client
human, command, sens = client
if command == cmd:
return human
raise TypeError("Not a valid Steam client selection")
class ErrorPopover(Gtk.Popover):
def __init__(self, relative_to: Gtk.Widget, position: Gtk.PositionType) -> None:
super().__init__(relative_to=relative_to, position=position)
+8 -16
View File
@@ -17,6 +17,7 @@ from dzgui.const.constants import (
from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.config import freedesktop
from dzgui.init.proc import path_to_steam_cmd
from dzgui.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard
@@ -25,7 +26,6 @@ from dzgui.util.open_links import open_link_by_url
from dzgui.util.css import add_class, load_css
from dzgui.views.components.buttons import WebButton
from dzgui.views.components.entry import APIEntry
from dzgui.views.components.misc import ClientCombo
from dzgui.views.mixins.colorscheme import ColorAwareApp
import gi
@@ -384,26 +384,17 @@ class PreferencesPage(EnumeratedWizardPage):
self.dist_box.add(self.radio_km)
self.dist_box.add(self.radio_miles)
client_label = Gtk.Label(label=wizard.label_client, halign=Gtk.Align.START)
self.client_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
self.client_combo = ClientCombo()
self.client_box.add(client_label)
self.client_box.add(self.client_combo)
outer_box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL, halign=Gtk.Align.CENTER, spacing=10
)
for el in self.name_box, self.client_box, self.dist_box:
for el in self.name_box, self.dist_box:
outer_box.add(el)
self.add_start(outer_box)
def get_prefs(self) -> tuple[str, bool, str]:
def get_prefs(self) -> tuple[str, bool]:
name = self.name_entry.get_text().strip()
use_miles = self.radio_miles.get_active()
model = self.client_combo.get_model()
ind = self.client_combo.get_active()
client = model[ind][1]
return name, use_miles, client
return name, use_miles
def _on_entry_changed(self, entry: Gtk.Entry) -> None:
text = entry.get_text()
@@ -499,15 +490,16 @@ class Assistant(Gtk.Assistant):
self.config_values = self.page_migration.migrated_conf
return self.get_final_page()
case SteamPathPage():
self.config_values["default_steam_path"] = page.get_path_from_radio()
path = page.get_path_from_radio()
self.config_values["default_steam_path"] = path
self.config_values["client"] = path_to_steam_cmd(path)
case SteamValidationPage():
self.config_values["steam_api"] = page.get_api_key()
case PreferencesPage():
# NOTE: collects config values before advancing to last page
name, use_miles, client = self.page_prefs.get_prefs()
name, use_miles = self.page_prefs.get_prefs()
self.config_values["name"] = name
self.config_values["use_miles"] = use_miles
self.config_values["client"] = client
self.page_shortcuts.set_steam_path(
self.config_values["default_steam_path"]
)
+1 -24
View File
@@ -25,7 +25,7 @@ from dzgui.views.components.eventbox import InfoEventBox
from dzgui.views.components.labels import LeftLabel
from dzgui.views.components.buttons import SpinnerButton, WebButton
from dzgui.views.components.frame import HeadingFrame
from dzgui.views.components.misc import ClientCombo, ErrorPopover
from dzgui.views.components.misc import ErrorPopover
from dzgui.views.dialogs.generic import ExceptionDialog
from dzgui.views.mixins.scrollable_mixin import ScrollableMixin
@@ -281,11 +281,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
Preferences.WINDOW,
)
self.client_combo = ClientCombo()
self.client_combo.connect("changed", self._on_client_changed)
client_hbox = ShortHBox(self.client_combo)
self.distance_toggle = ToggleField(
self.controller, strings.options.km, strings.options.mi, Preferences.DIST
)
@@ -309,7 +304,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
self.start_tab_combo.connect("changed", self._on_start_tab_changed)
pref_rows = [
[LeftLabel(strings.options.client), client_hbox],
[LeftLabel(strings.options.window_size), self.fullscreen_toggle],
[LeftLabel(strings.options.distance), self.distance_toggle],
[LeftLabel(options.start_tab), start_tab_hbox],
@@ -367,17 +361,11 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
self.connect("key-press-event", self._on_keypress)
def get_client_name(self, cmd: str) -> str:
return self.client_combo.get_client_human_readable(cmd)
def block_text_entry(self) -> None:
self.steam_box.block_text_entry()
# self.steam_entry.set_position(-1)
# self.steam_entry.set_can_focus(False)
def unblock_text_entry(self) -> None:
self.steam_box.unblock_text_entry()
# self.steam_entry.set_can_focus(True)
def _on_developers_clicked(self, button: Gtk.Button) -> None:
self.controller.show_developers_page()
@@ -412,13 +400,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
index = enum.value
self.controller.update_config(Preferences.START_TAB, index)
def _on_client_changed(self, combo: Gtk.ComboBoxText) -> None:
_iter = combo.get_active_iter()
if _iter is None:
raise ValueError(f"No active iterator set on {combo}")
real_cmd = combo.get_model()[_iter][1]
self.controller.update_config(Preferences.CLIENT, real_cmd)
def populate_settings(self) -> None:
prefs = self.controller.get_prefs()
# NOTE: re-check in case file was removed by user between runs
@@ -464,10 +445,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
self.dayz_version_label.set_text(dayz_version)
self.dayz_exp_version_label.set_text(dayz_exp_version)
# TODO: bicolumn list store with no cell renderer on index 1, use raw command names
active_combo = query.get_client_index(config["client"])
self.client_combo.set_active(active_combo)
start_tab = self.controller.query_config(Preferences.START_TAB)
self.start_tab_combo.set_active(start_tab)
+19
View File
@@ -0,0 +1,19 @@
import pytest
from dzgui.const.constants import FLATPAK_RUN_CMD, STEAM_CMD
from dzgui.controllers.mc import Controller
@pytest.mark.parametrize(
"value, expected", [(FLATPAK_RUN_CMD, "Flatpak"), (STEAM_CMD, "Steam")]
)
def test_human_name(monkeypatch, value, expected) -> None:
def mock_query_config(*args) -> str:
return value
monkeypatch.setattr(
"dzgui.controllers.mc.Controller.query_config", mock_query_config
)
controller = Controller()
client = controller.get_client_name()
assert client == expected
+23 -8
View File
@@ -3,6 +3,7 @@ from unittest.mock import patch
from dzgui.const.constants import (
FLATPAK_APPID,
FLATPAK_ROOT_PATH,
FLATPAK_RUN_CMD,
FLATPAK_SANDBOX,
STEAM_CMD,
@@ -45,16 +46,30 @@ class TestSteamDetection(unittest.TestCase):
def test_native_steam(self):
with patch.object(proc, "has_cmd", return_value=True):
for running in (True, False):
for output, expected in (
(
[
[FLATPAK_ROOT_PATH],
"/home/user/.local/share/Steam/ubuntu12_64/steamwebhelper",
],
True,
),
([[FLATPAK_ROOT_PATH]], False),
([[FLATPAK_ROOT_PATH], ["steam-runtime-launcher-service"]], False),
([["/home/user/.local/share/Steam/ubuntu12_64/steamwebhelper"]], True),
(
[
["/home/user/.local/share/Steam/ubuntu12_64/steamwebhelper"],
"steam-runtime-launcher-service",
],
True,
),
):
with (
self.subTest(running=running),
patch.object(proc, "is_running", return_value=running),
self.subTest(running=output),
patch.object(proc, "get_steam_procs", return_value=output),
):
self.assertEqual(proc.is_steam_running(STEAM_CMD), running)
def test_native_steam_while_flatpak_is_running(self):
with patch.object(proc, "is_flatpak_steam_running", return_value=True):
self.assertEqual(proc.is_steam_running(STEAM_CMD), False)
self.assertEqual(proc.is_native_steam_running(), expected)
def test_invalid_client(self):
with self.assertRaises(TypeError):