Skip to content

Commit

Permalink
Make zenity optional for setup or download
Browse files Browse the repository at this point in the history
- This commit makes the zenity popup window optional when downloading the runtime platform or a Proton build optional to avoid conflicting with a launcher's UI/UX, and hides the feature via the UMU_ZENITY environment variable.

- The launcher is designed to be used as a backend and GUI launchers develop using their own various toolkits, so zenity's default themes will not conform with it and lacks the customization to do so. On top of that, when gaming on a handheld device in a game mode session, the zenity popup window would look rather unpleasant. Instead of using curl and zenity as the default operation, the launcher will default to downloading using native Python functionality.

- In the future, alternative methods will be explored to notify download state to clients. To re-enable zenity, set UMU_ZENITY=1
  • Loading branch information
R1kaB3rN committed Mar 26, 2024
1 parent d4cd834 commit 670f001
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 59 deletions.
43 changes: 21 additions & 22 deletions umu/umu_dl_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ def get_umu_proton(env: Dict[str, str]) -> Union[Dict[str, str]]:
Downloads the latest if not first found in:
~/.local/share/Steam/compatibilitytools.d
The cache directory ~/.cache/umu is referenced for the latest then as
fallback
"""
files: List[Tuple[str, str]] = []
tmp: Path = Path(mkdtemp())
Expand Down Expand Up @@ -124,6 +121,7 @@ def _fetch_proton(
hash, hash_url = files[0]
proton, proton_url = files[1]
proton_dir: str = proton[: proton.find(".tar.gz")] # Proton dir
ret: int = 0 # Exit code from zenity

log.console(f"Downloading {hash} ...")

Expand All @@ -147,8 +145,8 @@ def _fetch_proton(
file.write(resp.read())

# Proton
# Check for Zenity otherwise print
try:
# Create a popup with zenity when the env var is set
if environ.get("UMU_ZENITY") == "1":
bin: str = "curl"
opts: List[str] = [
"-LJO",
Expand All @@ -157,18 +155,18 @@ def _fetch_proton(
"--output-dir",
tmp.as_posix(),
]

msg: str = f"Downloading {proton_dir} ..."
enable_zenity(bin, opts, msg)
except TimeoutError:
err: str = f"Unable to download {proton}\ngithub.com request timed out"
raise TimeoutError(err)
except FileNotFoundError:
ret = enable_zenity(bin, opts, msg)
if ret:
tmp.joinpath(proton).unlink(missing_ok=True)
log.warning("zenity exited with the status code: %s", ret)
log.console("Retrying from Python ...")
if not environ.get("UMU_ZENITY") or ret:
log.console(f"Downloading {proton} ...")

with urlopen(proton_url, timeout=180, context=create_default_context()) as resp: # noqa: S310
with urlopen( # noqa: S310
proton_url, timeout=300, context=create_default_context()
) as resp:
# Without Proton, the launcher will not work
# Continue by referring to cache
if resp.status != 200:
err: str = (
f"Unable to download {proton}\n"
Expand All @@ -185,7 +183,8 @@ def _fetch_proton(
sha512(file.read()).hexdigest()
!= tmp.joinpath(hash).read_text().split(" ")[0]
):
err: str = "Digests mismatched.\nFalling back to cache ..."
err: str = "Digests mismatched"
log.warning(err)
raise ValueError(err)
log.console(f"{proton}: SHA512 is OK")

Expand All @@ -196,33 +195,33 @@ def _fetch_proton(
return env


def _extract_dir(proton: Path, steam_compat: Path) -> None:
def _extract_dir(file: Path, steam_compat: Path) -> None:
"""Extract from the cache to another location."""
with tar_open(proton.as_posix(), "r:gz") as tar:
with tar_open(file.as_posix(), "r:gz") as tar:
if tar_filter:
log.debug("Using filter for archive")
tar.extraction_filter = tar_filter
else:
log.debug("Using no filter for archive")
log.warning("Archive will be extracted insecurely")

log.console(f"Extracting {proton} -> {steam_compat} ...")
log.console(f"Extracting {file} -> {steam_compat} ...")
# TODO: Rather than extracting all of the contents, we should prefer
# the difference (e.g., rsync)
tar.extractall(path=steam_compat.as_posix()) # noqa: S202
log.console("Completed.")


def _cleanup(tarball: str, proton: str, cache: Path, steam_compat: Path) -> None:
def _cleanup(tarball: str, proton: str, tmp: Path, steam_compat: Path) -> None:
"""Remove files that may have been left in an incomplete state to avoid corruption.
We want to do this when a download for a new release is interrupted
"""
log.console("Keyboard Interrupt.\nCleaning ...")

if cache.joinpath(tarball).is_file():
log.console(f"Purging {tarball} in {cache} ...")
cache.joinpath(tarball).unlink()
if tmp.joinpath(tarball).is_file():
log.console(f"Purging {tarball} in {tmp} ...")
tmp.joinpath(tarball).unlink()
if steam_compat.joinpath(proton).is_dir():
log.console(f"Purging {proton} in {steam_compat} ...")
rmtree(steam_compat.joinpath(proton).as_posix())
Expand Down
1 change: 1 addition & 0 deletions umu/umu_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ def enable_zenity(command: str, opts: List[str], msg: str) -> int:
f"--text={msg}",
"--percentage=0",
"--pulsate",
"--no-cancel",
],
stdin=PIPE,
) as zenity_proc,
Expand Down
1 change: 1 addition & 0 deletions umu/umu_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def main() -> int: # noqa: D103
"PROTON_VERB": "",
"UMU_ID": "",
"ULWGL_ID": "",
"UMU_ZENITY": "",
}
command: List[str] = []
opts: List[str] = None
Expand Down
63 changes: 26 additions & 37 deletions umu/umu_util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from tarfile import open as tar_open, TarInfo
from os import getuid
from os import getuid, environ
from umu_consts import CONFIG, STEAM_COMPAT, UMU_LOCAL
from typing import Any, Dict, List, Callable
from json import load, dump
Expand Down Expand Up @@ -72,46 +72,35 @@ def setup_runtime(json: Dict[str, Any]) -> None: # noqa: D103
# Step 1: Define the URL of the file to download
# We expect the archive name to not change
base_url: str = f"https://repo.steampowered.com/steamrt3/images/{version}/{archive}"
bin: str = "curl"
opts: List[str] = [
"-LJO",
"--silent",
f"{base_url}",
"--output-dir",
tmp.as_posix(),
]
ret: int = 0 # Exit code from zenity

log.debug("URL: %s", base_url)

# Download the runtime
# Attempt to create a popup with zenity otherwise print
try:
bin: str = "curl"
opts: List[str] = [
"-LJO",
"--silent",
base_url,
"--output-dir",
tmp.as_posix(),
]

msg: str = "Downloading Runtime, please wait..."
# Optionally create a popup with zenity
if environ.get("UMU_ZENITY") == "1":
msg: str = "Downloading UMU-Runtime ..."
ret: int = enable_zenity(bin, opts, msg)

# Handle the symbol lookup error from the zenity flatpak in lutris
if ret:
log.warning("zenity exited with the status code: %s", ret)
log.warning("zenity will not be used")
tmp.joinpath(archive).unlink(missing_ok=True)
raise FileNotFoundError
except TimeoutError:
# Without the runtime, the launcher will not work
# Just exit on timeout or download failure
err: str = (
"Unable to download the Steam Runtime\n"
"repo.steampowered.com request timed out "
"or timeout limit was reached"
)
raise TimeoutError(err)
except FileNotFoundError:
log.console(f"Downloading {runtime_platform_value} ...")

# We hardcode the URL and trust it
with urlopen(base_url, timeout=60, context=create_default_context()) as resp: # noqa: S310
log.warning("zenity exited with the status code: %s", ret)
log.console("Retrying from Python ...")
if not environ.get("UMU_ZENITY") or ret:
log.console(f"Downloading {runtime_platform_value}, please wait ...")
with urlopen( # noqa: S310
base_url, timeout=300, context=create_default_context()
) as resp:
if resp.status != 200:
err: str = (
"Unable to download the Steam Runtime\n"
f"Unable to download {hash}\n"
f"repo.steampowered.com returned the status: {resp.status}"
)
raise HTTPException(err)
Expand Down Expand Up @@ -225,11 +214,11 @@ def _install_umu(
local.mkdir(parents=True, exist_ok=True)

# Config
log.console(f"Copying {CONFIG} -> {local} ...")
log.console(f"Copied {CONFIG} -> {local}")
copy(root.joinpath(CONFIG), local.joinpath(CONFIG))

# Reaper
log.console(f"Copying reaper -> {local} ...")
log.console(f"Copied reaper -> {local}")
copy(root.joinpath("reaper"), local.joinpath("reaper"))

# Runtime platform
Expand All @@ -239,15 +228,15 @@ def _install_umu(
# Launcher files
for file in root.glob("*.py"):
if not file.name.startswith("umu_test"):
log.console(f"Copying {file} -> {local} ...")
log.console(f"Copied {file} -> {local}")
copy(file, local.joinpath(file.name))

local.joinpath("umu-run").symlink_to("umu_run.py")

# Runner
steam_compat.mkdir(parents=True, exist_ok=True)

log.console(f"Copying umu-launcher -> {steam_compat} ...")
log.console(f"Copied umu-launcher -> {steam_compat}")

# Remove existing files if they exist -- this is a clean install.
if steam_compat.joinpath("umu-launcher").is_dir():
Expand Down

0 comments on commit 670f001

Please sign in to comment.