├── .github ├── dependabot.yml └── workflows │ ├── build-umu-debian-12.yml │ ├── build-umu-debian-13.yml │ ├── build-umu-fedora-40.yml │ ├── build-umu-fedora-41.yml │ ├── build-umu-nix-flake.yml │ ├── build-umu-publish-release.yml │ ├── build-umu-ubuntu-noble.yml │ ├── build-umu-version.yml │ ├── build-umu-zipapp.yml │ ├── build.yml │ ├── e2e.yml │ ├── make.yml │ ├── release.yml │ ├── static.yml │ ├── umu-python.yml │ └── update-umu-nix-flake.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile.in ├── README.md ├── changelog_gen.sh ├── configure.sh ├── docs ├── umu.1.scd └── umu.5.scd ├── packaging ├── deb │ ├── 0001-deb-fix-build-by-using-rustup.patch │ ├── README.md │ ├── debian │ │ ├── bookworm │ │ │ ├── apparmor │ │ │ │ └── bwrap-userns-restrict-umu │ │ │ ├── changelog │ │ │ ├── control │ │ │ ├── copyright │ │ │ ├── python3-umu-launcher.install │ │ │ ├── python3-umu-launcher.postinst │ │ │ ├── python3-umu-launcher.postrm │ │ │ ├── rules │ │ │ ├── source │ │ │ │ ├── format │ │ │ │ └── options │ │ │ └── upstream │ │ │ │ └── metadata │ │ └── trixie │ │ │ ├── changelog │ │ │ ├── control │ │ │ ├── copyright │ │ │ ├── python3-umu-launcher.install │ │ │ ├── python3-umu-launcher.postinst │ │ │ ├── python3-umu-launcher.postrm │ │ │ ├── rules │ │ │ ├── source │ │ │ ├── format │ │ │ └── options │ │ │ └── upstream │ │ │ └── metadata │ └── ubuntu │ │ ├── apparmor │ │ └── bwrap-userns-restrict-umu │ │ ├── changelog │ │ ├── control │ │ ├── copyright │ │ ├── python3-umu-launcher.install │ │ ├── python3-umu-launcher.postinst │ │ ├── python3-umu-launcher.postrm │ │ ├── rules │ │ ├── source │ │ ├── format │ │ └── options │ │ └── upstream │ │ └── metadata ├── flatpak │ ├── README.md │ └── org.openwinecomponents.umu.umu-launcher.yml ├── nix │ ├── flake.lock │ ├── flake.nix │ ├── package.nix │ └── unwrapped.nix ├── rpm │ ├── README.md │ └── umu-launcher.spec └── snap │ ├── README.md │ ├── snap │ ├── hooks │ │ └── configure │ ├── keys │ │ └── B05498B7.asc │ └── snapcraft.yaml │ └── src │ ├── desktop-launch │ ├── glxgears │ ├── glxinfo │ ├── nvidia32 │ ├── vkcube │ └── vulkaninfo ├── pdm.lock ├── pyproject.toml ├── release_notes.md ├── requirements.in ├── src └── lib.rs ├── tests ├── test_config.sh ├── test_delta.py ├── test_flock.sh ├── test_install.sh ├── test_install_obsolete.sh ├── test_offline.sh ├── test_proton_resume.sh ├── test_resume.sh ├── test_update.sh ├── test_winetricks.sh └── testdata │ └── winetricks_verbs.txt ├── tools ├── WIP-notes.txt ├── umu-run-cli └── umu-run-posix └── umu ├── __init__.py ├── __main__.py ├── umu-launcher ├── compatibilitytool.vdf ├── toolmanifest.vdf └── umu-run.in ├── umu-run.in ├── umu_bspatch.py ├── umu_consts.py ├── umu_log.py ├── umu_plugins.py ├── umu_proton.py ├── umu_run.py ├── umu_runtime.py ├── umu_test.py ├── umu_test_plugins.py └── umu_util.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | allow: 8 | - dependency-type: "direct" 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-debian-12.yml: -------------------------------------------------------------------------------- 1 | name: UMU Deb Build - Debian 12 2 | on: 3 | workflow_call: 4 | inputs: 5 | version: 6 | required: true 7 | type: string 8 | 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | container: 14 | image: debian:bookworm 15 | volumes: 16 | - /proc:/proc 17 | options: --privileged -it 18 | 19 | steps: 20 | - name: Update APT Cache 21 | run: apt update -y 22 | 23 | - name: Install build dependencies 24 | run: apt install -y dh-make dpkg-dev git 25 | 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Configure Git safe directory 31 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 32 | 33 | - name: Extract Version and SHA 34 | run: | 35 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 36 | echo $VERSION 37 | 38 | # Get current commit SHA 39 | COMMIT_SHA=$(git rev-parse HEAD) 40 | echo $COMMIT_SHA 41 | 42 | # Store values in environment file 43 | echo "VERSION=$VERSION" >> $GITHUB_ENV 44 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 45 | 46 | # Always insert latest version into Makefile.in 47 | sed -i "s|^VERSION := .*$|VERSION := ${VERSION}|g" Makefile.in 48 | 49 | - name: Initialize submodules 50 | run: git submodule update --init --recursive 51 | 52 | - name: Copy debian packaging folder to the repository root 53 | run: cp -rvf ./packaging/deb/debian/bookworm ./debian 54 | 55 | - name: Setup dh_make quilt files 56 | run: | 57 | sed -re 's|(umu-launcher \()[1-9.]+(-1\).*$)|\1${{ env.VERSION }}\2|' -i ./debian/changelog 58 | LOGNAME=root dh_make --createorig -y -l -p umu-launcher_${{ env.VERSION }} || true 59 | 60 | - name: Install apt build dependencies 61 | run: apt build-dep -y ./ 62 | 63 | - name: Build 64 | run: dpkg-buildpackage --no-sign 65 | 66 | - name: Move and rename DEB files to upload artifact path 67 | run: | 68 | mkdir -p results 69 | mv ../python3-umu-launcher_${{ env.VERSION }}-1_amd64.deb \ 70 | results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-12.deb 71 | mv ../umu-launcher_${{ env.VERSION }}-1_all.deb \ 72 | results/umu-launcher_${{ env.VERSION }}-1_all_debian-12.deb 73 | 74 | - name: Upload artifact 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-12.deb 78 | path: results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-12.deb 79 | 80 | - name: Upload artifact 81 | uses: actions/upload-artifact@v4 82 | with: 83 | name: umu-launcher_${{ env.VERSION }}-1_all_debian-12.deb 84 | path: results/umu-launcher_${{ env.VERSION }}-1_all_debian-12.deb 85 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-debian-13.yml: -------------------------------------------------------------------------------- 1 | name: UMU Deb Build - Debian 13 2 | on: 3 | workflow_call: 4 | inputs: 5 | version: 6 | required: true 7 | type: string 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: debian:trixie 14 | volumes: 15 | - /proc:/proc 16 | options: --privileged -it 17 | 18 | steps: 19 | - name: Update APT Cache 20 | run: apt update -y 21 | 22 | - name: Install build dependencies 23 | run: apt install -y dh-make dpkg-dev git 24 | 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Configure Git safe directory 30 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 31 | 32 | - name: Extract Version and SHA 33 | run: | 34 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 35 | echo $VERSION 36 | 37 | # Get current commit SHA 38 | COMMIT_SHA=$(git rev-parse HEAD) 39 | echo $COMMIT_SHA 40 | 41 | # Store values in environment file 42 | echo "VERSION=$VERSION" >> $GITHUB_ENV 43 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 44 | 45 | # Always insert latest version into Makefile.in 46 | sed -i "s|^VERSION := .*$|VERSION := ${VERSION}|g" Makefile.in 47 | 48 | - name: Initialize submodules 49 | run: git submodule update --init --recursive 50 | 51 | - name: Copy debian packaging folder to the repository root 52 | run: cp -rvf ./packaging/deb/debian/trixie ./debian 53 | 54 | - name: Setup dh_make quilt files 55 | run: | 56 | sed -re 's|(umu-launcher \()[1-9.]+(-1\).*$)|\1${{ env.VERSION }}\2|' -i ./debian/changelog 57 | LOGNAME=root dh_make --createorig -y -l -p umu-launcher_${{ env.VERSION }} || true 58 | 59 | - name: Install apt build dependencies 60 | run: apt build-dep -y ./ 61 | 62 | - name: Build 63 | run: dpkg-buildpackage --no-sign 64 | 65 | - name: Move and rename DEB files to upload artifact path 66 | run: | 67 | mkdir -p results 68 | mv ../python3-umu-launcher_${{ env.VERSION }}-1_amd64.deb \ 69 | results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-13.deb 70 | mv ../umu-launcher_${{ env.VERSION }}-1_all.deb \ 71 | results/umu-launcher_${{ env.VERSION }}-1_all_debian-13.deb 72 | 73 | - name: Upload artifact 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-13.deb 77 | path: results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_debian-13.deb 78 | 79 | - name: Upload artifact 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: umu-launcher_${{ env.VERSION }}-1_all_debian-13.deb 83 | path: results/umu-launcher_${{ env.VERSION }}-1_all_debian-13.deb 84 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-fedora-40.yml: -------------------------------------------------------------------------------- 1 | name: UMU RPM Build - Fedora/Nobara 40 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | version: 7 | required: false 8 | type: string 9 | shasum: 10 | required: false 11 | type: string 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | container: 17 | image: fedora:40 18 | 19 | steps: 20 | - name: Install Git 21 | run: dnf install -y git 22 | 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | 28 | - name: Configure Git safe directory 29 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 30 | 31 | - name: Install build dependencies 32 | run: dnf install -y rpm-build meson ninja-build cmake g++ gcc-c++ scdoc git python3-devel python3-build python3-installer python3-hatchling python python3 cargo python3-hatch-vcs python3-wheel libzstd-devel python3-pyzstd python3-xlib wget 33 | 34 | - name: Extract Version and SHA 35 | run: | 36 | # Get version from git tags (assuming semantic versioning format) 37 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 38 | echo $VERSION 39 | 40 | # Get current commit SHA 41 | COMMIT_SHA=$(git rev-parse HEAD) 42 | echo $COMMIT_SHA 43 | 44 | # Store values in environment file 45 | echo "VERSION=$VERSION" >> $GITHUB_ENV 46 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 47 | 48 | - name: Build the project 49 | run: | 50 | # Use either provided input or extracted value 51 | git submodule update --init --recursive 52 | sed -i "s|^VERSION := .*$|VERSION := ${{ env.VERSION }}|g" Makefile.in 53 | 54 | cd .. 55 | mkdir -p ~/rpmbuild/SOURCES/ 56 | cp -R umu-launcher umu-launcher-${{ env.VERSION }}/ 57 | tar -cvzf umu-launcher-${{ env.VERSION }}.tar.gz umu-launcher-${{ env.VERSION }} 58 | mv umu-launcher-${{ env.VERSION }}.tar.gz ~/rpmbuild/SOURCES/ 59 | rm -Rf umu-launcher-${{ env.VERSION }}/ 60 | wget https://github.com/urllib3/urllib3/releases/download/2.3.0/urllib3-2.3.0.tar.gz 61 | mv urllib3-2.3.0.tar.gz ~/rpmbuild/SOURCES/ 62 | cd umu-launcher/ 63 | 64 | sed -i "s|^%global tag .*|%global tag ${{ env.VERSION }}|g" packaging/rpm/umu-launcher.spec 65 | cat packaging/rpm/umu-launcher.spec | grep tag 66 | 67 | sed -i "s|^%global commit .*|%global commit ${{ env.COMMIT_SHA }}|g" packaging/rpm/umu-launcher.spec 68 | cat packaging/rpm/umu-launcher.spec | grep commit 69 | 70 | rpmbuild -ba packaging/rpm/umu-launcher.spec 71 | mv ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}*.rpm \ 72 | ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}.fc40.rpm 73 | 74 | - name: Fedora-40 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: umu-launcher-${{ env.VERSION }}.fc40.rpm 78 | path: ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}.fc40.rpm 79 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-fedora-41.yml: -------------------------------------------------------------------------------- 1 | name: UMU RPM Build - Fedora/Nobara 41 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | version: 7 | required: false 8 | type: string 9 | shasum: 10 | required: false 11 | type: string 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | container: 17 | image: fedora:41 18 | 19 | steps: 20 | - name: Install Git 21 | run: dnf install -y git 22 | 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | 28 | - name: Configure Git safe directory 29 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 30 | 31 | - name: Install build dependencies 32 | run: dnf install -y rpm-build meson ninja-build cmake g++ gcc-c++ scdoc git python3-devel python3-build python3-installer python3-hatchling python python3 cargo python3-hatch-vcs python3-wheel libzstd-devel python3-pyzstd python3-xlib wget 33 | 34 | - name: Extract Version and SHA 35 | run: | 36 | # Get version from git tags (assuming semantic versioning format) 37 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 38 | echo $VERSION 39 | 40 | # Get current commit SHA 41 | COMMIT_SHA=$(git rev-parse HEAD) 42 | echo $COMMIT_SHA 43 | 44 | # Store values in environment file 45 | echo "VERSION=$VERSION" >> $GITHUB_ENV 46 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 47 | 48 | - name: Build the project 49 | run: | 50 | # Use either provided input or extracted value 51 | git submodule update --init --recursive 52 | sed -i "s|^VERSION := .*$|VERSION := ${{ env.VERSION }}|g" Makefile.in 53 | 54 | cd .. 55 | mkdir -p ~/rpmbuild/SOURCES/ 56 | cp -R umu-launcher umu-launcher-${{ env.VERSION }}/ 57 | tar -cvzf umu-launcher-${{ env.VERSION }}.tar.gz umu-launcher-${{ env.VERSION }} 58 | mv umu-launcher-${{ env.VERSION }}.tar.gz ~/rpmbuild/SOURCES/ 59 | rm -Rf umu-launcher-${{ env.VERSION }}/ 60 | wget https://github.com/urllib3/urllib3/releases/download/2.3.0/urllib3-2.3.0.tar.gz 61 | mv urllib3-2.3.0.tar.gz ~/rpmbuild/SOURCES/ 62 | cd umu-launcher/ 63 | 64 | sed -i "s|^%global tag .*|%global tag ${{ env.VERSION }}|g" packaging/rpm/umu-launcher.spec 65 | cat packaging/rpm/umu-launcher.spec | grep tag 66 | 67 | sed -i "s|^%global commit .*|%global commit ${{ env.COMMIT_SHA }}|g" packaging/rpm/umu-launcher.spec 68 | cat packaging/rpm/umu-launcher.spec | grep commit 69 | 70 | rpmbuild -ba packaging/rpm/umu-launcher.spec 71 | mv ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}*.rpm \ 72 | ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}.fc41.rpm 73 | 74 | - name: Fedora-40 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: umu-launcher-${{ env.VERSION }}.fc41.rpm 78 | path: ~/rpmbuild/RPMS/x86_64/umu-launcher-${{ env.VERSION }}.fc41.rpm 79 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-nix-flake.yml: -------------------------------------------------------------------------------- 1 | name: UMU Nix Flake Build 2 | on: 3 | push: 4 | branches: [ "main" ] 5 | pull_request: 6 | branches: [ "main" ] 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | # TODO: setup binary cache 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Install nix 19 | uses: cachix/install-nix-action@v31 20 | with: 21 | github_access_token: ${{ github.token }} 22 | - name: Check flake inputs 23 | uses: DeterminateSystems/flake-checker-action@v9 24 | with: 25 | flake-lock-path: packaging/nix/flake.lock 26 | - name: Check flake outputs 27 | run: nix flake check ./packaging/nix 28 | - name: Build 29 | run: nix build ./packaging/nix 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-publish-release.yml: -------------------------------------------------------------------------------- 1 | name: UMU Publish Release 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | version: 7 | required: true 8 | type: string 9 | file1: 10 | required: true 11 | type: string 12 | name1: 13 | required: true 14 | type: string 15 | name2: 16 | type: string 17 | default: "" 18 | file2: 19 | type: string 20 | default: "" 21 | 22 | 23 | jobs: 24 | release: 25 | name: Publish 26 | runs-on: ubuntu-latest 27 | strategy: 28 | matrix: 29 | include: 30 | - name: ${{ inputs.name1 }} 31 | file: ${{ inputs.file1 }} 32 | - name: ${{ inputs.name2 }} 33 | file: ${{ inputs.file2 }} 34 | steps: 35 | - name: Download ${{ matrix.name }} from artifact 36 | uses: actions/download-artifact@v4 37 | if: ${{ matrix.name != '' }} 38 | with: 39 | name: ${{ matrix.name }} 40 | - name: Upload ${{ matrix.name }} to release 41 | uses: svenstaro/upload-release-action@v2 42 | if: ${{ matrix.name != '' }} 43 | with: 44 | repo_token: ${{ secrets.GITHUB_TOKEN }} 45 | file: ${{ matrix.file }} 46 | asset_name: ${{ matrix.name }} 47 | tag: ${{ inputs.version }} 48 | overwrite: true 49 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-ubuntu-noble.yml: -------------------------------------------------------------------------------- 1 | name: UMU Deb Build - Noble (Ubuntu 24.04 LTS) 2 | on: 3 | workflow_call: 4 | inputs: 5 | version: 6 | required: true 7 | type: string 8 | 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | container: 14 | image: ubuntu:noble 15 | volumes: 16 | - /proc:/proc 17 | options: --privileged -it 18 | 19 | steps: 20 | - name: Update APT Cache 21 | run: apt update -y 22 | 23 | - name: Install build dependencies 24 | run: apt install -y dh-make dpkg-dev git 25 | 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | 30 | - name: Configure Git safe directory 31 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 32 | 33 | - name: Extract Version and SHA 34 | run: | 35 | # Get version from git tags (assuming semantic versioning format) 36 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 37 | echo $VERSION 38 | 39 | # Get current commit SHA 40 | COMMIT_SHA=$(git rev-parse HEAD) 41 | echo $COMMIT_SHA 42 | 43 | # Store values in environment file 44 | echo "VERSION=$VERSION" >> $GITHUB_ENV 45 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 46 | 47 | # Always insert latest version into Makefile.in 48 | sed -i "s|^VERSION := .*$|VERSION := ${VERSION}|g" Makefile.in 49 | 50 | - name: Initialize submodules 51 | run: git submodule update --init --recursive 52 | 53 | - name: Copy debian packaging folder to the repository root 54 | run: cp -rvf ./packaging/deb/ubuntu ./debian 55 | 56 | - name: Setup dh_make quilt files 57 | run: | 58 | sed -re 's|(umu-launcher \()[1-9.]+(-1\).*$)|\1${{ env.VERSION }}\2|' -i ./debian/changelog 59 | LOGNAME=root dh_make --createorig -y -l -p umu-launcher_${{ env.VERSION }} || true 60 | 61 | - name: Install apt build dependencies 62 | run: apt build-dep -y ./ 63 | 64 | - name: Build 65 | run: dpkg-buildpackage --no-sign 66 | 67 | - name: Move and rename DEB files to upload artifact path 68 | run: | 69 | mkdir -p results 70 | mv ../python3-umu-launcher_${{ env.VERSION }}-1_amd64.deb \ 71 | results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_ubuntu-noble.deb 72 | mv ../umu-launcher_${{ env.VERSION }}-1_all.deb \ 73 | results/umu-launcher_${{ env.VERSION }}-1_all_ubuntu-noble.deb 74 | 75 | - name: Upload artifact 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: python3-umu-launcher_${{ env.VERSION }}-1_amd64_ubuntu-noble.deb 79 | path: results/python3-umu-launcher_${{ env.VERSION }}-1_amd64_ubuntu-noble.deb 80 | 81 | - name: Upload artifact 82 | uses: actions/upload-artifact@v4 83 | with: 84 | name: umu-launcher_${{ env.VERSION }}-1_all_ubuntu-noble.deb 85 | path: results/umu-launcher_${{ env.VERSION }}-1_all_ubuntu-noble.deb 86 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-version.yml: -------------------------------------------------------------------------------- 1 | name: UMU Describe Version 2 | 3 | on: 4 | workflow_call: 5 | outputs: 6 | version: 7 | value: "${{ jobs.version.outputs.tag_abbrev }}.${{ jobs.version.outputs.tag_offset }}" 8 | branch: 9 | value: "${{ jobs.version.outputs.branch }}" 10 | 11 | 12 | jobs: 13 | version: 14 | name: Version 15 | runs-on: ubuntu-latest 16 | outputs: 17 | tag_abbrev: ${{ steps.describe.outputs.tag_abbrev }} 18 | tag_offset: ${{ steps.describe.outputs.tag_offset }} 19 | sha_short: ${{ steps.describe.outputs.sha_short }} 20 | full_desc: ${{ steps.describe.outputs.full_desc }} 21 | branch: ${{ steps.describe.outputs.branch }} 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | - name: Describe 27 | id: describe 28 | shell: bash 29 | run: | 30 | tag_abbrev=$(git tag --sort=v:refname | grep -oE "(^[0-9]+\.[0-9]+(.[0-9]+)?)$" | tail -1) 31 | echo "tag_abbrev=$tag_abbrev" >> $GITHUB_OUTPUT 32 | echo "tag_offset=$(git rev-list $tag_abbrev..HEAD --count)" >> $GITHUB_OUTPUT 33 | echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT 34 | echo "full_desc=$(git describe --long --tags)" >> $GITHUB_OUTPUT 35 | echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 36 | -------------------------------------------------------------------------------- /.github/workflows/build-umu-zipapp.yml: -------------------------------------------------------------------------------- 1 | name: UMU Zipapp Build 2 | on: 3 | workflow_call: 4 | inputs: 5 | version: 6 | required: true 7 | type: string 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: debian:bookworm 14 | volumes: 15 | - /proc:/proc 16 | options: --privileged -it 17 | 18 | steps: 19 | - name: Update APT Cache 20 | run: apt update -y 21 | 22 | - name: Install build dependencies 23 | run: apt install -y python3-venv python3-all bash make scdoc python3-hatchling python3-installer python3-build cargo git 24 | 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Configure Git safe directory 30 | run: git config --global --add safe.directory "$GITHUB_WORKSPACE" 31 | 32 | - name: Extract Version and SHA 33 | run: | 34 | ls -la 35 | VERSION=$(git describe --tags --abbrev=0 || echo "unknown") 36 | echo $VERSION 37 | 38 | # Get current commit SHA 39 | COMMIT_SHA=$(git rev-parse HEAD) 40 | echo $COMMIT_SHA 41 | 42 | # Store values in environment file 43 | echo "VERSION=$VERSION" >> $GITHUB_ENV 44 | echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV 45 | 46 | # Always insert latest version into Makefile.in 47 | sed -i "s|^VERSION := .*$|VERSION := ${VERSION}|g" Makefile.in 48 | 49 | - name: Configure 50 | run: ./configure.sh --user-install 51 | 52 | - name: Build 53 | run: make all 54 | 55 | - name: Move DEB files to upload artifact path 56 | run: mkdir -p results && cp -rvf builddir/umu-run results/ 57 | 58 | - name: Create symlink for launchers 59 | # To preserve file mode bits and link file, use a tar archive. 60 | # See https://github.com/actions/upload-artifact?tab=readme-ov-file#permission-loss 61 | run: cd results && ln -s umu-run umu_run.py && mkdir umu && mv umu-run umu/ && mv umu_run.py umu/ && tar cvf umu-launcher-${{ env.VERSION }}-zipapp.tar umu/ 62 | 63 | - name: Upload artifact 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: umu-launcher-${{ env.VERSION }}-zipapp.tar 67 | path: results/umu-launcher-${{ env.VERSION }}-zipapp.tar 68 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and upload development artifacts 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | jobs: 10 | version: 11 | name: Describe 12 | uses: ./.github/workflows/build-umu-version.yml 13 | 14 | fedora40-build: 15 | name: Fedora 40 Build 16 | uses: ./.github/workflows/build-umu-fedora-40.yml 17 | needs: version 18 | with: 19 | version: ${{ needs.version.outputs.version }} 20 | shasum: ${{ github.sha }} 21 | 22 | fedora41-build: 23 | name: Fedora 41 Build 24 | uses: ./.github/workflows/build-umu-fedora-41.yml 25 | needs: version 26 | with: 27 | version: ${{ needs.version.outputs.version }} 28 | shasum: ${{ github.sha }} 29 | 30 | debian12-build: 31 | name: Debian 12 Build 32 | uses: ./.github/workflows/build-umu-debian-12.yml 33 | needs: version 34 | with: 35 | version: ${{ needs.version.outputs.version }} 36 | 37 | debian13-build: 38 | name: Debian 13 Build 39 | uses: ./.github/workflows/build-umu-debian-13.yml 40 | needs: version 41 | with: 42 | version: ${{ needs.version.outputs.version }} 43 | 44 | ubuntu-noble-build: 45 | name: Ubuntu 24.04 Build 46 | uses: ./.github/workflows/build-umu-ubuntu-noble.yml 47 | needs: version 48 | with: 49 | version: ${{ needs.version.outputs.version }} 50 | 51 | zipapp-build: 52 | name: Zipapp Build 53 | uses: ./.github/workflows/build-umu-zipapp.yml 54 | needs: version 55 | with: 56 | version: ${{ needs.version.outputs.version }} 57 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: e2e 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Install dependencies 21 | run: | 22 | sudo apt-get update 23 | echo "deb http://archive.ubuntu.com/ubuntu/ noble noble-updates noble-backports main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list 24 | sudo apt-get install meson scdoc python3-hatchling python3-build python3-installer python3-filelock shellcheck bubblewrap cargo libzstd1 python3-xxhash python3-cbor2 apparmor apparmor-profiles apparmor-profiles-extra python3-xlib python3-hatch-vcs 25 | sudo apparmor_parser /usr/share/apparmor/extra-profiles/bwrap-userns-restrict 26 | python3 -m pip install --upgrade pip 27 | pip install uv 28 | - name: Initialize submodules 29 | run: | 30 | git submodule update --init --recursive 31 | - name: Make user install 32 | run: | 33 | ./configure.sh --user-install 34 | make install 35 | - name: Make system install 36 | run: | 37 | ./configure.sh --prefix=/usr 38 | sudo make install 39 | - name: Run shellcheck 40 | run: | 41 | shellcheck tests/*.sh 42 | - name: Setup venv 43 | run: | 44 | uv venv --python 3.10 45 | - name: Test delta update 46 | run: | 47 | python3 tests/test_delta.py 48 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" "$HOME/.cache/umu" 49 | - name: Test steamrt install 50 | run: | 51 | source .venv/bin/activate 52 | sh tests/test_install.sh 53 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" 54 | - name: Test obsolete steamrt install 55 | run: | 56 | source .venv/bin/activate 57 | sh tests/test_install_obsolete.sh 58 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" 59 | - name: Test steamrt update 60 | run: | 61 | source .venv/bin/activate 62 | sh tests/test_update.sh 63 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" 64 | - name: Test steamrt resume 65 | run: | 66 | source .venv/bin/activate 67 | sh tests/test_resume.sh 68 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" "$HOME/.cache/umu" 69 | - name: Test Proton resume 70 | run: | 71 | source .venv/bin/activate 72 | sh tests/test_proton_resume.sh 73 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" "$HOME/.cache/umu" 74 | - name: Test Filelock 75 | run: | 76 | source .venv/bin/activate 77 | sh tests/test_flock.sh 78 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" "$HOME/.cache/umu" 79 | - name: Test winetricks 80 | run: | 81 | source .venv/bin/activate 82 | sh tests/test_winetricks.sh 83 | rm -rf "$HOME/.local/share/umu" "$HOME/.local/share/Steam/compatibilitytools.d" 84 | - name: Test configuration file 85 | run: | 86 | uv venv --python 3.11 87 | source .venv/bin/activate 88 | sh tests/test_config.sh 89 | rm -rf "$HOME/.local/share/umu" "$HOME/Games/umu" "$HOME/.local/share/Steam/compatibilitytools.d" "$HOME/.cache/umu" 90 | - name: Test offline launch 91 | run: | 92 | source .venv/bin/activate 93 | sh tests/test_offline.sh 94 | -------------------------------------------------------------------------------- /.github/workflows/make.yml: -------------------------------------------------------------------------------- 1 | name: make 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Install dependencies 21 | run: | 22 | sudo apt-get install meson shellcheck scdoc python3-hatchling python3-build python3-installer python3-filelock cargo python3-hatch-vcs python3-wheel libzstd-dev 23 | - name: Initialize submodules 24 | run: | 25 | git submodule update --init --recursive 26 | - name: Lint using Shellcheck 27 | run: | 28 | shellcheck configure.sh 29 | - name: Make system package 30 | run: | 31 | ./configure.sh --prefix=/usr 32 | make DESTDIR=dist install 33 | - name: Make release 34 | run: | 35 | # TODO: Figure out release without requiring root 36 | sudo make release 37 | - name: Make user install 38 | run: | 39 | ./configure.sh --user-install 40 | # TODO: Figure out user install without requiring root 41 | sudo make install 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build and upload release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | fedora40-build: 9 | name: Fedora 40 Build 10 | uses: ./.github/workflows/build-umu-fedora-40.yml 11 | with: 12 | version: ${{ github.ref_name }} 13 | shasum: ${{ github.sha }} 14 | fedora40-release: 15 | name: Fedora 40 Release ${{ github.ref_name }} 16 | needs: fedora40-build 17 | uses: ./.github/workflows/build-umu-publish-release.yml 18 | with: 19 | version: ${{ github.ref_name }} 20 | file1: umu-launcher-${{ github.ref_name }}.fc40.rpm 21 | name1: umu-launcher-${{ github.ref_name }}.fc40.rpm 22 | 23 | fedora41-build: 24 | name: Fedora 41 Build 25 | uses: ./.github/workflows/build-umu-fedora-41.yml 26 | with: 27 | version: ${{ github.ref_name }} 28 | shasum: ${{ github.sha }} 29 | fedora41-release: 30 | name: Fedora 41 Release ${{ github.ref_name }} 31 | needs: fedora41-build 32 | uses: ./.github/workflows/build-umu-publish-release.yml 33 | with: 34 | version: ${{ github.ref_name }} 35 | file1: umu-launcher-${{ github.ref_name }}.fc41.rpm 36 | name1: umu-launcher-${{ github.ref_name }}.fc41.rpm 37 | 38 | debian12-build: 39 | name: Debian 12 Build 40 | uses: ./.github/workflows/build-umu-debian-12.yml 41 | with: 42 | version: ${{ github.ref_name }} 43 | debian12-release: 44 | name: Debian 12 Release ${{ github.ref_name }} 45 | needs: debian12-build 46 | uses: ./.github/workflows/build-umu-publish-release.yml 47 | with: 48 | version: ${{ github.ref_name }} 49 | file1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_debian-12.deb 50 | name1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_debian-12.deb 51 | file2: umu-launcher_${{ github.ref_name }}-1_all_debian-12.deb 52 | name2: umu-launcher_${{ github.ref_name }}-1_all_debian-12.deb 53 | 54 | debian13-build: 55 | name: Debian 13 Build 56 | uses: ./.github/workflows/build-umu-debian-13.yml 57 | with: 58 | version: ${{ github.ref_name }} 59 | debian13-release: 60 | name: Debian 13 Release ${{ github.ref_name }} 61 | needs: debian13-build 62 | uses: ./.github/workflows/build-umu-publish-release.yml 63 | with: 64 | version: ${{ github.ref_name }} 65 | file1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_debian-13.deb 66 | name1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_debian-13.deb 67 | file2: umu-launcher_${{ github.ref_name }}-1_all_debian-13.deb 68 | name2: umu-launcher_${{ github.ref_name }}-1_all_debian-13.deb 69 | 70 | ubuntu-noble-build: 71 | name: Ubuntu 24.04 Build 72 | uses: ./.github/workflows/build-umu-ubuntu-noble.yml 73 | with: 74 | version: ${{ github.ref_name }} 75 | ubuntu-noble-release: 76 | name: Ubuntu 24.04 Release ${{ github.ref_name }} 77 | needs: ubuntu-noble-build 78 | uses: ./.github/workflows/build-umu-publish-release.yml 79 | with: 80 | version: ${{ github.ref_name }} 81 | file1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_ubuntu-noble.deb 82 | name1: python3-umu-launcher_${{ github.ref_name }}-1_amd64_ubuntu-noble.deb 83 | file2: umu-launcher_${{ github.ref_name }}-1_all_ubuntu-noble.deb 84 | name2: umu-launcher_${{ github.ref_name }}-1_all_ubuntu-noble.deb 85 | 86 | zipapp-build: 87 | name: Zipapp Build 88 | uses: ./.github/workflows/build-umu-zipapp.yml 89 | with: 90 | version: ${{ github.ref_name }} 91 | zipapp-release: 92 | name: Zippapp Release ${{ github.ref_name }} 93 | needs: zipapp-build 94 | uses: ./.github/workflows/build-umu-publish-release.yml 95 | with: 96 | version: ${{ github.ref_name }} 97 | file1: umu-launcher-${{ github.ref_name }}-zipapp.tar 98 | name1: umu-launcher-${{ github.ref_name }}-zipapp.tar 99 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: mypy 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | version: ["3.10"] 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Set up Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{ matrix.version }} 26 | - name: Install dependencies 27 | run: | 28 | python3 -m pip install --upgrade pip uv mypy 29 | - name: Setup venv 30 | run: | 31 | uv venv --python 3.10 32 | source .venv/bin/activate 33 | uv pip install -r requirements.in 34 | - name: Check types with mypy 35 | run: | 36 | source .venv/bin/activate 37 | mypy . 38 | -------------------------------------------------------------------------------- /.github/workflows/umu-python.yml: -------------------------------------------------------------------------------- 1 | name: umu-launcher 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | # tomllib requires Python 3.11 17 | # Ubuntu latest (Jammy) provides Python 3.10 18 | version: ["3.10", "3.11", "3.12", "3.13"] 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Set up Python 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: ${{ matrix.version }} 28 | - name: Install dependencies 29 | run: | 30 | python3 -m pip install --upgrade pip 31 | pip install ruff 32 | pip install python-xlib 33 | pip install filelock 34 | pip install uv 35 | - name: Lint umu_*.py files with Ruff 36 | run: | 37 | pip install ruff 38 | ruff check --output-format github ./umu/umu_*.py 39 | - name: Setup venv 40 | run: | 41 | uv venv --python $pyver 42 | source .venv/bin/activate 43 | uv pip install -r requirements.in 44 | uv sync --all-extras 45 | maturin develop -r 46 | mv target/release/libumu_delta.so umu/umu_delta.so 47 | env: 48 | pyver: ${{ matrix.version }} 49 | - name: Test with unittest 50 | run: | 51 | source .venv/bin/activate 52 | python3 ./umu/umu_test.py 53 | - name: Test with unittest for plugins 54 | if: ${{ matrix.version == '3.11' || matrix.version == '3.12' || matrix.version == '3.13' }} 55 | run: | 56 | source .venv/bin/activate 57 | python3 ./umu/umu_test_plugins.py 58 | -------------------------------------------------------------------------------- /.github/workflows/update-umu-nix-flake.yml: -------------------------------------------------------------------------------- 1 | name: UMU Update flake.lock 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | # Every Saturday at noon 6 | - cron: '0 12 * * sat' 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | flake_lock: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | - name: Install nix 19 | uses: cachix/install-nix-action@v31 20 | with: 21 | github_access_token: ${{ github.token }} 22 | - name: Update flake lockfile 23 | id: update 24 | uses: DeterminateSystems/update-flake-lock@main 25 | with: 26 | # NOTE: PR pushes by @github-actions don't trigger CI. 27 | # One workaround is to provide a Personal Access Token, 28 | # but then the PAT-owner is marked as the PR author. 29 | # TODO: Consider providing a PAT. Maybe of a "bot account"? 30 | path-to-flake-dir: packaging/nix 31 | branch: update_flake/${{ github.ref_name }} 32 | pr-labels: | 33 | dependencies 34 | github_actions 35 | pr-reviewers: | 36 | beh-10257 37 | MattSturgeon 38 | LovingMelody 39 | - name: Print summary 40 | env: 41 | num: ${{ steps.update.outputs.pull-request-number }} 42 | url: ${{ steps.update.outputs.pull-request-url }} 43 | operation: ${{ steps.update.outputs.pull-request-operation }} 44 | run: | 45 | echo "Pull request #$num was $operation." 46 | echo "$url" 47 | ( 48 | echo "Pull request" 49 | echo 50 | echo "[#$num]($url) was $operation." 51 | echo 52 | ) >> $GITHUB_STEP_SUMMARY 53 | 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | var 2 | builddir 3 | __pycache__ 4 | .ref 5 | umu_version.json 6 | umu_version.json.in.tmp 7 | Makefile 8 | /debian 9 | umu_delta*.so 10 | target 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "subprojects/pyzstd"] 2 | path = subprojects/pyzstd 3 | url = https://github.com/Rogdham/pyzstd.git 4 | [submodule "subprojects/urllib3"] 5 | path = subprojects/urllib3 6 | url = https://github.com/urllib3/urllib3.git 7 | [submodule "subprojects/python-xlib"] 8 | path = subprojects/python-xlib 9 | url = https://github.com/python-xlib/python-xlib.git 10 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.4.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 10 | 11 | [[package]] 12 | name = "base16ct" 13 | version = "0.2.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 16 | 17 | [[package]] 18 | name = "base64ct" 19 | version = "1.6.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 22 | 23 | [[package]] 24 | name = "block-buffer" 25 | version = "0.10.4" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 28 | dependencies = [ 29 | "generic-array", 30 | ] 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 37 | 38 | [[package]] 39 | name = "cipher" 40 | version = "0.4.4" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 43 | dependencies = [ 44 | "crypto-common", 45 | "inout", 46 | ] 47 | 48 | [[package]] 49 | name = "cpufeatures" 50 | version = "0.2.16" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" 53 | dependencies = [ 54 | "libc", 55 | ] 56 | 57 | [[package]] 58 | name = "crypto-common" 59 | version = "0.1.6" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 62 | dependencies = [ 63 | "generic-array", 64 | "typenum", 65 | ] 66 | 67 | [[package]] 68 | name = "curve25519-dalek" 69 | version = "4.1.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 72 | dependencies = [ 73 | "cfg-if", 74 | "cpufeatures", 75 | "curve25519-dalek-derive", 76 | "digest", 77 | "fiat-crypto", 78 | "rustc_version", 79 | "subtle", 80 | ] 81 | 82 | [[package]] 83 | name = "curve25519-dalek-derive" 84 | version = "0.1.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 87 | dependencies = [ 88 | "proc-macro2", 89 | "quote", 90 | "syn", 91 | ] 92 | 93 | [[package]] 94 | name = "digest" 95 | version = "0.10.7" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 98 | dependencies = [ 99 | "block-buffer", 100 | "crypto-common", 101 | ] 102 | 103 | [[package]] 104 | name = "ed25519" 105 | version = "2.2.3" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 108 | dependencies = [ 109 | "signature", 110 | ] 111 | 112 | [[package]] 113 | name = "ed25519-dalek" 114 | version = "2.1.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" 117 | dependencies = [ 118 | "curve25519-dalek", 119 | "ed25519", 120 | "sha2", 121 | "subtle", 122 | ] 123 | 124 | [[package]] 125 | name = "fiat-crypto" 126 | version = "0.2.9" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 129 | 130 | [[package]] 131 | name = "generic-array" 132 | version = "0.14.7" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 135 | dependencies = [ 136 | "typenum", 137 | "version_check", 138 | ] 139 | 140 | [[package]] 141 | name = "heck" 142 | version = "0.5.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 145 | 146 | [[package]] 147 | name = "indoc" 148 | version = "2.0.5" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 151 | 152 | [[package]] 153 | name = "inout" 154 | version = "0.1.3" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 157 | dependencies = [ 158 | "generic-array", 159 | ] 160 | 161 | [[package]] 162 | name = "libc" 163 | version = "0.2.169" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 166 | 167 | [[package]] 168 | name = "memoffset" 169 | version = "0.9.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 172 | dependencies = [ 173 | "autocfg", 174 | ] 175 | 176 | [[package]] 177 | name = "once_cell" 178 | version = "1.20.2" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 181 | 182 | [[package]] 183 | name = "pem-rfc7468" 184 | version = "0.7.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 187 | dependencies = [ 188 | "base64ct", 189 | ] 190 | 191 | [[package]] 192 | name = "portable-atomic" 193 | version = "1.10.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" 196 | 197 | [[package]] 198 | name = "proc-macro2" 199 | version = "1.0.92" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 202 | dependencies = [ 203 | "unicode-ident", 204 | ] 205 | 206 | [[package]] 207 | name = "pyo3" 208 | version = "0.25.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "f239d656363bcee73afef85277f1b281e8ac6212a1d42aa90e55b90ed43c47a4" 211 | dependencies = [ 212 | "indoc", 213 | "libc", 214 | "memoffset", 215 | "once_cell", 216 | "portable-atomic", 217 | "pyo3-build-config", 218 | "pyo3-ffi", 219 | "pyo3-macros", 220 | "unindent", 221 | ] 222 | 223 | [[package]] 224 | name = "pyo3-build-config" 225 | version = "0.25.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "755ea671a1c34044fa165247aaf6f419ca39caa6003aee791a0df2713d8f1b6d" 228 | dependencies = [ 229 | "once_cell", 230 | "target-lexicon", 231 | ] 232 | 233 | [[package]] 234 | name = "pyo3-ffi" 235 | version = "0.25.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "fc95a2e67091e44791d4ea300ff744be5293f394f1bafd9f78c080814d35956e" 238 | dependencies = [ 239 | "libc", 240 | "pyo3-build-config", 241 | ] 242 | 243 | [[package]] 244 | name = "pyo3-macros" 245 | version = "0.25.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "a179641d1b93920829a62f15e87c0ed791b6c8db2271ba0fd7c2686090510214" 248 | dependencies = [ 249 | "proc-macro2", 250 | "pyo3-macros-backend", 251 | "quote", 252 | "syn", 253 | ] 254 | 255 | [[package]] 256 | name = "pyo3-macros-backend" 257 | version = "0.25.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "9dff85ebcaab8c441b0e3f7ae40a6963ecea8a9f5e74f647e33fcf5ec9a1e89e" 260 | dependencies = [ 261 | "heck", 262 | "proc-macro2", 263 | "pyo3-build-config", 264 | "quote", 265 | "syn", 266 | ] 267 | 268 | [[package]] 269 | name = "quote" 270 | version = "1.0.38" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 273 | dependencies = [ 274 | "proc-macro2", 275 | ] 276 | 277 | [[package]] 278 | name = "rand_core" 279 | version = "0.6.4" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 282 | 283 | [[package]] 284 | name = "rustc_version" 285 | version = "0.4.1" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 288 | dependencies = [ 289 | "semver", 290 | ] 291 | 292 | [[package]] 293 | name = "semver" 294 | version = "1.0.24" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" 297 | 298 | [[package]] 299 | name = "sha2" 300 | version = "0.10.9" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 303 | dependencies = [ 304 | "cfg-if", 305 | "cpufeatures", 306 | "digest", 307 | ] 308 | 309 | [[package]] 310 | name = "signature" 311 | version = "2.2.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 314 | 315 | [[package]] 316 | name = "ssh-cipher" 317 | version = "0.2.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" 320 | dependencies = [ 321 | "cipher", 322 | "ssh-encoding", 323 | ] 324 | 325 | [[package]] 326 | name = "ssh-encoding" 327 | version = "0.2.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" 330 | dependencies = [ 331 | "base64ct", 332 | "pem-rfc7468", 333 | "sha2", 334 | ] 335 | 336 | [[package]] 337 | name = "ssh-key" 338 | version = "0.6.7" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" 341 | dependencies = [ 342 | "ed25519-dalek", 343 | "rand_core", 344 | "sha2", 345 | "signature", 346 | "ssh-cipher", 347 | "ssh-encoding", 348 | "subtle", 349 | "zeroize", 350 | ] 351 | 352 | [[package]] 353 | name = "subtle" 354 | version = "2.6.1" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 357 | 358 | [[package]] 359 | name = "syn" 360 | version = "2.0.96" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 363 | dependencies = [ 364 | "proc-macro2", 365 | "quote", 366 | "unicode-ident", 367 | ] 368 | 369 | [[package]] 370 | name = "target-lexicon" 371 | version = "0.13.2" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 374 | 375 | [[package]] 376 | name = "typenum" 377 | version = "1.17.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 380 | 381 | [[package]] 382 | name = "umu" 383 | version = "0.1.0" 384 | dependencies = [ 385 | "base16ct", 386 | "pyo3", 387 | "sha2", 388 | "ssh-key", 389 | ] 390 | 391 | [[package]] 392 | name = "unicode-ident" 393 | version = "1.0.14" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 396 | 397 | [[package]] 398 | name = "unindent" 399 | version = "0.2.3" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" 402 | 403 | [[package]] 404 | name = "version_check" 405 | version = "0.9.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 408 | 409 | [[package]] 410 | name = "zeroize" 411 | version = "1.8.1" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 414 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "umu" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.65" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [lib] 9 | name = "umu_delta" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | pyo3 = { version = "0.25.0", features = ["extension-module"] } 14 | ssh-key = { version = "0.6.7", default-features = false, features = [ 15 | "ed25519", 16 | "alloc", 17 | ] } 18 | sha2 = "0.10.9" 19 | base16ct = { version = "0.2.0", features = ["alloc"] } 20 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | PROJECT := umu-launcher 2 | 3 | # Define the interpreters to use to prevent shebang complaints 4 | PYTHON_INTERPRETER = /usr/bin/env python3 5 | 6 | # If this is changed to umu (uppercase), `uninstall` target will also remove the SLR directory 7 | INSTALLDIR ?= umu 8 | 9 | OBJDIR := builddir 10 | 11 | PREFIX ?= /usr 12 | BINDIR := $(PREFIX)/bin 13 | LIBDIR := $(PREFIX)/lib 14 | DATADIR := $(PREFIX)/share 15 | MANDIR := $(DATADIR)/man 16 | PYTHONDIR := $(shell $(PYTHON_INTERPRETER) -c "import site; print(site.getsitepackages()[0])") 17 | 18 | DESTDIR ?= 19 | USERINSTALL ?= xfalse 20 | FLATPAK ?= xfalse 21 | 22 | # For vendors that prefer to use native pyzstd and urllib3. 23 | # Ex. Arch and Fedora have pyzstd but ubuntu and debian don't 24 | USE_SYSTEM_PYZSTD ?= xfalse 25 | USE_SYSTEM_URLLIB ?= xfalse 26 | 27 | INSTALLER_ARGS := -m installer $(OBJDIR)/umu_launcher*.whl 28 | ifdef DESTDIR 29 | INSTALLER_ARGS += --destdir=$(DESTDIR) 30 | endif 31 | ifdef PREFIX 32 | INSTALLER_ARGS += --prefix=$(PREFIX) 33 | endif 34 | 35 | .PHONY: all 36 | ifeq ($(FLATPAK), xtrue) 37 | all: umu-dist umu-vendored 38 | endif 39 | 40 | .PHONY: install 41 | ifeq ($(USERINSTALL), xtrue) 42 | SOURCE_DATE_EPOCH = $(shell LC_ALL=C date --date='@1580601600') 43 | all: zipapp 44 | install: zipapp-install 45 | else 46 | all: umu-dist umu-docs umu-vendored umu-delta 47 | install: umu-install umu-vendored-install umu-delta-install 48 | endif 49 | 50 | 51 | $(OBJDIR)/.build-umu-docs: | $(OBJDIR) 52 | $(info :: Building umu man pages ) 53 | scdoc < docs/umu.1.scd > $(OBJDIR)/umu.1 54 | scdoc < docs/umu.5.scd > $(OBJDIR)/umu.5 55 | touch $(@) 56 | 57 | .PHONY: umu-docs 58 | umu-docs: $(OBJDIR)/.build-umu-docs 59 | 60 | umu-docs-install: umu-docs 61 | $(info :: Installing umu man pages ) 62 | install -d $(DESTDIR)$(MANDIR)/man1 63 | install -d $(DESTDIR)$(MANDIR)/man5 64 | install -m644 $(OBJDIR)/umu.1 $(DESTDIR)$(MANDIR)/man1/umu.1 65 | install -m644 $(OBJDIR)/umu.5 $(DESTDIR)$(MANDIR)/man5/umu.5 66 | 67 | 68 | $(OBJDIR)/.build-umu-dist: | $(OBJDIR) 69 | $(info :: Building umu ) 70 | $(PYTHON_INTERPRETER) -m build --wheel --skip-dependency-check --no-isolation --outdir=$(OBJDIR) 71 | touch $(@) 72 | 73 | .PHONY: umu-dist 74 | umu-dist: $(OBJDIR)/.build-umu-dist 75 | 76 | umu-dist-install: umu-dist 77 | $(info :: Installing umu ) 78 | install -d $(DESTDIR)$(PYTHONDIR)/$(INSTALLDIR) 79 | $(PYTHON_INTERPRETER) $(INSTALLER_ARGS) 80 | 81 | ifeq ($(FLATPAK), xtrue) 82 | umu-install: umu-dist-install umu-delta-install 83 | else 84 | umu-install: umu-dist-install umu-delta-install umu-docs-install 85 | endif 86 | 87 | 88 | $(OBJDIR)/.build-umu-vendored: | $(OBJDIR) 89 | $(info :: Building vendored dependencies ) 90 | @if [ "$(USE_SYSTEM_PYZSTD)" != "xtrue" ]; then \ 91 | sed -i 's/setuptools>=64,<74/setuptools/' subprojects/pyzstd/pyproject.toml; \ 92 | cd subprojects/pyzstd && $(PYTHON_INTERPRETER) -m build -wn -C=--build-option=--dynamic-link-zstd --outdir=$(OBJDIR); \ 93 | fi 94 | @if [ "$(USE_SYSTEM_URLLIB)" != "xtrue" ]; then \ 95 | cd subprojects/urllib3 && \ 96 | sed -i 's/license-files = \["LICENSE.txt"\]//g' pyproject.toml && \ 97 | $(PYTHON_INTERPRETER) -m build -wn --outdir=$(OBJDIR); \ 98 | fi 99 | touch $(@) 100 | 101 | .PHONY: umu-vendored 102 | umu-vendored: $(OBJDIR)/.build-umu-vendored 103 | 104 | umu-vendored-install: umu-vendored 105 | $(info :: Installing subprojects ) 106 | install -d $(DESTDIR)$(PYTHONDIR)/umu/_vendor 107 | @if [ "$(USE_SYSTEM_PYZSTD)" != "xtrue" ]; then \ 108 | $(PYTHON_INTERPRETER) -m installer --destdir=$(DESTDIR)$(PYTHONDIR)/umu/_vendor subprojects/pyzstd/$(OBJDIR)/pyzstd*.whl; \ 109 | find $(DESTDIR)$(PYTHONDIR)/umu/_vendor -type d -name pyzstd | xargs -I {} mv {} $(DESTDIR)$(PYTHONDIR)/umu/_vendor; \ 110 | fi 111 | @if [ "$(USE_SYSTEM_URLLIB)" != "xtrue" ]; then \ 112 | $(PYTHON_INTERPRETER) -m installer --destdir=$(DESTDIR)$(PYTHONDIR)/umu/_vendor subprojects/urllib3/$(OBJDIR)/urllib3*.whl; \ 113 | find $(DESTDIR)$(PYTHONDIR)/umu/_vendor -type d -name urllib3 | xargs -I {} mv {} $(DESTDIR)$(PYTHONDIR)/umu/_vendor; \ 114 | fi 115 | @if [ "$(USE_SYSTEM_PYZSTD)" != "xtrue" ] || [ "$(USE_SYSTEM_URLLIB)" != "xtrue" ]; then \ 116 | rm -r $(DESTDIR)$(PYTHONDIR)/umu/_vendor/$(PREFIX); \ 117 | fi 118 | 119 | $(OBJDIR): 120 | @mkdir -p $(@) 121 | 122 | 123 | .PHONY: clean 124 | clean: 125 | $(info :: Cleaning source directory ) 126 | @rm -rf -v ./$(RELEASEDIR) $(RELEASEDIR).tar.gz 127 | 128 | 129 | RELEASEDIR := $(PROJECT)-$(shell git describe --abbrev=0) 130 | $(RELEASEDIR): 131 | mkdir -p $(@) 132 | 133 | .PHONY: release 134 | release: $(RELEASEDIR) | zipapp 135 | $(info :: Creating source distribution for release ) 136 | mkdir -p $(<) 137 | rm -rf umu/__pycache__ 138 | cp -r umu packaging Makefile.in configure.sh README.md LICENSE $(<) 139 | tar -cvzf $(<).tar.gz $(<) 140 | cp -a $(ZIPAPP) . 141 | sha512sum umu-run > umu-run.sha512sum 142 | 143 | 144 | .PHONY: uninstall 145 | # NEVER use a wildcard here 146 | uninstall: 147 | $(info :: Removing $(INSTALLDIR) files in $(DESTDIR)$(BINDIR) ) 148 | @rm -rf -v --preserve-root=all $(DESTDIR)$(BINDIR)/umu-run 149 | 150 | 151 | ZIPAPP := $(OBJDIR)/umu-run 152 | ZIPAPP_STAGING := $(OBJDIR)/zipapp_staging 153 | ZIPAPP_VENV := $(OBJDIR)/zipapp_venv 154 | 155 | $(OBJDIR)/.build-zipapp: | $(OBJDIR) 156 | $(info :: Building umu-launcher as zipapp ) 157 | $(PYTHON_INTERPRETER) -m venv $(ZIPAPP_VENV) 158 | . $(ZIPAPP_VENV)/bin/activate && python3 -m pip install -t "$(ZIPAPP_STAGING)" -U --no-compile . truststore 159 | rm -r "$(ZIPAPP_STAGING)"/bin 160 | cp umu/__main__.py "$(ZIPAPP_STAGING)" 161 | find "$(ZIPAPP_STAGING)" -exec touch -h -d "$(SOURCE_DATE_EPOCH)" {} + 162 | . $(ZIPAPP_VENV)/bin/activate && python3 -m zipapp $(ZIPAPP_STAGING) -o $(ZIPAPP) -p "$(PYTHON_INTERPRETER)" -c 163 | touch -d "$(SOURCE_DATE_EPOCH)" $(ZIPAPP) 164 | touch $(@) 165 | 166 | .PHONY: zipapp 167 | zipapp: $(OBJDIR)/.build-zipapp 168 | 169 | zipapp-install: zipapp 170 | $(info :: Installing umu-launcher as zipapp ) 171 | install -d $(DESTDIR)$(PREFIX)/bin 172 | install -Dm755 -p $(ZIPAPP) $(DESTDIR)$(BINDIR) 173 | @echo "Standalone application 'umu-run' created at '$(DESTDIR)$(PREFIX)/bin'" 174 | 175 | PYTHON_PLATFORM_TAG = $(shell $(PYTHON_INTERPRETER) -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))') 176 | 177 | $(OBJDIR)/.build-umu-delta: | $(OBJDIR) 178 | $(info :: Building delta dependencies ) 179 | cargo build -r --target-dir $(OBJDIR) 180 | touch $(@) 181 | 182 | .PHONY: umu-delta 183 | umu-delta: $(OBJDIR)/.build-umu-delta 184 | 185 | umu-delta-install: umu-delta 186 | $(info :: Installing delta dependencies ) 187 | install -m755 $(OBJDIR)/release/libumu_delta.so $(DESTDIR)$(PYTHONDIR)/$(INSTALLDIR)/umu_delta$(PYTHON_PLATFORM_TAG) 188 | 189 | # vim: ft=make 190 | -------------------------------------------------------------------------------- /changelog_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export VERSION=$(git tag --sort=-v:refname | head -1) 3 | export PREVIOUS_VERSION=$(git tag --sort=-v:refname | head -2 | awk '{split($0, tags, "\n")} END {print tags[1]}') 4 | export CHANGES=$(git log --pretty="- %s" $VERSION...$PREVIOUS_VERSION) 5 | printf "# 🎁 Release notes (\`$VERSION\`)\n\n## Changes\n$CHANGES\n\n## Metadata\n\`\`\`\nThis version -------- $VERSION\nPrevious version ---- $PREVIOUS_VERSION\nTotal commits ------- $(echo "$CHANGES" | wc -l)\n\`\`\`\n" > release_notes.md 6 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | # Output helpers 6 | COLOR_ERR="" 7 | COLOR_STAT="" 8 | COLOR_INFO="" 9 | COLOR_CMD="" 10 | COLOR_CLEAR="" 11 | if [[ $(tput colors 2>/dev/null || echo 0) -gt 0 ]]; then 12 | COLOR_ERR=$'\e[31;1m' 13 | COLOR_STAT=$'\e[32;1m' 14 | COLOR_INFO=$'\e[30;1m' 15 | COLOR_CMD=$'\e[93;1m' 16 | COLOR_CLEAR=$'\e[0m' 17 | fi 18 | 19 | sh_quote() { 20 | local quoted 21 | quoted="$(printf '%q ' "$@")"; [[ $# -eq 0 ]] || echo "${quoted:0:-1}"; 22 | } 23 | err() { echo >&2 "${COLOR_ERR}!!${COLOR_CLEAR} $*"; } 24 | stat() { echo >&2 "${COLOR_STAT}::${COLOR_CLEAR} $*"; } 25 | info() { echo >&2 "${COLOR_INFO}::${COLOR_CLEAR} $*"; } 26 | showcmd() { echo >&2 "+ ${COLOR_CMD}$(sh_quote "$@")${COLOR_CLEAR}"; } 27 | die() { err "$@"; exit 1; } 28 | finish() { stat "$@"; exit 0; } 29 | cmd() { showcmd "$@"; "$@"; } 30 | 31 | 32 | # 33 | # Configure 34 | # 35 | 36 | THIS_COMMAND="$0 $*" # For printing, not evaling 37 | MAKEFILE="./Makefile" 38 | 39 | # This is not rigorous. Do not use this for untrusted input. Do not. If you need a version of 40 | # this for untrusted input, rethink the path that got you here. 41 | function escape_for_make() { 42 | local escape="$1" 43 | escape="${escape//\\/\\\\}" # '\' -> '\\' 44 | escape="${escape//#/\\#}" # '#' -> '\#' 45 | escape="${escape//\$/\$\$}" # '$' -> '$$' 46 | escape="${escape// /\\ }" # ' ' -> '\ ' 47 | echo "$escape" 48 | } 49 | 50 | function configure() { 51 | ## Checks before writing config 52 | if [[ -n "$arg_user_install" ]]; then 53 | arg_prefix="$HOME/.local" 54 | fi 55 | 56 | if [[ $arg_prefix != $(realpath "$arg_prefix") ]]; then 57 | die "PREFIX needs to be an absolute path" 58 | fi 59 | 60 | ## Write out config 61 | [[ ! -e "$MAKEFILE" ]] || rm "$MAKEFILE" 62 | 63 | { 64 | # Config 65 | echo "# Generated by: $THIS_COMMAND" 66 | echo "" 67 | if [[ -n "$arg_user_install" ]]; then 68 | echo "USERINSTALL := xtrue" 69 | echo "INSTALLDIR := umu-launcher" 70 | fi 71 | 72 | if [[ -n "$arg_use_system_pyzstd" ]]; then 73 | echo "USE_SYSTEM_PYZSTD := xtrue" 74 | fi 75 | if [[ -n "$arg_use_system_urllib" ]]; then 76 | echo "USE_SYSTEM_URLLIB := xtrue" 77 | fi 78 | 79 | # Prefix was specified, baking it into the Makefile 80 | if [[ -n $arg_prefix ]]; then 81 | echo "PREFIX := $(escape_for_make "$arg_prefix")" 82 | fi 83 | 84 | # Include base 85 | echo "" 86 | echo "include Makefile.in" 87 | } >> "$MAKEFILE" 88 | 89 | stat "Created $MAKEFILE, now run \"make\" to build." 90 | } 91 | 92 | # 93 | # Parse arguments 94 | # 95 | 96 | arg_prefix="" 97 | arg_user_install="" 98 | arg_help="" 99 | arg_use_system_pyzstd="" 100 | arg_use_system_urllib="" 101 | function parse_args() { 102 | local arg; 103 | local val; 104 | local val_used; 105 | local val_passed; 106 | if [[ $# -eq 0 ]]; then 107 | return 1 108 | fi 109 | while [[ $# -gt 0 ]]; do 110 | arg="$1" 111 | val='' 112 | val_used='' 113 | val_passed='' 114 | if [[ -z $arg ]]; then # Sanity 115 | err "Unexpected empty argument" 116 | return 1 117 | elif [[ ${arg:0:2} != '--' ]]; then 118 | err "Unexpected positional argument ($1)" 119 | return 1 120 | fi 121 | 122 | # Looks like an argument does it have a --foo=bar value? 123 | if [[ ${arg%=*} != "$arg" ]]; then 124 | val="${arg#*=}" 125 | arg="${arg%=*}" 126 | val_passed=1 127 | else 128 | # Otherwise for args that want a value, assume "--arg val" form 129 | val="${2:-}" 130 | fi 131 | 132 | # The args 133 | if [[ $arg = --help || $arg = --usage ]]; then 134 | arg_help=1 135 | elif [[ $arg = --prefix ]]; then 136 | if [[ -n $arg_user_install ]]; then 137 | die "--prefix cannot be used with --user-install" 138 | fi 139 | arg_prefix="$val" 140 | val_used=1 141 | elif [[ $arg = --user-install ]]; then 142 | if [[ -n $arg_prefix ]]; then 143 | die "--user-install cannot be used with --prefix" 144 | fi 145 | arg_user_install="1" 146 | elif [[ $arg = --use-system-pyzstd ]]; then 147 | arg_use_system_pyzstd="1" 148 | elif [[ $arg = --use-system-urllib ]]; then 149 | arg_use_system_urllib="1" 150 | else 151 | err "Unrecognized option $arg" 152 | return 1 153 | fi 154 | 155 | # Check if this arg used the value and shouldn't have or vice-versa 156 | if [[ -n $val_used && -z $val_passed ]]; then 157 | # "--arg val" form, used $2 as the value. 158 | 159 | # Don't allow this if it looked like "--arg --val" 160 | if [[ ${val#--} != "$val" ]]; then 161 | err "Ambiguous format for argument with value \"$arg $val\"" 162 | err " (use $arg=$val or $arg='' $val)" 163 | return 1 164 | fi 165 | 166 | # Error if this was the last positional argument but expected $val 167 | if [[ $# -le 1 ]]; then 168 | err "$arg takes a parameter, but none given" 169 | return 1 170 | fi 171 | 172 | shift # consume val 173 | elif [[ -z $val_used && -n $val_passed ]]; then 174 | # Didn't use a value, but passed in --arg=val form 175 | err "$arg does not take a parameter" 176 | return 1 177 | fi 178 | 179 | shift # consume arg 180 | done 181 | } 182 | 183 | usage() { 184 | "$1" "Usage: $0 { --prefix=path }" 185 | "$1" " Generate a Makefile for building umu" 186 | "$1" "" 187 | "$1" " Options" 188 | "$1" " --help" 189 | "$1" " --usage Show this help text and exit" 190 | "$1" "" 191 | "$1" " --prefix=PREFIX Install architecture-independent files in PREFIX" 192 | "$1" " [/usr]" 193 | "$1" " --user-install Install under user-only location. Incompatible with --prefix" 194 | "$1" " [$HOME/.local]" 195 | "$1" " --use-system-pyzstd" 196 | "$1" " Do not use vendored pyzstd" 197 | "$1" " --use-system-urllib" 198 | "$1" " Do not use vendored urllib" 199 | "$1" "" 200 | exit 1; 201 | } 202 | 203 | parse_args "$@" || usage err 204 | [[ -z $arg_help ]] || usage info 205 | 206 | configure 207 | -------------------------------------------------------------------------------- /docs/umu.1.scd: -------------------------------------------------------------------------------- 1 | umu(1) 2 | 3 | # NAME 4 | 5 | umu-run - Unified Launcher for Windows Games on Linux 6 | 7 | # SYNOPSIS 8 | 9 | *umu-run* [_FILE_] 10 | 11 | *umu-run* [_FILE_ [_ARG_...]] 12 | 13 | *umu-run* [_winetricks_ [_ARG_...]] 14 | 15 | *umu-run* *--config* [_FILE_] 16 | 17 | *umu-run* *--help* 18 | 19 | # POSITIONAL ARGUMENTS 20 | 21 | *winetricks* 22 | Run *winetricks*[1] verbs (Requires GE-Proton or UMU-Proton). 23 | 24 | See *winetricks*(1) for more info or below for an example. 25 | 26 | # OPTIONS 27 | 28 | *-h, --help* 29 | Show this help message. 30 | 31 | *--config* 32 | Path to TOML configuration file (Requires Python 3.11+). 33 | 34 | See *umu*(5) for more info and examples. 35 | 36 | # DESCRIPTION 37 | 38 | The Unified Launcher for Windows Games on Linux (umu) was created to make 39 | Valve's *Proton*[2] and the *protonfixes*[3] project accessible outside the 40 | Steam client, providing a standardized way for other clients (e.g., Lutris, 41 | Heroic Games Launcher, Bottles, or Rare) to run games via Proton and configure 42 | WINE prefixes. 43 | 44 | As a result, clients: 45 | - No longer require Steam or Steam binaries to be installed 46 | - Can contribute and automatically benefit from protonfixes 47 | - Can run games through Proton as it were a native Steam game 48 | - Can reference a unified online database of game fixes (protonfixes) 49 | 50 | You can run umu directly from a terminal emulator, or through your launcher of 51 | choice if supported. 52 | 53 | # EXAMPLES 54 | 55 | *Example 1. Run a game* 56 | 57 | ``` 58 | $ WINEPREFIX=~/.wine PROTONPATH=~/GE-Proton9-4 umu-run foo.exe 59 | ``` 60 | 61 | *Example 2. Run a game and apply a specific protonfix* 62 | 63 | ``` 64 | # Applies the Star Citizen fix to the current WINE prefix 65 | $ WINEPREFIX=~/.wine GAMEID=umu-starcitizen PROTONPATH=~/GE-Proton9-4 umu-run foo.exe 66 | ``` 67 | 68 | *Example 3. Run a game via a configuration file* 69 | 70 | ``` 71 | # config.toml 72 | [umu] 73 | prefix = "~/.wine" 74 | proton = "~/GE-Proton30" 75 | game_id = "0" 76 | exe = "~/foo.exe" 77 | launch_args = ["-opengl", "-SkipBuildPatchPrereq"] 78 | store = "gog" 79 | $ umu-run --config config.toml 80 | ``` 81 | 82 | *Example 4. Create a umu WINE prefix* 83 | 84 | ``` 85 | $ WINEPREFIX=~/foo PROTONPATH=~/GE-Proton9-4 umu-run "" 86 | ``` 87 | 88 | *Example 5. Run a game and automatically set Proton* 89 | 90 | ``` 91 | # Uses the latest UMU-Proton and automatically removes old UMU-Proton builds 92 | $ WINEPREFIX=~/.wine umu-run foo.exe 93 | ``` 94 | 95 | *Example 6. Run a game, automatically set Proton, and create a WINE prefix* 96 | 97 | ``` 98 | # Creates the prefix as $HOME/Games/umu/umu-default and uses the latest UMU-Proton 99 | $ umu-run foo.exe 100 | ``` 101 | 102 | *Example 7. Run a game and explicitly set a valid Proton verb* 103 | 104 | ``` 105 | $ WINEPREFIX=~/.wine GAMEID=0 PROTONPATH=~/GE-Proton9-4 PROTON_VERB=waitforexitandrun umu-run foo.exe 106 | ``` 107 | 108 | *Example 8. Run a game and enable debug logs* 109 | 110 | ``` 111 | $ UMU_LOG=debug WINEPREFIX=~/.wine PROTONPATH=~/GE-Proton9-4 umu-run foo.exe 112 | ``` 113 | 114 | *Example 9. Run a game and set a Proton by its version name* 115 | 116 | ``` 117 | # Checks for GE-Proton9-1 at $HOME/.local/share/Steam/compatibilitytools.d 118 | $ WINEPREFIX=~/.wine PROTONPATH=GE-Proton9-1 umu-run foo.exe 119 | ``` 120 | 121 | *Example 10. Run a game and automatically use the latest GE-Proton* 122 | 123 | ``` 124 | $ WINEPREFIX=~/.wine PROTONPATH=GE-Proton umu-run foo.exe 125 | ``` 126 | 127 | *Example 11. Run winetricks verbs* 128 | 129 | ``` 130 | $ PROTONPATH=GE-Proton umu-run winetricks quartz wmp11 qasf 131 | ``` 132 | 133 | *Example 12. Run a game, but do not apply protonfixes* 134 | 135 | ``` 136 | $ GAMEID=umu-genshin PROTONPATH=GE-Proton PROTONFIXES_DISABLE=1 umu-run foo.exe 137 | ``` 138 | 139 | *Example 13. Run a game and automatically use the latest GE-Proton, but apply a 140 | delta update to it* 141 | 142 | ``` 143 | $ PROTONPATH=GE-Latest umu-run foo.exe 144 | ``` 145 | 146 | # ENVIRONMENT VARIABLES 147 | 148 | _GAMEID_ 149 | Optional. Can be an arbitrary value or a valid id in the *umu-database*[4]. 150 | If unset, defaults to _umu-default_ (does not apply any fixes automatically). 151 | 152 | _PROTONPATH_ 153 | Optional. Path to a Proton directory, version name (e.g., GE-Proton9-5) or 154 | codename (e.g., GE-Proton). 155 | 156 | Otherwise, defaults to using UMU-Proton. 157 | 158 | Valid codenames include: _GE-Proton_, _UMU-Latest_, and _GE-Latest_. 159 | 160 | _WINEPREFIX_ 161 | Optional. Path to a WINE prefix directory. Otherwise, defaults to _$HOME/Games/umu/$GAMEID_. 162 | If no _GAMEID_ is set, defaults to _$HOME/Games/umu/umu-default_. 163 | 164 | _UMU_LOG_ 165 | Optional. Enables debug logs for the launcher. 166 | 167 | 168 | Set _1_ to enable all logs. 169 | 170 | _STORE_ 171 | Optional. Can be an arbitrary value or a valid store id in the umu-database. 172 | 173 | _PROTON_VERB_ 174 | Optional. Must be a valid Proton verb. Otherwise, defaults to _waitforexitandrun_. 175 | 176 | _UMU_ZENITY_ 177 | Optional. Creates a *zenity*[5] popup window when downloading large files. 178 | 179 | _UMU_RUNTIME_UPDATE_ 180 | Optional. Disables automatic updates to the *Steam Linux Runtime*[6]. 181 | 182 | Set _0_ to disable updates. 183 | 184 | _UMU_NO_PROTON_ 185 | Optional. Runs the executable natively within the Steam Linux Runtime. Intended for native Linux games. 186 | 187 | Set _1_ to run the executable natively within the SLR. 188 | 189 | # SEE ALSO 190 | 191 | _umu_(5), _winetricks_(1), _zenity_(1) 192 | 193 | # NOTES 194 | 195 | . https://github.com/Winetricks/winetricks 196 | . https://github.com/ValveSoftware/Proton 197 | . https://github.com/Open-Wine-Components/umu-protonfixes 198 | . https://github.com/Open-Wine-Components/umu-database 199 | . https://gitlab.gnome.org/GNOME/zenity 200 | . https://gitlab.steamos.cloud/steamrt/steam-runtime-tools/-/blob/main/docs/container-runtime.md 201 | 202 | # AUTHORS 203 | 204 | Maintained by Open Wine Components members, and assisted by other open source 205 | contributors. For more information about umu development, see 206 | https://github.com/Open-Wine-Components/umu-launcher. 207 | -------------------------------------------------------------------------------- /docs/umu.5.scd: -------------------------------------------------------------------------------- 1 | umu(5) 2 | 3 | # NAME 4 | 5 | umu-run - configuration file 6 | 7 | # DESCRIPTION 8 | 9 | umu-run can optionally read a configuration file instead of reading 10 | environment variables set from the command line. The configuration file for 11 | umu-run must be written in *Tom's Obvious Minimal Language (TOML)*[1]. 12 | 13 | In the configuration file, the table *umu* is required as well as the keys 14 | *prefix*, *proton* and *exe* which all must be basic strings. e.g.: 15 | 16 | ``` 17 | [umu] 18 | prefix = "~/WINE/epic-games-store" 19 | exe = "~/EpicGamesLauncher.exe" 20 | proton = "~/.local/share/Steam/compatibilitytools.d/GE-Proton30" 21 | ``` 22 | 23 | Afterwards, run the command: 24 | ``` 25 | $ umu-run --config config.toml 26 | ``` 27 | 28 | # [umu] SECTION 29 | 30 | *exe* 31 | Path to the game executable to run. Value must be a basic *string*. 32 | 33 | *proton* 34 | Path to a Proton directory. Value must be a basic *string*. 35 | 36 | *prefix* 37 | Path to a WINE prefix directory. Value must be a basic *string*. 38 | 39 | *game_id* 40 | The umu id to assign to the game. Depending on the value assigned, a proton 41 | fix will be applied to the prefix if available. Please refer to the *umu 42 | database*[2] for an extended list of game ids. 43 | 44 | *store* 45 | The distribution platform of the executable. Value must be a basic *string*. 46 | Expects the values: egs, gog, battlenet, amazon, humble, itchio, and ubisoft. 47 | 48 | *launch_args* 49 | Launch arguments for the executable. Value must be a basic *string* or an 50 | *array* of basic strings. When using a string, assumes each argument is 51 | space-separated. 52 | 53 | # NOTES 54 | 55 | . https://toml.io/en/v1.0.0 56 | . https://github.com/Open-Wine-Components/umu-database/blob/main/umu-database.csv 57 | 58 | # AUTHORS 59 | 60 | Maintained by Open Wine Components members, and assisted by other open source 61 | contributors. For more information about umu development, see 62 | https://github.com/Open-Wine-Components/umu-launcher. 63 | -------------------------------------------------------------------------------- /packaging/deb/0001-deb-fix-build-by-using-rustup.patch: -------------------------------------------------------------------------------- 1 | From e78b30b05387e8d45c01dae4b5c2bc7b3abdedb2 Mon Sep 17 00:00:00 2001 2 | From: R1kaB3rN <100738684+R1kaB3rN@users.noreply.github.com> 3 | Date: Sun, 11 May 2025 13:28:47 -0700 4 | Subject: [PATCH] fix: update patch for bookworm 5 | 6 | --- 7 | Makefile.in | 17 ++++++++++++----- 8 | packaging/deb/debian/bookworm/control | 1 + 9 | 2 files changed, 13 insertions(+), 5 deletions(-) 10 | 11 | diff --git a/Makefile.in b/Makefile.in 12 | index fe2a529d..c173df3b 100644 13 | --- a/Makefile.in 14 | +++ b/Makefile.in 15 | @@ -2,6 +2,7 @@ PROJECT := umu-launcher 16 | 17 | # Define the interpreters to use to prevent shebang complaints 18 | PYTHON_INTERPRETER = /usr/bin/env python3 19 | +SHELL_INTERPRETER = /usr/bin/env sh 20 | 21 | # If this is changed to umu (uppercase), `uninstall` target will also remove the SLR directory 22 | INSTALLDIR ?= umu 23 | @@ -84,6 +85,7 @@ else 24 | umu-install: umu-dist-install umu-delta-install umu-docs-install 25 | endif 26 | 27 | +URLLIB3_URL := https://github.com/urllib3/urllib3/releases/download/2.4.0/urllib3-2.4.0-py3-none-any.whl 28 | 29 | $(OBJDIR)/.build-umu-vendored: | $(OBJDIR) 30 | $(info :: Building vendored dependencies ) 31 | @@ -92,9 +94,7 @@ $(OBJDIR)/.build-umu-vendored: | $(OBJDIR) 32 | cd subprojects/pyzstd && $(PYTHON_INTERPRETER) -m build -wn --outdir=$(OBJDIR); \ 33 | fi 34 | @if [ "$(USE_SYSTEM_URLLIB)" != "xtrue" ]; then \ 35 | - cd subprojects/urllib3 && \ 36 | - sed -i 's/license-files = \["LICENSE.txt"\]//g' pyproject.toml && \ 37 | - $(PYTHON_INTERPRETER) -m build -wn --outdir=$(OBJDIR); \ 38 | + curl -LJO --tlsv1.3 $(URLLIB3_URL) --output-dir $(OBJDIR); \ 39 | fi 40 | touch $(@) 41 | 42 | @@ -105,7 +105,7 @@ umu-vendored-install: umu-vendored 43 | $(info :: Installing subprojects ) 44 | install -d $(DESTDIR)$(PYTHONDIR)/umu/_vendor 45 | @if [ "$(USE_SYSTEM_PYZSTD)" != "xtrue" ]; then \ 46 | - $(PYTHON_INTERPRETER) -m installer --destdir=$(DESTDIR)$(PYTHONDIR)/umu/_vendor subprojects/pyzstd/$(OBJDIR)/pyzstd*.whl; \ 47 | + $(PYTHON_INTERPRETER) -m installer --destdir=$(DESTDIR)$(PYTHONDIR)/umu/_vendor $(OBJDIR)/urllib3*.whl; \ 48 | find $(DESTDIR)$(PYTHONDIR)/umu/_vendor -type d -name pyzstd | xargs -I {} mv {} $(DESTDIR)$(PYTHONDIR)/umu/_vendor; \ 49 | fi 50 | @if [ "$(USE_SYSTEM_URLLIB)" != "xtrue" ]; then \ 51 | @@ -173,10 +173,17 @@ zipapp-install: zipapp 52 | @echo "Standalone application 'umu-run' created at '$(DESTDIR)$(PREFIX)/bin'" 53 | 54 | PYTHON_PLATFORM_TAG = $(shell $(PYTHON_INTERPRETER) -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))') 55 | +CARGO_BIN := $(HOME)/.cargo/bin/cargo 56 | +RUSTUP_BIN := $(HOME)/.cargo/bin/rustup 57 | +RUSTUP_URL := https://raw.githubusercontent.com/rust-lang/rustup/refs/tags/1.27.1/rustup-init.sh 58 | 59 | $(OBJDIR)/.build-umu-delta: | $(OBJDIR) 60 | $(info :: Building delta dependencies ) 61 | - cargo build -r --target-dir $(OBJDIR) 62 | + curl -LJO --tlsv1.3 $(RUSTUP_URL) 63 | + chmod u+x ./rustup-init.sh 64 | + $(SHELL_INTERPRETER) rustup-init.sh --default-toolchain none -y 65 | + $(RUSTUP_BIN) toolchain install 1.65 66 | + $(CARGO_BIN) build -r --target-dir $(OBJDIR) 67 | touch $(@) 68 | 69 | .PHONY: umu-delta 70 | diff --git a/packaging/deb/debian/bookworm/control b/packaging/deb/debian/bookworm/control 71 | index adfd8025..b725d9f5 100644 72 | --- a/packaging/deb/debian/bookworm/control 73 | +++ b/packaging/deb/debian/bookworm/control 74 | @@ -18,6 +18,7 @@ Build-Depends: 75 | python3-hatch-vcs, 76 | libzstd-dev, 77 | python3-dev, 78 | + curl, 79 | Standards-Version: 4.6.2 80 | Homepage: https://github.com/Open-Wine-Components/umu-launcher 81 | Vcs-Browser: https://github.com/Open-Wine-Components/umu-launcher 82 | -- 83 | 2.49.0 84 | 85 | -------------------------------------------------------------------------------- /packaging/deb/README.md: -------------------------------------------------------------------------------- 1 | # Copy debian packaging folder to the repository root: 2 | ``` 3 | cp -rvf ./packaging/deb/debian ./ 4 | ``` 5 | 6 | 7 | # Install build dependencies: 8 | ``` 9 | sudo apt install -y dh-make dpkg-dev 10 | ``` 11 | 12 | # Setup dh_make quilt files 13 | ``` 14 | LOGNAME=root dh_make --createorig -y -l -p umu-launcher_{PUT UMU VERSION HERE} || true 15 | ``` 16 | 17 | # Install apt build dependencies: 18 | ``` 19 | sudo apt build-dep -y ./ 20 | ``` 21 | 22 | # Build: 23 | ``` 24 | dpkg-buildpackage --no-sign 25 | ``` 26 | 27 | # Install: 28 | ``` 29 | sudo apt install -y ../umu-launcher*.deb ../python3-umu-launcher*.deb 30 | ``` 31 | 32 | # Remove 33 | ``` 34 | sudo apt remove -y umu-launcher python3-umu-launcher 35 | ``` 36 | 37 | # Usage examples: 38 | 39 | # winecfg: 40 | ``` 41 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen umu-run winecfg 42 | ``` 43 | 44 | # running a game using the default latest UMU-Proton: 45 | ``` 46 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen umu-run /path/to/some/game.exe 47 | ``` 48 | 49 | # running a game using the latest GE-Proton: 50 | ``` 51 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen PROTONPATH=GE-Proton umu-run /path/to/some/game.exe 52 | ``` 53 | 54 | # running a game using a specific proton version: 55 | ``` 56 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen PROTONPATH=GE-Proton9-1 umu-run /path/to/some/game.exe 57 | ``` 58 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/apparmor/bwrap-userns-restrict-umu: -------------------------------------------------------------------------------- 1 | /usr/share/apparmor/extra-profiles/bwrap-userns-restrict -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/changelog: -------------------------------------------------------------------------------- 1 | umu-launcher (1.1.4-1) noble; urgency=medium 2 | 3 | * 1.1.4 Release. 4 | 5 | -- Tom Crider Sat, 12 Oct 2024 05:49:24 -0700 6 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/control: -------------------------------------------------------------------------------- 1 | Source: umu-launcher 2 | Section: python 3 | Priority: optional 4 | Maintainer: Ward Nakchbandi 5 | Rules-Requires-Root: no 6 | Build-Depends: 7 | debhelper-compat (= 13), 8 | dh-python, 9 | python3-all, 10 | bash, 11 | make, 12 | scdoc, 13 | python3-hatchling, 14 | python3-installer, 15 | python3-build, 16 | python3-pip, 17 | python3-wheel, 18 | python3-hatch-vcs, 19 | libzstd-dev, 20 | python3-dev, 21 | Standards-Version: 4.6.2 22 | Homepage: https://github.com/Open-Wine-Components/umu-launcher 23 | Vcs-Browser: https://github.com/Open-Wine-Components/umu-launcher 24 | Vcs-Git: https://github.com/Open-Wine-Components/umu-launcher 25 | 26 | Package: umu-launcher 27 | Section: games 28 | Architecture: all 29 | Depends: 30 | python3-umu-launcher (= ${binary:Version}), 31 | ${misc:Depends}, 32 | Recommends: fontconfig, fonts-liberation, libasound2-plugins, libegl1, libexpat1, libfontconfig1, libgbm1, libnm0, libsdl2-2.0-0, libusb-1.0-0, libva-drm2, libva-glx2, libx11-6, libx11-xcb1, libxau6, libxcb-dri2-0, libxcb-glx0, libxcb-present0, libxcb-sync1, libxdamage1, libxdmcp6, libxext6, libxfixes3, libxss1, libxxf86vm1, mesa-vulkan-drivers, steam-devices, va-driver-all | va-driver, xdg-desktop-portal, xdg-desktop-portal-gtk | xdg-desktop-portal-backend, xdg-utils, xterm | x-terminal-emulator, zenity | python3-xxhash, python3-cbor2 33 | Suggests: libudev0, nvidia-driver-libs, nvidia-vulkan-icd, pipewire 34 | Description: A tool for launching non-steam games with proton. 35 | 36 | Package: python3-umu-launcher 37 | Architecture: amd64 38 | Depends: 39 | ${shlibs:Depends}, 40 | ${misc:Depends}, 41 | python3, 42 | python3-xlib (>= 0.33), 43 | apparmor-profiles, 44 | libgl1-mesa-dri:i386, 45 | libglx-mesa0:i386, 46 | libzstd1 47 | Description: A tool for launching non-steam games with proton. 48 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/python3-umu-launcher.install: -------------------------------------------------------------------------------- 1 | usr/bin/umu-run 2 | usr/share/man/* 3 | usr/lib/python3/dist-packages/umu* 4 | debian/apparmor/bwrap-userns-restrict-umu etc/apparmor.d/ 5 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/python3-umu-launcher.postinst: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/python3-umu-launcher.postrm: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # See debhelper(7) (uncomment to enable). 4 | # Output every command that modifies files on the build system. 5 | export DH_VERBOSE = 1 6 | 7 | nproc = $(shell nproc) 8 | 9 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 10 | toplevel_sitepackage = $(shell python3 -c "import site; print(site.getsitepackages()[0])") 11 | ### 12 | 13 | PYTHONDIR = /usr/lib/python3/dist-packages 14 | 15 | 16 | # See FEATURE AREAS in dpkg-buildflags(1). 17 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 18 | 19 | # See ENVIRONMENT in dpkg-buildflags(1). 20 | # Package maintainers to append CFLAGS. 21 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 22 | # Package maintainers to append LDFLAGS. 23 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 24 | 25 | 26 | %: 27 | dh $@ 28 | 29 | override_dh_auto_configure: 30 | patch -p1 < packaging/deb/0001-deb-fix-build-by-using-rustup.patch 31 | ./configure.sh --prefix=/usr 32 | 33 | override_dh_auto_build: 34 | true 35 | 36 | override_dh_auto_install: 37 | make -j$(nproc) DESTDIR=debian/tmp PYTHONDIR=$(PYTHONDIR) install 38 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 39 | ifneq ($(toplevel_sitepackage), $(PYTHONDIR)) 40 | cp -rvf debian/tmp/$(toplevel_sitepackage)/* debian/tmp/$(PYTHONDIR)/ 41 | rm -rvf debian/tmp/$(toplevel_sitepackage)/* 42 | cp -rvf debian/tmp/$(toplevel_sitepackage)/../../../* debian/tmp/usr/ 43 | rm -rvf debian/tmp/$(toplevel_sitepackage)/../../../* 44 | endif 45 | 46 | override_dh_fixperms: 47 | dh_fixperms --exclude debian/python3-umu-launcher 48 | 49 | ### 50 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/source/options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore = "Makefile" 2 | -------------------------------------------------------------------------------- /packaging/deb/debian/bookworm/upstream/metadata: -------------------------------------------------------------------------------- 1 | Bug-Database: https://github.com/Open-Wine-Components/umu-launcher/issues 2 | Bug-Submit: https://github.com/Open-Wine-Components/umu-launcher/issues/new 3 | Changelog: https://github.com/Open-Wine-Components/umu-launcher/blob/master/CHANGES 4 | Documentation: https://github.com/Open-Wine-Components/umu-launcher/wiki 5 | Repository-Browse: https://github.com/Open-Wine-Components/umu-launcher 6 | Repository: https://github.com/Open-Wine-Components/umu-launcher.git -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/changelog: -------------------------------------------------------------------------------- 1 | umu-launcher (1.1.4-1) noble; urgency=medium 2 | 3 | * 1.1.4 Release. 4 | 5 | -- Tom Crider Sat, 12 Oct 2024 05:49:24 -0700 6 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/control: -------------------------------------------------------------------------------- 1 | Source: umu-launcher 2 | Section: python 3 | Priority: optional 4 | Maintainer: Ward Nakchbandi 5 | Rules-Requires-Root: no 6 | Build-Depends: 7 | debhelper-compat (= 13), 8 | dh-python, 9 | python3-all, 10 | bash, 11 | make, 12 | scdoc, 13 | python3-hatchling, 14 | python3-installer, 15 | python3-build, 16 | python3-pip, 17 | python3-wheel, 18 | python3-hatch-vcs, 19 | cargo, 20 | Standards-Version: 4.6.2 21 | Homepage: https://github.com/Open-Wine-Components/umu-launcher 22 | Vcs-Browser: https://github.com/Open-Wine-Components/umu-launcher 23 | Vcs-Git: https://github.com/Open-Wine-Components/umu-launcher 24 | 25 | Package: umu-launcher 26 | Section: games 27 | Architecture: all 28 | Depends: 29 | python3-umu-launcher (= ${binary:Version}), 30 | ${misc:Depends}, 31 | Recommends: fontconfig, fonts-liberation, libasound2-plugins, libegl1, libexpat1, libfontconfig1, libgbm1, libnm0, libsdl2-2.0-0, libusb-1.0-0, libva-drm2, libva-glx2, libx11-6, libx11-xcb1, libxau6, libxcb-dri2-0, libxcb-glx0, libxcb-present0, libxcb-sync1, libxdamage1, libxdmcp6, libxext6, libxfixes3, libxss1, libxxf86vm1, mesa-vulkan-drivers, steam-devices, va-driver-all | va-driver, xdg-desktop-portal, xdg-desktop-portal-gtk | xdg-desktop-portal-backend, xdg-utils, xterm | x-terminal-emulator, zenity 32 | Suggests: libudev0, nvidia-driver-libs, nvidia-vulkan-icd, pipewire 33 | Description: A tool for launching non-steam games with proton. 34 | 35 | Package: python3-umu-launcher 36 | Architecture: amd64 37 | Depends: 38 | ${shlibs:Depends}, 39 | ${misc:Depends}, 40 | python3, 41 | python3-xlib (>= 0.33), 42 | apparmor-profiles, 43 | libgl1-mesa-dri:i386, 44 | libglx-mesa0:i386, 45 | python3-pyzstd, 46 | python3-xxhash, 47 | python3-truststore, 48 | python3-urllib3, 49 | python3-cbor2, 50 | Description: A tool for launching non-steam games with proton. 51 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/python3-umu-launcher.install: -------------------------------------------------------------------------------- 1 | usr/bin/umu-run 2 | usr/share/man/* 3 | usr/lib/python3/dist-packages/umu* 4 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/python3-umu-launcher.postinst: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/python3-umu-launcher.postrm: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # See debhelper(7) (uncomment to enable). 4 | # Output every command that modifies files on the build system. 5 | export DH_VERBOSE = 1 6 | 7 | nproc = $(shell nproc) 8 | 9 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 10 | toplevel_sitepackage = $(shell python3 -c "import site; print(site.getsitepackages()[0])") 11 | ### 12 | 13 | PYTHONDIR = /usr/lib/python3/dist-packages 14 | 15 | 16 | # See FEATURE AREAS in dpkg-buildflags(1). 17 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 18 | 19 | # See ENVIRONMENT in dpkg-buildflags(1). 20 | # Package maintainers to append CFLAGS. 21 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 22 | # Package maintainers to append LDFLAGS. 23 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 24 | 25 | 26 | %: 27 | dh $@ 28 | 29 | override_dh_auto_configure: 30 | ./configure.sh --prefix=/usr --use-system-pyzstd --use-system-urllib 31 | 32 | override_dh_auto_build: 33 | true 34 | 35 | override_dh_auto_install: 36 | make -j$(nproc) DESTDIR=debian/tmp PYTHONDIR=$(PYTHONDIR) install 37 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 38 | ifneq ($(toplevel_sitepackage), $(PYTHONDIR)) 39 | cp -rvf debian/tmp/$(toplevel_sitepackage)/* debian/tmp/$(PYTHONDIR)/ 40 | rm -rvf debian/tmp/$(toplevel_sitepackage)/* 41 | cp -rvf debian/tmp/$(toplevel_sitepackage)/../../../* debian/tmp/usr/ 42 | rm -rvf debian/tmp/$(toplevel_sitepackage)/../../../* 43 | endif 44 | 45 | override_dh_fixperms: 46 | dh_fixperms --exclude debian/python3-umu-launcher 47 | 48 | ### 49 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/source/options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore = "Makefile" 2 | -------------------------------------------------------------------------------- /packaging/deb/debian/trixie/upstream/metadata: -------------------------------------------------------------------------------- 1 | Bug-Database: https://github.com/Open-Wine-Components/umu-launcher/issues 2 | Bug-Submit: https://github.com/Open-Wine-Components/umu-launcher/issues/new 3 | Changelog: https://github.com/Open-Wine-Components/umu-launcher/blob/master/CHANGES 4 | Documentation: https://github.com/Open-Wine-Components/umu-launcher/wiki 5 | Repository-Browse: https://github.com/Open-Wine-Components/umu-launcher 6 | Repository: https://github.com/Open-Wine-Components/umu-launcher.git -------------------------------------------------------------------------------- /packaging/deb/ubuntu/apparmor/bwrap-userns-restrict-umu: -------------------------------------------------------------------------------- 1 | /usr/share/apparmor/extra-profiles/bwrap-userns-restrict -------------------------------------------------------------------------------- /packaging/deb/ubuntu/changelog: -------------------------------------------------------------------------------- 1 | umu-launcher (1.1.4-1) noble; urgency=medium 2 | 3 | * 1.1.4 Release. 4 | 5 | -- Tom Crider Sat, 12 Oct 2024 05:49:24 -0700 6 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/control: -------------------------------------------------------------------------------- 1 | Source: umu-launcher 2 | Section: python 3 | Priority: optional 4 | Maintainer: Ward Nakchbandi 5 | Rules-Requires-Root: no 6 | Build-Depends: 7 | debhelper-compat (= 13), 8 | dh-python, 9 | python3-all, 10 | bash, 11 | make, 12 | scdoc, 13 | python3-hatchling, 14 | python3-installer, 15 | python3-build, 16 | python3-pip, 17 | cargo, 18 | python3-hatch-vcs, 19 | libzstd-dev, 20 | python3-dev, 21 | python3-wheel, 22 | Standards-Version: 4.6.2 23 | Homepage: https://github.com/Open-Wine-Components/umu-launcher 24 | Vcs-Browser: https://github.com/Open-Wine-Components/umu-launcher 25 | Vcs-Git: https://github.com/Open-Wine-Components/umu-launcher 26 | 27 | Package: umu-launcher 28 | Section: games 29 | Architecture: all 30 | Depends: 31 | python3-umu-launcher (= ${binary:Version}), 32 | ${misc:Depends}, 33 | Recommends: fontconfig, fonts-liberation, libasound2-plugins, libegl1, libexpat1, libfontconfig1, libgbm1, libnm0, libsdl2-2.0-0, libusb-1.0-0, libva-drm2, libva-glx2, libx11-6, libx11-xcb1, libxau6, libxcb-dri2-0, libxcb-glx0, libxcb-present0, libxcb-sync1, libxdamage1, libxdmcp6, libxext6, libxfixes3, libxss1, libxxf86vm1, mesa-vulkan-drivers, steam-devices, va-driver-all | va-driver, xdg-desktop-portal, xdg-desktop-portal-gtk | xdg-desktop-portal-backend, xdg-utils, xterm | x-terminal-emulator, zenity | python3-xxhash, python3-cbor2 34 | Suggests: libudev0, nvidia-driver-libs, nvidia-vulkan-icd, pipewire 35 | Description: A tool for launching non-steam games with proton. 36 | 37 | Package: python3-umu-launcher 38 | Architecture: amd64 39 | Depends: 40 | ${shlibs:Depends}, 41 | ${misc:Depends}, 42 | python3, 43 | python3-xlib (>= 0.33), 44 | apparmor-profiles, 45 | libgl1-mesa-dri:i386, 46 | libglx-mesa0:i386, 47 | libzstd1 48 | Description: A tool for launching non-steam games with proton. 49 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/python3-umu-launcher.install: -------------------------------------------------------------------------------- 1 | usr/bin/umu-run 2 | usr/share/man/* 3 | usr/lib/python3/dist-packages/umu* 4 | debian/apparmor/bwrap-userns-restrict-umu etc/apparmor.d/ 5 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/python3-umu-launcher.postinst: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/python3-umu-launcher.postrm: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | systemctl restart apparmor.service || true 4 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # See debhelper(7) (uncomment to enable). 4 | # Output every command that modifies files on the build system. 5 | export DH_VERBOSE = 1 6 | 7 | nproc = $(shell nproc) 8 | 9 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 10 | toplevel_sitepackage = $(shell python3 -c "import site; print(site.getsitepackages()[0])") 11 | ### 12 | 13 | PYTHONDIR = /usr/lib/python3/dist-packages 14 | 15 | 16 | # See FEATURE AREAS in dpkg-buildflags(1). 17 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 18 | 19 | # See ENVIRONMENT in dpkg-buildflags(1). 20 | # Package maintainers to append CFLAGS. 21 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 22 | # Package maintainers to append LDFLAGS. 23 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 24 | 25 | 26 | %: 27 | dh $@ 28 | 29 | override_dh_auto_configure: 30 | ./configure.sh --prefix=/usr 31 | 32 | override_dh_auto_build: 33 | true 34 | 35 | override_dh_auto_install: 36 | make -j$(nproc) DESTDIR=debian/tmp PYTHONDIR=$(PYTHONDIR) install 37 | ### PYTHON TOPLEVEL WORKAROUND (REMOVE ONCE the umu install process properly respects PYTHONPATH) 38 | ifneq ($(toplevel_sitepackage), $(PYTHONDIR)) 39 | cp -rvf debian/tmp/$(toplevel_sitepackage)/* debian/tmp/$(PYTHONDIR)/ 40 | rm -rvf debian/tmp/$(toplevel_sitepackage)/* 41 | cp -rvf debian/tmp/$(toplevel_sitepackage)/../../../* debian/tmp/usr/ 42 | rm -rvf debian/tmp/$(toplevel_sitepackage)/../../../* 43 | endif 44 | 45 | override_dh_fixperms: 46 | dh_fixperms --exclude debian/python3-umu-launcher 47 | 48 | ### 49 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) -------------------------------------------------------------------------------- /packaging/deb/ubuntu/source/options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore = "Makefile" 2 | -------------------------------------------------------------------------------- /packaging/deb/ubuntu/upstream/metadata: -------------------------------------------------------------------------------- 1 | Bug-Database: https://github.com/Open-Wine-Components/umu-launcher/issues 2 | Bug-Submit: https://github.com/Open-Wine-Components/umu-launcher/issues/new 3 | Changelog: https://github.com/Open-Wine-Components/umu-launcher/blob/master/CHANGES 4 | Documentation: https://github.com/Open-Wine-Components/umu-launcher/wiki 5 | Repository-Browse: https://github.com/Open-Wine-Components/umu-launcher 6 | Repository: https://github.com/Open-Wine-Components/umu-launcher.git -------------------------------------------------------------------------------- /packaging/flatpak/README.md: -------------------------------------------------------------------------------- 1 | # Install dependencies: 2 | ``` 3 | Fedora: 4 | sudo dnf install flatpak-builder 5 | 6 | Ubuntu: 7 | sudo apt install flatpak-builder 8 | ``` 9 | 10 | # Build + install (for testing): 11 | ``` 12 | flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo 13 | flatpak-builder --force-clean --user --install-deps-from=flathub --repo=umu-repo --install umu-launcher org.openwinecomponents.umu.umu-launcher.yml 14 | ``` 15 | 16 | # Remove 17 | ``` 18 | flatpak --user remove umu-launcher 19 | ``` 20 | 21 | # Usage examples: 22 | 23 | # winecfg: 24 | ``` 25 | flatpak run --env=GAMEID=umu-starcitizen --env=WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen org.openwinecomponents.umu.umu-launcher winecfg 26 | ``` 27 | 28 | # running a game using the default latest UMU-Proton: 29 | ``` 30 | flatpak run --env=GAMEID=umu-starcitizen --env=WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen org.openwinecomponents.umu.umu-launcher /path/to/some/game.exe 31 | ``` 32 | 33 | # running a game using the latest GE-Proton: 34 | ``` 35 | flatpak run --env=GAMEID=umu-starcitizen --env=WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen --env=PROTONPATH=GE-Proton org.openwinecomponents.umu.umu-launcher /path/to/some/game.exe 36 | ``` 37 | 38 | # running a game using a specific proton version: 39 | ``` 40 | flatpak run --env=GAMEID=umu-starcitizen --env=WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen --env=PROTONPATH=GE-Proton9-1 org.openwinecomponents.umu.umu-launcher /path/to/some/game.exe 41 | ``` 42 | -------------------------------------------------------------------------------- /packaging/nix/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1740547748, 6 | "narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "3a05eebede89661660945da1f151959900903b6a", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixpkgs-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /packaging/nix/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "umu universal game launcher"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | }; 7 | 8 | outputs = { 9 | self, 10 | nixpkgs, 11 | }: let 12 | inherit (nixpkgs) lib; 13 | 14 | # Utility function for producing consistent rename warning messages 15 | rename = old: new: lib.warn "`${old}` has been renamed to `${new}`"; 16 | 17 | # Supported platforms & package sets 18 | platforms = lib.platforms.linux; 19 | supportedPkgs = lib.filterAttrs (system: _: builtins.elem system platforms) nixpkgs.legacyPackages; 20 | 21 | # Use the current revision for the default version 22 | version = self.dirtyShortRev or self.shortRev or self.lastModifiedDate; 23 | in { 24 | overlays.default = final: prev: { 25 | umu-launcher = final.callPackage ./package.nix { 26 | inherit (prev) umu-launcher; 27 | }; 28 | umu-launcher-unwrapped = final.callPackage ./unwrapped.nix { 29 | inherit (prev) umu-launcher-unwrapped; 30 | inherit version; 31 | }; 32 | # Deprecated in https://github.com/Open-Wine-Components/umu-launcher/pull/345 (2025-01-31) 33 | umu = rename "umu" "umu-launcher" final.umu-launcher; 34 | umu-run = rename "umu-run" "umu-launcher" final.umu-launcher; 35 | }; 36 | 37 | formatter = builtins.mapAttrs (system: pkgs: pkgs.alejandra) nixpkgs.legacyPackages; 38 | 39 | packages = 40 | builtins.mapAttrs (system: pkgs: rec { 41 | default = umu-launcher; 42 | inherit 43 | (pkgs.extend self.overlays.default) 44 | umu-launcher 45 | umu-launcher-unwrapped 46 | ; 47 | # Deprecated in https://github.com/Open-Wine-Components/umu-launcher/pull/345 (2025-01-31) 48 | umu = rename "packages.${system}.umu" "packages.${system}.umu-launcher" umu-launcher; 49 | }) 50 | supportedPkgs; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /packaging/nix/package.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Dependencies 3 | lib, 4 | umu-launcher, 5 | umu-launcher-unwrapped, 6 | # Public API 7 | version ? null, 8 | withTruststore ? args.truststore or true, 9 | withDeltaUpdates ? true, 10 | # Freeform args 11 | ... 12 | } @ args: let 13 | # Args not handled here; to be passed to the nixpkgs package 14 | # E.g. to support overriding `extraPkgs` or `extraLibraries` 15 | # NOTE: All known args must be removed here 16 | unknownArgs = builtins.removeAttrs args [ 17 | "lib" 18 | "umu-launcher" 19 | "umu-launcher-unwrapped" 20 | "version" 21 | "withTruststore" 22 | "withDeltaUpdates" 23 | "truststore" 24 | "cbor2" 25 | ]; 26 | 27 | # Overrides for umu-launcher-unwrapped 28 | overrides = 29 | # Warnings added in https://github.com/Open-Wine-Components/umu-launcher/pull/345 (2025-01-31) 30 | lib.warnIf (args ? truststore) "umu-launcher: the argument `truststore` has been renamed to `withTruststore`." 31 | lib.warnIf (args ? cbor2) "umu-launcher: the argument `cbor2` has never had any effect. The new argument `withDeltaUpdates` should be used instead." 32 | lib.warnIf (version == umu-launcher-unwrapped.version) "umu-launcher: the argument `version` is no longer necessary. The version now uses `shortRev` by default." 33 | lib.optionalAttrs (args ? version) {inherit version;} 34 | // lib.optionalAttrs (args ? withTruststore || args ? truststore) {inherit withTruststore;} 35 | // lib.optionalAttrs (args ? withDeltaUpdates) {inherit withDeltaUpdates;}; 36 | in 37 | umu-launcher.override ( 38 | unknownArgs 39 | // { 40 | umu-launcher-unwrapped = 41 | if overrides == {} 42 | then umu-launcher-unwrapped 43 | else umu-launcher-unwrapped.override overrides; 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /packaging/nix/unwrapped.nix: -------------------------------------------------------------------------------- 1 | { 2 | # Dependencies 3 | lib, 4 | rustPlatform, 5 | umu-launcher-unwrapped, 6 | version, 7 | # Freeform overrides 8 | ... 9 | } @ args: 10 | # Nixpkgs bumped 1.1.4 -> 1.2.5 on 2025-02-20 11 | # Available in all unstable channels since 2025-02-24 12 | # https://github.com/NixOS/nixpkgs/pull/381975 13 | assert lib.assertMsg (lib.versionAtLeast umu-launcher-unwrapped.version "1.2.0") '' 14 | You have updated your umu-launcher input, however you have an outdated nixpkgs input. A nixpkgs input with umu-launcher 1.2.0+ is required. 15 | Please update your nixpkgs revision by running `nix flake lock --update-input nixpkgs` or `nix flake update`. 16 | ''; let 17 | # Unknown args will be used to override the nixpkgs package 18 | # NOTE: All known args must be removed here 19 | overrideArgs = builtins.removeAttrs args [ 20 | "lib" 21 | "rustPlatform" 22 | "umu-launcher-unwrapped" 23 | "version" 24 | ]; 25 | 26 | # Use the unwrapped package as-is or override it, 27 | # based on whether we have any override args 28 | package = 29 | if overrideArgs == {} 30 | then umu-launcher-unwrapped 31 | else umu-launcher-unwrapped.override overrideArgs; 32 | in 33 | package.overridePythonAttrs { 34 | inherit version; 35 | src = ../../.; 36 | cargoDeps = rustPlatform.importCargoLock { 37 | lockFile = ../../Cargo.lock; 38 | }; 39 | 40 | # Specify ourselves which tests are disabled 41 | disabledTests = [ 42 | # Broken? Asserts that $STEAM_RUNTIME_LIBRARY_PATH is non-empty 43 | # Fails with AssertionError: '' is not true : Expected two elements in STEAM_RUNTIME_LIBRARY_PATHS 44 | "test_game_drive_empty" 45 | "test_game_drive_libpath_empty" 46 | 47 | # Broken? Tests parse_args with no options (./umu_run.py) 48 | # Fails with AssertionError: SystemExit not raised 49 | "test_parse_args_noopts" 50 | ]; 51 | } 52 | -------------------------------------------------------------------------------- /packaging/rpm/README.md: -------------------------------------------------------------------------------- 1 | # Install dependencies: 2 | ``` 3 | sudo dnf install -y mock fedpkg 4 | sudo usermod -aG mock username 5 | su username 6 | ``` 7 | 8 | # Build: 9 | ``` 10 | fedpkg --release f40 srpm 11 | mock -r /etc/mock/fedora-40-x86_64.cfg --rebuild --enable-network *.src.rpm 12 | mv /var/lib/mock/fedora-40-x86_64/result . 13 | ``` 14 | 15 | # Install: 16 | ``` 17 | cd result 18 | sudo dnf install -y umu-launcher*.rpm 19 | ``` 20 | 21 | # Remove 22 | ``` 23 | sudo dnf remove -y umu-launcher 24 | ``` 25 | 26 | # Usage examples: 27 | 28 | # winecfg: 29 | ``` 30 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen umu-run winecfg 31 | ``` 32 | 33 | # running a game using the default latest UMU-Proton: 34 | ``` 35 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen umu-run /path/to/some/game.exe 36 | ``` 37 | 38 | # running a game using the latest GE-Proton: 39 | ``` 40 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen PROTONPATH=GE-Proton umu-run /path/to/some/game.exe 41 | ``` 42 | 43 | # running a game using a specific proton version: 44 | ``` 45 | GAMEID=umu-starcitizen WINEPREFIX=/home/tcrider/Games/umu/umu-starcitizen PROTONPATH=GE-Proton9-1 umu-run /path/to/some/game.exe 46 | ``` 47 | -------------------------------------------------------------------------------- /packaging/rpm/umu-launcher.spec: -------------------------------------------------------------------------------- 1 | # Tag is auto-inserted by workflow 2 | %global tag 1.2.5 3 | 4 | # Manual commit is auto-inserted by workflow 5 | %global commit a11540cbf2221a5671c4ced97c0bf7e61c98d21e 6 | 7 | %global shortcommit %(c=%{commit}; echo ${c:0:7}) 8 | 9 | %global build_timestamp %(date +"%Y%m%d") 10 | 11 | %global rel_build 1.%{build_timestamp}.%{shortcommit}%{?dist} 12 | 13 | # F41 doesn't ship urllib3 >= 2.0 needed 14 | %global urllib3 2.3.0 15 | 16 | Name: umu-launcher 17 | Version: %{tag} 18 | Release: %{rel_build} 19 | Summary: A tool for launching non-steam games with proton 20 | 21 | License: GPLv3 22 | URL: https://github.com/Open-Wine-Components/umu-launcher 23 | Source0: %{url}/archive/refs/tags/%{tag}.tar.gz#/%{name}-%{tag}.tar.gz 24 | Source1: https://github.com/urllib3/urllib3/releases/download/%{urllib3}/urllib3-%{urllib3}.tar.gz 25 | 26 | BuildArch: x86_64 27 | BuildRequires: meson >= 0.54.0 28 | BuildRequires: ninja-build 29 | BuildRequires: cmake 30 | BuildRequires: g++ 31 | BuildRequires: gcc-c++ 32 | BuildRequires: scdoc 33 | BuildRequires: git 34 | BuildRequires: python3-devel 35 | BuildRequires: python3-build 36 | BuildRequires: python3-installer 37 | BuildRequires: python3-hatchling 38 | BuildRequires: python 39 | BuildRequires: python3 40 | BuildRequires: python3-pip 41 | BuildRequires: libzstd-devel 42 | BuildRequires: python3-hatch-vcs 43 | BuildRequires: python3-wheel 44 | BuildRequires: python3-xlib 45 | BuildRequires: python3-pyzstd 46 | BuildRequires: cargo 47 | 48 | # Can't use these yet, F41 doesn't ship urllib3 >= 2.0 needed 49 | #BuildRequires: python3-urllib3 50 | 51 | Requires: python 52 | Requires: python3 53 | Requires: python3-xlib 54 | Requires: python3-filelock 55 | Requires: python3-pyzstd 56 | 57 | # Can't use these yet, F41 doesn't ship urllib3 >= 2.0 needed 58 | #Requires: python3-urllib3 59 | 60 | Recommends: python3-cbor2 61 | Recommends: python3-xxhash 62 | Recommends: libzstd 63 | 64 | # We need this for now to allow umu's builtin urllib3 version to be used. 65 | # Can be removed when python3-urllib3 version is bumped >= 2.0 66 | AutoReqProv: no 67 | 68 | 69 | %description 70 | %{name} A tool for launching non-steam games with proton 71 | 72 | %prep 73 | %autosetup -p 1 74 | if ! find subprojects/urllib3/ -mindepth 1 -maxdepth 1 | read; then 75 | # Directory is empty, perform action 76 | mv %{SOURCE1} . 77 | tar -xf urllib3-%{urllib3}.tar.gz 78 | rm *.tar.gz 79 | mv urllib3-%{urllib3}/* subprojects/urllib3/ 80 | fi 81 | 82 | %build 83 | # Update this when fedora ships urllib3 >= 2.0 84 | #./configure.sh --prefix=/usr --use-system-pyzstd --use-system-urllib 85 | ./configure.sh --prefix=/usr --use-system-pyzstd 86 | make 87 | 88 | %install 89 | make DESTDIR=%{buildroot} PYTHONDIR=%{python3_sitelib} install 90 | 91 | %files 92 | %{_bindir}/umu-run 93 | %{_datadir}/man/* 94 | %{python3_sitelib}/umu* 95 | 96 | %changelog 97 | -------------------------------------------------------------------------------- /packaging/snap/README.md: -------------------------------------------------------------------------------- 1 | ## NOTICE ## 2 | 3 | Due to the mounting and security requirements of pressure-vessel it is unfortunately unlikely that this will ever be accepted as an official snap within the snap store. 4 | 5 | With that being said, we have put the snap together for convenience for those that wish to use it in snap-based environments. 6 | 7 | Please be aware that this runs in devmode with without any standard snap confinements. 8 | 9 | 1. Install build dependencies: 10 | ``` 11 | snap install snapcraft --classic 12 | ``` 13 | 2. Create snap structure: 14 | ``` 15 | mkdir snap 16 | cp snapcraft.yaml snap/ 17 | ``` 18 | 3. Build: 19 | ``` 20 | snapcraft 21 | ``` 22 | 4. Install: 23 | ``` 24 | sudo snap install --dangerous --devmode umu-launcher*.snap 25 | ``` 26 | 5. Test: 27 | ``` 28 | sudo snap connect umu-launcher:gaming-mesa gaming-graphics-core22 29 | WINEPREFIX=~/umu-test STORE=none GAMEID=0 umu-launcher.umu-run winecfg 30 | ``` 31 | -------------------------------------------------------------------------------- /packaging/snap/snap/hooks/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://github.com/canonical/steam-snap/issues/19#issuecomment-1171984524 4 | # https://gist.github.com/jhenstridge/425c769949d034033f3d5d90acc2f181 5 | 6 | set -e 7 | 8 | mkdir -p $SNAP_DATA/etc 9 | 10 | snap_ld_so_conf=$SNAP_DATA/etc/snap-ld.so.conf 11 | snap_ld_so_cache=$SNAP_DATA/etc/snap-ld.so.cache 12 | real_ld_so_cache=$SNAP_DATA/etc/ld.so.cache 13 | 14 | RUNTIME=$SNAP/gnome-platform 15 | 16 | cat > "$snap_ld_so_conf" < $real_ld_so_cache 46 | -------------------------------------------------------------------------------- /packaging/snap/snap/keys/B05498B7.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQENBFDBPfgBCADWEjO8rbwGE7/4II9aZ+gNNarD58/n8PFRerOsUKpo6EvGGr2i 4 | /yIhbEmCTkahQFPHsjPUG4jCcWyIaf2+Cue1lPBmSM5CxVkjwUZPABIhBTmw4T/B 5 | AW8sVq1OQe0uaZNVT8faJlSWQxPrutzi1P+RWyaIUh7DgEXawpRezAOuYxLW8FD3 6 | ppGQs3XV3419aJNMl8UT/GG50sZe9SORC09A4g5M7j0rG8urStp5mH8gPNRrSJWr 7 | ve7tIXVaMuZL7a8Ei0PalAS9ebVwfM9h2J4xyduz2X4PQ8W5fXCfSsZenrdWwLbT 8 | 8QiwKomVZXkHwtUOhraBemef1CjMz4u6EG7XABEBAAG0KlZhbHZlIENvcnBvcmF0 9 | aW9uIDxsaW51eEBzdGVhbXBvd2VyZWQuY29tPokBOAQTAQIAIgUCUMujbQIbAwYL 10 | CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ8krqn7BUmLeJAAf+PfCXGV/KLYaz 11 | F8o5qLAqqUiuInoF1rJMZhfmd+bfF3q6jNu5oYuu358lsms8ucmXLQ3KObbWrFQz 12 | DQgx+gFxPmI9jSWWynYslulqjbJksc91H1ppRPfWk4eGd/7VXOS/a9moBqbJ2kkv 13 | QbVsGLEo69RIC8/Jt88pZ52FxtRCFBU5ag4ZjeKi1WJgoUo1I7F51CVGaXMH/Wfs 14 | 0LrCLO5FD0PLsXuz091L2rXCsIeq0OE4SnBtZP2wr2lwg14YPkPPoqQEIufswPrr 15 | cfGqXP7zbJO2gFpWPxjCDafcY1dz1turbzBO/w/nQ10hL3FsSlcPloNrClbIoqNg 16 | ULYOCXy+irQrVmFsdmUgU29mdHdhcmUgTExDIDxsaW51eEBzdGVhbXBvd2VyZWQu 17 | Y29tPokBNQQwAQIAHwUCUMuqYRgdIEluY29ycmVjdCBjb21wYW55IG5hbWUACgkQ 18 | 8krqn7BUmLcnEwf/f2DaLY5mojYe8Z56zr9PBNn15Db7fybVovs2E1KENMWwFe5H 19 | 5dbyaNZ5shretlWt7md0KKG/9q6oTrCve5KaHxFFXFIUZt490CAc9Pdh3mLdEC7B 20 | Qo+Cx/O3eeRiltnpqsFTzQvUD7X4mb8Ey2G3ZYUw6CISw2YmkYHOv7tCtz/Z0WSF 21 | SpUvwC0yjlhVR5tKdlaw+8ODeR+o0Y7/u999UsnBoQilc/yFFEvdVgKNGFVT8E5C 22 | AOkZOaXaLSphwPGCIbmRtJeJe53c5/ZRpq6L9WZbzD72IVVY4GafPLVunKu4nXCg 23 | 8QONqxKNs1OuV+gePvXGAJ0dGlLJJKOg4ljxrYkBOAQTAQIAIgUCUME9+AIbAwYL 24 | CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ8krqn7BUmLdTaQf9GU9Ofv08YM/1 25 | KmuKP7gqMWeb0GALQSI/E5Gge6WidU5npjQGEAjscyVCm5p+XrduDJwkZJLzZCg7 26 | t3llxcABEPeasfEn9VFx59h9xDNBRL0Hmr4jj8g5barbDVcQU7oALAKDNstrLnRS 27 | ZNn+AufIviY3Rw86jUhK0adBI+d88ozDFEWw0MSIvJiIovbB3SsV9WrssJ8UeeCU 28 | dVkZhjHAKRiBVt7lgQ3/kerKkaOeyxGSn8Aqai+fEivJO1VTyDNsTj5tczUdvCsj 29 | aPb1O6ImFVuku5mz5lfphytukF6bNKqtmmsJK3+e+9Bb2diWVeBYbmahHJ6cqalz 30 | GLbyRXLftLkBDQRQwT34AQgAxrJRTslUiqGDjuOV4fPCW5c34hylIaJO0hXbVTrV 31 | unpYm7qDctSRzH6QAtBCxrWSZjUVOy/mEvyxmQLvX33ZtH8w4ewX4OZtADaDGBSN 32 | vhe0ddXyxYCMMFtRq/3DgVODmwC6eaMLMa2WGP9NROCvtc5xQs0Xr6T113JoKv7k 33 | 1WR++0V+m36dX+Lv39pq0PJCg6CHpD84g5cmsiePKE+Mp4ZV/js2nOiYT3P7zkPZ 34 | GXp0yvUOT5tmo6xO5N+ving18l75X104/xMBY2VOukCLlgfZVRqbIGGtZqpSaSkE 35 | U/N5bHRaJOpTD4hmGdnKMhoQxfmgsIve5XzV0r+OP3yL2wARAQABiQEfBBgBAgAJ 36 | BQJQwT34AhsMAAoJEPJK6p+wVJi3mCwH/36Vpr7eQU/uOt1xFw+pAvcbCZ485tGU 37 | aFw4MFd4XfAv7ky6UTpx8i6looWadS5nPkpOCc+qwYejM0VmvffgPARt7l2qMjOe 38 | 7/K50MCYN2fAhWvqCVwu5h1xVhF0lxzJWOzs1kM9m8bXT3uxhQOiYh+jGzHrnp+A 39 | rAfsUfAlLASpuxi2q9IP3V8qgoN8O/DNPCXv41CdApvzsAYIa8N3nrvM4gO81/Gj 40 | 0jKKNJm63YhSEmlfqkm0uKsSTz2f8BPRcwxpGQ9+r5gWIB+9NloH7hXDs/XvZGcg 41 | 6bNtSqK4I2ndK/RDoIkzd894A3+LjIpuIT/D04txs87mZzG1r9mb/lo= 42 | =BGxp 43 | -----END PGP PUBLIC KEY BLOCK----- 44 | -------------------------------------------------------------------------------- /packaging/snap/snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: umu-launcher 2 | version: '1.1.4' # You can set this to the appropriate version 3 | summary: UMU Launcher 4 | description: | 5 | UMU Launcher is a tool for managing and launching applications. 6 | 7 | base: core22 # or core18, core22 depending on your needs 8 | confinement: devmode # You can change this to 'classic' if needed 9 | architectures: 10 | - build-on: amd64 11 | grade: stable 12 | 13 | lint: 14 | # Snapcraft's `ldd` lint can't handle 32-bit things, 15 | # So just make it quiet and also make builds a surprising amount faster 16 | ignore: 17 | - library: 18 | - lib/i386-linux-gnu/** 19 | - usr/lib/i386-linux-gnu/** 20 | - lib32/** 21 | - usr/lib32/** 22 | 23 | package-repositories: 24 | - type: apt 25 | url: http://repo.steampowered.com/steam/ 26 | suites: [stable] 27 | components: [steam] 28 | architectures: [amd64, i386] 29 | key-id: BA1816EF8E75005FCF5E27A1F24AEA9FB05498B7 30 | 31 | layout: 32 | /usr/share/drirc.d: 33 | bind: $SNAP/graphics/usr/share/drirc.d 34 | /usr/share/glvnd/egl_vendor.d: 35 | bind: $SNAP/graphics/usr/share/glvnd/egl_vendor.d 36 | /usr/lib/x86_64-linux-gnu/alsa-lib: 37 | bind: $SNAP/usr/lib/x86_64-linux-gnu/alsa-lib 38 | /usr/share/alsa: 39 | bind: $SNAP/usr/share/alsa 40 | /usr/share/X11/xkb: 41 | bind: $SNAP/usr/share/X11/xkb 42 | /usr/lib/x86_64-linux-gnu/libvulkan_intel.so: 43 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libvulkan_intel.so 44 | /usr/lib/i386-linux-gnu/libvulkan_intel.so: 45 | symlink: $SNAP/graphics/usr/lib/i386-linux-gnu/libvulkan_intel.so 46 | /usr/lib/x86_64-linux-gnu/libvulkan_lvp.so: 47 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libvulkan_lvp.so 48 | /usr/lib/i386-linux-gnu/libvulkan_lvp.so: 49 | symlink: $SNAP/graphics/usr/lib/i386-linux-gnu/libvulkan_lvp.so 50 | /usr/lib/x86_64-linux-gnu/libvulkan_radeon.so: 51 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libvulkan_radeon.so 52 | /usr/lib/i386-linux-gnu/libvulkan_radeon.so: 53 | symlink: $SNAP/graphics/usr/lib/i386-linux-gnu/libvulkan_radeon.so 54 | /usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0.0.0: 55 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0.0.0 56 | /usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0: 57 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0.0.0 58 | /usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0: 59 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 60 | /usr/lib/x86_64-linux-gnu/libxcb.so: 61 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 62 | /usr/lib/x86_64-linux-gnu/libxcb.so.1: 63 | symlink: $SNAP/graphics/usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 64 | /etc/ld.so.cache: 65 | bind-file: $SNAP_DATA/etc/ld.so.cache 66 | /etc/fonts: 67 | bind: $SNAP/etc/fonts 68 | 69 | plugs: 70 | gaming-mesa: 71 | interface: content 72 | target: $SNAP/graphics 73 | default-provider: gaming-graphics-core22 74 | gtk-3-themes: 75 | interface: content 76 | target: $SNAP/share/themes 77 | default-provider: gtk-common-themes 78 | icon-themes: 79 | interface: content 80 | target: $SNAP/share/icons 81 | default-provider: gtk-common-themes 82 | sound-themes: 83 | interface: content 84 | target: $SNAP/share/sounds 85 | default-provider: gtk-common-themes 86 | dot-local-share-steam: 87 | interface: personal-files 88 | write: 89 | - $HOME/.local/share/Steam 90 | - $HOME/Steam 91 | dot-local-share-applications: 92 | interface: personal-files 93 | write: 94 | - $HOME/.local/share/applications 95 | dot-local-share-icons: 96 | interface: personal-files 97 | write: 98 | - $HOME/.local/share/icons 99 | desktop: 100 | mount-host-font-cache: false 101 | shmem: 102 | interface: shared-memory 103 | private: true 104 | 105 | slots: 106 | dbus: 107 | interface: dbus 108 | bus: session 109 | name: com.steampowered.PressureVessel.LaunchAlongsideSteam 110 | 111 | hooks: 112 | configure: 113 | plugs: 114 | - opengl 115 | 116 | environment: 117 | LD_LIBRARY_PATH: $SNAP/graphics/lib/i386-linux-gnu:$SNAP/graphics/usr/lib:$SNAP/usr/lib/i386-linux-gnu:$SNAP/lib/i386-linux-gnu:$SNAP/usr/lib/i386-linux-gnu/pulseaudio${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} 118 | LIBGL_DRIVERS_PATH: $SNAP/graphics/usr/lib/i386-linux-gnu/dri:$SNAP/graphics/usr/lib/x86_64-linux-gnu/dri:${LIBGL_DRIVERS_PATH:+:$LIBGL_DRIVERS_PATH} 119 | 120 | parts: 121 | launcher: 122 | plugin: nil 123 | source: ./src 124 | override-build: | 125 | mkdir -p $CRAFT_PART_INSTALL/bin 126 | cp * $CRAFT_PART_INSTALL/bin/ 127 | stage-packages: 128 | - python3-gi 129 | - gir1.2-gtk-3.0 130 | stage: 131 | - bin/* 132 | - usr/lib/python3/dist-packages/* 133 | - usr/share/mime/* 134 | - usr/share/icons/* 135 | - usr/lib/**/gtk-3.0* 136 | - etc/gtk-3.0* 137 | - usr/share/*/gir1.2-gtk-3.0* 138 | - usr/**/libfontconfig* 139 | - usr/**/cairo-1* 140 | - usr/**/libatspi* 141 | - usr/**/libavahi-client* 142 | - usr/**/libavahi-common* 143 | - usr/**/libcairo-gobject* 144 | - usr/**/libcairo* 145 | - usr/**/libcolord* 146 | - usr/**/libcups* 147 | - usr/**/libdatrie* 148 | - usr/**/libdconf* 149 | - usr/**/libdeflate* 150 | - usr/**/libepoxy* 151 | - usr/**/libfribidi* 152 | - usr/**/*girepository* 153 | - usr/**/libgraphite2* 154 | - usr/**/libgtk-3* 155 | - usr/**/libharfbuzz* 156 | - usr/**/libjbig* 157 | - usr/**/libjpeg* 158 | - usr/**/liblcms2* 159 | - usr/**/libpango-1* 160 | - usr/**/libpangocairo-1* 161 | - usr/**/libpangoft2-1* 162 | - usr/**/libpangoxft-1* 163 | - usr/**/libpixman-1* 164 | - usr/**/libthai* 165 | - usr/**/libtiff* 166 | - usr/**/libwayland-cursor* 167 | - usr/**/libwebp* 168 | - usr/**/libxcb-render* 169 | - usr/**/libxkbcommon* 170 | 171 | umu-run: 172 | plugin: dump 173 | source: https://github.com/Open-Wine-Components/umu-launcher.git 174 | source-tag: 1.1.4 175 | build-packages: 176 | - git 177 | - make 178 | - gcc 179 | - scdoc 180 | - dpkg-dev 181 | - dirmngr 182 | - python3-build 183 | - python3-hatchling 184 | - python3-installer 185 | stage-packages: 186 | - libappindicator3-1 187 | - libxcb-dri3-0:amd64 188 | - libxcb-dri3-0:i386 189 | - libpci3 190 | - libvulkan1:i386 191 | - libvulkan1:amd64 192 | - libxml2:i386 193 | - libxml2:amd64 194 | - libicu70:i386 195 | - libicu70:amd64 196 | - zlib1g:i386 197 | - zlib1g:amd64 198 | - xdg-utils 199 | - xdg-user-dirs 200 | - fontconfig-config 201 | - fontconfig:i386 202 | - fontconfig:amd64 203 | - pciutils 204 | - lsof 205 | - locales-all 206 | - usbutils # Allows finding controllers etc 207 | - psmisc 208 | - python3-gi 209 | - gir1.2-gtk-3.0 210 | override-build: | 211 | set -e 212 | git submodule update --init --recursive 213 | ./configure.sh --prefix=$SNAPCRAFT_PART_INSTALL 214 | make 215 | make install DESTDIR=umu_build 216 | cp -R umu_build$SNAPCRAFT_PART_INSTALL/* $SNAPCRAFT_PART_INSTALL/ 217 | cp -R umu_build$SNAPCRAFT_PART_INSTALL/* $SNAPCRAFT_PART_INSTALL/ 218 | cp -R umu_build/usr/local/bin $SNAPCRAFT_PART_INSTALL/usr/ 219 | cp -R umu_build/usr/local/lib/python*/* $SNAPCRAFT_PART_INSTALL/usr/lib/python3/ 220 | rm -Rf umu_build 221 | prime: 222 | - -usr/share/doc 223 | - -usr/share/man 224 | - -usr/share/bug 225 | - -usr/share/gdb 226 | - -usr/share/emacs* 227 | - -usr/share/lintian 228 | - -usr/share/drirc.d 229 | - -usr/share/vulkan 230 | - -usr/share/Xsession.d 231 | - -usr/lib/*/dri 232 | - -usr/lib/*/vdpau 233 | - -usr/lib/*/libvkd3d* 234 | - -usr/lib/*/libvulkan* 235 | - -usr/lib/*/libVk* 236 | - -usr/lib/*/libLLVM* 237 | build-snaps: [core22] 238 | # For some braindead stupid reason sed doesn't work here so we have to overwrite the file instead. 239 | override-prime: | 240 | set -eux 241 | craftctl default 242 | cp -a /snap/core22/current/usr/lib/i386-linux-gnu usr/lib/ 243 | 244 | py-deps: 245 | after: [umu-run] 246 | plugin: nil 247 | stage-packages: 248 | - python3-xlib 249 | - python3-xxhash 250 | - python3-cbor2 251 | - libzstd 252 | prime: 253 | - usr/lib/python3 254 | 255 | alsa-mixin: 256 | plugin: dump 257 | source: https://github.com/diddlesnaps/snapcraft-alsa.git 258 | source-subdir: snapcraft-assets 259 | build-packages: 260 | - libasound2-dev 261 | stage-packages: 262 | - libasound2 263 | - libasound2-plugins 264 | - yad 265 | stage: 266 | # restrict to only audio-related files - you need to ensure 267 | # that gtk3 is staged for yad to work correctly, to prompt 268 | # users to connect the alsa plug or proceed with pulseaudio. 269 | # 270 | # This helps prevent symbol conflicts in situations where 271 | # you're using a non-default library, such as those that the 272 | # gnome-3-34 extension for core18 provides. 273 | - etc/asound.conf 274 | - snap/command-chain/alsa-launch 275 | - usr/bin/yad* 276 | - usr/lib/*/alsa-lib 277 | - usr/lib/*/libasound* 278 | - usr/lib/*/libasyncns* 279 | - usr/lib/*/libdnsfile* 280 | - usr/lib/*/libFLAC* 281 | - usr/lib/*/libjack* 282 | - usr/lib/*/libpulse* 283 | - usr/lib/*/libsamplerate* 284 | - usr/lib/*/libsndfile* 285 | - usr/lib/*/libspeex* 286 | - usr/lib/*/libvorbis* 287 | - usr/lib/*/pulseaudio 288 | - usr/share/alsa 289 | 290 | apps: 291 | umu-run: 292 | command-chain: [snap/command-chain/alsa-launch, bin/desktop-launch] 293 | command: usr/bin/umu-run 294 | plugs: 295 | - shmem 296 | - desktop 297 | - desktop-legacy 298 | - wayland 299 | - home 300 | - x11 301 | - gsettings 302 | - hardware-observe 303 | - mount-observe 304 | - system-observe 305 | - joystick 306 | - network 307 | - network-bind 308 | - opengl 309 | - audio-playback 310 | - audio-record 311 | - screen-inhibit-control 312 | - process-control 313 | - bluez 314 | - network-control 315 | - fuse-support 316 | - steam-support 317 | - removable-media 318 | - upower-observe 319 | - uinput 320 | environment: 321 | HOME: $SNAP_USER_COMMON 322 | #LIBGL_DEBUG: verbose 323 | TMPDIR: $XDG_RUNTIME_DIR 324 | ALWAYS_USE_PULSEAUDIO: 1 325 | DISABLE_WAYLAND: 1 326 | PKG_CONFIG_PATH: $SNAP/usr/lib/i386-linux-gnu/pkgconfig:$SNAP/usr/lib/x86_64-linux-gnu/pkgconfig:$SNAP/usr/lib/pkgconfig 327 | PYTHONPATH: $SNAP/usr/lib/python3/dist-packages 328 | GI_TYPELIB_PATH: $SNAP/usr/lib/x86_64-linux-gnu/girepository-1.0 329 | vulkaninfo: 330 | command-chain: [bin/desktop-launch] 331 | command: bin/vulkaninfo 332 | plugs: 333 | - opengl 334 | - x11 335 | - desktop 336 | vkcube: 337 | command-chain: [bin/desktop-launch] 338 | command: bin/vkcube 339 | plugs: 340 | - opengl 341 | - x11 342 | - desktop 343 | glxinfo: 344 | command-chain: [bin/desktop-launch] 345 | command: bin/glxinfo 346 | plugs: 347 | - opengl 348 | - x11 349 | - desktop 350 | glxgears: 351 | command-chain: [bin/desktop-launch] 352 | command: bin/glxgears 353 | plugs: 354 | - opengl 355 | - x11 356 | - desktop 357 | -------------------------------------------------------------------------------- /packaging/snap/src/desktop-launch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ################# 3 | # Launcher init # 4 | ################# 5 | 6 | # shellcheck disable=SC2034 7 | START=$(date +%s.%N) 8 | 9 | if ! snapctl is-connected "gaming-mesa"; then 10 | echo "ERROR: not connected to the gaming-mesa content interface." 11 | echo "To connect:" 12 | echo "sudo snap connect umu-launcher:gaming-mesa gaming-graphics-core22" 13 | exit 1 14 | fi 15 | 16 | # ensure_dir_exists calls `mkdir -p` if the given path is not a directory. 17 | # This speeds up execution time by avoiding unnecessary calls to mkdir. 18 | # 19 | # Usage: ensure_dir_exists []... 20 | # 21 | function ensure_dir_exists() { 22 | [ -d "$1" ] || mkdir -p "$@" 23 | } 24 | 25 | declare -A PIDS 26 | function async_exec() { 27 | "$@" & 28 | PIDS[$!]=$* 29 | } 30 | function wait_for_async_execs() { 31 | for pid in "${!PIDS[@]}" 32 | do 33 | wait "$pid" && continue || echo "ERROR: ${PIDS[$pid]} exited abnormally with status $?" 34 | done 35 | } 36 | 37 | # shellcheck source=/dev/null 38 | source "$SNAP_USER_DATA/.last_revision" 2>/dev/null || true 39 | if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_REVISION" ]; then 40 | needs_update=false 41 | else 42 | needs_update=true 43 | fi 44 | 45 | # Set $REALHOME to the users real home directory 46 | REALHOME=$(getent passwd $UID | cut -d ':' -f 6) 47 | 48 | export SNAP_DESKTOP_RUNTIME=$SNAP 49 | 50 | # Set config folder to local path 51 | export XDG_CONFIG_HOME="$SNAP_USER_COMMON/.config" 52 | ensure_dir_exists "$XDG_CONFIG_HOME" 53 | chmod 700 "$XDG_CONFIG_HOME" 54 | 55 | # If the user has modified their user-dirs settings, force an update 56 | if [[ -f "$XDG_CONFIG_HOME/user-dirs.dirs.md5sum" ]]; then 57 | if [[ "$(md5sum < "$REALHOME/.config/user-dirs.dirs")" != "$(cat "$XDG_CONFIG_HOME/user-dirs.dirs.md5sum")" || 58 | ( -f "$XDG_CONFIG_HOME/user-dirs.locale.md5sum" && 59 | "$(md5sum < "$REALHOME/.config/user-dirs.locale")" != "$(cat "$XDG_CONFIG_HOME/user-dirs.locale.md5sum")" ) ]]; then 60 | needs_update=true 61 | fi 62 | else 63 | # shellcheck disable=SC2034 64 | needs_update=true 65 | fi 66 | 67 | # If the user has saves in the old $SNAP_USER_DATA/.config, move them 68 | # to $SNAP_USER_COMMON/.config 69 | if [[ -d $SNAP_USER_DATA/.config ]]; then 70 | mv -n $SNAP_USER_DATA/.config/* $XDG_CONFIG_HOME 71 | rm -r $SNAP_USER_DATA/.config 72 | fi 73 | 74 | if [ "$SNAP_ARCH" = "amd64" ]; then 75 | ARCH="x86_64-linux-gnu" 76 | elif [ "$SNAP_ARCH" = "armhf" ]; then 77 | ARCH="arm-linux-gnueabihf" 78 | elif [ "$SNAP_ARCH" = "arm64" ]; then 79 | ARCH="aarch64-linux-gnu" 80 | elif [ "$SNAP_ARCH" = "ppc64el" ]; then 81 | ARCH="powerpc64le-linux-gnu" 82 | else 83 | ARCH="$SNAP_ARCH-linux-gnu" 84 | fi 85 | 86 | # Force i386 87 | export ARCH="i386-linux-gnu" 88 | export SNAP_LAUNCHER_ARCH_TRIPLET="$ARCH" 89 | 90 | ############################################### 91 | # Launcher common exports for any desktop app # 92 | ############################################### 93 | 94 | # Note: We avoid using `eval` because we don't want to expand variable names 95 | # in paths. For example: LD_LIBRARY_PATH paths might contain `$LIB`. 96 | function prepend_dir() { 97 | local -n var="$1" 98 | local dir="$2" 99 | # We can't check if the dir exists when the dir contains variables 100 | if [[ "$dir" == *"\$"* || -d "$dir" ]]; then 101 | export "${!var}=${dir}${var:+:$var}" 102 | fi 103 | } 104 | 105 | function append_dir() { 106 | local -n var="$1" 107 | local dir="$2" 108 | # We can't check if the dir exists when the dir contains variables 109 | if [[ "$dir" == *"\$"* || -d "$dir" ]]; then 110 | export "${!var}=${var:+$var:}${dir}" 111 | fi 112 | } 113 | 114 | 115 | function strip_unreachable_dirs() { 116 | local -n var=$1 117 | local dirs="${var}" 118 | 119 | tmp="" 120 | local IFS=: 121 | for d in $dirs; do 122 | # Just checking existence and the `x` bit isn't enough 123 | # we need to see if we can actually `ls` it because of apparmor 124 | # quirks 125 | ls $d > /dev/null 2>&1 126 | if [[ $? -eq 0 ]]; then 127 | append_dir tmp "$d" 128 | else 129 | echo "INFO: filtering $d out of ${!var} because it is unreachable" 130 | fi 131 | 132 | done 133 | 134 | export "${!var}=${tmp}" 135 | unset tmp 136 | } 137 | 138 | function is_subpath() { 139 | dir="$(realpath "$1")" 140 | parent="$(realpath "$2")" 141 | [ "${dir##$parent/}" != "$dir" ] && return 0 || return 1 142 | } 143 | 144 | append_dir PATH "$SNAP_DESKTOP_RUNTIME/usr/bin" 145 | 146 | # XKB config 147 | export XKB_CONFIG_ROOT="$SNAP_DESKTOP_RUNTIME/usr/share/X11/xkb" 148 | 149 | # Give XOpenIM a chance to locate locale data. 150 | # This is required for text input to work in SDL2 games. 151 | export XLOCALEDIR="$SNAP_DESKTOP_RUNTIME/usr/share/X11/locale" 152 | 153 | # Set XCursors path 154 | export XCURSOR_PATH="$SNAP_DESKTOP_RUNTIME/usr/share/icons" 155 | prepend_dir XCURSOR_PATH "$SNAP/share/icons" 156 | 157 | # Mesa Libs for OpenGL support 158 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/mesa" 159 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/mesa-egl" 160 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/mesa" 161 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/mesa-egl" 162 | # append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/mesa" 163 | # append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/mesa-egl" 164 | # append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/mesa" 165 | # append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/mesa-egl" 166 | 167 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH" 168 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu" 169 | 170 | # Tell libGL and libva where to find the drivers 171 | export LIBGL_DRIVERS_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/dri" 172 | append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/dri" 173 | append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/dri" 174 | append_dir LIBGL_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/dri" 175 | append_dir LD_LIBRARY_PATH "$LIBGL_DRIVERS_PATH" 176 | 177 | append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/dri" 178 | append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/x86_64-linux-gnu/dri" 179 | append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/$ARCH/dri" 180 | append_dir LIBVA_DRIVERS_PATH "$SNAP_DESKTOP_RUNTIME/graphics/usr/lib/x86_64-linux-gnu/dri" 181 | 182 | # Set where the VDPAU drivers are located 183 | export VDPAU_DRIVER_PATH="/usr/lib/$ARCH/vdpau/" 184 | if [ -e "/var/lib/snapd/lib/gl/vdpau/libvdpau_nvidia.so" ]; then 185 | export VDPAU_DRIVER_PATH="/var/lib/snapd/lib/gl/vdpau" 186 | # Prevent picking VA-API (Intel/AMD) over NVIDIA VDPAU; on PRIME systems for example 187 | unset LIBVA_DRIVERS_PATH 188 | fi 189 | 190 | # Export Vulkan ICD filename paths 191 | export VK_ICD_FILENAMES="/var/lib/snapd/lib/vulkan/icd.d/nvidia_icd.json:$SNAP/graphics/usr/share/vulkan/icd.d/radeon_icd.x86_64.json:$SNAP/graphics/usr/share/vulkan/icd.d/radeon_icd.i686.json:$SNAP/graphics/usr/share/vulkan/icd.d/intel_icd.x86_64.json:$SNAP/graphics/usr/share/vulkan/icd.d/intel_icd.i686.json" 192 | 193 | # Workaround in snapd for proprietary nVidia drivers mounts the drivers in 194 | # /var/lib/snapd/lib/gl that needs to be in LD_LIBRARY_PATH 195 | # Without that OpenGL using apps do not work with the nVidia drivers. 196 | # Ref.: https://bugs.launchpad.net/snappy/+bug/1588192 197 | append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl" 198 | append_dir LD_LIBRARY_PATH "/var/lib/snapd/lib/gl/vdpau" 199 | 200 | # Unity7 export (workaround for https://launchpad.net/bugs/1638405) 201 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/libunity" 202 | 203 | # Pulseaudio export 204 | append_dir LD_LIBRARY_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/pulseaudio" 205 | 206 | # EGL vendor files on glvnd enabled systems 207 | prepend_dir __EGL_VENDOR_LIBRARY_DIRS "/var/lib/snapd/lib/glvnd/egl_vendor.d" 208 | append_dir __EGL_VENDOR_LIBRARY_DIRS "$SNAP_DESKTOP_RUNTIME/usr/share/glvnd/egl_vendor.d" 209 | 210 | # Tell GStreamer where to find its plugins 211 | export GST_PLUGIN_PATH="$SNAP/usr/lib/$ARCH/gstreamer-1.0" 212 | export GST_PLUGIN_SYSTEM_PATH="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gstreamer-1.0" 213 | # gst plugin scanner doesn't install in the correct path: https://github.com/ubuntu/snapcraft-desktop-helpers/issues/43 214 | export GST_PLUGIN_SCANNER="$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" 215 | 216 | # XDG Config 217 | prepend_dir XDG_CONFIG_DIRS "$SNAP_DESKTOP_RUNTIME/etc/xdg" 218 | prepend_dir XDG_CONFIG_DIRS "$SNAP/etc/xdg" 219 | 220 | # Define snaps' own data dir 221 | prepend_dir XDG_DATA_DIRS "$SNAP_DESKTOP_RUNTIME/usr/share" 222 | prepend_dir XDG_DATA_DIRS "$SNAP/usr/share" 223 | prepend_dir XDG_DATA_DIRS "$SNAP/share" 224 | prepend_dir XDG_DATA_DIRS "$SNAP/data-dir" 225 | prepend_dir XDG_DATA_DIRS "$SNAP_USER_DATA" 226 | 227 | # Set XDG_DATA_HOME to local path 228 | # steam snap 229 | export XDG_DATA_HOME="$SNAP_USER_COMMON/.local/share" 230 | ensure_dir_exists "$XDG_DATA_HOME" 231 | 232 | # Workaround for GLib < 2.53.2 not searching for schemas in $XDG_DATA_HOME: 233 | # https://bugzilla.gnome.org/show_bug.cgi?id=741335 234 | prepend_dir XDG_DATA_DIRS "$XDG_DATA_HOME" 235 | 236 | # Set cache folder to local path 237 | export XDG_CACHE_HOME="$SNAP_USER_COMMON/.cache" 238 | if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$XDG_CACHE_HOME" ]]; then 239 | # the .cache directory used to be stored under $SNAP_USER_DATA, migrate it 240 | mv "$SNAP_USER_DATA/.cache" "$SNAP_USER_COMMON/" 241 | fi 242 | ensure_dir_exists "$XDG_CACHE_HOME" 243 | 244 | # Create $XDG_RUNTIME_DIR if not exists (to be removed when LP: #1656340 is fixed) 245 | # shellcheck disable=SC2174 246 | ensure_dir_exists "$XDG_RUNTIME_DIR" -m 700 247 | 248 | # Ensure the app finds locale definitions (requires locales-all to be installed) 249 | append_dir LOCPATH "$SNAP_DESKTOP_RUNTIME/usr/lib/locale" 250 | 251 | # If detect wayland server socket, then set environment so applications prefer 252 | # wayland, and setup compat symlink (until we use user mounts. Remember, 253 | # XDG_RUNTIME_DIR is /run/user//snap.$SNAP so look in the parent directory 254 | # for the socket. For details: 255 | # https://forum.snapcraft.io/t/wayland-dconf-and-xdg-runtime-dir/186/10 256 | # Applications that don't support wayland natively may define DISABLE_WAYLAND 257 | # (to any non-empty value) to skip that logic entirely. 258 | wayland_available=false 259 | if [[ -n "$XDG_RUNTIME_DIR" && -z "$DISABLE_WAYLAND" ]]; then 260 | wdisplay="wayland-0" 261 | if [ -n "$WAYLAND_DISPLAY" ]; then 262 | wdisplay="$WAYLAND_DISPLAY" 263 | fi 264 | wayland_sockpath="$XDG_RUNTIME_DIR/../$wdisplay" 265 | wayland_snappath="$XDG_RUNTIME_DIR/$wdisplay" 266 | if [ -S "$wayland_sockpath" ]; then 267 | # if running under wayland, use it 268 | #export WAYLAND_DEBUG=1 269 | # shellcheck disable=SC2034 270 | wayland_available=true 271 | # create the compat symlink for now 272 | if [ ! -e "$wayland_snappath" ]; then 273 | ln -s "$wayland_sockpath" "$wayland_snappath" 274 | fi 275 | fi 276 | fi 277 | 278 | # Make PulseAudio socket available inside the snap-specific $XDG_RUNTIME_DIR 279 | if [ -n "$XDG_RUNTIME_DIR" ]; then 280 | pulsenative="pulse/native" 281 | pulseaudio_sockpath="$XDG_RUNTIME_DIR/../$pulsenative" 282 | if [ -S "$pulseaudio_sockpath" ]; then 283 | export PULSE_SERVER="unix:${pulseaudio_sockpath}" 284 | fi 285 | fi 286 | 287 | # Keep an array of data dirs, for looping through them 288 | IFS=':' read -r -a data_dirs_array <<< "$XDG_DATA_DIRS" 289 | 290 | if [ "$needs_update" = true ]; then 291 | rm -rf "$XDG_DATA_HOME"/{themes,.themes} 292 | fi 293 | 294 | ############################## 295 | # GTK launcher specific part # 296 | ############################## 297 | 298 | # shellcheck disable=SC2154 299 | if [ "$wayland_available" = true ]; then 300 | export GDK_BACKEND="wayland" 301 | export CLUTTER_BACKEND="wayland" 302 | # Does not hurt to specify this as well, just in case 303 | export QT_QPA_PLATFORM=wayland-egl 304 | fi 305 | append_dir GTK_PATH "$SNAP_DESKTOP_RUNTIME/usr/lib/$ARCH/gtk-3.0" 306 | 307 | ############################### 308 | # Mark update and exec binary # 309 | ############################### 310 | 311 | # shellcheck disable=SC2154 312 | [ "$needs_update" = true ] && echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" > "$SNAP_USER_DATA/.last_revision" 313 | 314 | wait_for_async_execs 315 | 316 | # Copy the yaru into place so it's available at runtime 317 | ensure_dir_exists "$XDG_DATA_HOME/Steam/skins" 318 | cp -rp $SNAP/usr/share/Steam/skins/Yaru $XDG_DATA_HOME/Steam/skins/ 319 | 320 | if [ -n "$SNAP_DESKTOP_DEBUG" ]; then 321 | echo "desktop-launch elapsed time: $(date +%s.%N --date="$START seconds ago")" 322 | echo "Now running: exec $*" 323 | fi 324 | 325 | # Fix default run command 326 | # The default works on a system with only the Snap installed, but if they also 327 | # have the deb then the default command will run the deb instead of the Snap. 328 | find "$SNAP_USER_COMMON/.local/share/applications" -type f | while read -r file; do 329 | sed -i "s/Exec=steam/Exec=snap run steam/" "$file" 330 | done 331 | 332 | # Links game icons to host 333 | find "$SNAP_USER_COMMON/.local/share/icons/hicolor" -type f -name "steam_icon_*.png" | while read -r file; do 334 | dest="${file/$SNAP_USER_COMMON/$REALHOME}" 335 | ensure_dir_exists "$(dirname $dest)" 336 | ln -sf "$file" "$dest" 337 | done 338 | 339 | # Link .desktop files to host 340 | ln -sf $SNAP_USER_COMMON/.local/share/applications/* $REALHOME/.local/share/applications/ 341 | 342 | strip_unreachable_dirs XDG_DATA_DIRS 343 | strip_unreachable_dirs XDG_CONFIG_DIRS 344 | strip_unreachable_dirs XDG_SPECIAL_DIRS 345 | 346 | $SNAP/bin/nvidia32 & 347 | "$@" 348 | killall steam-runtime-launcher-service 349 | -------------------------------------------------------------------------------- /packaging/snap/src/glxgears: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec $SNAP/graphics/usr/bin/glxgears "$@" 4 | exit $? 5 | -------------------------------------------------------------------------------- /packaging/snap/src/glxinfo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec $SNAP/graphics/usr/bin/glxinfo "$@" 4 | exit $? 5 | -------------------------------------------------------------------------------- /packaging/snap/src/nvidia32: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import gi 4 | gi.require_version("Gtk", "3.0") 5 | from gi.repository import Gtk, Gdk 6 | import os 7 | import argparse 8 | 9 | TITLE = "NVIDIA 32-bit Not Found" 10 | TEXT = """32-bit NVIDIA driver files were not found on your host system. 11 | It is recommended that you install them for the best experience. 12 | 13 | See https://github.com/canonical/steam-snap/wiki/FAQ#32-bit-driver for more information.""" 14 | COMMAND = """sudo dpkg --add-architecture i386 15 | sudo apt update 16 | sudo apt install libnvidia-gl-{}:i386""" 17 | 18 | DO_NOT_SHOW_PATH = os.path.expandvars("$SNAP_USER_COMMON/.nvidia32") 19 | 20 | def do_not_show_file(do_not_show: bool): 21 | """Ok button press. 22 | 23 | `do_not_show`: whether the Do not show again checkbox is checked or not 24 | """ 25 | if do_not_show: 26 | open(DO_NOT_SHOW_PATH, "a").close() 27 | elif os.path.exists(DO_NOT_SHOW_PATH): 28 | os.remove(DO_NOT_SHOW_PATH) 29 | 30 | 31 | def nvidia_version(): 32 | """Try to find NVIDIA driver version from modinfo. 33 | 34 | Returns a tuple of the full NVIDIA version triplet and NVIDIA major version. 35 | """ 36 | modinfo = os.popen( 37 | "modinfo /usr/lib/modules/$(uname -r)/updates/dkms/nvidia.ko* 2> /dev/null" 38 | " | grep -m 1 '^version:'" 39 | " | sed 's/version:\s*//'" 40 | ).read().splitlines() 41 | modinfo = modinfo or os.popen( 42 | "modinfo /usr/lib/modules/$(uname -r)/kernel/nvidia*/nvidia.ko* 2> /dev/null" 43 | " | grep -m 1 '^version:'" 44 | " | sed 's/version:\s*//'" 45 | ).read().splitlines() 46 | 47 | if not modinfo: 48 | return () 49 | nvidia_version = modinfo[0].strip() 50 | nvidia_version_major = nvidia_version.split(".")[0] 51 | return (nvidia_version, nvidia_version_major) 52 | 53 | 54 | def nvidia_missing(): 55 | """Returns whether NVIDIA 32 is missing or not. 56 | """ 57 | version = nvidia_version() 58 | if not version: 59 | return False 60 | 61 | print(f"Found NVIDIA version: {version[0]}") 62 | needs_32 = os.path.exists(f"/var/lib/snapd/hostfs/usr/lib/x86_64-linux-gnu/libnvidia-glcore.so.{version[0]}")\ 63 | and not os.path.exists(f"/var/lib/snapd/hostfs/usr/lib/i386-linux-gnu/libnvidia-glcore.so.{version[0]}") 64 | print(f"Need NVIDIA 32-bit: {needs_32}") 65 | return needs_32 66 | 67 | 68 | class NvidiaWindow(Gtk.Window): 69 | def __init__(self): 70 | super().__init__() 71 | self.set_resizable(False) 72 | self.set_border_width(10) 73 | self.set_position(Gtk.WindowPosition.CENTER) 74 | self.set_keep_above(True) 75 | 76 | header = Gtk.HeaderBar(title=TITLE) 77 | self.set_titlebar(header) 78 | 79 | self.grid = Gtk.Grid( 80 | row_spacing=10, 81 | column_spacing=10 82 | ) 83 | self.add(self.grid) 84 | 85 | # main text 86 | self.label = Gtk.Label() 87 | self.label.set_markup(TEXT) 88 | self.grid.attach(self.label, 0, 0, 2, 1) 89 | 90 | # run prompt 91 | self.run_label = Gtk.Label( 92 | margin_top=20, 93 | halign=Gtk.Align.START 94 | ) 95 | self.run_label.set_markup("To install, run:") 96 | self.grid.attach_next_to(self.run_label, self.label, Gtk.PositionType.BOTTOM, 1, 1) 97 | 98 | # copy button 99 | self.copy_btn = Gtk.Button.new_from_icon_name("edit-copy-symbolic", Gtk.IconSize.MENU) 100 | self.copy_btn.set_margin_top(20) 101 | self.copy_btn.set_tooltip_text("Copy") 102 | self.copy_btn.connect("clicked", lambda x: self.button_copy()) 103 | self.copy_btn.set_halign(Gtk.Align.END) 104 | self.grid.attach_next_to(self.copy_btn, self.run_label, Gtk.PositionType.RIGHT, 1, 1) 105 | 106 | # command text 107 | self.nvidia_version = nvidia_version() 108 | self.textview = Gtk.TextView( 109 | editable=False, 110 | monospace=True, 111 | top_margin=5, 112 | bottom_margin=5, 113 | right_margin=10, 114 | left_margin=10 115 | ) 116 | command_text = COMMAND.format("") 117 | if self.nvidia_version: command_text = COMMAND.format(self.nvidia_version[1]) 118 | self.textview.get_buffer().set_text(command_text) 119 | self.grid.attach_next_to(self.textview, self.run_label, Gtk.PositionType.BOTTOM, 2, 1) 120 | 121 | # Do not show again button 122 | self.checkbox = Gtk.CheckButton( 123 | label="Do not show again", 124 | halign=Gtk.Align.END, 125 | tooltip_markup="This window can always be shown again by running " 126 | "$SNAP/bin/nvidia32 --reset in the Steam Snap shell." 127 | ) 128 | self.grid.attach_next_to(self.checkbox, self.textview, Gtk.PositionType.BOTTOM, 1, 1) 129 | 130 | # Ok button 131 | self.btn = Gtk.Button(label="Ok") 132 | self.btn.connect("clicked", lambda x: self.button_ok(self.checkbox.get_active())) 133 | self.grid.attach_next_to(self.btn, self.checkbox, Gtk.PositionType.RIGHT, 1, 1) 134 | 135 | 136 | def button_ok(self, toggled: bool): 137 | do_not_show_file(toggled) 138 | self.close() 139 | 140 | 141 | def button_copy(self): 142 | clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) 143 | clipboard.set_text(COMMAND.format(self.nvidia_version[1]), -1) 144 | 145 | 146 | def main(): 147 | parser = argparse.ArgumentParser() 148 | parser.add_argument( 149 | "--reset", 150 | help="Show the dialog again if you selected 'Do not show again'", 151 | action="store_true" 152 | ) 153 | parser.add_argument( 154 | "--show", 155 | help="Show the dialog regardless of conditions met", 156 | action="store_true" 157 | ) 158 | args = parser.parse_args() 159 | 160 | if args.reset: 161 | do_not_show_file(False) 162 | 163 | if args.show or (nvidia_missing() and not os.path.exists(DO_NOT_SHOW_PATH)): 164 | win = NvidiaWindow() 165 | win.connect("destroy", Gtk.main_quit) 166 | win.show_all() 167 | Gtk.main() 168 | 169 | 170 | if __name__ == "__main__": 171 | main() 172 | -------------------------------------------------------------------------------- /packaging/snap/src/vkcube: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec $SNAP/graphics/usr/bin/vkcube "$@" 4 | exit $? 5 | -------------------------------------------------------------------------------- /packaging/snap/src/vulkaninfo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec $SNAP/graphics/usr/bin/vulkaninfo "$@" 4 | exit $? 5 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "umu-launcher" 7 | dynamic = ["version"] 8 | description = "Unified Linux Wine Game Launcher." 9 | authors = [ 10 | { name = "GloriousEggroll", email = "gloriouseggroll@gmail.com" }, 11 | { name = "Mathieu Comandon", email = "strycore@gmail.com" }, 12 | { name = "Paweł Lidwin", email = "lidwinpawel@gmail.com" }, 13 | { name = "mirkobrombin", email = "send@mirko.pm" }, 14 | { name = "Flavio F Lima", email = "flavioislima@users.noreply.github.com" }, 15 | ] 16 | maintainers = [ 17 | { name = "GloriousEggroll", email = "gloriouseggroll@gmail.com" }, 18 | { name = "R1kaB3rN", email = "100738684+R1kaB3rN@users.noreply.github.com" }, 19 | ] 20 | requires-python = ">=3.10" 21 | license = { file = "README.md" } 22 | keywords = ["umu", "umu-launcher"] 23 | classifiers = [ 24 | "Development Status :: 4 - Beta", 25 | "Intended Audience :: End Users/Desktop", 26 | "Intended Audience :: Developers", 27 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 28 | "Programming Language :: Python :: 3.10", 29 | "Programming Language :: Python :: 3.11", 30 | "Programming Language :: Python :: 3.12", 31 | "Programming Language :: Python :: Implementation :: CPython", 32 | "Programming Language :: Python :: Implementation :: PyPy", 33 | "Topic :: Software Development :: Libraries :: Python Modules", 34 | ] 35 | urls = { repository = "https://github.com/Open-Wine-Components/umu-launcher" } 36 | # Note: urllib3 is a vendored dependency. When using our Makefile, it will be 37 | # installed automatically. 38 | dependencies = ["python-xlib>=0.33", "urllib3>=2.0.0"] 39 | 40 | [project.optional-dependencies] 41 | # Recommended 42 | # For network requests, use the system's CA bundle instead of certifi's 43 | cli = ["truststore"] 44 | # Support for delta updates for Proton and the Steam Linux Runtime 45 | delta-updates = ["cbor2>=5.4.6,<6.0.0", "xxhash>=3.2.0,<4.0.0", "pyzstd>=0.16.2"] 46 | 47 | [project.scripts] 48 | umu-run = "umu.__main__:main" 49 | 50 | [dependency-groups] 51 | dev = ["pip", "hatch", "installer", "build", "patchelf", "maturin"] 52 | 53 | [tool.hatch.build.targets.sdist] 54 | exclude = [ 55 | "/.github", 56 | "/.git", 57 | "/umu/__pycache__", 58 | "/.mypy_cache", 59 | "/.ruff_cache", 60 | ] 61 | 62 | [tool.hatch.build.targets.wheel] 63 | packages = ["umu"] 64 | exclude = [ 65 | "/umu/umu-launcher", 66 | "/umu/__pycache__", 67 | "/umu/ruff.toml", 68 | "/umu/umu_version.json.in", 69 | "/umu/umu-run.in", 70 | "/umu/umu_test.py", 71 | "/umu/umu_test_plugins.py", 72 | ] 73 | 74 | [tool.hatch.version] 75 | path = "umu/__init__.py" 76 | 77 | [tool.isort] 78 | profile = "black" 79 | py_version = 312 80 | 81 | [tool.mypy] 82 | python_version = "3.10" 83 | warn_return_any = true 84 | ignore_missing_imports = true 85 | disable_error_code = [ 86 | # Allow redefinitions since we redefine an error variable before raising exceptions 87 | "no-redef", 88 | ] 89 | exclude = [ 90 | 'umu/umu_test.py', 91 | 'umu/umu_test_plugins.py', 92 | 'subprojects', 93 | 'builddir', 94 | 'dist', 95 | ] 96 | 97 | [tool.pdm] 98 | distribution = true 99 | 100 | [tool.pdm.dev-dependencies] 101 | dev = [ 102 | "black>=24.4.2", 103 | "GitPython>=3.1.40", 104 | "isort>=5.13.2", 105 | "mypy>=1.10.0", 106 | "ruff>=0.4.2", 107 | "toml-sort>=0.23.1", 108 | "types-PyYAML>=6.0.12.20240311", 109 | ] 110 | 111 | [tool.pdm.scripts] 112 | # static analysis 113 | format = { composite = [ 114 | "black umu/ tests/", 115 | "isort --settings-path ./pyproject.toml umu/ tests/", 116 | "toml-sort -i ./pyproject.toml", 117 | ] } 118 | lint-fix = "ruff check --config ./pyproject.toml . --fix" 119 | type-check = "mypy --config-file ./pyproject.toml --enable-incomplete-feature=NewGenericSyntax umu/" 120 | 121 | [tool.pdm.version] 122 | fallback_version = "0.0.0" 123 | source = "scm" 124 | write_template = "__version__: str = \"{}\"\n" 125 | write_to = "code_review/_version.py" 126 | 127 | [tool.ruff] 128 | exclude = [ 129 | ".bzr", 130 | ".direnv", 131 | ".eggs", 132 | ".git", 133 | ".git-rewrite", 134 | ".hg", 135 | ".ipynb_checkpoints", 136 | ".mypy_cache", 137 | ".nox", 138 | ".pants.d", 139 | ".pyenv", 140 | ".pytest_cache", 141 | ".pytype", 142 | ".ruff_cache", 143 | ".svn", 144 | ".tox", 145 | ".venv", 146 | ".vscode", 147 | "__pypackages__", 148 | "_build", 149 | "buck-out", 150 | "build", 151 | "dist", 152 | "node_modules", 153 | "site-packages", 154 | "venv", 155 | "builddir", 156 | "subprojects", 157 | ] 158 | # Same as Black. 159 | line-length = 88 160 | indent-width = 4 161 | target-version = "py310" 162 | 163 | [tool.ruff.format] 164 | # Like Black, use double quotes for strings. 165 | quote-style = "double" 166 | # Like Black, indent with spaces, rather than tabs. 167 | indent-style = "space" 168 | # Like Black, respect magic trailing commas. 169 | skip-magic-trailing-comma = false 170 | # Like Black, automatically detect the appropriate line ending. 171 | line-ending = "auto" 172 | # Enable auto-formatting of code examples in docstrings. Markdown, 173 | # reStructuredText code/literal blocks and doctests are all supported. 174 | # 175 | # This is currently disabled by default, but it is planned for this 176 | # to be opt-out in the future. 177 | docstring-code-format = false 178 | # Set the line length limit used when formatting code snippets in 179 | # docstrings. 180 | # 181 | # This only has an effect when the `docstring-code-format` setting is 182 | # enabled. 183 | docstring-code-line-length = "dynamic" 184 | 185 | [tool.ruff.lint] 186 | select = [ 187 | # Ruff defaults 188 | "E4", 189 | "E7", 190 | "E9", 191 | "F", 192 | # Ensure we're being smart when raising, returning or breaking 193 | "RET", 194 | # Enforce pydocstyle 195 | "W", 196 | # Enforce more formats 197 | # See https://docs.astral.sh/ruff/rules/#warning-w 198 | "D", 199 | # Enforce pathlib when working with paths 200 | "PTH", 201 | # Ensure exceptions are not blindly caught 202 | "BLE001", 203 | # Enforce flake8 error messages 204 | "EM", 205 | # Ensure we're being smart when working with archives/executing shell commands 206 | # Or when dealing with input returned from web services (e.g., Github) 207 | "S", 208 | # Ensure we do not exceed our line length 209 | "E501", 210 | # Enforce types for variables 211 | "ANN", 212 | # Ensure we're smart when accessing dicts 213 | "RUF019", 214 | # Enforce not concatenating collections 215 | "RUF005", 216 | # Flag unused qa directives 217 | "RUF100", 218 | # Enforce flake8 concat strings because the + operator can be unnecessary 219 | "ISC", 220 | # Ensure we're smart when using the logger 221 | # Specific log statements should execute only when necessary 222 | "G", 223 | # Prefer collapsing nested if-elif in the else branch 224 | "PLR5501", 225 | # Simply and catch redundant logic 226 | "SIM", 227 | # Ensure no useless arguments/paramaters 228 | "ARG", 229 | # Ensure explicit check= for subprocess.run to avoid silent failures 230 | "PLW1510", 231 | "UP", 232 | "FURB", 233 | # Sort imports 234 | "I", 235 | # Ensure we're smart when using list comprehensions 236 | "C4", 237 | # Ensure we do not shadow any Python built-in functionality 238 | "A", 239 | # Ensure there's no empty comments 240 | "PLR2044", 241 | # Prefer refactoring nested if-else to elif 242 | "PLR5501", 243 | # Enforce using set literal membership than other data structures 244 | "PLR6201", 245 | # Prefer creating constants instead of magic values 246 | "PLR2004", 247 | # Refactor long equality expressions to membership tests 248 | # Example: 249 | # i == 'foo' or i == 'bar' or i == 'baz' 250 | # 251 | # Use instead: 252 | # i in {'foo', 'bar', 'baz'} 253 | "PLR1714", 254 | # Ensure our pyproject.toml follows relevant PEPs whenever we make changes to it 255 | "RUF200", 256 | # Parenthesize chained operations in expressions. 257 | # Example: 258 | # a, b, c = 1, 0, 2 259 | # x = a or b and c 260 | # 261 | # Use instead: 262 | # a, b, c = 1, 0, 2 263 | # x = a or (b and c) 264 | "RUF021", 265 | # When importing modules, ensure we do not shadow built-ins 266 | "A004", 267 | # Ensure we do not shadow built-ins in lambda expressions 268 | "A006", 269 | # Enforce simpler boolean expressions 270 | # Example: 271 | # d = a < b and b < c 272 | # 273 | # Use instead: 274 | # d = a < b < c 275 | "PLR1716", 276 | # Check for useless if-else conditionals 277 | "RUF034", 278 | # Check for duplicate union members for both variables and parameters 279 | # Example: 280 | # def foo(bar: int | int): 281 | # pass 282 | "PYI016", 283 | # Ensure we're being smart when using the logging module 284 | "LOG", 285 | # Check for unnecessary len() calls on Sequence types in boolean expressions 286 | # a = [1, 2] 287 | # if len(a): 288 | # print(a) 289 | # 290 | # Use instead: 291 | # if a: 292 | # print(a) 293 | "PLC1802", 294 | # Check for shallow copies of os.environ 295 | "PLW1507", 296 | # Check if-key-in-dict-del logic when working with MutableMapping types 297 | # if key in d: 298 | # del d[key] 299 | # 300 | # Use instead: 301 | # d.pop(key, None) 302 | "RUF051", 303 | # Check invalid default values for os.getenv or similar 304 | # Example: 305 | # int(os.getenv("FOO", 1)) 306 | "PLW1508", 307 | ] 308 | ignore = [ 309 | # Format 310 | "D100", 311 | "D203", 312 | "D213", 313 | "E501", 314 | # Ignore untrusted input for subprocess 315 | # This raises false negatives and currently we shell out when: 316 | # Using Zenity or executing the final command 317 | # In the above cases, we *always* verify the existence of the command/files 318 | "S603", 319 | # We use a static location for the crash report dir: /tmp/umu_crashreports 320 | "S108", 321 | # Don't flag missing type for self 322 | "ANN201", 323 | # Single line implicit concat conflicts with the formatter 324 | "ISC001", 325 | "S404", 326 | ] 327 | # Allow fix for all enabled rules (when `--fix`) is provided. 328 | fixable = ["ALL"] 329 | unfixable = [] 330 | # Allow unused variables when underscore-prefixed. 331 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 332 | logger-objects = ["umu.umu_log.log"] 333 | 334 | [tool.ruff.lint.per-file-ignores] 335 | "umu_test.py" = ["ANN"] 336 | "umu_test_plugins.py" = ["ANN"] 337 | -------------------------------------------------------------------------------- /release_notes.md: -------------------------------------------------------------------------------- 1 | # 🎁 Release notes (`1.2.6`) 2 | 3 | ## Changes 4 | - Merge pull request #426 from Open-Wine-Components/revert-410-unruntime 5 | - Revert "umu_run: handle Protons without an explicit runtime requirement" 6 | - bump release notes 7 | - Merge pull request #408 from R1kaB3rN/bump-version-1.2.6 8 | - Merge pull request #424 from Open-Wine-Components/fix/install-path 9 | - Merge pull request #410 from loathingKernel/unruntime 10 | - improv: default to environment provided STEAM_COMPAT_INSTALL_PATH 11 | - umu: update tests 12 | - umu_run: move toml config loading earlier and merge it with the environment 13 | - build(deps): bump cachix/install-nix-action from 30 to 31 (#423) 14 | - umu: update tests 15 | - umu_run: extract function from `umu_run` to download proton if needed 16 | - umu_run: raise exception if PROTONPATH doesn't exist while checking for runtime version 17 | - umu_run: try to decouple get_umu_proton from check_env 18 | - umu_run: handle Protons without an explicit runtime requirement 19 | - Merge pull request #402 from MattSturgeon/nix/drop-old 20 | - Merge pull request #413 from loathingKernel/isatty 21 | - packaging: update umu-launcher debian packages (#422) 22 | - deb: update rustup patch (#421) 23 | - refactor: use __package__ to determine module (#420) 24 | - feat: extend lint rules (#419) 25 | - umu_log: do an early return if not tty 26 | - umu_log: do not use colors if stderr is not an interactive terminal 27 | - build(deps): bump pyo3 from 0.23.5 to 0.24.0 (#411) 28 | - Bump version to 1.2.6 29 | - fix: adhere to the XDG spec for compatibilitytools.d 30 | - build: remove umu-launcher install from packaging 31 | - build: remove umu-launcher build target 32 | - Don't package and distrbute umu-launcher as a compatibility tool -- steam ends up using it on every launch (bug), and there's also not really any point 33 | - build(nix): drop support for outdated nixpkgs revisions 34 | - packaging/nix/flake.lock: Update (#406) 35 | - build(deps): bump pyo3 from 0.23.4 to 0.23.5 (#405) 36 | - refactor: update runtime directory structure (#400) 37 | - Support overriding 1.2.0+ nix package (#374) 38 | - bump commit on rpm spec sheet to match tag just in case of manual builds 39 | - bump release notes for 1.2.5 (again) 40 | 41 | ## Metadata 42 | ``` 43 | This version -------- 1.2.6 44 | Previous version ---- 1.2.5 45 | Total commits ------- 36 46 | ``` 47 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | python-xlib>=0.33 2 | urllib3>=2.0.0,<3.0.0 3 | xxhash>=3.2.0 4 | pyzstd>=0.16.2 5 | cbor2>=5.4.6 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use base16ct::lower::encode_string; 2 | use pyo3::prelude::*; 3 | use sha2::{Digest, Sha512}; 4 | use ssh_key::{PublicKey, SshSig}; 5 | 6 | /// Required parameter to create/verify digital signatures 7 | /// See https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.sshsig?annotate=HEAD 8 | const NAMESPACE: &str = "umu.openwinecomponents.org"; 9 | 10 | /// Whitelist of valid OpenSSH formatted, Ed25519 public keys 11 | /// Used for delta updates to create the root of trust 12 | const PUBLIC_KEYS: [&str; 1] = ["5b0b4cd1dad99cd013d5a88cf27d6c7414db33ece7f3146f96fb0f62c64ec15317a22f3f05048ac29177be9d95c47856e01b6e2a3dc61dd8202df4156465899c"]; 13 | 14 | #[pyfunction] 15 | fn valid_key(source: &str) -> bool { 16 | let hash = Sha512::digest(source.as_bytes()); 17 | let hash_hex = &encode_string(&hash); 18 | PUBLIC_KEYS.contains(&hash_hex.as_str()) 19 | } 20 | 21 | #[pyfunction] 22 | fn valid_signature(source: &str, message: &[u8], pem: &[u8]) -> bool { 23 | let public_key = match PublicKey::from_openssh(source) { 24 | Ok(ret) => ret, 25 | Err(e) => { 26 | eprintln!("{}", e); 27 | return false; 28 | } 29 | }; 30 | let ssh_sig = match SshSig::from_pem(pem) { 31 | Ok(ret) => ret, 32 | Err(e) => { 33 | eprintln!("{}", e); 34 | return false; 35 | } 36 | }; 37 | public_key.verify(NAMESPACE, message, &ssh_sig).is_ok() 38 | } 39 | 40 | #[pymodule(name = "umu_delta")] 41 | fn umu(m: &Bound<'_, PyModule>) -> PyResult<()> { 42 | m.add_function(wrap_pyfunction!(valid_signature, m)?)?; 43 | m.add_function(wrap_pyfunction!(valid_key, m)?)?; 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /tests/test_config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | python --version 4 | 5 | tmp=$(mktemp) 6 | mkdir -p "$HOME/.local/share/Steam/compatibilitytools.d" 7 | mkdir -p "$HOME/Games/umu/umu-0" 8 | curl -LJO "https://github.com/Open-Wine-Components/umu-proton/releases/download/UMU-Proton-9.0-3.2/UMU-Proton-9.0-3.2.tar.gz" 9 | tar xaf UMU-Proton-9.0-3.2.tar.gz 10 | mv UMU-Proton-9.0-3.2 "$HOME/.local/share/Steam/compatibilitytools.d" 11 | 12 | echo "[umu] 13 | proton = '~/.local/share/Steam/compatibilitytools.d/UMU-Proton-9.0-3.2' 14 | game_id = 'umu-1141086411' 15 | prefix = '~/Games/umu/umu-0' 16 | exe = '~/Games/umu/umu-0/drive_c/windows/syswow64/wineboot.exe' 17 | launch_args = ['-u'] 18 | store = 'gog' 19 | " >> "$tmp" 20 | 21 | 22 | RUNTIMEPATH=steamrt3 UMU_LOG=debug GAMEID=umu-1141086411 STORE=gog "$PWD/.venv/bin/python" "$HOME/.local/bin/umu-run" --config "$tmp" 2> /tmp/umu-log.txt && grep -E "INFO: Non-steam game Silent Hill 4: The Room \(umu-1141086411\)" /tmp/umu-log.txt 23 | # Run the 'game' using UMU-Proton9.0-3.2 and ensure the protonfixes module finds its fix in umu-database.csv 24 | -------------------------------------------------------------------------------- /tests/test_delta.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import os 5 | import shutil 6 | import ssl 7 | import subprocess 8 | import sys 9 | import tarfile 10 | import tempfile 11 | from pathlib import Path 12 | from urllib.request import Request, urlopen 13 | 14 | import cbor2 15 | 16 | 17 | def main(): # noqa: D103 18 | url = ( 19 | "https://api.github.com/repos/Open-Wine-Components/umu-mkpatch/releases/latest" 20 | ) 21 | headers = { 22 | "Accept": "application/vnd.github+json", 23 | "X-GitHub-Api-Version": "2022-11-28", 24 | "User-Agent": "", 25 | } 26 | codename = "UMU-Latest" 27 | ctx = ssl.create_default_context() 28 | 29 | durl = "" 30 | with urlopen( # noqa: S310 31 | Request(url, headers=headers), # noqa: S310 32 | context=ctx, 33 | ) as resp: 34 | releases = json.loads(resp.read()) 35 | for release in releases["assets"]: 36 | if not release["name"].endswith("cbor"): 37 | continue 38 | if release["name"].startswith(codename): 39 | durl = release["browser_download_url"] 40 | break 41 | 42 | if not durl: 43 | print(f"Could not find release with codename '{codename}', skipping") 44 | return 0 45 | 46 | version = "" 47 | target = "" 48 | with urlopen(durl) as resp: # noqa: S310 49 | patch = cbor2.loads(resp.read()) 50 | for content in patch.get("contents"): 51 | if content.get("source", "").startswith("UMU-Proton"): 52 | version = content["source"] 53 | target = content["target"] 54 | break 55 | 56 | # Verify the latest release matches the patch's target 57 | url = "https://api.github.com/repos/Open-Wine-Components/umu-proton/releases/latest" 58 | durl = "" 59 | is_target = False 60 | with urlopen( # noqa: S310 61 | Request(url, headers=headers), # noqa: S310 62 | context=ctx, 63 | ) as resp: 64 | releases = json.loads(resp.read()) 65 | for release in releases["assets"]: 66 | if release["name"].startswith(target) and release["name"].endswith( 67 | ".tar.gz" 68 | ): 69 | is_target = True 70 | break 71 | 72 | # Case when the latest release doesn't match the patch target 73 | if not is_target: 74 | print(f"Latest release is not expected patch target '{target}', skipping") 75 | return 0 76 | 77 | durl = f"https://github.com/Open-Wine-Components/umu-proton/releases/download/{version}/{version}.tar.gz" 78 | with urlopen(durl) as resp: # noqa: S310 79 | buffer = bytearray(64 * 1024) 80 | view = memoryview(buffer) 81 | with tempfile.NamedTemporaryFile(mode="ab+", buffering=0) as file: 82 | while size := resp.readinto(buffer): 83 | file.write(view[:size]) 84 | cache = Path.home().joinpath(".cache", "umu") 85 | cache.mkdir(parents=True, exist_ok=True) 86 | file.seek(0) 87 | cache.joinpath(f"{version}.tar.gz").write_bytes(file.read()) 88 | 89 | with tarfile.open(cache.joinpath(f"{version}.tar.gz")) as tar: 90 | tar.extraction_filter = tarfile.tar_filter 91 | tar.extractall(path=cache) # noqa: S202 92 | compat = Path.home().joinpath(".local", "share", "umu", "compatibilitytools") 93 | compat.mkdir(parents=True, exist_ok=True) 94 | shutil.move(cache.joinpath(version), compat) 95 | compat.joinpath(version).rename(compat.joinpath("UMU-Latest")) 96 | 97 | path = "/usr/local/bin" 98 | exe = shutil.which("umu-run", path=path) 99 | if exe is None: 100 | print(f"Could not find umu-run in '{path}', exiting") 101 | return 1 102 | 103 | return subprocess.run( 104 | (exe, "wineboot", "-u"), 105 | env={ 106 | "PROTONPATH": "UMU-Latest", 107 | "GAMEID": "umu-0", 108 | "UMU_LOG": "1", 109 | "PATH": os.environ["PATH"], 110 | }, 111 | check=False, 112 | ).returncode 113 | 114 | 115 | if __name__ == "__main__": 116 | sys.exit(main()) 117 | -------------------------------------------------------------------------------- /tests/test_flock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Ensure umu-launcher does not download its fetched resources more than once 5 | # when multiple processes of itself are created 6 | # 7 | 8 | tmp1=$(mktemp) 9 | tmp2=$(mktemp) 10 | 11 | UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" wineboot -u 2> "$tmp1" & 12 | sleep 1 13 | UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" wineboot -u 2> "$tmp2" & 14 | wait 15 | 16 | grep "exited with wait status" "$tmp1" && grep -E "exited with wait status" "$tmp2" 17 | 18 | # Ensure the 2nd proc didn't download the runtime 19 | grep -E "\(latest\), please wait..." "$tmp2" 20 | exit_code=$? 21 | if "$exit_code" -ne 0; then 22 | exit 1 23 | fi 24 | 25 | # Ensure the 2nd proc didn't download Proton 26 | grep "Downloading" "$tmp2" 27 | exit_code=$? 28 | if "$exit_code" -ne 0; then 29 | exit 1 30 | fi 31 | 32 | -------------------------------------------------------------------------------- /tests/test_install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | UMU_LOG=debug "$HOME/.local/bin/umu-run" wineboot -u 4 | -------------------------------------------------------------------------------- /tests/test_install_obsolete.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | mkdir -p "$HOME/.local/share/Steam/compatibilitytools.d" 4 | curl -LJO "https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton7-55/GE-Proton7-55.tar.gz" 5 | tar xaf GE-Proton7-55.tar.gz 6 | mv GE-Proton7-55 "$HOME/.local/share/Steam/compatibilitytools.d" 7 | 8 | UMU_LOG=debug PROTONPATH=GE-Proton7-55 "$HOME/.local/bin/umu-run" wineboot -u 9 | -------------------------------------------------------------------------------- /tests/test_offline.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | tmp=$(mktemp) 4 | name=$(curl -L "https://api.github.com/repos/Open-Wine-Components/umu-proton/releases/latest" | jq .assets[1].name | tr -d '"') 5 | 6 | # Ensure the Proton directory doesn't exist 7 | rm "$name" 8 | 9 | # Download runtime 10 | curl -LJO "https://repo.steampowered.com/steamrt3/images/latest-container-runtime-public-beta/SteamLinuxRuntime_sniper.tar.xz" 11 | url=$(curl -L "https://api.github.com/repos/Open-Wine-Components/umu-proton/releases/latest" | jq .assets[1].browser_download_url | tr -d '"') 12 | 13 | # Download Proton 14 | curl -LJO "$url" 15 | 16 | mkdir -p "$HOME"/.local/share/Steam/compatibilitytools.d "$HOME"/.local/share/umu/steamrt3 "$HOME"/Games/umu 17 | 18 | # Extract the archives 19 | tar xaf "$name" -C "$HOME"/.local/share/Steam/compatibilitytools.d 20 | tar xaf SteamLinuxRuntime_sniper.tar.xz 21 | 22 | cp -a SteamLinuxRuntime_sniper/* "$HOME"/.local/share/umu/steamrt3 23 | mv "$HOME"/.local/share/umu/steamrt3/_v2-entry-point "$HOME"/.local/share/umu/steamrt3/umu 24 | 25 | # Run offline using bwrap 26 | # TODO: Figure out why the command exits with a 127 when offline. The point 27 | # is that we're able to enter the container and we do not crash. For now, 28 | # just query a string that shows that session was offline 29 | RUNTIMEPATH=steamrt3 UMU_LOG=debug GAMEID=umu-0 bwrap --unshare-net --bind / / --dev /dev --bind "$HOME" "$HOME" -- "$HOME/.local/bin/umu-run" wineboot -u 2> "$tmp" 30 | 31 | # Check if we exited. If we logged this statement then there were no errors 32 | # before entering the container 33 | grep "exited with wait status" "$tmp" 34 | # Check if we were offline 35 | grep "unreachable" "$tmp" 36 | -------------------------------------------------------------------------------- /tests/test_proton_resume.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | tmp=$(mktemp) 4 | name=$(curl -L "https://api.github.com/repos/Open-Wine-Components/umu-proton/releases/latest" | jq .assets[1].name | tr -d '"') 5 | url=$(curl -L "https://api.github.com/repos/Open-Wine-Components/umu-proton/releases/latest" | jq .assets[1].browser_download_url | tr -d '"') 6 | # Request the first 100MB of the latest UMU-Proton release 7 | curl -LJO --range 0-104857599 "$url" 8 | # Move the incomplete file to our cache to be picked up 9 | # Note: Must include the *.parts extension 10 | mkdir -p "$HOME"/.cache/umu 11 | mv "$name" "$HOME"/.cache/umu/"$name".parts 12 | UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" wineboot -u 2> "$tmp" 13 | grep "resuming" "$tmp" && grep "exited with wait status" "$tmp" 14 | -------------------------------------------------------------------------------- /tests/test_resume.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | tmp=$(mktemp) 4 | # Request the BUILD_ID.txt value. We append this to the working file to ID it 5 | id=$(curl -L "https://repo.steampowered.com/steamrt3/images/latest-container-runtime-public-beta/BUILD_ID.txt" | tr -d "\n") 6 | # Request the first 100MB of the runtime archive 7 | curl -LJO --range 0-104857599 "https://repo.steampowered.com/steamrt3/images/latest-container-runtime-public-beta/SteamLinuxRuntime_sniper.tar.xz" 8 | mkdir -p "$HOME"/.cache/umu 9 | # Move to our cache so it can be picked up then resumed. 10 | # Note: Must include the *.parts extension 11 | mv SteamLinuxRuntime_sniper.tar.xz "$HOME"/.cache/umu/SteamLinuxRuntime_sniper.tar.xz."$id".parts 12 | RUNTIMEPATH=steamrt3 UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" wineboot -u 2> "$tmp" 13 | grep "resuming" "$tmp" && grep "exited with wait status" "$tmp" 14 | -------------------------------------------------------------------------------- /tests/test_update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | mkdir -p "$HOME/.local/share/umu/steamrt3" 4 | 5 | curl -LJO "https://repo.steampowered.com/steamrt3/images/0.20240916.101795/SteamLinuxRuntime_sniper.tar.xz" 6 | tar xaf SteamLinuxRuntime_sniper.tar.xz 7 | mv SteamLinuxRuntime_sniper/* "$HOME/.local/share/umu/steamrt3" 8 | mv "$HOME/.local/share/umu/steamrt3/_v2-entry-point" "$HOME/.local/share/umu/steamrt3/umu" 9 | echo "$@" > "$HOME/.local/share/umu/steamrt3/umu-shim" && chmod 700 "$HOME/.local/share/umu/steamrt3/umu-shim" 10 | 11 | # Perform a preflight step, where we ensure everything is in order and create '$HOME/.local/share/umu/var' 12 | # Afterwards, run a 2nd time to perform the runtime update and ensure '$HOME/.local/share/umu/var' is removed 13 | UMU_LOG=debug GAMEID=umu-0 UMU_RUNTIME_UPDATE=0 "$HOME/.local/bin/umu-run" wineboot -u && RUNTIMEPATH=steamrt3 UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" wineboot -u 14 | -------------------------------------------------------------------------------- /tests/test_winetricks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | UMU_LOG=debug GAMEID=umu-0 "$HOME/.local/bin/umu-run" winetricks good 4 | -------------------------------------------------------------------------------- /tests/testdata/winetricks_verbs.txt: -------------------------------------------------------------------------------- 1 | 3m_library 2 | 7zip 3 | adobe_diged4 4 | adobe_diged 5 | autohotkey 6 | busybox 7 | cmake 8 | colorprofile 9 | controlpad 10 | controlspy 11 | dbgview 12 | depends 13 | dotnet20sdk 14 | dxsdk_aug2006 15 | dxsdk_jun2010 16 | dxwnd 17 | emu8086 18 | ev3 19 | firefox 20 | fontxplorer 21 | foobar2000 22 | hhw 23 | iceweasel 24 | irfanview 25 | kindle 26 | kobo 27 | mingw 28 | mozillabuild 29 | mpc 30 | mspaint 31 | mt4 32 | njcwp_trial 33 | njjwp_trial 34 | nook 35 | npp 36 | office2003pro 37 | office2007pro 38 | office2013pro 39 | ollydbg110 40 | ollydbg200 41 | ollydbg201 42 | openwatcom 43 | origin 44 | procexp 45 | protectionid 46 | psdk2003 47 | psdkwin71 48 | qqintl 49 | qq 50 | safari 51 | sketchup 52 | steam 53 | ubisoftconnect 54 | utorrent3 55 | utorrent 56 | vc2005expresssp1 57 | vc2005express 58 | vc2005trial 59 | vc2008express 60 | vc2010express 61 | vlc 62 | vstools2019 63 | winamp 64 | winrar 65 | wme9 66 | 3dmark03 67 | 3dmark05 68 | 3dmark06 69 | 3dmark2000 70 | 3dmark2001 71 | stalker_pripyat_bench 72 | unigine_heaven 73 | wglgears 74 | allcodecs 75 | amstream 76 | art2k7min 77 | art2kmin 78 | atmlib 79 | avifil32 80 | binkw32 81 | cabinet 82 | cinepak 83 | cmd 84 | cnc_ddraw 85 | comctl32ocx 86 | comctl32 87 | comdlg32ocx 88 | crypt32 89 | crypt32_winxp 90 | d2gl 91 | d3dcompiler_42 92 | d3dcompiler_43 93 | d3dcompiler_46 94 | d3dcompiler_47 95 | d3drm 96 | d3dx10_43 97 | d3dx10 98 | d3dx11_42 99 | d3dx11_43 100 | d3dx9_24 101 | d3dx9_25 102 | d3dx9_26 103 | d3dx9_27 104 | d3dx9_28 105 | d3dx9_29 106 | d3dx9_30 107 | d3dx9_31 108 | d3dx9_32 109 | d3dx9_33 110 | d3dx9_34 111 | d3dx9_35 112 | d3dx9_36 113 | d3dx9_37 114 | d3dx9_38 115 | d3dx9_39 116 | d3dx9_40 117 | d3dx9_41 118 | d3dx9_42 119 | d3dx9_43 120 | d3dx9 121 | d3dxof 122 | dbghelp 123 | devenum 124 | dinput8 125 | dinput 126 | dirac 127 | directmusic 128 | directplay 129 | directshow 130 | directx9 131 | dmband 132 | dmcompos 133 | dmime 134 | dmloader 135 | dmscript 136 | dmstyle 137 | dmsynth 138 | dmusic32 139 | dmusic 140 | dotnet11sp1 141 | dotnet11 142 | dotnet20sp1 143 | dotnet20sp2 144 | dotnet20 145 | dotnet30sp1 146 | dotnet30 147 | dotnet35sp1 148 | dotnet35 149 | dotnet40_kb2468871 150 | dotnet40 151 | dotnet452 152 | dotnet45 153 | dotnet461 154 | dotnet462 155 | dotnet46 156 | dotnet471 157 | dotnet472 158 | dotnet48 159 | dotnet6 160 | dotnet7 161 | dotnet8 162 | dotnetcore2 163 | dotnetcore3 164 | dotnetcoredesktop3 165 | dotnetdesktop6 166 | dotnetdesktop7 167 | dotnetdesktop8 168 | dotnet_verifier 169 | dpvoice 170 | dsdmo 171 | dsound 172 | dswave 173 | dx8vb 174 | dxdiagn_feb2010 175 | dxdiagn 176 | dxdiag 177 | dxtrans 178 | dxvk0054 179 | dxvk0060 180 | dxvk0061 181 | dxvk0062 182 | dxvk0063 183 | dxvk0064 184 | dxvk0065 185 | dxvk0070 186 | dxvk0071 187 | dxvk0072 188 | dxvk0080 189 | dxvk0081 190 | dxvk0090 191 | dxvk0091 192 | dxvk0092 193 | dxvk0093 194 | dxvk0094 195 | dxvk0095 196 | dxvk0096 197 | dxvk1000 198 | dxvk1001 199 | dxvk1002 200 | dxvk1003 201 | dxvk1011 202 | dxvk1020 203 | dxvk1021 204 | dxvk1022 205 | dxvk1023 206 | dxvk1030 207 | dxvk1031 208 | dxvk1032 209 | dxvk1033 210 | dxvk1034 211 | dxvk1040 212 | dxvk1041 213 | dxvk1042 214 | dxvk1043 215 | dxvk1044 216 | dxvk1045 217 | dxvk1046 218 | dxvk1050 219 | dxvk1051 220 | dxvk1052 221 | dxvk1053 222 | dxvk1054 223 | dxvk1055 224 | dxvk1060 225 | dxvk1061 226 | dxvk1070 227 | dxvk1071 228 | dxvk1072 229 | dxvk1073 230 | dxvk1080 231 | dxvk1081 232 | dxvk1090 233 | dxvk1091 234 | dxvk1092 235 | dxvk1093 236 | dxvk1094 237 | dxvk1100 238 | dxvk1101 239 | dxvk1102 240 | dxvk1103 241 | dxvk2000 242 | dxvk2010 243 | dxvk2020 244 | dxvk2030 245 | dxvk_nvapi0061 246 | dxvk 247 | esent 248 | faudio1901 249 | faudio1902 250 | faudio1903 251 | faudio1904 252 | faudio1905 253 | faudio190607 254 | faudio1906 255 | faudio 256 | ffdshow 257 | filever 258 | galliumnine02 259 | galliumnine03 260 | galliumnine04 261 | galliumnine05 262 | galliumnine06 263 | galliumnine07 264 | galliumnine08 265 | galliumnine09 266 | galliumnine 267 | gdiplus 268 | gdiplus_winxp 269 | gfw 270 | glidewrapper 271 | glut 272 | gmdls 273 | hid 274 | icodecs 275 | ie6 276 | ie7 277 | ie8_kb2936068 278 | ie8_tls12 279 | ie8 280 | iertutil 281 | itircl 282 | itss 283 | jet40 284 | l3codecx 285 | lavfilters702 286 | lavfilters 287 | mdac27 288 | mdac28 289 | mdx 290 | mfc100 291 | mfc110 292 | mfc120 293 | mfc140 294 | mfc40 295 | mfc42 296 | mfc70 297 | mfc71 298 | mfc80 299 | mfc90 300 | mf 301 | msaa 302 | msacm32 303 | msasn1 304 | msctf 305 | msdelta 306 | msdxmocx 307 | msflxgrd 308 | msftedit 309 | mshflxgd 310 | msls31 311 | msmask 312 | mspatcha 313 | msscript 314 | msvcirt 315 | msvcrt40 316 | msxml3 317 | msxml4 318 | msxml6 319 | nuget 320 | ogg 321 | ole32 322 | oleaut32 323 | openal 324 | otvdm090 325 | otvdm 326 | pdh_nt4 327 | pdh 328 | peverify 329 | physx 330 | pngfilt 331 | powershell_core 332 | powershell 333 | prntvpt 334 | python26 335 | python27 336 | qasf 337 | qcap 338 | qdvd 339 | qedit 340 | quartz_feb2010 341 | quartz 342 | quicktime72 343 | quicktime76 344 | riched20 345 | riched30 346 | richtx32 347 | sapi 348 | sdl 349 | secur32 350 | setupapi 351 | shockwave 352 | speechsdk 353 | tabctl32 354 | ucrtbase2019 355 | uiribbon 356 | updspapi 357 | urlmon 358 | usp10 359 | vb2run 360 | vb3run 361 | vb4run 362 | vb5run 363 | vb6run 364 | vcrun2003 365 | vcrun2005 366 | vcrun2008 367 | vcrun2010 368 | vcrun2012 369 | vcrun2013 370 | vcrun2015 371 | vcrun2017 372 | vcrun2019 373 | vcrun2022 374 | vcrun6sp6 375 | vcrun6 376 | vjrun20 377 | vkd3d 378 | webio 379 | windowscodecs 380 | winhttp 381 | wininet 382 | wininet_win2k 383 | wmi 384 | wmp10 385 | wmp11 386 | wmp9 387 | wmv9vcm 388 | wsh57 389 | xact 390 | xact_x64 391 | xaudio29 392 | xinput 393 | xmllite 394 | xna31 395 | xna40 396 | xvid 397 | allfonts 398 | andale 399 | arial 400 | baekmuk 401 | calibri 402 | cambria 403 | candara 404 | cjkfonts 405 | comicsans 406 | consolas 407 | constantia 408 | corbel 409 | corefonts 410 | courier 411 | droid 412 | eufonts 413 | fakechinese 414 | fakejapanese_ipamona 415 | fakejapanese 416 | fakejapanese_vlgothic 417 | fakekorean 418 | georgia 419 | impact 420 | ipamona 421 | liberation 422 | lucida 423 | meiryo 424 | micross 425 | opensymbol 426 | pptfonts 427 | sourcehansans 428 | tahoma 429 | takao 430 | times 431 | trebuchet 432 | uff 433 | unifont 434 | verdana 435 | vlgothic 436 | webdings 437 | wenquanyi 438 | wenquanyizenhei 439 | alldlls=builtin 440 | alldlls=default 441 | autostart_winedbg=disabled 442 | autostart_winedbg=enabled 443 | bad 444 | cfc=disabled 445 | cfc=enabled 446 | csmt=force 447 | csmt=off 448 | csmt=on 449 | fontfix 450 | fontsmooth=bgr 451 | fontsmooth=disable 452 | fontsmooth=gray 453 | fontsmooth=rgb 454 | forcemono 455 | good 456 | grabfullscreen=n 457 | grabfullscreen=y 458 | graphics=default 459 | graphics=mac 460 | graphics=wayland 461 | graphics=x11 462 | gsm=0 463 | gsm=1 464 | gsm=2 465 | gsm=3 466 | heapcheck 467 | hidewineexports=disable 468 | hidewineexports=enable 469 | hosts 470 | isolate_home 471 | mackeyremap=both 472 | mackeyremap=left 473 | mackeyremap=none 474 | mimeassoc=off 475 | mimeassoc=on 476 | mwo=disable 477 | mwo=enabled 478 | mwo=force 479 | native_mdac 480 | native_oleaut32 481 | nocrashdialog 482 | npm=repack 483 | nt351 484 | nt40 485 | orm=backbuffer 486 | orm=fbo 487 | psm=0 488 | psm=1 489 | psm=2 490 | psm=3 491 | remove_mono 492 | renderer=gdi 493 | renderer=gl 494 | renderer=no3d 495 | renderer=vulkan 496 | rtlm=auto 497 | rtlm=disabled 498 | rtlm=readdraw 499 | rtlm=readtex 500 | rtlm=texdraw 501 | rtlm=textex 502 | sandbox 503 | set_mididevice 504 | set_userpath 505 | shader_backend=arb 506 | shader_backend=glsl 507 | shader_backend=none 508 | sound=alsa 509 | sound=coreaudio 510 | sound=disabled 511 | sound=oss 512 | sound=pulse 513 | ssm=disabled 514 | ssm=enabled 515 | usetakefocus=n 516 | usetakefocus=y 517 | vd=1024x768 518 | vd=1280x1024 519 | vd=1440x900 520 | vd=640x480 521 | vd=800x600 522 | vd=off 523 | videomemorysize=1024 524 | videomemorysize=2048 525 | videomemorysize=512 526 | videomemorysize=default 527 | vista 528 | vsm=0 529 | vsm=1 530 | vsm=2 531 | vsm=3 532 | win10 533 | win11 534 | win20 535 | win2k3 536 | win2k8r2 537 | win2k8 538 | win2k 539 | win30 540 | win31 541 | win7 542 | win81 543 | win8 544 | win95 545 | win98 546 | windowmanagerdecorated=n 547 | windowmanagerdecorated=y 548 | windowmanagermanaged=n 549 | windowmanagermanaged=y 550 | winme 551 | winver= 552 | winxp 553 | -------------------------------------------------------------------------------- /tools/WIP-notes.txt: -------------------------------------------------------------------------------- 1 | meson 2 | cmake 3 | ninja 4 | pandoc 5 | glslang 6 | libselinux-devel 7 | libcap-devel 8 | waffle-devel 9 | glib2-devel 10 | json-glib-devel 11 | elfutils-libelf-devel 12 | libva-devel 13 | libvdpau-devel 14 | vulkan-devel 15 | libxcb-devel 16 | meson-libGL-devel 17 | 18 | 19 | 20 | Run-time dependency libselinux found: YES 3.5 21 | Run-time dependency libcap found: YES 2.48 22 | Run-time dependency waffle-1 found: YES 1.7.0 23 | Configuring _steam-runtime-tools-config.h using configuration 24 | Run-time dependency glib-2.0 found: YES 2.78.3 25 | Run-time dependency gobject-2.0 found: YES 2.78.3 26 | Run-time dependency gio-2.0 found: YES 2.78.3 27 | Run-time dependency gio-unix-2.0 found: YES 2.78.3 28 | Run-time dependency json-glib-1.0 found: YES 1.8.0 29 | Run-time dependency threads found: YES 30 | Run-time dependency libelf found: YES 0.190 31 | Run-time dependency libva found: YES 1.20.0 32 | Run-time dependency libva-x11 found: YES 1.20.0 33 | Run-time dependency vdpau found: YES 1.5 34 | Run-time dependency vulkan found: YES 1.3.270 35 | Run-time dependency xau found: YES 1.0.11 36 | Run-time dependency xcb found: YES 1.13.1 37 | Run-time dependency gl found: YES 1.2 38 | Run-time dependency x11 found: YES 1.8.7 39 | Run-time dependency xrandr found: YES 1.5.4; 40 | 41 | mkdir -p _build 42 | meson -Dlibcurl_compat=true -Dman=enabled --werror _build/devel -Dinstalled_tests=false -Dprefix=$PWD/_build/RELEASE/ 43 | ninja -C _build/devel 44 | ninja -C _build/devel install 45 | 46 | 47 | provides pressure-vessel folder contents and main folder contents (mtree.txt.gz,run,run-in-sniper,toolmanifest.vdf,_v2-entry-point(umu)): 48 | https://gitlab.steamos.cloud/steamrt/steam-runtime-tools 49 | 50 | provides sniper_platform_*: 51 | https://gitlab.steamos.cloud/steamrt/sniper/platform#running-the-steam-container-runtime-without-steam 52 | ``` 53 | Each versioned subdirectory of 54 | https://repo.steampowered.com/steamrt3/images/ 55 | contains an archive named steam-container-runtime-complete.tar.gz, which 56 | is used to build the official SteamLinuxRuntime_sniper releases. 57 | These can also be used without going via Steam. 58 | To do this, download and unpack steam-container-runtime-complete.tar.gz. 59 | All subdirectories except for depot/ can be discarded. 60 | The depot directory is the equivalent of the SteamLinuxRuntime_sniper 61 | directory in official releases: for example, 62 | 63 | .../depot/run -- xterm 64 | 65 | 66 | will launch an interactive terminal in the container environment. 67 | ``` 68 | 69 | From proton/docker/Makefile: 70 | STEAMRT_VERSION = 0.20230905.59202 71 | Keep an eye on this version. This is the version we want to pull from https://repo.steampowered.com/steamrt3/images/ to base our own on. 72 | -------------------------------------------------------------------------------- /tools/umu-run-cli: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # use for debug only. 4 | # set -x 5 | 6 | umu_PROTON_VER="umu-Proton-8.0-5-3" 7 | umu_LAUNCHER_VER="0.1-RC3" 8 | 9 | me="$(readlink -f "$0")" 10 | 11 | umu_link="https://github.com/Open-Wine-Components/umu-launcher/releases/download/$umu_LAUNCHER_VER/umu-launcher.tar.gz" 12 | umu_dir="$HOME"/.local/share/umu 13 | 14 | proton_link="https://github.com/Open-Wine-Components/umu-Proton/releases/download/$umu_PROTON_VER/$umu_PROTON_VER" 15 | proton_dir="$HOME"/.local/share/Steam/compatibilitytools.d 16 | 17 | umu_cache="$HOME"/.cache/umu 18 | 19 | if [ ! -d "$umu_cache" ]; then 20 | mkdir -p "$umu_cache" 21 | fi 22 | 23 | # Self-update 24 | # In flatpak it will check for /app/share/umu/umu-launcher.tar.gz and check version 25 | # In distro package it will check for /usr/share/umu/umu-launcher.tar.gz and check version 26 | # If tarball does not exist it will just download it. 27 | if [ ! -d "$umu_dir" ]; then 28 | mkdir -p "$umu_dir" 29 | if [ -f "${me%/*/*}"/share/umu/umu-launcher.tar.gz ]; then 30 | tar -zxvf "${me%/*/*}"/share/umu/umu-launcher.tar.gz --one-top-level="$umu_dir" 31 | else 32 | wget "$umu_link" -O "$umu_cache/umu-launcher.tar.gz" 33 | tar -zxvf "$umu_cache/umu-launcher.tar.gz" --one-top-level="$umu_dir" 34 | rm "$umu_cache/umu-launcher.tar.gz" 35 | fi 36 | else 37 | if [ "$umu_LAUNCHER_VER" != "$(cat "$umu_dir"/umu-VERSION)" ]; then 38 | rm -Rf "$umu_dir" --preserve-root=all 39 | if [ -f "${me%/*/*}"/share/umu/umu-launcher.tar.gz ]; then 40 | tar -zxvf "${me%/*/*}"/share/umu/umu-launcher.tar.gz --one-top-level="$umu_dir" 41 | else 42 | wget "$umu_link" -O "$umu_cache/umu-launcher.tar.gz" 43 | tar -zxvf "$umu_cache/umu-launcher.tar.gz" --one-top-level="$umu_dir" 44 | rm "$umu_cache/umu-launcher.tar.gz" 45 | fi 46 | fi 47 | fi 48 | 49 | if [ -z "$PROTONPATH" ]; then 50 | if [ ! -d "$proton_dir"/$umu_PROTON_VER ]; then 51 | wget "$proton_link".tar.gz -O "$umu_cache/$umu_PROTON_VER".tar.gz 52 | wget "$proton_link".sha512sum -O "$umu_cache/$umu_PROTON_VER".sha512sum 53 | cd "$umu_cache" || exit 54 | checksum=$(sha512sum "$umu_PROTON_VER".tar.gz) 55 | cd - || exit 56 | if [ "$checksum" = "$(cat "$umu_cache/$umu_PROTON_VER".sha512sum)" ]; then 57 | tar -zxvf "$umu_cache/$umu_PROTON_VER".tar.gz --one-top-level="$proton_dir" 58 | rm "$umu_cache/$umu_PROTON_VER".tar.gz 59 | rm "$umu_cache/$umu_PROTON_VER".sha512sum 60 | else 61 | echo "ERROR: $umu_cache/$umu_PROTON_VER.tar.gz checksum does not match $umu_cache/$umu_PROTON_VER.sha512sum, aborting!" 62 | rm "$umu_cache/$umu_PROTON_VER".tar.gz 63 | rm "$umu_cache/$umu_PROTON_VER".sha512sum 64 | exit 1 65 | fi 66 | fi 67 | export PROTONPATH="$proton_dir/$umu_PROTON_VER" 68 | else 69 | export PROTONPATH="$PROTONPATH" 70 | fi 71 | 72 | "$umu_dir/umu-run" "$@" 73 | -------------------------------------------------------------------------------- /tools/umu-run-posix: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # use for debug only. 4 | # set -x 5 | 6 | if [ -z "$1" ] || [ -z "$WINEPREFIX" ] || [ -z "$GAMEID" ] || [ -z "$PROTONPATH" ]; then 7 | echo "Usage: WINEPREFIX= GAMEID= PROTONPATH= ./gamelauncher.sh " 8 | echo "Ex:" 9 | echo "WINEPREFIX=$HOME/Games/epic-games-store GAMEID=egs PROTONPATH=\"$HOME/.steam/steam/compatibilitytools.d/GE-Proton8-28\" ./gamelauncher.sh \"$HOME/Games/epic-games-store/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/EpicGamesLauncher.exe\" \"-opengl -SkipBuildPatchPrereq\"" 10 | exit 1 11 | fi 12 | 13 | me="$(readlink -f "$0")" 14 | here="${me%/*}" 15 | 16 | if [ "$WINEPREFIX" ]; then 17 | if [ ! -d "$WINEPREFIX" ]; then 18 | mkdir -p "$WINEPREFIX" 19 | export PROTON_DLL_COPY="*" 20 | fi 21 | if [ ! -d "$WINEPREFIX"/pfx ]; then 22 | ln -s "$WINEPREFIX" "$WINEPREFIX"/pfx > /dev/null 2>&1 23 | fi 24 | if [ ! -f "$WINEPREFIX"/tracked_files ]; then 25 | touch "$WINEPREFIX"/tracked_files 26 | fi 27 | if [ ! -f "$WINEPREFIX/dosdevices/" ]; then 28 | mkdir -p "$WINEPREFIX"/dosdevices 29 | ln -s "../drive_c" "$WINEPREFIX/dosdevices/c:" > /dev/null 2>&1 30 | fi 31 | fi 32 | 33 | if [ -n "$PROTONPATH" ]; then 34 | if [ ! -d "$PROTONPATH" ]; then 35 | echo "ERROR: $PROTONPATH is invalid, aborting!" 36 | exit 1 37 | fi 38 | fi 39 | 40 | export UMU_ID="$GAMEID" 41 | export STEAM_COMPAT_APP_ID="0" 42 | numcheck='^[0-9]+$' 43 | if echo "$UMU_ID" | cut -d "-" -f 2 | grep -Eq "$numcheck"; then 44 | STEAM_COMPAT_APP_ID=$(echo "$UMU_ID" | cut -d "-" -f 2) 45 | export STEAM_COMPAT_APP_ID 46 | fi 47 | export SteamAppId="$STEAM_COMPAT_APP_ID" 48 | export SteamGameId="$STEAM_COMPAT_APP_ID" 49 | 50 | # TODO: Ideally this should be the main game install path, which is often, but not always the path of the game's executable. 51 | if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then 52 | exepath="$(readlink -f "$1")" 53 | gameinstallpath="${exepath%/*}" 54 | export STEAM_COMPAT_INSTALL_PATH="$gameinstallpath" 55 | fi 56 | 57 | compat_lib_path=$(findmnt -T "$STEAM_COMPAT_INSTALL_PATH" | tail -n 1 | awk '{ print $1 }') 58 | if [ "$compat_lib_path" != "/" ]; then 59 | export STEAM_COMPAT_LIBRARY_PATHS="${STEAM_COMPAT_LIBRARY_PATHS:+"${STEAM_COMPAT_LIBRARY_PATHS}:"}$compat_lib_path" 60 | fi 61 | 62 | if [ -z "$STEAM_RUNTIME_LIBRARY_PATH" ]; then 63 | # The following info taken from steam ~/.local/share/ubuntu12_32/steam-runtime/run.sh 64 | host_library_paths= 65 | exit_status=0 66 | ldconfig_output=$(/sbin/ldconfig -XNv 2> /dev/null; exit $?) || exit_status=$? 67 | if [ $exit_status != 0 ]; then 68 | echo "Warning: An unexpected error occurred while executing \"/sbin/ldconfig -XNv\", the exit status was $exit_status" 69 | fi 70 | 71 | while read -r line; do 72 | # If line starts with a leading / and contains :, it's a new path prefix 73 | case "$line" in /*:*) 74 | library_path_prefix=$(echo "$line" | cut -d: -f1) 75 | host_library_paths=$host_library_paths$library_path_prefix: 76 | esac 77 | done < Namespace | tuple[str, list[str]]: # noqa: D103 20 | opt_args: set[str] = {"--help", "-h", "--config", "--version", "-v"} 21 | parser: ArgumentParser = ArgumentParser( 22 | description="Unified Linux Wine Game Launcher", 23 | epilog=( 24 | "See umu(1) for more info and examples, or visit\n" 25 | "https://github.com/Open-Wine-Components/umu-launcher" 26 | ), 27 | formatter_class=RawTextHelpFormatter, 28 | ) 29 | parser.add_argument( 30 | "-v", 31 | "--version", 32 | action="version", 33 | version=f"umu-launcher version {__version__} ({sys.version})", 34 | help="show this version and exit", 35 | ) 36 | parser.add_argument("--config", help=("path to TOML file (requires Python 3.11+)")) 37 | parser.add_argument( 38 | "winetricks", 39 | help=("run winetricks verbs (requires UMU-Proton or GE-Proton)"), 40 | nargs="?", 41 | default=None, 42 | ) 43 | 44 | if not sys.argv[1:]: 45 | parser.print_help(sys.stderr) 46 | sys.exit(1) 47 | 48 | # Winetricks 49 | # Exit if no winetricks verbs were passed 50 | if sys.argv[1].endswith("winetricks") and not sys.argv[2:]: 51 | err: str = "No winetricks verb specified" 52 | log.error(err) 53 | sys.exit(1) 54 | 55 | # Exit if argument is not a verb 56 | if sys.argv[1].endswith("winetricks") and not is_winetricks_verb(sys.argv[2:]): 57 | sys.exit(1) 58 | 59 | if sys.argv[1:][0] in opt_args: 60 | return parser.parse_args(sys.argv[1:]) 61 | 62 | if sys.argv[1] in PROTON_VERBS: 63 | if "PROTON_VERB" not in os.environ: 64 | os.environ["PROTON_VERB"] = sys.argv[1] 65 | sys.argv.pop(1) 66 | 67 | return sys.argv[1], sys.argv[2:] 68 | 69 | 70 | def main() -> int: # noqa: D103 71 | args: Namespace | tuple[str, list[str]] = parse_args() 72 | 73 | # Adjust logger for debugging when configured 74 | if os.environ.get("UMU_LOG") in {"1", "debug"}: 75 | log.setLevel(level="DEBUG") 76 | log.set_formatter(os.environ["UMU_LOG"]) 77 | for key, val in os.environ.items(): 78 | log.debug("%s=%s", key, val) 79 | 80 | if os.geteuid() == 0: 81 | err: str = "This script should never be run as the root user" 82 | log.error(err) 83 | sys.exit(1) 84 | 85 | if "musl" in os.environ.get("LD_LIBRARY_PATH", ""): 86 | err: str = "This script is not designed to run on musl-based systems" 87 | log.error(err) 88 | sys.exit(1) 89 | 90 | return umu_run(args) 91 | 92 | 93 | if __name__ == "__main__": 94 | try: 95 | sys.exit(main()) 96 | except KeyboardInterrupt: 97 | log.warning("Keyboard Interrupt") 98 | except SystemExit as e: 99 | if e.code: 100 | sys.exit(e.code) 101 | except BaseException as e: 102 | log.exception(e) 103 | -------------------------------------------------------------------------------- /umu/umu-launcher/compatibilitytool.vdf: -------------------------------------------------------------------------------- 1 | "compatibilitytools" 2 | { 3 | "compat_tools" 4 | { 5 | "umu-launcher" // Internal name of this tool 6 | { 7 | "install_path" "." 8 | "display_name" "umu-launcher" 9 | "from_oslist" "windows" 10 | "to_oslist" "linux" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /umu/umu-launcher/toolmanifest.vdf: -------------------------------------------------------------------------------- 1 | // Generated file, do not edit 2 | "manifest" 3 | { 4 | "commandline" "/umu-run %verb%" 5 | "version" "2" 6 | "use_tool_subprocess_reaper" "1" 7 | "unlisted" "1" 8 | "compatmanager_layer_name" "umu-launcher" 9 | } 10 | -------------------------------------------------------------------------------- /umu/umu-launcher/umu-run.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | ##INSTALL_PATH## "$@" 3 | 4 | -------------------------------------------------------------------------------- /umu/umu-run.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | ##INSTALL_PATH##/umu_run.py "$@" 3 | 4 | -------------------------------------------------------------------------------- /umu/umu_consts.py: -------------------------------------------------------------------------------- 1 | import os 2 | from enum import Enum 3 | from pathlib import Path 4 | 5 | 6 | # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 7 | # 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 8 | # Python Software Foundation; 9 | # Source: https://raw.githubusercontent.com/python/cpython/refs/heads/3.11/Lib/http/__init__.py 10 | # License: https://raw.githubusercontent.com/python/cpython/refs/heads/3.11/LICENSE 11 | class HTTPMethod(Enum): 12 | """HTTP methods and descriptions. 13 | 14 | Methods from the following RFCs are all observed: 15 | 16 | * RFF 9110: HTTP Semantics, obsoletes 7231, which obsoleted 2616 17 | * RFC 5789: PATCH Method for HTTP 18 | 19 | """ 20 | 21 | CONNECT = "CONNECT" 22 | DELETE = "DELETE" 23 | GET = "GET" 24 | HEAD = "HEAD" 25 | OPTIONS = "OPTIONS" 26 | PATCH = "PATCH" 27 | POST = "POST" 28 | PUT = "PUT" 29 | TRACE = "TRACE" 30 | 31 | 32 | class GamescopeAtom(Enum): 33 | """Represent Gamescope-specific X11 atom names.""" 34 | 35 | SteamGame = "STEAM_GAME" 36 | BaselayerAppId = "GAMESCOPECTRL_BASELAYER_APPID" 37 | 38 | 39 | class FileLock(Enum): 40 | """Files placed with an exclusive lock via flock(2).""" 41 | 42 | Runtime = "umu.lock" # UMU_RUNTIME lock 43 | Compat = "compatibilitytools.d.lock" # PROTONPATH lock 44 | Prefix = "pfx.lock" # WINEPREFIX lock 45 | 46 | 47 | # Gamescope looks for the app ID 769 to determine if the window is Steam's 48 | # The app ID is expected to be in the atom GAMESCOPECTRL_BASELAYER_APPID and is 49 | # a required value in the sequence 50 | STEAM_WINDOW_ID = 769 51 | 52 | PROTON_VERBS = { 53 | "waitforexitandrun", 54 | "run", 55 | "runinprefix", 56 | "destroyprefix", 57 | "getcompatpath", 58 | "getnativepath", 59 | } 60 | 61 | XDG_CACHE_HOME: Path = ( 62 | Path(os.environ["XDG_CACHE_HOME"]) 63 | if os.environ.get("XDG_CACHE_HOME") 64 | else Path.home().joinpath(".cache") 65 | ) 66 | 67 | # Installation path of the runtime files that respects the XDG Base Directory 68 | # Specification and Systemd container interface. 69 | # See https://systemd.io/CONTAINER_INTERFACE 70 | # See https://specifications.freedesktop.org/basedir-spec/latest/index.html#basics 71 | # NOTE: For Flatpaks, the runtime will be installed in $HOST_XDG_DATA_HOME 72 | # then $XDG_DATA_HOME as fallback, and will be required to update their 73 | # manifests by adding the permission 'xdg-data/umu:create'. 74 | # See https://github.com/Open-Wine-Components/umu-launcher/pull/229#discussion_r1799289068 75 | if os.environ.get("container") == "flatpak": # noqa: SIM112 76 | XDG_DATA_HOME: Path = ( 77 | Path(os.environ["HOST_XDG_DATA_HOME"]) 78 | if os.environ.get("HOST_XDG_DATA_HOME") 79 | else Path.home().joinpath(".local", "share") 80 | ) 81 | elif os.environ.get("SNAP"): 82 | XDG_DATA_HOME: Path = Path(os.environ["SNAP_REAL_HOME"]) 83 | else: 84 | XDG_DATA_HOME: Path = ( 85 | Path(os.environ["XDG_DATA_HOME"]) 86 | if os.environ.get("XDG_DATA_HOME") 87 | else Path.home().joinpath(".local", "share") 88 | ) 89 | 90 | UMU_LOCAL: Path = XDG_DATA_HOME.joinpath("umu") 91 | 92 | # Temporary directory for downloaded resources moved from tmpfs 93 | UMU_CACHE: Path = XDG_CACHE_HOME.joinpath("umu") 94 | 95 | # Directory storing Proton and other compatibility tools built against the SLR 96 | UMU_COMPAT: Path = XDG_DATA_HOME.joinpath("umu", "compatibilitytools") 97 | 98 | STEAM_COMPAT: Path = XDG_DATA_HOME.joinpath("Steam", "compatibilitytools.d") 99 | 100 | # Constant defined in prctl.h 101 | # See prctl(2) for more details 102 | PR_SET_CHILD_SUBREAPER = 36 103 | 104 | # Winetricks settings verbs dumped from wintricks 20240105 105 | # Script: 106 | # -- 107 | # import sys 108 | # from subprocess import PIPE, Popen 109 | # 110 | # with (Popen(["winetricks", "settings", "list"], stdout=PIPE) as wpt, 111 | # Popen(["awk", "{print $1}"], stdout=PIPE, stdin=wpt.stdout) as awkp 112 | # ): 113 | # sys.stdout.write("WINETRICKS_SETTINGS_VERBS = {\n") 114 | # for line in awkp.stdout: 115 | # line = line.decode("utf-8").strip() 116 | # sys.stdout.write(f" \"{line}\",\n") 117 | # sys.stdout.write("}\n") 118 | # -- 119 | WINETRICKS_SETTINGS_VERBS = { 120 | "alldlls=builtin", 121 | "alldlls=default", 122 | "autostart_winedbg=disabled", 123 | "autostart_winedbg=enabled", 124 | "bad", 125 | "cfc=disabled", 126 | "cfc=enabled", 127 | "csmt=force", 128 | "csmt=off", 129 | "csmt=on", 130 | "fontfix", 131 | "fontsmooth=bgr", 132 | "fontsmooth=disable", 133 | "fontsmooth=gray", 134 | "fontsmooth=rgb", 135 | "forcemono", 136 | "good", 137 | "grabfullscreen=n", 138 | "grabfullscreen=y", 139 | "gsm=0", 140 | "gsm=1", 141 | "gsm=2", 142 | "gsm=3", 143 | "heapcheck", 144 | "hidewineexports=disable", 145 | "hidewineexports=enable", 146 | "hosts", 147 | "isolate_home", 148 | "macdriver=mac", 149 | "macdriver=x11", 150 | "mackeyremap=both", 151 | "mackeyremap=left", 152 | "mackeyremap=none", 153 | "mimeassoc=off", 154 | "mimeassoc=on", 155 | "mwo=disable", 156 | "mwo=enabled", 157 | "mwo=force", 158 | "native_mdac", 159 | "native_oleaut32", 160 | "nocrashdialog", 161 | "npm=repack", 162 | "nt351", 163 | "nt40", 164 | "orm=backbuffer", 165 | "orm=fbo", 166 | "psm=0", 167 | "psm=1", 168 | "psm=2", 169 | "psm=3", 170 | "remove_mono", 171 | "renderer=gdi", 172 | "renderer=gl", 173 | "renderer=no3d", 174 | "renderer=vulkan", 175 | "rtlm=auto", 176 | "rtlm=disabled", 177 | "rtlm=readdraw", 178 | "rtlm=readtex", 179 | "rtlm=texdraw", 180 | "rtlm=textex", 181 | "sandbox", 182 | "set_mididevice", 183 | "set_userpath", 184 | "shader_backend=arb", 185 | "shader_backend=glsl", 186 | "shader_backend=none", 187 | "sound=alsa", 188 | "sound=coreaudio", 189 | "sound=disabled", 190 | "sound=oss", 191 | "sound=pulse", 192 | "ssm=disabled", 193 | "ssm=enabled", 194 | "usetakefocus=n", 195 | "usetakefocus=y", 196 | "vd=1024x768", 197 | "vd=1280x1024", 198 | "vd=1440x900", 199 | "vd=640x480", 200 | "vd=800x600", 201 | "vd=off", 202 | "videomemorysize=1024", 203 | "videomemorysize=2048", 204 | "videomemorysize=512", 205 | "videomemorysize=default", 206 | "vista", 207 | "vsm=0", 208 | "vsm=1", 209 | "vsm=2", 210 | "vsm=3", 211 | "win10", 212 | "win11", 213 | "win20", 214 | "win2k3", 215 | "win2k8r2", 216 | "win2k8", 217 | "win2k", 218 | "win30", 219 | "win31", 220 | "win7", 221 | "win81", 222 | "win8", 223 | "win95", 224 | "win98", 225 | "windowmanagerdecorated=n", 226 | "windowmanagerdecorated=y", 227 | "windowmanagermanaged=n", 228 | "windowmanagermanaged=y", 229 | "winme", 230 | "winver=", 231 | "winxp", 232 | } 233 | -------------------------------------------------------------------------------- /umu/umu_log.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from logging import ( 4 | Formatter, 5 | Logger, 6 | LogRecord, 7 | StreamHandler, 8 | ) 9 | 10 | 11 | class Color: # noqa: D101 12 | ERROR = "\033[31m" # Red 13 | DEBUG = "\u001b[35m" # Purple 14 | INFO = "\u001b[34m" # Blue 15 | WARNING = "\033[33m" # Yellow 16 | CRITICAL = "\033[33m" 17 | BOLD = "\033[1m" 18 | GREY = "\033[90m" 19 | RESET = "\u001b[0m" 20 | 21 | 22 | SIMPLE_FORMAT = "%(levelname)s: %(message)s" 23 | 24 | DEBUG_FORMAT = "[%(name)s.%(module)s:%(lineno)d] %(levelname)s: %(message)s" 25 | 26 | 27 | class CustomLogger(Logger): # noqa: D101 28 | def __init__(self, name: str) -> None: # noqa: D107 29 | self._custom_fmt = SIMPLE_FORMAT 30 | super().__init__(name) 31 | 32 | def set_formatter(self, level: str) -> None: # noqa: D102 33 | console_handler: StreamHandler 34 | if level in {"1", "debug"}: # Values for UMU_LOG 35 | self._custom_fmt = DEBUG_FORMAT 36 | self.setLevel("DEBUG") 37 | console_handler = StreamHandler(stream=sys.stderr) 38 | console_handler.setFormatter(CustomFormatter(self._custom_fmt)) 39 | for handler in self.handlers: 40 | self.removeHandler(handler) 41 | log.addHandler(console_handler) 42 | 43 | 44 | class CustomFormatter(Formatter): # noqa: D101 45 | def format(self, record: LogRecord) -> str: # noqa: D102 46 | if not os.isatty(sys.stderr.fileno()): 47 | return super().format(record) 48 | color: str 49 | match record.levelname: 50 | case "INFO": 51 | color = Color.INFO 52 | case "DEBUG": 53 | color = Color.DEBUG 54 | case "CRITICAL": 55 | color = Color.CRITICAL 56 | case "ERROR": 57 | color = Color.ERROR 58 | case "WARNING": 59 | color = Color.WARNING 60 | case "WARN": 61 | color = Color.WARNING 62 | case "FATAL": 63 | color = Color.WARNING 64 | case _: 65 | color = Color.BOLD 66 | record.levelname = f"{color}{Color.BOLD}{record.levelname}{Color.RESET}" 67 | return super().format(record) 68 | 69 | 70 | log: CustomLogger = CustomLogger(__package__ or "umu") 71 | 72 | console_handler: StreamHandler = StreamHandler(stream=sys.stderr) 73 | console_handler.setFormatter(CustomFormatter(SIMPLE_FORMAT)) 74 | log.addHandler(console_handler) 75 | log.setLevel("INFO") 76 | -------------------------------------------------------------------------------- /umu/umu_plugins.py: -------------------------------------------------------------------------------- 1 | from argparse import Namespace 2 | from pathlib import Path 3 | from typing import Any 4 | 5 | 6 | def set_env_toml( 7 | env: dict[str, str], args: Namespace 8 | ) -> tuple[dict[str, str], list[str]]: 9 | """Read key/values in a TOML file and map them to umu env. variables. 10 | 11 | In the TOML file, certain keys map to environment variables: 12 | 13 | proton -> $PROTONPATH 14 | prefix -> $WINEPREFIX 15 | game_id -> $GAMEID 16 | exe -> $EXE 17 | 18 | -which will be used as a base to create other required env variables for 19 | the Steam Runtime (e.g., STEAM_COMPAT_INSTALL_PATH). To note, some features 20 | are lost in this usage, such as running winetricks verbs and automatic 21 | updates to Proton. 22 | """ 23 | try: 24 | import tomllib 25 | except ModuleNotFoundError: 26 | err: str = "tomllib requires Python 3.11" 27 | raise ModuleNotFoundError(err) 28 | 29 | # User configuration containing required key/value pairs 30 | toml: dict[str, Any] 31 | # Configuration file path 32 | config_path: Path 33 | # Name of the configuration file 34 | config: str = getattr(args, "config", "") 35 | # Executable options, if any 36 | opts: list[str] = [] 37 | 38 | if not config: 39 | err: str = f"Property 'config' does not exist in type '{type(args)}'" 40 | raise AttributeError(err) 41 | 42 | config_path = Path(config).expanduser() 43 | 44 | if not config_path.is_file(): 45 | err: str = f"Path to configuration is not a file: '{config}'" 46 | raise FileNotFoundError(err) 47 | 48 | with config_path.open(mode="rb") as file: 49 | toml = tomllib.load(file) 50 | 51 | _check_env_toml(toml) 52 | 53 | # Required environment variables 54 | env["WINEPREFIX"] = toml["umu"]["prefix"] 55 | env["PROTONPATH"] = toml["umu"]["proton"] 56 | env["EXE"] = toml["umu"]["exe"] 57 | # Optional 58 | env["GAMEID"] = toml["umu"].get("game_id", "") 59 | env["STORE"] = toml["umu"].get("store", "") 60 | 61 | if isinstance(toml["umu"].get("launch_args"), list): 62 | opts = toml["umu"]["launch_args"] 63 | elif isinstance(toml["umu"].get("launch_args"), str): 64 | opts = toml["umu"]["launch_args"].split(" ") 65 | 66 | return env, opts 67 | 68 | 69 | def _check_env_toml(toml: dict[str, Any]) -> dict[str, Any]: 70 | """Check for required or empty key/value pairs in user configuration file. 71 | 72 | Casing matters in the config and we do not check if the game id is set. 73 | """ 74 | # Required table in configuration file 75 | table: str = "umu" 76 | # Required keys with their values expected to be file paths 77 | required_keys: list[str] = ["proton", "prefix", "exe"] 78 | 79 | if table not in toml: 80 | err: str = f"Table '{table}' in TOML is not defined." 81 | raise ValueError(err) 82 | 83 | for key in required_keys: 84 | path: Path 85 | 86 | if key not in toml[table]: 87 | err: str = f"The following key in table '[{table}]' is required: '{key}'" 88 | raise ValueError(err) 89 | 90 | path = Path(toml[table][key]).expanduser() 91 | 92 | # Raise an error for executables that do not exist. One case this can 93 | # can happen is when game options are appended at the end of the exe. 94 | # Users should use `launch_args` for game options 95 | if key == "exe" and not path.is_file(): 96 | err: str = f"Value for key '{key}' is not a file: '{toml[table][key]}'" 97 | raise FileNotFoundError(err) 98 | 99 | if (key == "proton" and not path.is_dir()) or ( 100 | key == "prefix" and not path.is_dir() 101 | ): 102 | err: str = f"Value for key '{key}' is not a directory: '{toml[table][key]}'" 103 | raise NotADirectoryError(err) 104 | 105 | # Raise an error for empty values 106 | for key, val in toml[table].items(): 107 | if not val and isinstance(val, str): 108 | err: str = ( 109 | f"Value is empty for '{key}'.\n" 110 | f"Please specify a value or remove the entry: '{key}={val}'" 111 | ) 112 | raise ValueError(err) 113 | 114 | return toml 115 | -------------------------------------------------------------------------------- /umu/umu_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from collections.abc import Generator 4 | from contextlib import contextmanager 5 | from ctypes.util import find_library 6 | from fcntl import LOCK_EX, LOCK_UN, flock 7 | from functools import lru_cache 8 | from hashlib import new as hashnew 9 | from io import BufferedIOBase, BufferedRandom 10 | from pathlib import Path 11 | from re import Pattern 12 | from re import compile as re_compile 13 | from shutil import which 14 | from subprocess import PIPE, STDOUT, Popen, TimeoutExpired 15 | from tarfile import open as taropen 16 | from typing import Any 17 | 18 | from urllib3.response import BaseHTTPResponse 19 | from Xlib import display 20 | 21 | from umu.umu_consts import UMU_LOCAL, WINETRICKS_SETTINGS_VERBS 22 | from umu.umu_log import log 23 | 24 | 25 | @contextmanager 26 | def unix_flock(path: str): 27 | """Create a file and configure it to be locking.""" 28 | fd: int | None = None 29 | 30 | try: 31 | fd = os.open(path, os.O_CREAT | os.O_WRONLY, 0o644) 32 | # See https://man7.org/linux/man-pages/man2/flock.2.html 33 | flock(fd, LOCK_EX) 34 | yield fd 35 | finally: 36 | if fd is not None: 37 | flock(fd, LOCK_UN) 38 | os.close(fd) 39 | 40 | 41 | @contextmanager 42 | def memfdfile(name: str) -> Generator[BufferedRandom, Any, None]: 43 | """Create an anonymous file.""" 44 | fp: BufferedRandom | None = None 45 | 46 | try: 47 | fd = os.memfd_create(name, os.MFD_CLOEXEC) 48 | os.set_inheritable(fd, True) 49 | fp = os.fdopen(fd, mode="rb+") 50 | yield fp 51 | finally: 52 | if fp is not None: 53 | fp.close() 54 | 55 | 56 | @lru_cache 57 | def get_libc() -> str: 58 | """Find libc.so from the user's system.""" 59 | return find_library("c") or "" 60 | 61 | 62 | @lru_cache 63 | def get_library_paths() -> set[str]: 64 | """Find the shared library paths from the user's system.""" 65 | library_paths: set[str] = set() 66 | paths: set[str] = set() 67 | ldconfig: str = which("ldconfig") or "" 68 | root = "/" 69 | 70 | if not ldconfig: 71 | log.warning("ldconfig not found in $PATH, cannot find library paths") 72 | return library_paths 73 | 74 | # Find all shared library path prefixes within the assumptions of the 75 | # Steam Runtime container framework. The framework already works hard by 76 | # attempting to work with various distibutions' quirks. Unless it's Flatpak 77 | # related, let's continue to make it their job. 78 | try: 79 | # Here, opt to using the ld.so cache similar to the stdlib 80 | # implementation of _findSoname_ldconfig. 81 | with Popen( 82 | (ldconfig, "-p"), 83 | text=True, 84 | encoding="utf-8", 85 | stdout=PIPE, 86 | env={"LC_ALL": "C", "LANG": "C"}, 87 | ) as proc: 88 | if not proc.stdout: 89 | return library_paths 90 | for line in proc.stdout: 91 | lines = line.split() 92 | if not lines: 93 | continue 94 | line = lines[-1] 95 | prefix = line[: line.rfind(root)] 96 | if not line.startswith(root) or prefix in paths: 97 | continue 98 | paths.add(prefix) 99 | library_paths.add(os.path.realpath(prefix)) 100 | except OSError as e: 101 | log.exception(e) 102 | 103 | return library_paths 104 | 105 | 106 | def run_zenity(command: str, opts: list[str], msg: str) -> int: 107 | """Execute the command and pipe the output to zenity. 108 | 109 | Intended to be used for long running operations (e.g. large file downloads) 110 | """ 111 | zenity: str = which("zenity") or "" 112 | cmd: str = which(command) or "" 113 | ret: int = 0 # Exit code returned from zenity 114 | 115 | if not zenity: 116 | log.warning("zenity was not found in system") 117 | return -1 118 | 119 | if not cmd: 120 | log.warning("%s was not found in system", command) 121 | return -1 122 | 123 | # Communicate a process with zenity 124 | with ( # noqa: SIM117 125 | Popen( 126 | [cmd, *opts], 127 | stdout=PIPE, 128 | stderr=STDOUT, 129 | ) as proc, 130 | ): 131 | with Popen( 132 | [ 133 | f"{zenity}", 134 | "--progress", 135 | "--auto-close", 136 | f"--text={msg}", 137 | "--percentage=0", 138 | "--pulsate", 139 | "--no-cancel", 140 | ], 141 | stdin=PIPE, 142 | ) as zenity_proc: 143 | try: 144 | proc.wait(timeout=300) 145 | except TimeoutExpired: 146 | zenity_proc.terminate() 147 | log.warning("%s timed out after 5 min.", cmd) 148 | raise TimeoutError 149 | 150 | if zenity_proc.stdin: 151 | zenity_proc.stdin.close() 152 | 153 | ret = zenity_proc.wait() 154 | 155 | if ret: 156 | log.warning("zenity exited with the status code: %s", ret) 157 | 158 | return ret 159 | 160 | 161 | def is_installed_verb(verb: list[str], pfx: Path) -> bool: 162 | """Check if a winetricks verb is installed in the umu prefix. 163 | 164 | Determines the installation of verbs by reading winetricks.log file. 165 | """ 166 | wt_log: Path 167 | verbs: set[str] 168 | is_installed: bool = False 169 | 170 | if not pfx: 171 | err: str = f"Value is '{pfx}' for WINE prefix" 172 | raise FileNotFoundError(err) 173 | 174 | if not verb: 175 | err: str = "winetricks was passed an empty verb" 176 | raise ValueError(err) 177 | 178 | wt_log = pfx.joinpath("winetricks.log") 179 | verbs = set(verb) 180 | 181 | if not wt_log.is_file(): 182 | return is_installed 183 | 184 | with wt_log.open(mode="r", encoding="utf-8") as file: 185 | for line in file: 186 | line: str = line.strip() 187 | if line in verbs and line not in WINETRICKS_SETTINGS_VERBS: 188 | is_installed = True 189 | err: str = f"winetricks verb '{line}' is already installed in '{pfx}'" 190 | log.error(err) 191 | break 192 | 193 | return is_installed 194 | 195 | 196 | def is_winetricks_verb( 197 | verbs: list[str], pattern: str = r"^[a-zA-Z_0-9-]+(=[a-zA-Z0-9]*)?$" 198 | ) -> bool: 199 | """Check if a string is a winetricks verb.""" 200 | regex: Pattern 201 | 202 | if not verbs: 203 | return False 204 | 205 | # When passed a sequence, check each verb and log the non-verbs 206 | regex = re_compile(pattern) 207 | for verb in verbs: 208 | if not regex.match(verb): 209 | err: str = f"Value is not a winetricks verb: '{verb}'" 210 | log.error(err) 211 | return False 212 | 213 | return True 214 | 215 | 216 | @contextmanager 217 | def xdisplay(no: str): 218 | """Create a Display.""" 219 | d: display.Display | None = None 220 | 221 | try: 222 | d = display.Display(no) 223 | yield d 224 | finally: 225 | if d is not None: 226 | d.close() 227 | 228 | 229 | def write_file_chunks( 230 | path: Path, 231 | resp: BufferedIOBase | BaseHTTPResponse, 232 | # Note: hashlib._Hash is internal and an exception will be raised when imported 233 | hasher, # noqa: ANN001 234 | chunk_size: int = 64 * 1024, 235 | ): 236 | """Write a file to path in chunks from a response stream while hashing it. 237 | 238 | Args: 239 | path: file path 240 | resp: urllib3 response streamed response 241 | hasher: hashlib object 242 | chunk_size: max size of data to read from the streamed response 243 | Returns: 244 | hashlib._Hash instance 245 | 246 | """ 247 | buffer: bytearray 248 | view: memoryview 249 | 250 | if not chunk_size: 251 | chunk_size = 64 * 1024 252 | 253 | buffer = bytearray(chunk_size) 254 | view = memoryview(buffer) 255 | with path.open(mode="ab+", buffering=0) as file: 256 | while size := resp.readinto(buffer): 257 | file.write(view[:size]) 258 | hasher.update(view[:size]) 259 | 260 | return hasher 261 | 262 | 263 | def extract_tarfile(path: Path, dest: Path) -> Path | None: 264 | """Read and securely extract a compressed TAR archive to path. 265 | 266 | Warns the user if unable to extract the archive securely, falling 267 | back to unsafe extraction. The filter used is 'tar_filter'. 268 | 269 | See https://docs.python.org/3/library/tarfile.html#tarfile.tar_filter 270 | """ 271 | if not path.is_file(): 272 | return None 273 | 274 | # Note: r:tar is a valid mode in cpython. 275 | # See https://github.com/python/cpython/blob/b83be9c9718aac42d0d8fc689a829d6594192afa/Lib/tarfile.py#L1871 276 | with taropen(path, f"r:{path.suffix.removeprefix('.')}") as tar: # type: ignore 277 | try: 278 | from tarfile import tar_filter 279 | 280 | tar.extraction_filter = tar_filter 281 | log.debug("Using data filter for archive") 282 | except ImportError: 283 | # User is on a distro that did not backport extraction filters 284 | log.warning("Python: %s", sys.version) 285 | log.warning("Using no data filter for archive") 286 | log.warning("Archive will be extracted insecurely") 287 | 288 | log.debug("Extracting: %s -> %s", path, dest) 289 | tar.extractall(path=dest) # noqa: S202 290 | 291 | return dest 292 | 293 | 294 | def has_umu_setup(path: Path = UMU_LOCAL) -> bool: 295 | """Check if umu has been setup in our runtime directory.""" 296 | return path.exists() and any( 297 | file for file in path.glob("*") if not file.name.endswith("lock") 298 | ) 299 | 300 | 301 | # Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) 302 | # Licensed to PSF under a Contributor Agreement. 303 | # Source: https://raw.githubusercontent.com/python/cpython/refs/heads/3.11/Lib/hashlib.py 304 | # License: https://raw.githubusercontent.com/python/cpython/refs/heads/3.11/LICENSE 305 | def file_digest(fileobj, digest, /, *, _bufsize=2**18): # noqa: ANN001 306 | """Hash the contents of a file-like object. Returns a digest object. 307 | 308 | *fileobj* must be a file-like object opened for reading in binary mode. 309 | It accepts file objects from open(), io.BytesIO(), and SocketIO objects. 310 | The function may bypass Python's I/O and use the file descriptor *fileno* 311 | directly. 312 | 313 | *digest* must either be a hash algorithm name as a *str*, a hash 314 | constructor, or a callable that returns a hash object. 315 | """ 316 | # On Linux we could use AF_ALG sockets and sendfile() to archive zero-copy 317 | # hashing with hardware acceleration. 318 | digestobj = hashnew(digest) if isinstance(digest, str) else digest() 319 | 320 | if hasattr(fileobj, "getbuffer"): 321 | # io.BytesIO object, use zero-copy buffer 322 | digestobj.update(fileobj.getbuffer()) 323 | return digestobj 324 | 325 | # Only binary files implement readinto(). 326 | if not ( 327 | hasattr(fileobj, "readinto") 328 | and hasattr(fileobj, "readable") 329 | and fileobj.readable() 330 | ): 331 | err = f"'{fileobj!r}' is not a file-like object in binary reading mode." 332 | raise ValueError(err) 333 | 334 | # binary file, socket.SocketIO object 335 | # Note: socket I/O uses different syscalls than file I/O. 336 | buf = bytearray(_bufsize) # Reusable buffer to reduce allocations. 337 | view = memoryview(buf) 338 | while True: 339 | size = fileobj.readinto(buf) 340 | if size == 0: 341 | break # EOF 342 | digestobj.update(view[:size]) 343 | 344 | return digestobj 345 | --------------------------------------------------------------------------------