mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
The session sets TU_DEBUG=sysmem for the Steam client's interface and hands games their own value in DROIDDECK_GAME_TU_DEBUG, which droiddeck-game-env applies to Steam launches (#1188). droiddeck-appimage-run never did, so emulators and other AppImage programs rendered every pass through system memory instead of Turnip's per render pass choice. Apply the game value in droiddeck-appimage-run the same way: an empty value unsets TU_DEBUG, any other value replaces it. Chromium-based apps, like the Steam client's own interface, keep the session's setting.
152 lines
7.8 KiB
Python
152 lines
7.8 KiB
Python
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
RUN = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-appimage-run"
|
|
KEYS = ("TU_DEBUG", "DROIDDECK_GAME_TU_DEBUG", "QT_QPA_PLATFORM", "WAYLAND_DISPLAY", "SDL_VIDEODRIVER", "XDG_SESSION_TYPE", "QT_WAYLAND_DISABLE_WINDOWDECORATION", "DISABLE_GAMESCOPE_WSI")
|
|
QUICK = ("usr/lib/libQt6Quick.so.6", "usr/lib/libvulkan.so.1", "usr/plugins/platforms/libqwayland-generic.so")
|
|
RUN_MODE = {"GAMESCOPE_WAYLAND_DISPLAY": "gamescope-0", "WAYLAND_DISPLAY": "gamescope-0", "DISPLAY": ":0",
|
|
"QT_QPA_PLATFORM": "xcb", "SDL_VIDEODRIVER": "x11", "XDG_SESSION_TYPE": "x11"}
|
|
DESKTOP = {"WAYLAND_DISPLAY": "wayland-1", "DISPLAY": ":1", "QT_QPA_PLATFORM": "wayland;xcb",
|
|
"SDL_VIDEODRIVER": "wayland,x11", "XDG_SESSION_TYPE": "wayland"}
|
|
|
|
|
|
class AppImageRunTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = Path(tempfile.mkdtemp())
|
|
self.addCleanup(shutil.rmtree, self.tmp, True)
|
|
self.dir = self.tmp / "opennow"
|
|
self.out = self.tmp / "env"
|
|
app = self.dir / "app"
|
|
app.mkdir(parents=True)
|
|
run = app / "AppRun"
|
|
run.write_text("#!/bin/sh\nfor k in %s; do eval \"echo $k=\\${$k-unset}\"; done > %s\n" % (" ".join(KEYS), self.out))
|
|
run.chmod(0o755)
|
|
|
|
def bundle(self, *paths):
|
|
for path in paths:
|
|
lib = self.dir / "app" / path
|
|
lib.parent.mkdir(parents=True, exist_ok=True)
|
|
lib.write_bytes(b"")
|
|
|
|
def launch(self, session):
|
|
env = {k: v for k, v in os.environ.items() if k not in KEYS + ("GAMESCOPE_WAYLAND_DISPLAY", "DISPLAY")}
|
|
env.update(session, HOME=str(self.tmp / "home"), DBUS_SESSION_BUS_ADDRESS="unix:path=/dev/null")
|
|
result = subprocess.run(["bash", str(RUN), str(self.dir)], env=env, capture_output=True, text=True, timeout=30)
|
|
self.assertEqual(0, result.returncode, result.stderr)
|
|
return dict(line.split("=", 1) for line in self.out.read_text().splitlines()), result.stdout
|
|
|
|
def test_a_vulkan_qt_quick_app_draws_on_gamescope_wayland(self):
|
|
self.bundle(*QUICK)
|
|
env, log = self.launch(RUN_MODE)
|
|
self.assertEqual("wayland;xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertEqual("gamescope-0", env["WAYLAND_DISPLAY"])
|
|
self.assertEqual("wayland", env["SDL_VIDEODRIVER"])
|
|
self.assertEqual("wayland", env["XDG_SESSION_TYPE"])
|
|
self.assertEqual("1", env["QT_WAYLAND_DISABLE_WINDOWDECORATION"])
|
|
self.assertEqual("1", env["DISABLE_GAMESCOPE_WSI"])
|
|
self.assertIn("Qt wayland (wayland;xcb)", log)
|
|
|
|
def test_a_quick_app_that_loads_vulkan_itself_is_found_by_its_binary(self):
|
|
self.bundle(QUICK[0], QUICK[2])
|
|
binary = self.dir / "app/usr/bin/opennow-qt"
|
|
binary.parent.mkdir(parents=True, exist_ok=True)
|
|
binary.write_bytes(b"\x7fELF\0_ZN7QWindow17setVulkanInstanceEP15QVulkanInstance\0")
|
|
env, _ = self.launch(RUN_MODE)
|
|
self.assertEqual("wayland;xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertEqual("1", env["DISABLE_GAMESCOPE_WSI"])
|
|
|
|
def test_other_apps_stay_on_xwayland(self):
|
|
self.bundle("usr/lib/libQt6Widgets.so.6", "usr/lib/libvulkan.so.1", "usr/plugins/platforms/libqxcb.so")
|
|
env, _ = self.launch(RUN_MODE)
|
|
self.assertEqual("xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertEqual("x11", env["SDL_VIDEODRIVER"])
|
|
self.assertEqual("unset", env["QT_WAYLAND_DISABLE_WINDOWDECORATION"])
|
|
self.assertEqual("unset", env["DISABLE_GAMESCOPE_WSI"])
|
|
|
|
def test_a_quick_app_without_the_wayland_plugin_stays_on_xwayland(self):
|
|
self.bundle(*QUICK[:2])
|
|
env, log = self.launch(RUN_MODE)
|
|
self.assertEqual("xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertIn("stays on X11", log)
|
|
|
|
def test_the_desktop_keeps_its_decorations(self):
|
|
self.bundle(*QUICK)
|
|
env, _ = self.launch(DESKTOP)
|
|
self.assertEqual("wayland;xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertEqual("wayland-1", env["WAYLAND_DISPLAY"])
|
|
self.assertEqual("wayland", env["SDL_VIDEODRIVER"])
|
|
self.assertEqual("unset", env["QT_WAYLAND_DISABLE_WINDOWDECORATION"])
|
|
self.assertEqual("unset", env["DISABLE_GAMESCOPE_WSI"])
|
|
|
|
def test_how_the_program_ended_is_logged(self):
|
|
run = self.dir / "app/AppRun"
|
|
run.write_text("#!/bin/sh\nexit 3\n")
|
|
env = {k: v for k, v in os.environ.items() if k not in KEYS}
|
|
env.update(RUN_MODE, HOME=str(self.tmp / "home"), DBUS_SESSION_BUS_ADDRESS="unix:path=/dev/null")
|
|
result = subprocess.run(["bash", str(RUN), str(self.dir)], env=env, capture_output=True, text=True, timeout=30)
|
|
self.assertEqual(3, result.returncode)
|
|
self.assertIn("exited with status 3", result.stdout)
|
|
run.write_text("#!/bin/sh\nkill -SEGV $$\n")
|
|
result = subprocess.run(["bash", str(RUN), str(self.dir)], env=env, capture_output=True, text=True, timeout=30)
|
|
self.assertEqual(139, result.returncode)
|
|
self.assertIn("ended by signal 11 (SEGV)", result.stdout)
|
|
|
|
def test_a_failed_program_shows_the_log_it_wrote(self):
|
|
home = self.tmp / "home"
|
|
old = home / ".local/share/emulator/log/old.log"
|
|
old.parent.mkdir(parents=True)
|
|
old.write_text("from an earlier run\n")
|
|
os.utime(old, (1, 1))
|
|
(self.dir / "app/AppRun").write_text(
|
|
"#!/bin/sh\nmkdir -p \"$HOME/.local/share/emulator/log\"\n"
|
|
"printf 'Assertion failed: vulkan device\\n\\345\\001\\n' > \"$HOME/.local/share/emulator/log/emulator_log.txt\"\n"
|
|
"kill -ILL $$\n")
|
|
env = {k: v for k, v in os.environ.items() if k not in KEYS}
|
|
env.update(RUN_MODE, HOME=str(home), DBUS_SESSION_BUS_ADDRESS="unix:path=/dev/null")
|
|
result = subprocess.run(["bash", str(RUN), str(self.dir)], env=env, capture_output=True, text=True, timeout=30)
|
|
self.assertIn("ended by signal 4 (ILL)", result.stdout)
|
|
self.assertIn("end of %s" % (home / ".local/share/emulator/log/emulator_log.txt"), result.stdout)
|
|
self.assertIn("Assertion failed: vulkan device", result.stdout)
|
|
self.assertNotIn("from an earlier run", result.stdout)
|
|
(self.dir / "app/AppRun").write_text("#!/bin/sh\necho again > \"$HOME/.local/share/emulator/log/emulator_log.txt\"\n")
|
|
result = subprocess.run(["bash", str(RUN), str(self.dir)], env=env, capture_output=True, text=True, timeout=30)
|
|
self.assertNotIn("end of", result.stdout)
|
|
|
|
def test_the_per_app_choice_wins(self):
|
|
self.bundle(*QUICK)
|
|
(self.dir / "qt").write_text("xcb\n")
|
|
env, log = self.launch(DESKTOP)
|
|
self.assertEqual("xcb", env["QT_QPA_PLATFORM"])
|
|
self.assertIn("Qt xcb (xcb)", log)
|
|
(self.dir / "qt").write_text("wayland\n")
|
|
(self.dir / "app/usr/lib/libQt6Quick.so.6").unlink()
|
|
env, _ = self.launch(RUN_MODE)
|
|
self.assertEqual("wayland;xcb", env["QT_QPA_PLATFORM"])
|
|
|
|
|
|
def test_a_program_drops_the_sessions_sysmem_like_a_steam_game(self):
|
|
env, _ = self.launch(dict(RUN_MODE, TU_DEBUG="sysmem", DROIDDECK_GAME_TU_DEBUG=""))
|
|
self.assertEqual("unset", env["TU_DEBUG"])
|
|
self.assertEqual("unset", env["DROIDDECK_GAME_TU_DEBUG"])
|
|
|
|
def test_a_program_gets_the_game_driver_setting(self):
|
|
env, _ = self.launch(dict(RUN_MODE, TU_DEBUG="sysmem", DROIDDECK_GAME_TU_DEBUG="gmem"))
|
|
self.assertEqual("gmem", env["TU_DEBUG"])
|
|
|
|
def test_a_chromium_app_keeps_the_session_setting(self):
|
|
self.bundle("chrome_100_percent.pak")
|
|
env, _ = self.launch(dict(RUN_MODE, TU_DEBUG="sysmem", DROIDDECK_GAME_TU_DEBUG=""))
|
|
self.assertEqual("sysmem", env["TU_DEBUG"])
|
|
|
|
def test_without_the_game_setting_the_session_value_stays(self):
|
|
env, _ = self.launch(dict(RUN_MODE, TU_DEBUG="sysmem"))
|
|
self.assertEqual("sysmem", env["TU_DEBUG"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|