Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d6f6c1b82
|
||
|
|
4451d7e26a | ||
|
|
a4199e2a16
|
||
|
|
401d1ddac2
|
||
|
|
86f66a1174
|
||
|
|
8d5a99f0de
|
||
|
|
92f795f0e5
|
||
|
|
821c9172b2 | ||
|
|
9c2febb40a | ||
|
|
da21309b0c
|
||
|
|
9be9187ea5
|
||
|
|
18d1ad2d92
|
||
|
|
f9cefe501b
|
||
|
|
d385e4e120
|
||
|
|
4d9f7be000 | ||
|
|
ee6255a507 | ||
|
|
2a8630db1b | ||
|
|
078bb9e8c7 | ||
|
|
be7f7056d3 | ||
|
|
b31dcb9c63 | ||
|
|
0958914cf1
|
||
|
|
ff23e4c0df
|
||
|
|
4ae1b71823
|
||
|
|
25bf639f00
|
||
|
|
5dfdc91b75
|
||
|
|
e5076a683c | ||
|
|
c3d3668eee | ||
|
|
f6d2a37d6f | ||
|
|
6969c534a8 | ||
|
|
4fcf48a7cf
|
||
|
|
090818a356
|
||
|
|
7451d1f876 | ||
|
|
f30e3fd405
|
||
|
|
01e2c3bfd5
|
||
|
|
52e1b303ba
|
||
|
|
ab954936fc
|
||
|
|
d25342331d | ||
|
|
26d1661f63 | ||
|
|
6ddb630fea
|
||
|
|
2b934bef6e | ||
|
|
5d2101475e | ||
|
|
c16b481679
|
||
|
|
b91ed1b135
|
||
|
|
a25c30f463
|
||
|
|
ebded297d3
|
||
|
|
73d8c8e030
|
||
|
|
079d79f2c8
|
||
|
|
4f8e0c2d41
|
||
|
|
37d9104274 | ||
|
|
764bb3ced5
|
||
|
|
37dd71284f | ||
|
|
03da1b2c61 | ||
|
|
e77a7edf4c | ||
|
|
065cf835c5 | ||
|
|
3001fdac30
|
||
|
|
4456d06b50
|
||
|
|
1d423d7ebe | ||
|
|
1c91219cef
|
||
|
|
718b7f0eaa
|
||
|
|
0b4fcd37d4
|
||
|
|
f08ae3091e
|
||
|
|
947a5d522a
|
||
|
|
2ad468491e | ||
|
|
5bb0848f56
|
||
|
|
6d6ed36bc0
|
||
|
|
93d6c4610c
|
||
|
|
72ec18e099
|
||
|
|
a04b0e41e9
|
||
|
|
6e4664589b | ||
|
|
6ccf99c73f | ||
|
|
20372331d6 | ||
|
|
b17bbb2f46
|
||
|
|
4debfae5aa | ||
|
|
36a086129e
|
||
|
|
912dc2c77e
|
||
|
|
26f3a35560
|
||
|
|
ad8cbb35dc
|
||
|
|
612c10595d
|
||
|
|
ac3d1045e4
|
||
|
|
363ff0d86e | ||
|
|
fe6b84db56 | ||
|
|
02ebc440ec | ||
|
|
bf6421547d | ||
|
|
4679fb7eb0 | ||
|
|
5c5d544bc7 | ||
|
|
a4abeca70d
|
||
|
|
bb39b07445
|
||
|
|
4c27ca8365
|
||
|
|
8b0eb13d72
|
||
|
|
08ec0773fa
|
||
|
|
52f7c04cff
|
||
|
|
00e20f2165
|
||
|
|
758c731106 | ||
|
|
01a82510f2
|
||
|
|
72daab8cd6
|
||
|
|
a0445d8c45
|
||
|
|
343bc12c1e
|
||
|
|
8aff408bda
|
||
|
|
66586b2a36
|
||
|
|
3fb6727818
|
||
|
|
520fa55988
|
||
|
|
b34004cff3 | ||
|
|
2eb8c1f7b6 | ||
|
|
f36073ee32 | ||
|
|
a258a63c8e
|
||
|
|
def3fe27f8
|
||
|
|
45fd7da18a
|
||
|
|
6afce40576
|
||
|
|
c2f45736db
|
||
|
|
4f3fdb621e
|
||
|
|
e40e9811cf
|
||
|
|
d1bfab6c7c | ||
|
|
4f8929da40
|
||
|
|
4b0b613659
|
||
|
|
0802e7852f
|
||
|
|
4a03d803f4
|
||
|
|
4271461282
|
||
|
|
f4477101cc
|
||
|
|
0dc11ecd3c
|
||
|
|
3248392e78
|
||
|
|
9468c3f286
|
||
|
|
c826153ddf
|
||
|
|
f2b80604cb
|
||
|
|
dd61c72a3a
|
||
|
|
36a36ad7d2
|
||
|
|
03ada79e88
|
||
|
|
20444bc3d1
|
||
|
|
ae89b5a0fb
|
||
|
|
3372bb58ba
|
||
|
|
200b257622
|
||
|
|
0b8ef53cdd
|
||
|
|
41aea94fff
|
||
|
|
0a4cb81906
|
||
|
|
1743c69261
|
||
|
|
d0d1dff778
|
||
|
|
07e07dbe09
|
||
|
|
004916a82b
|
||
|
|
ae2115ad0a | ||
|
|
357a0dcf42
|
||
|
|
e5df48b9b0
|
||
|
|
48217b93f5
|
||
|
|
cc2372107a
|
||
|
|
a32d63f5e5
|
||
|
|
cbf29005ff
|
||
|
|
8d10bae8f2
|
||
|
|
28d0417ede
|
||
|
|
7efb0aadec
|
||
|
|
d4d7e9c044
|
||
|
|
eb18178958
|
||
|
|
979e208969
|
||
|
|
8704ba00e9
|
||
|
|
d9d8242475 | ||
|
|
64e626b2b2 | ||
|
|
8af84460eb
|
||
|
|
b7e83ad1d2
|
||
|
|
ffff6efa68
|
||
|
|
fabf14fc46
|
||
|
|
88db436b94
|
||
|
|
592d24c9c6
|
||
|
|
c472c785cc
|
||
|
|
759e931dfc
|
||
|
|
93f5ea49d4
|
||
|
|
d9c55bed61 | ||
|
|
64bbabd0f1 | ||
|
|
c3e9433f9d
|
||
|
|
c4274bbef6
|
||
|
|
cdfd983580
|
||
|
|
749113b48c
|
||
|
|
5fdba88c5c
|
||
|
|
a20887da5b
|
||
|
|
c9b8871fb1
|
||
|
|
6db27ef7af
|
||
|
|
97de53303c
|
||
|
|
07ac67e934 | ||
|
|
66e1e437f8 | ||
|
|
12c98adbd7
|
||
|
|
d7de29008e
|
||
|
|
4d99a66e84
|
||
|
|
288242195b | ||
|
|
892124efba | ||
|
|
ae7bd3e5ff | ||
|
|
042b90d663 | ||
|
|
4fdaf0115e | ||
|
|
b00f62776c
|
||
|
|
69674b5c75
|
||
|
|
eaa3467410
|
||
|
|
47a949fe6e
|
||
|
|
8902369fe7 | ||
|
|
c0529ac9fd | ||
|
|
a9ed377b00 | ||
|
|
bc523762b6 | ||
|
|
2f5159129c
|
||
|
|
eef8c7d496
|
||
|
|
64cad59d53
|
No files matched your search
@@ -1,142 +0,0 @@
|
||||
name: Nightly Build - AppImage, Arch, Fedora
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_BRANCH: ${{ github.ref_name }}
|
||||
PKGDEST: "/tmp/portprotonqt"
|
||||
PACKAGE: "portprotonqt"
|
||||
|
||||
jobs:
|
||||
build-appimage:
|
||||
name: Build AppImage
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:6db772fd1dd128b9cf7b7231ca5284af70879596fe4ec754c911cfc4ac30d5eb
|
||||
options: --privileged --device /dev/fuse
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman -Sy --noconfirm archlinux-keyring
|
||||
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
||||
|
||||
- uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Install appimage dependencies
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
||||
./get-dependencies.sh --local --branch "${{ env.BUILD_BRANCH }}"
|
||||
|
||||
- name: Build AppImage
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
./portprotonqt-appimage.sh
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-AppImage
|
||||
path: build-aux/AppImage/dist/*.AppImage
|
||||
|
||||
build-fedora:
|
||||
name: Build Fedora RPM
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
fedora_version: [42, 43, 44, rawhide]
|
||||
|
||||
container:
|
||||
image: fedora:${{ matrix.fedora_version }}
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||
systemd-rpm-macros vulkan-loader-devel redhat-rpm-config nodejs npm \
|
||||
gcc gettext
|
||||
|
||||
- name: Setup rpmbuild environment
|
||||
run: |
|
||||
useradd rpmbuild -u 5002 -g users || true
|
||||
mkdir -p /home/rpmbuild/{BUILD,RPMS,SPECS,SRPMS,SOURCES}
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
echo '%_topdir /home/rpmbuild' > /home/rpmbuild/.rpmmacros
|
||||
|
||||
- name: Checkout repo
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Copy fedora.spec
|
||||
run: |
|
||||
sed "s|git clone https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|git clone -b ${{ env.BUILD_BRANCH }} https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|" \
|
||||
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
|
||||
- name: Build RPM
|
||||
run: |
|
||||
su rpmbuild -c "rpmbuild -ba /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec"
|
||||
|
||||
- name: Upload RPM package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
|
||||
path: /home/rpmbuild/RPMS/**/*.rpm
|
||||
|
||||
build-arch:
|
||||
name: Build Arch Package
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:6db772fd1dd128b9cf7b7231ca5284af70879596fe4ec754c911cfc4ac30d5eb
|
||||
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman -Syuu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm
|
||||
sed -i 's/#MAKEFLAGS="-j2"/MAKEFLAGS="-j$(nproc) -l$(nproc)"/g' /etc/makepkg.conf
|
||||
sed -i 's/OPTIONS=(.*)/OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge lto)/g' /etc/makepkg.conf
|
||||
yes | pacman -Scc
|
||||
pacman-key --init
|
||||
pacman -S --noconfirm archlinux-keyring
|
||||
mkdir -p /__w/portproton-repo
|
||||
pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com
|
||||
pacman-key --lsign-key 3056513887B78AEB
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst'
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst'
|
||||
cat << EOM >> /etc/pacman.conf
|
||||
|
||||
[chaotic-aur]
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
EOM
|
||||
pacman -Syy
|
||||
useradd -m user -G wheel && echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
echo "PACKAGER=\"Boris Yumankulov <boria138@altlinux.org>\"" >> /etc/makepkg.conf
|
||||
chown user -R /tmp
|
||||
chown user -R ..
|
||||
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd /__w/portproton-repo
|
||||
cp -r "$GITHUB_WORKSPACE" PortProtonQt
|
||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git#branch=${{ env.BUILD_BRANCH }}\")|" PKGBUILD-git
|
||||
chown user -R ..
|
||||
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
||||
|
||||
- name: Upload Arch package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Arch
|
||||
path: ${{ env.PKGDEST }}/*
|
||||
@@ -25,12 +25,12 @@ jobs:
|
||||
apt-get install -y build-essential pkg-config libvulkan-dev git curl wget nodejs npm
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: https://gitea.com/actions/setup-node@v2
|
||||
uses: https://git.linux-gaming.ru/actions/setup-node@v2
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Checkout code
|
||||
uses: https://gitea.com/actions/checkout@v3
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@v3
|
||||
|
||||
- name: Compile vk_gpu_info
|
||||
run: |
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload vk_gpu_info binary
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: vk_gpu_info-debian11-binary
|
||||
path: build-aux/vk_gpu_info
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build AppImage, Arch and Fedora Packages
|
||||
name: Build AppImage, Arch, Fedora and Debian Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common version, will be used for tagging the release
|
||||
VERSION: 1.0
|
||||
VERSION: 1.2.0
|
||||
PKGDEST: "/tmp/portprotonqt"
|
||||
PACKAGE: "portprotonqt"
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
pacman -Sy --noconfirm archlinux-keyring
|
||||
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
||||
|
||||
- uses: https://gitea.com/actions/checkout@v4
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@v4
|
||||
|
||||
- name: Install appimage dependencies
|
||||
run: |
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
./portprotonqt-appimage.sh
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-AppImage
|
||||
path: build-aux/AppImage/dist/*.AppImage
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
chown user -R ..
|
||||
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
su user -c "yes '' | makepkg --noconfirm -s"
|
||||
|
||||
- name: Upload Arch package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Arch
|
||||
path: ${{ env.PKGDEST }}/*
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
echo '%_topdir /home/rpmbuild' > /home/rpmbuild/.rpmmacros
|
||||
|
||||
- name: Checkout repo
|
||||
uses: https://gitea.com/actions/checkout@v4
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@v4
|
||||
|
||||
- name: Copy fedora.spec
|
||||
run: |
|
||||
@@ -133,16 +133,46 @@ jobs:
|
||||
su rpmbuild -c "rpmbuild -ba /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec"
|
||||
|
||||
- name: Upload RPM package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
|
||||
path: /home/rpmbuild/RPMS/**/*.rpm
|
||||
|
||||
build-debian:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:13
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential debhelper dpkg-dev gettext git \
|
||||
libvulkan-dev meson ninja-build nodejs npm \
|
||||
pkgconf python3
|
||||
|
||||
- name: Checkout
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@v4
|
||||
|
||||
- name: Build Debian package
|
||||
run: |
|
||||
dpkg-buildpackage -us -uc -b
|
||||
mkdir -p dist/deb
|
||||
mv ../*.deb ../*.buildinfo ../*.changes dist/deb/
|
||||
|
||||
- name: Upload Debian package
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-DEB-Debian-13
|
||||
path: dist/deb/*
|
||||
|
||||
release:
|
||||
name: Create and Publish Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@v4
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@v4
|
||||
|
||||
- name: Install required dependencies
|
||||
run: |
|
||||
@@ -150,7 +180,7 @@ jobs:
|
||||
sudo apt install -y original-awk unzip
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: https://gitea.com/actions/download-artifact@v3
|
||||
uses: https://git.linux-gaming.ru/actions/download-artifact@v3
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: release/
|
||||
@@ -170,7 +200,7 @@ jobs:
|
||||
awk "/^## \\[$VERSION\\]/ {flag=1; next} /^## \\[/ || /^---/ {flag=0} flag" CHANGELOG.md > changelog.txt
|
||||
|
||||
- name: Release
|
||||
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-release-action@v1
|
||||
env:
|
||||
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
|
||||
with:
|
||||
|
||||
@@ -15,12 +15,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: https://gitea.com/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
uses: https://git.linux-gaming.ru/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
name: Build Check - AppImage, Arch, Fedora
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'build-aux/AppImage/**'
|
||||
- 'build-aux/fedora-git.spec'
|
||||
- 'build-aux/PKGBUILD-git'
|
||||
- '.gitea/workflows/code-build.yml'
|
||||
|
||||
env:
|
||||
PKGDEST: "/tmp/portprotonqt"
|
||||
PACKAGE: "portprotonqt"
|
||||
PR_REPO_URL: ${{ github.event.pull_request.head.repo.clone_url }}
|
||||
PR_BRANCH: ${{ github.head_ref }}
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
appimage: ${{ steps.check.outputs.appimage }}
|
||||
fedora: ${{ steps.check.outputs.fedora }}
|
||||
arch: ${{ steps.check.outputs.arch }}
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Ensure git is installed
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y git
|
||||
|
||||
- name: Check changed files
|
||||
id: check
|
||||
run: |
|
||||
# Get changed files
|
||||
git diff --name-only ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} > changed_files.txt
|
||||
|
||||
echo "Changed files:"
|
||||
cat changed_files.txt
|
||||
|
||||
# Check AppImage files
|
||||
if grep -q "build-aux/AppImage/" changed_files.txt; then
|
||||
echo "appimage=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "appimage=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check Fedora spec files (only fedora-git.spec)
|
||||
if grep -q "build-aux/fedora-git.spec" changed_files.txt; then
|
||||
echo "fedora=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "fedora=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# Check Arch PKGBUILD-git
|
||||
if grep -q "build-aux/PKGBUILD-git" changed_files.txt; then
|
||||
echo "arch=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "arch=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build-appimage:
|
||||
name: Build AppImage
|
||||
runs-on: ubuntu-22.04
|
||||
needs: changes
|
||||
if: needs.changes.outputs.appimage == 'true' || github.event_name == 'workflow_dispatch'
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:6db772fd1dd128b9cf7b7231ca5284af70879596fe4ec754c911cfc4ac30d5eb
|
||||
options: --privileged --device /dev/fuse
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman -Sy --noconfirm --disable-download-timeout --needed archlinux-keyring
|
||||
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
||||
|
||||
- uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Install appimage dependencies
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
||||
./get-dependencies.sh --local --branch "${{ env.PR_BRANCH }}" --repo "${{ env.PR_REPO_URL }}"
|
||||
|
||||
- name: Build AppImage
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
./portprotonqt-appimage.sh
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-AppImage
|
||||
path: build-aux/AppImage/dist/*.AppImage
|
||||
|
||||
build-fedora:
|
||||
name: Build Fedora RPM
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.fedora == 'true' || github.event_name == 'workflow_dispatch'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
fedora_version: [42, 43, 44, rawhide]
|
||||
|
||||
container:
|
||||
image: fedora:${{ matrix.fedora_version }}
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||
systemd-rpm-macros redhat-rpm-config nodejs npm \
|
||||
gcc vulkan-loader-devel gettext
|
||||
|
||||
- name: Setup rpmbuild environment
|
||||
run: |
|
||||
useradd rpmbuild -u 5002 -g users || true
|
||||
mkdir -p /home/rpmbuild/{BUILD,RPMS,SPECS,SRPMS,SOURCES}
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
echo '%_topdir /home/rpmbuild' > /home/rpmbuild/.rpmmacros
|
||||
|
||||
- name: Checkout repo
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Copy fedora-git.spec
|
||||
run: |
|
||||
sed "s|git clone https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }}|" \
|
||||
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
|
||||
- name: Build RPM
|
||||
run: |
|
||||
su rpmbuild -c "rpmbuild -ba /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec"
|
||||
|
||||
- name: Upload RPM package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
|
||||
path: /home/rpmbuild/RPMS/**/*.rpm
|
||||
|
||||
build-arch:
|
||||
name: Build Arch Package
|
||||
runs-on: ubuntu-22.04
|
||||
needs: changes
|
||||
if: needs.changes.outputs.arch == 'true' || github.event_name == 'workflow_dispatch'
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:6db772fd1dd128b9cf7b7231ca5284af70879596fe4ec754c911cfc4ac30d5eb
|
||||
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman -Sy --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm
|
||||
sed -i 's/#MAKEFLAGS="-j2"/MAKEFLAGS="-j$(nproc) -l$(nproc)"/g' /etc/makepkg.conf
|
||||
sed -i 's/OPTIONS=(.*)/OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge lto)/g' /etc/makepkg.conf
|
||||
yes | pacman -Scc
|
||||
pacman-key --init
|
||||
pacman -S --noconfirm archlinux-keyring
|
||||
mkdir -p /__w/portproton-repo
|
||||
pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com
|
||||
pacman-key --lsign-key 3056513887B78AEB
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst'
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst'
|
||||
cat << EOM >> /etc/pacman.conf
|
||||
|
||||
[chaotic-aur]
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
EOM
|
||||
pacman -Syy
|
||||
useradd -m user -G wheel && echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
echo "PACKAGER=\"Boris Yumankulov <boria138@altlinux.org>\"" >> /etc/makepkg.conf
|
||||
chown user -R /tmp
|
||||
chown user -R ..
|
||||
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd /__w/portproton-repo
|
||||
git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }} PortProtonQt
|
||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git\")|source=(\"git+${{ env.PR_REPO_URL }}#branch=${{ env.PR_BRANCH }}\")|" PKGBUILD-git
|
||||
chown user -R ..
|
||||
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
||||
|
||||
- name: Upload Arch package
|
||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Arch
|
||||
path: ${{ env.PKGDEST }}/*
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
name: Check code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: https://gitea.com/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: https://git.linux-gaming.ru/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: https://gitea.com/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
uses: https://git.linux-gaming.ru/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
name: Package Build - AppImage, Arch, Fedora, Debian
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'build-aux/AppImage/**'
|
||||
- 'build-aux/fedora-git.spec'
|
||||
- 'build-aux/PKGBUILD-git'
|
||||
- 'debian/**'
|
||||
- '.gitea/workflows/package-build.yml'
|
||||
|
||||
env:
|
||||
BUILD_BRANCH: ${{ github.ref_name }}
|
||||
PKGDEST: "/tmp/portprotonqt"
|
||||
PACKAGE: "portprotonqt"
|
||||
PR_REPO_URL: ${{ github.event.pull_request.head.repo.clone_url }}
|
||||
PR_BRANCH: ${{ github.head_ref }}
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
appimage: ${{ steps.check.outputs.appimage }}
|
||||
fedora: ${{ steps.check.outputs.fedora }}
|
||||
arch: ${{ steps.check.outputs.arch }}
|
||||
debian: ${{ steps.check.outputs.debian }}
|
||||
steps:
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Ensure git is installed
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y git
|
||||
|
||||
- name: Check changed files
|
||||
if: github.event_name == 'pull_request'
|
||||
id: check
|
||||
run: |
|
||||
git diff --name-only ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} > changed_files.txt
|
||||
|
||||
echo "Changed files:"
|
||||
cat changed_files.txt
|
||||
|
||||
if grep -q "build-aux/AppImage/" changed_files.txt; then
|
||||
echo "appimage=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "appimage=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if grep -q "build-aux/fedora-git.spec" changed_files.txt; then
|
||||
echo "fedora=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "fedora=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if grep -q "build-aux/PKGBUILD-git" changed_files.txt; then
|
||||
echo "arch=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "arch=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if grep -q "debian/" changed_files.txt; then
|
||||
echo "debian=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "debian=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Skip change detection
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: true
|
||||
|
||||
build-appimage:
|
||||
name: Build AppImage
|
||||
runs-on: ubuntu-22.04
|
||||
needs: changes
|
||||
if: needs.changes.outputs.appimage == 'true' || github.event_name == 'workflow_dispatch'
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:c84ad63503efc5d386e30a5f44945fa8eee10fcb21b3d5fceda260783de394ff
|
||||
options: --privileged --device /dev/fuse
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman -Sy --noconfirm archlinux-keyring
|
||||
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
||||
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Install appimage dependencies
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
./get-dependencies.sh --local --branch "${{ env.PR_BRANCH }}" --repo "${{ env.PR_REPO_URL }}"
|
||||
else
|
||||
./get-dependencies.sh --local --branch "${{ env.BUILD_BRANCH }}"
|
||||
fi
|
||||
|
||||
- name: Build AppImage
|
||||
run: |
|
||||
cd build-aux/AppImage
|
||||
./portprotonqt-appimage.sh
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-AppImage
|
||||
path: build-aux/AppImage/dist/*.AppImage
|
||||
|
||||
build-fedora:
|
||||
name: Build Fedora RPM
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.fedora == 'true' || github.event_name == 'workflow_dispatch'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
fedora_version: [42, 43, 44, rawhide]
|
||||
|
||||
container:
|
||||
image: fedora:${{ matrix.fedora_version }}
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||
systemd-rpm-macros vulkan-loader-devel redhat-rpm-config nodejs npm \
|
||||
gcc gettext
|
||||
|
||||
- name: Setup rpmbuild environment
|
||||
run: |
|
||||
useradd rpmbuild -u 5002 -g users || true
|
||||
mkdir -p /home/rpmbuild/{BUILD,RPMS,SPECS,SRPMS,SOURCES}
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
echo '%_topdir /home/rpmbuild' > /home/rpmbuild/.rpmmacros
|
||||
|
||||
- name: Checkout PR
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Checkout branch
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Copy fedora.spec
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
sed "s|git clone https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }}|" \
|
||||
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||
else
|
||||
sed "s|git clone https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|git clone -b ${{ env.BUILD_BRANCH }} https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git|" \
|
||||
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||
fi
|
||||
chown -R rpmbuild:users /home/rpmbuild
|
||||
|
||||
- name: Build RPM
|
||||
run: |
|
||||
su rpmbuild -c "rpmbuild -ba /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec"
|
||||
|
||||
- name: Upload RPM package
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
|
||||
path: /home/rpmbuild/RPMS/**/*.rpm
|
||||
|
||||
build-arch:
|
||||
name: Build Arch Package
|
||||
runs-on: ubuntu-22.04
|
||||
needs: changes
|
||||
if: needs.changes.outputs.arch == 'true' || github.event_name == 'workflow_dispatch'
|
||||
container:
|
||||
image: archlinux:base-devel@sha256:c84ad63503efc5d386e30a5f44945fa8eee10fcb21b3d5fceda260783de394ff
|
||||
|
||||
steps:
|
||||
- name: Prepare container
|
||||
run: |
|
||||
pacman -Syuu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm
|
||||
sed -i 's/#MAKEFLAGS="-j2"/MAKEFLAGS="-j$(nproc) -l$(nproc)"/g' /etc/makepkg.conf
|
||||
sed -i 's/OPTIONS=(.*)/OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge lto)/g' /etc/makepkg.conf
|
||||
yes | pacman -Scc
|
||||
pacman-key --init
|
||||
pacman -S --noconfirm archlinux-keyring
|
||||
mkdir -p /__w/portproton-repo
|
||||
pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com
|
||||
pacman-key --lsign-key 3056513887B78AEB
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst'
|
||||
pacman -U --noconfirm 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst'
|
||||
cat << EOM >> /etc/pacman.conf
|
||||
|
||||
[chaotic-aur]
|
||||
Include = /etc/pacman.d/chaotic-mirrorlist
|
||||
EOM
|
||||
pacman -Syy
|
||||
useradd -m user -G wheel && echo "user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
echo "PACKAGER=\"Boris Yumankulov <boria138@altlinux.org>\"" >> /etc/makepkg.conf
|
||||
chown user -R /tmp
|
||||
chown user -R ..
|
||||
|
||||
- name: Checkout PR
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Checkout branch
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd /__w/portproton-repo
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }} PortProtonQt
|
||||
else
|
||||
cp -r "$GITHUB_WORKSPACE" PortProtonQt
|
||||
fi
|
||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git\")|source=(\"git+${{ env.PR_REPO_URL }}#branch=${{ env.PR_BRANCH }}\")|" PKGBUILD-git
|
||||
else
|
||||
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git#branch=${{ env.BUILD_BRANCH }}\")|" PKGBUILD-git
|
||||
fi
|
||||
chown user -R ..
|
||||
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
||||
|
||||
- name: Upload Arch package
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-Arch
|
||||
path: ${{ env.PKGDEST }}/*
|
||||
|
||||
build-debian:
|
||||
name: Build Debian Package
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.debian == 'true' || github.event_name == 'workflow_dispatch'
|
||||
container:
|
||||
image: debian:13@sha256:4ae67669760b807c19f23902a3fd7c121a6a70cf2ae709035674b23e712e4d62
|
||||
options: --privileged
|
||||
|
||||
steps:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential debhelper dpkg-dev gettext git \
|
||||
libvulkan-dev meson ninja-build nodejs npm \
|
||||
pkgconf python3
|
||||
|
||||
- name: Checkout PR
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
- name: Checkout branch
|
||||
uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
|
||||
- name: Build Debian package
|
||||
run: |
|
||||
dpkg-buildpackage -us -uc -b
|
||||
mkdir -p dist/deb
|
||||
mv ../*.deb ../*.buildinfo ../*.changes dist/deb/
|
||||
|
||||
- name: Upload Debian package
|
||||
uses: https://git.linux-gaming.ru/actions/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: PortProtonQt-DEB-Debian-13
|
||||
path: dist/deb/*
|
||||
@@ -8,12 +8,12 @@ on:
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/renovatebot/renovate:latest@sha256:2ccc5b1f0340593c40e1598547aa98feee4e521a0906a423fe0be0431a733dfa
|
||||
container: ghcr.io/renovatebot/renovate:latest@sha256:47421f2f3f82048e969b5195dd808d5ac2c5f69d43d192191c4a3dca390afe06
|
||||
steps:
|
||||
- uses: https://gitea.com/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- uses: https://git.linux-gaming.ru/actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: https://gitea.com/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: https://git.linux-gaming.ru/actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ wheels/
|
||||
|
||||
# Meson
|
||||
builddir/
|
||||
build-deb/
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
|
||||
@@ -628,6 +628,11 @@ shadow.setBlurRadius(self.theme.shadow_blur_radius)
|
||||
label.setStyleSheet(self.theme.CONTENT_STYLE)
|
||||
```
|
||||
|
||||
**Tooltip rule:**
|
||||
- Prefer the themed `gamepad_tooltip`/`TOOLTIP_STYLE` mechanism for in-app `QWidget` tooltips
|
||||
- Avoid `setToolTip()` for regular widgets when the themed tooltip can be used
|
||||
- `setToolTip()` is acceptable only for APIs that are not regular widgets, such as `QSystemTrayIcon`
|
||||
|
||||
### Comments
|
||||
```python
|
||||
# NEVER: Russian or verbose
|
||||
@@ -785,6 +790,11 @@ PortProtonQt/
|
||||
- These scripts are for developer convenience only
|
||||
- Do not refactor or improve dev-scripts unless explicitly requested
|
||||
|
||||
## PortProton Scripts
|
||||
|
||||
- In `build-aux/share/portproton/scripts/`, run `portprotonqt.scripts_utils` modules through the existing `python_module` function
|
||||
- Do not add direct `python3 -m portprotonqt.scripts_utils.*` calls where `python_module` is available
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -3,6 +3,125 @@
|
||||
Все заметные изменения в этом проекте фиксируются в этом файле.
|
||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [1.2.0] - 2026-06-10
|
||||
|
||||
### Added
|
||||
- Добавлено удаление и переименование файла в файловом менеджере
|
||||
- Добавлена поддержка Special K
|
||||
- Добавлены настройки Wine, префикса и 3D Api по умолчанию
|
||||
- Добавлена настройка выбора звукового драйвера
|
||||
- Добавлена поддержка команд в настройке `запуск после`
|
||||
- Добавлена кнопка выхода во вкладку `Система`
|
||||
- Добавлена поддержка перетаскивания `.ppack` для восстановления префиксов
|
||||
- Добавлен скрытый тип отображения времени, скрывающий последний запуск и проведённое время
|
||||
- Добавлена сборка Deb-пакета (Debian 13+ и Ubuntu 25.04+)
|
||||
- Добавлена кнопка открытия установленного приложения из карточки автоустановки
|
||||
- Добавлено создание `.desktop` ярлыков под AppImage и их миграция при смене AppImage
|
||||
- Добавлено обновление `steam_scripts` в миграции ярлыков
|
||||
- Добавлена вкладка избранных параметров в диалог настроек exe
|
||||
- Добавлена вкладка настроек vkBasalt в диалог настроек exe
|
||||
- Добавлена проверка хэш-суммы после скачивания компонентов
|
||||
- Добавлена поддержка запуска `.nrg`
|
||||
- Начат португальский перевод `https://translate.codeberg.org/projects/portprotonqt/portprotonqt/pt_BR/`
|
||||
|
||||
### Changed
|
||||
- Действия управления питанием во вкладке `Система` переведены на использование `loginctl`
|
||||
- В пресет MangoHud `PortProton` добавлен параметр `display_server`
|
||||
- Возвращена настройка типа геймпада для подсказок: Авто, Xbox или PlayStation (для авто нужен libSDL3)
|
||||
- Убрано лишнее сообщение об успешной очистке PPDB в диалоге настроек exe
|
||||
- Улучшено выделение строк и читаемость комбобоксов в настройках exe
|
||||
- Увеличена скорость передвижения курсора при эмуляции мыши
|
||||
- Ярлыки по умолчанию создаются только внутри PortProtonQt, без копирования в меню приложений и на рабочий стол
|
||||
- Обновлён WINE_LG: 11-1 -> 11-10
|
||||
- Обновлён испанский перевод `https://translate.codeberg.org/projects/portprotonqt/portprotonqt/es/`
|
||||
|
||||
### Fixed
|
||||
- Исправлена работа Winetricks с версиями Proton/Wine имеющими путь `files/bin/wine`
|
||||
- `exiftool` больше не запускается, если он не установлен
|
||||
- Исправлено копирование PPDB к exe
|
||||
- Исправлено отображение настроек `.bat`, `.cmd`, `.msi` и `.reg`
|
||||
- Исправлена настройка запуска нативного Wayland
|
||||
- Исправлен запуск PortProtonQt AppImage в режиме `--silent`
|
||||
- Исправлен запуск добавленных в Steam приложений из AppImage
|
||||
- Исправлено отключение скачивания контейнера во Flatpak
|
||||
- Исправлено чтение XDG Base Directory в скриптах: теперь пути читаются всегда, если заданы, без привязки к Flatpak
|
||||
- Исправлена миграция ярлыков на рабочем столе
|
||||
- Исправлен выбор системного `MangoHud.conf` в Flatpak: сначала проверяется `~/.config`, затем `XDG_CONFIG_HOME`
|
||||
- Исправлен выбор системного `vkBasalt.conf` в Flatpak: сначала проверяется `~/.config`, затем `XDG_CONFIG_HOME`
|
||||
- Исправлено авто-включение MangoHud при наличии `MANGOHUD_CONFIG`
|
||||
- Добавлена пропущенная поддержка `.cmd` рядом с `.bat`
|
||||
- Исправлено определение каталога Steam на OpenSUSE и Deb-based системах
|
||||
- Исправлен запуск игр Steam с учётом native, Snap и Flatpak установок
|
||||
- Исправлена передача параметров запуска игр в Steam compatibility mode
|
||||
- Исправлено определение установленных автоустановок по `.desktop`
|
||||
- Исправлена загрузка встроенных цветовых схем терминала
|
||||
- Исправлено попадание NFS и SMB шар в список смонтированных дисков
|
||||
- Исправлено создание папки в файловом менеджере
|
||||
- Исправлено редактирование имени папки в файловом менеджере
|
||||
- Исправлен сброс фокуса в файловом менеджере при навигации геймпадом
|
||||
- Исправлено прерывание автоустановки при выходе из карточки
|
||||
- Исправлено чтение последнего запуска для приложений с пробелами в имени exe
|
||||
- Исправлено отображение настроек exe с расширением в верхнем регистре
|
||||
- Исправлена неработоспособность правого стика геймпада из-за обработки прокрутки
|
||||
- Получение списка компонентов winetricks теперь занимает 0.11 с, а не 6.8
|
||||
- Исправлено бесконечное скачивание компонентов из автоустановок, если скорость скачивания падает до нуля
|
||||
|
||||
### Contributors
|
||||
- @VanoMJ
|
||||
- @CastroFidel
|
||||
- @Dervart
|
||||
- @rogerio-mattos
|
||||
- @gallegonovato
|
||||
|
||||
---
|
||||
|
||||
## [1.1.0] - 2026-05-26
|
||||
|
||||
### Added
|
||||
- Добавлен диалог прогресса для создания и восстановления резервных копий префиксов
|
||||
- Добавлено автосоздание ярлыков GOG игр
|
||||
- Для обложек добавлена поддержка SVG
|
||||
- Добавлена поддержка `.bat`, `.msi` и `.reg`
|
||||
- Добавлена кнопка `Быстрый запуск` в библиотеку для открытия детальной страницы любого исполняемого файла без создания ярлыка
|
||||
- Добавлена проверка доступа на чтение и запись для `PORT_DATA_PATH` при выборе и запуске
|
||||
|
||||
### Changed
|
||||
- Обезличен интерфейс: игровые термины (Игра, Игровое время, Играть и т.д.) заменены на более общие (Приложение, Время в приложении, Запуск)
|
||||
- Резервные копии `.ppack` теперь создаются через `libarchive-c` в формате `pax` с `zstd` вместо SquashFS (лучше скорость и качество сжатия)
|
||||
- При ошибке загрузки обложки Steam/SGDB теперь используется иконка из `.exe`
|
||||
- Удалены заголовки вкладок `Библиотека` и `Автоустановка`
|
||||
- Улучшена навигация по библиотеке с клавиатуры и геймпада: фокус ставится на первую карточку, а верхние кнопки доступны из первого ряда
|
||||
- Пустая библиотека теперь корректно обновляется после запуска приложения через правую кнопку мыши
|
||||
- Для тем с режимом библиотеки `list` теперь используются обложки автоустановок
|
||||
- Установка Wine/LG-сборок в `data/dist` теперь не зависит от настройки загрузки Proton в Steam
|
||||
- При сбросе настроек PortProtonQt теперь также сбрасывается `user.conf` PortProton
|
||||
- Удалён отдельный пакет `portprotonqt-steam-compat` теперь установленный через галочку режим совместимости self-update
|
||||
- D-Bus операции скриптов перенесены в `scripts_utils.dbus_tools` на базе `dbus-fast`
|
||||
- Настройка авто-перехода в полноэкранный режим при подключении геймпада перенесена в подпункт настройки полноэкранного запуска
|
||||
|
||||
### Fixed
|
||||
- Исправлена некорректная установка DPI в Wine, когда опция `Использовать системный DPI` отключена
|
||||
- Исправлено определение версии DXVK в логе, если у приложения отсутствует PPDB
|
||||
- Исправлена пустая вкладка настроек Wine при отсутствии каталога `data/dist`
|
||||
- Исправлено обновление прогресса кнопок при загрузке библиотек и зависимостей PortProton
|
||||
- Исправлен повторный запуск восстановления префикса после смены темы сразу после восстановления
|
||||
- Исправлена эмуляция мыши с геймпада после запуска приложений через лаунчеры
|
||||
- Исправлен запуск PortProtonQt в полноэкранном режиме, если геймпад подключён до старта приложения
|
||||
- Исправлено отображение префиксов `DEFAULT` и `DOTNET` при отсутствии соответствующих каталогов
|
||||
- Исправлена локализация AppImage на РОСА «ФРЕШ»
|
||||
- Исправлено обновление библиотеки после создания ярлыков PortProton
|
||||
- Исправлен запуск `exiftool` для ярлыков с отсутствующими `.exe`
|
||||
- Исправлено восстановление профиля питания после остановки приложения из трея
|
||||
- Исправлено применения языка `Китайский (упрощённый)`
|
||||
- Исправлено попадание служебных строк PortProton в список видеокарт и скрытие выбора видеокарты при одной доступной GPU
|
||||
|
||||
### Contributors
|
||||
- @Castro-Fidel
|
||||
- @Simple16
|
||||
- @Dervart
|
||||
|
||||
---
|
||||
|
||||
## [1.0] - 2026-05-23
|
||||
|
||||
### Added
|
||||
@@ -59,7 +178,7 @@
|
||||
- Улучшенно качестве иконок из `exe` файлов
|
||||
- В трей добавлены компактный и детальный режимы меню
|
||||
- Автоустановки теперь можно остановить
|
||||
- Префиксы `DEFAULT`, `DOTNET` и `PROGRAMS` теперь всегда отображаются вверху списков префиксов
|
||||
- Префиксы `DEFAULT` и `DOTNET` теперь всегда отображаются вверху списков префиксов
|
||||
- Из лога удалена версия скриптов, так как скрипты теперь внутри PortProtonQt
|
||||
- Из downloader удалена отдельная проверка сети через Yandex и Google
|
||||
- Выбор видеокарты теперь не отображается в настройках PortProtonQt если видеокарта всего одна
|
||||
|
||||
@@ -60,7 +60,7 @@ PortProtonQt использует код и зависимости от след
|
||||
|
||||
- [Icoextract](https://github.com/jlu5/icoextract) — библиотека для извлечения иконок, лицензия [MIT](https://github.com/jlu5/icoextract/blob/master/LICENSE).
|
||||
- [HowLongToBeat Python API](https://github.com/ScrappyCocco/HowLongToBeat-PythonAPI) — библиотека для взаимодействия с HowLongToBeat, лицензия [MIT](https://github.com/ScrappyCocco/HowLongToBeat-PythonAPI/blob/master/LICENSE.md).
|
||||
- [iat](https://sourceforge.net/projects/iat.berlios) — библиотека для конвертации mdf в iso, лицензия GPLv3
|
||||
- [iat](https://sourceforge.net/projects/iat.berlios) — библиотека для конвертации mdf и nrg в iso, лицензия GPLv3
|
||||
- [pyte](https://github.com/selectel/pyte) — разбор ANSI escape-кодов, лицензия [LGPLv3](https://github.com/selectel/pyte?tab=LGPL-3.0-1-ov-file)
|
||||
Полный текст лицензий см. в файле [LICENSE](LICENSE).
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
pkgname=('portprotonqt' 'portprotonqt-steam-compat')
|
||||
pkgver=1.0
|
||||
pkgname=('portprotonqt')
|
||||
pkgver=1.2.0
|
||||
pkgrel=1
|
||||
pkgdesc="Modern GUI for managing and launching games from PortProton and Steam"
|
||||
arch=('any')
|
||||
url="https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt"
|
||||
license=('GPL-3.0')
|
||||
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pygame' 'python-orjson'
|
||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'python-pefile' 'python-pillow' 'perl-image-exiftool' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'jq' 'file' 'findutils' 'gawk' 'grep' 'tar' 'xz' 'zstd' 'gzip' 'unrar' 'qt6-svg' 'qt6-imageformats' 'pciutils' 'mesa-utils' 'vulkan-icd-loader' 'procps-ng' 'psmisc' 'squashfs-tools' '7zip' 'python-dbus-fast')
|
||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'python-pefile' 'python-pillow' 'perl-image-exiftool' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'jq' 'file' 'findutils' 'gawk' 'grep' 'tar' 'xz' 'zstd' 'gzip' 'unrar' 'qt6-svg' 'qt6-imageformats' 'pciutils' 'mesa-utils' 'vulkan-icd-loader' 'procps-ng' 'psmisc' '7zip' 'python-dbus-fast' 'sdl3')
|
||||
makedepends=('meson' 'ninja' 'vulkan-headers' 'gettext')
|
||||
conflicts=("portprotonqt-steam-compat")
|
||||
provides=('portprotonqt-steam-compat')
|
||||
replaces=('portprotonqt-steam-compat')
|
||||
source=("git+https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt#tag=v$pkgver")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
@@ -24,6 +27,7 @@ package_portprotonqt() {
|
||||
'upower: System tab bluetooth battery level'
|
||||
'libpulse: System tab audio volume/output management'
|
||||
'python-qrcode: Wi-Fi QR code generation'
|
||||
'squashfs-tools: for legacy PortProton prefix backup support'
|
||||
)
|
||||
|
||||
DESTDIR="$pkgdir" meson install -C build --skip-subprojects
|
||||
@@ -32,18 +36,4 @@ package_portprotonqt() {
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/portprotonqt" -t "$pkgdir/usr/share/bash-completion/completions"
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/portprotonqt.fish" -t "$pkgdir/usr/share/fish/vendor_completions.d"
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/_portprotonqt" -t "$pkgdir/usr/share/zsh/site-functions"
|
||||
|
||||
# Remove steam-compat files from main package
|
||||
rm -rf "$pkgdir/usr/share/steam"
|
||||
}
|
||||
|
||||
package_portprotonqt-steam-compat() {
|
||||
pkgdesc="Steam compatibility tool for PortProtonQt"
|
||||
depends=('portprotonqt')
|
||||
|
||||
DESTDIR="$pkgdir" meson install -C build --skip-subprojects
|
||||
|
||||
# Keep only steam-compat files
|
||||
find "$pkgdir/usr" -mindepth 1 -type f -not -path "${pkgdir}/usr/share/steam*" -print0 | xargs -0 rm -f
|
||||
find "$pkgdir/usr" -mindepth 1 -type d -not -path "${pkgdir}/usr/share/steam*" -empty -delete
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pkgname=('portprotonqt-git' 'portprotonqt-git-steam-compat')
|
||||
pkgname=('portprotonqt-git')
|
||||
pkgver=.
|
||||
pkgrel=1
|
||||
pkgdesc="Modern GUI for managing and launching games from PortProton and Steam (development build)"
|
||||
@@ -6,8 +6,11 @@ arch=('any')
|
||||
url="https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt"
|
||||
license=('GPL-3.0')
|
||||
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pygame' 'python-orjson'
|
||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'python-pefile' 'python-pillow' 'perl-image-exiftool' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'jq' 'file' 'findutils' 'gawk' 'grep' 'tar' 'xz' 'zstd' 'gzip' 'unrar' 'qt6-svg' 'qt6-imageformats' 'pciutils' 'mesa-utils' 'vulkan-icd-loader' 'procps-ng' 'psmisc' 'squashfs-tools' '7zip' 'python-dbus-fast')
|
||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'python-pefile' 'python-pillow' 'perl-image-exiftool' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'jq' 'file' 'findutils' 'gawk' 'grep' 'tar' 'xz' 'zstd' 'gzip' 'unrar' 'qt6-svg' 'qt6-imageformats' 'pciutils' 'mesa-utils' 'vulkan-icd-loader' 'procps-ng' 'psmisc' '7zip' 'python-dbus-fast' 'sdl3')
|
||||
makedepends=('meson' 'ninja' 'vulkan-headers' 'gettext')
|
||||
conflicts=("portprotonqt-steam-compat")
|
||||
provides=('portprotonqt-steam-compat')
|
||||
replaces=('portprotonqt-steam-compat')
|
||||
source=("git+https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt.git")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
@@ -29,6 +32,7 @@ package_portprotonqt-git() {
|
||||
'upower: System tab bluetooth battery level'
|
||||
'libpulse: System tab audio volume/output management'
|
||||
'python-qrcode: Wi-Fi QR code generation'
|
||||
'squashfs-tools: for legacy PortProton prefix backup support'
|
||||
)
|
||||
|
||||
DESTDIR="$pkgdir" meson install -C build --skip-subprojects
|
||||
@@ -37,18 +41,4 @@ package_portprotonqt-git() {
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/portprotonqt" -t "$pkgdir/usr/share/bash-completion/completions"
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/portprotonqt.fish" -t "$pkgdir/usr/share/fish/vendor_completions.d"
|
||||
install -Dpm 0644 "$srcdir/PortProtonQt/completions/_portprotonqt" -t "$pkgdir/usr/share/zsh/site-functions"
|
||||
|
||||
# Remove steam-compat files from main package
|
||||
rm -rf "$pkgdir/usr/share/steam"
|
||||
}
|
||||
|
||||
package_portprotonqt-git-steam-compat() {
|
||||
pkgdesc="Steam compatibility tool for PortProtonQt (development build)"
|
||||
depends=('portprotonqt-git')
|
||||
|
||||
DESTDIR="$pkgdir" meson install -C build --skip-subprojects
|
||||
|
||||
# Keep only steam-compat files
|
||||
find "$pkgdir/usr" -mindepth 1 -type f -not -path "${pkgdir}/usr/share/steam*" -print0 | xargs -0 rm -f
|
||||
find "$pkgdir/usr" -mindepth 1 -type d -not -path "${pkgdir}/usr/share/steam*" -empty -delete
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
%global pypi_name portprotonqt
|
||||
%global pypi_version 1.0
|
||||
%global pypi_version 1.2.0
|
||||
%global oname PortProtonQt
|
||||
%global build_timestamp %(date +"%Y%m%d")
|
||||
%global _python_no_extras_requires 1
|
||||
@@ -13,7 +13,7 @@ Summary: Modern GUI for managing and launching games from PortProton and
|
||||
|
||||
License: GPL-3.0
|
||||
URL: https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt
|
||||
ExclusiveArch: x86_64 aarch64
|
||||
ExclusiveArch: x86_64
|
||||
|
||||
BuildRequires: meson >= 0.61.2
|
||||
BuildRequires: ninja-build
|
||||
@@ -43,6 +43,7 @@ Requires: python3-pillow-qt
|
||||
Requires: python3-rapidfuzz
|
||||
Requires: python3-libarchive-c
|
||||
Requires: perl-Image-ExifTool
|
||||
Requires: SDL3
|
||||
Requires: qt6-qtsvg
|
||||
Requires: qt6-qtimageformats
|
||||
Requires: cabextract
|
||||
@@ -63,7 +64,6 @@ Requires: pciutils
|
||||
Requires: vulkan-loader
|
||||
Requires: procps-ng
|
||||
Requires: psmisc
|
||||
Requires: squashfs-tools
|
||||
Requires: 7zip
|
||||
Requires: python3-dbus-fast
|
||||
|
||||
@@ -74,18 +74,15 @@ Recommends: upower
|
||||
Recommends: pulseaudio-utils
|
||||
Recommends: python3-qrcode
|
||||
|
||||
# For legacy PortProton prefix backup support
|
||||
Recommends: squashfs-tools
|
||||
|
||||
Obsoletes: %{pypi_name}-steam-compat < %{?epoch:%{epoch}:}%{version}-%{release}
|
||||
Provides: %{pypi_name}-steam-compat = %{?epoch:%{epoch}:}%{version}-%{release}
|
||||
|
||||
%description
|
||||
A modern and intuitive interface for managing and launching games from PortProton and Steam. Combines libraries in one place and simplifies running Windows games on Linux.
|
||||
|
||||
%package -n %{pypi_name}-git-steam-compat
|
||||
Summary: Steam compatibility tool for PortProtonQt (development build)
|
||||
License: GPL-3.0
|
||||
Requires: %{pypi_name}-git = %{version}-%{release}
|
||||
|
||||
%description -n %{pypi_name}-git-steam-compat
|
||||
Steam compatibility tool integration for PortProtonQt. This package installs
|
||||
the necessary files to use PortProtonQt as a Proton compatibility tool in Steam.
|
||||
|
||||
%{?python_disable_dependency_generator}
|
||||
|
||||
%prep
|
||||
@@ -125,7 +122,4 @@ install -Dpm 0644 ./completions/_portprotonqt -t %{buildroot}%{zsh_completions_d
|
||||
%{fish_completions_dir}/portprotonqt.fish
|
||||
%{zsh_completions_dir}/_portprotonqt
|
||||
|
||||
%files -n %{pypi_name}-git-steam-compat
|
||||
%{_datadir}/steam/compatibilitytools.d/PortProtonQt/
|
||||
|
||||
%changelog
|
||||
@@ -1,5 +1,5 @@
|
||||
%global pypi_name portprotonqt
|
||||
%global pypi_version 1.0
|
||||
%global pypi_version 1.2.0
|
||||
%global oname PortProtonQt
|
||||
%global _python_no_extras_requires 1
|
||||
|
||||
@@ -10,7 +10,7 @@ Summary: Modern GUI for managing and launching games from PortProton and
|
||||
|
||||
License: GPL-3.0
|
||||
URL: https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt
|
||||
ExclusiveArch: x86_64 aarch64
|
||||
ExclusiveArch: x86_64
|
||||
|
||||
BuildRequires: meson >= 0.61.2
|
||||
BuildRequires: ninja-build
|
||||
@@ -40,6 +40,7 @@ Requires: python3-pillow-qt
|
||||
Requires: python3-rapidfuzz
|
||||
Requires: python3-libarchive-c
|
||||
Requires: perl-Image-ExifTool
|
||||
Requires: SDL3
|
||||
Requires: qt6-qtsvg
|
||||
Requires: qt6-qtimageformats
|
||||
Requires: cabextract
|
||||
@@ -60,7 +61,6 @@ Requires: pciutils
|
||||
Requires: vulkan-loader
|
||||
Requires: procps-ng
|
||||
Requires: psmisc
|
||||
Requires: squashfs-tools
|
||||
Requires: 7zip
|
||||
Requires: python3-dbus-fast
|
||||
|
||||
@@ -71,18 +71,15 @@ Recommends: upower
|
||||
Recommends: pulseaudio-utils
|
||||
Recommends: python3-qrcode
|
||||
|
||||
# For legacy PortProton prefix backup support
|
||||
Recommends: squashfs-tools
|
||||
|
||||
Obsoletes: %{pypi_name}-steam-compat < %{?epoch:%{epoch}:}%{version}-%{release}
|
||||
Provides: %{pypi_name}-steam-compat = %{?epoch:%{epoch}:}%{version}-%{release}
|
||||
|
||||
%description
|
||||
A modern and intuitive interface for managing and launching games from PortProton and Steam. Combines libraries in one place and simplifies running Windows games on Linux.
|
||||
|
||||
%package -n %{pypi_name}-steam-compat
|
||||
Summary: Steam compatibility tool for PortProtonQt
|
||||
License: GPL-3.0
|
||||
Requires: %{pypi_name} = %{version}-%{release}
|
||||
|
||||
%description -n %{pypi_name}-steam-compat
|
||||
Steam compatibility tool integration for PortProtonQt. This package installs
|
||||
the necessary files to use PortProtonQt as a Proton compatibility tool in Steam.
|
||||
|
||||
%{?python_disable_dependency_generator}
|
||||
|
||||
%prep
|
||||
@@ -123,7 +120,4 @@ install -Dpm 0644 ./completions/_portprotonqt -t %{buildroot}%{zsh_completions_d
|
||||
%{fish_completions_dir}/portprotonqt.fish
|
||||
%{zsh_completions_dir}/_portprotonqt
|
||||
|
||||
%files -n %{pypi_name}-steam-compat
|
||||
%{_datadir}/steam/compatibilitytools.d/PortProtonQt/
|
||||
|
||||
%changelog
|
||||
@@ -11,4 +11,4 @@ Terminal=false
|
||||
Icon=ru.linux_gaming.PortProtonQt
|
||||
StartupWMClass=ru.linux_gaming.PortProtonQt
|
||||
Categories=Game;Utility;
|
||||
MimeType=x-scheme-handler/portproton;application/x-portproton-prefix-backup;application/x-ms-dos-exec;application/x-executable;application/x-dosexec;application/x-iso9660-image;application/x-cd-image;application/x-raw-disk-image;application/x-mdf;application/x-mds-disk;
|
||||
MimeType=x-scheme-handler/portproton;application/x-portproton-prefix-backup;application/x-ms-dos-exec;application/x-ms-dos-batch;application/x-bat;application/x-msi;text/x-ms-regedit;application/x-executable;application/x-dosexec;application/x-iso9660-image;application/x-cd-image;application/x-raw-disk-image;application/x-mdf;application/x-mds-disk;application/x-nrg;
|
||||
@@ -24,34 +24,51 @@
|
||||
</developer>
|
||||
|
||||
<releases>
|
||||
<release version="1.0.0" date="2026-04-28">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Built-in PortProton scripts with full PortProton feature parity</li>
|
||||
<li>New classic theme for an easier transition from PortProton</li>
|
||||
<li>Network-saving mode, improved caching, and fully local Steam game covers</li>
|
||||
<li>File manager actions for creating, deleting, and renaming folders</li>
|
||||
<li>Support for animated covers, JXL images, MDF launch, and external Steam Proton builds</li>
|
||||
<li>New tray controls for stopping a running game and silent exe launch support</li>
|
||||
<li>Improved gamepad controls with automatic Xbox and PlayStation type selection</li>
|
||||
<li>Improved tray, auto-install page, lost exe handling, and Wine/Proton list ordering</li>
|
||||
<li>Fixes for Bluetooth, SteamGridDB, QComboBox scrolling, Steam Compatdata, and desktop shortcuts</li>
|
||||
</ul>
|
||||
</description>
|
||||
<release version="1.2.0" date="2026-06-10">
|
||||
<description></description>
|
||||
</release>
|
||||
<release version="1.1.0" date="2026-05-26">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="1.0" date="2026-05-23">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.12" date="2026-04-17" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.11" date="2026-02-18" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.10" date="2026-01-14" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.9" date="2025-12-08" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.8" date="2025-10-18" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.7" date="2025-10-12" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.6" date="2025-09-23" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.5" date="2025-08-31" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.4" date="2025-07-21" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.3" date="2025-07-05" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.2" date="2025-06-15" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
<release version="0.1.1" date="2025-05-17" type="development">
|
||||
<description/>
|
||||
</release>
|
||||
|
||||
<release version="0.1.12" date="2026-04-17" type="development"/>
|
||||
<release version="0.1.11" date="2026-02-18" type="development"/>
|
||||
<release version="0.1.10" date="2026-01-14" type="development"/>
|
||||
<release version="0.1.9" date="2025-12-08" type="development"/>
|
||||
<release version="0.1.8" date="2025-10-18" type="development"/>
|
||||
<release version="0.1.7" date="2025-10-12" type="development"/>
|
||||
<release version="0.1.6" date="2025-09-23" type="development"/>
|
||||
<release version="0.1.5" date="2025-08-31" type="development"/>
|
||||
<release version="0.1.4" date="2025-07-21" type="development"/>
|
||||
<release version="0.1.3" date="2025-07-05" type="development"/>
|
||||
<release version="0.1.2" date="2025-06-15" type="development"/>
|
||||
<release version="0.1.1" date="2025-05-17" type="development"/>
|
||||
</releases>
|
||||
|
||||
<recommends>
|
||||
|
||||
@@ -4,6 +4,5 @@
|
||||
<comment>PortProton prefix backup</comment>
|
||||
<comment xml:lang="ru">Резервная копия префикса PortProton</comment>
|
||||
<glob pattern="*.ppack"/>
|
||||
<sub-class-of type="application/vnd.squashfs"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -13,7 +13,7 @@ export PORTWINE_CREATE_SHORTCUT_NAME="DC Universe Online"
|
||||
start_portproton
|
||||
if try_download "https://launch.daybreakgames.com/installer/DCUO_setup.exe" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/Program Files (x86)/Daybreak Game Company"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/Program Files (x86)/Daybreak Game Company"
|
||||
PW_EXE_FILE="$WINEPREFIX/drive_c/Program Files (x86)/Daybreak Game Company/LaunchPad.exe"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
try_remove_file "${PW_EXE_FILE}.ppdb"
|
||||
|
||||
@@ -26,10 +26,10 @@ if try_download "https://launcher.ccpgames.com/eve-online/release/win32/x64/eve-
|
||||
then
|
||||
EVE_LAUNCHER_DIR="${WINEPREFIX}/drive_c/users/steamuser/AppData/Local/eve-online"
|
||||
|
||||
$pw_7z x "${PORT_WINE_TMP_PATH}/eve-online-latest+Setup.exe" -o"${EVE_LAUNCHER_DIR}" -y
|
||||
unpack "${PORT_WINE_TMP_PATH}/eve-online-latest+Setup.exe" "${EVE_LAUNCHER_DIR}"
|
||||
EVE_VERSION="$(cat "${EVE_LAUNCHER_DIR}/RELEASES" | awk '{print $2}' | awk -F"-" '{print $3}')"
|
||||
|
||||
$pw_7z x "${EVE_LAUNCHER_DIR}/eve-online-${EVE_VERSION}-full.nupkg" -o"${EVE_LAUNCHER_DIR}/tmp" -y
|
||||
unpack "${EVE_LAUNCHER_DIR}/eve-online-${EVE_VERSION}-full.nupkg" "${EVE_LAUNCHER_DIR}/tmp"
|
||||
|
||||
|
||||
create_new_dir "${EVE_LAUNCHER_DIR}/app-$EVE_VERSION"
|
||||
|
||||
@@ -15,8 +15,8 @@ start_portproton
|
||||
if try_download "https://static-gl.lilithgame.com/p/pcsdk/launcher/10060/prodef4621e968ad6b383b8754148b63/0/launcher/res/release/Farlight84_setup_1.3.0.1_238.exe" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
create_new_dir "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84"
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/Program Files (x86)/Farlight84"
|
||||
"$pw_7z" x -y "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84/launcher.7z" -o"$WINEPREFIX/drive_c/Program Files (x86)/Farlight84"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84"
|
||||
unpack "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84/launcher.7z" "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84"
|
||||
PW_EXE_FILE="$WINEPREFIX/drive_c/Program Files (x86)/Farlight84/Farlight84Launcher.exe"
|
||||
try_remove_file "$WINEPREFIX/drive_c/Program Files (x86)/Farlight84/launcher.7z"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
|
||||
@@ -13,7 +13,7 @@ export PORTWINE_CREATE_SHORTCUT_NAME="Last Chaos"
|
||||
start_portproton
|
||||
if try_download "https://last-chaos.ru/download/Last%20Chaos.zip" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"${WINEPREFIX}/drive_c/Program Files/"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "${WINEPREFIX}/drive_c/Program Files/"
|
||||
PW_EXE_FILE="${WINEPREFIX}/drive_c/Program Files/Last Chaos/LC.exe"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
try_remove_file "${PW_EXE_FILE}.ppdb"
|
||||
|
||||
@@ -15,7 +15,7 @@ URL_LOST_LIGHT="$(curl -s --list-only "https://adl.easebar.com/d/g/yhtj/c/lostli
|
||||
start_portproton
|
||||
if try_download "${URL_LOST_LIGHT}" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/Program Files (x86)/LostLight"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/Program Files (x86)/LostLight"
|
||||
export PW_EXE_FILE="$WINEPREFIX/drive_c/Program Files (x86)/LostLight/launcher.exe"
|
||||
pw_create_unique_exe "lostlight_launcher_pp"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
|
||||
@@ -15,7 +15,7 @@ export PORTWINE_CREATE_SHORTCUT_NAME="Mini World"
|
||||
start_portproton
|
||||
if try_download "https://mwu-api.miniworldgame.com/app_install/${URL_VER//[[:space:]]/}universal" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/users/steamuser/AppData/Roaming/miniworldOverseasgame"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/users/steamuser/AppData/Roaming/miniworldOverseasgame"
|
||||
PW_EXE_FILE="$WINEPREFIX/drive_c/users/steamuser/AppData/Roaming/miniworldOverseasgame/MicroMiniNew.exe"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
try_remove_file "${PW_EXE_FILE}.ppdb"
|
||||
|
||||
@@ -14,7 +14,7 @@ export PORTWINE_CREATE_SHORTCUT_NAME="Rise of Flight"
|
||||
start_portproton
|
||||
if try_download "https://il2bosrd1.cdnvideo.ru/ROF/ROF_UE.zip" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/Program Files (x86)/1C-777/Rise of Flight"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/Program Files (x86)/1C-777/Rise of Flight"
|
||||
pw_kill_autostart "3rd_party" &
|
||||
pw_run "$WINEPREFIX/drive_c/Program Files (x86)/1C-777/Rise of Flight/ROF_UE/setup.exe"
|
||||
PW_EXE_FILE="$WINEPREFIX/drive_c/Program Files (x86)/1C-777/Rise of Flight/bin_game/release/rof_updater.exe"
|
||||
|
||||
@@ -12,7 +12,7 @@ export PW_AUTOINSTALL_EXE="${PORT_WINE_TMP_PATH}/saturn.zip"
|
||||
start_portproton
|
||||
if try_download "https://cdn-s3.quadrosim.ru/saturn/saturn.zip" "${PW_AUTOINSTALL_EXE}" no_mirror
|
||||
then
|
||||
"$pw_7z" x -y "${PW_AUTOINSTALL_EXE}" -o"$WINEPREFIX/drive_c/Program Files (x86)/saturn"
|
||||
unpack "${PW_AUTOINSTALL_EXE}" "$WINEPREFIX/drive_c/Program Files (x86)/saturn"
|
||||
PW_EXE_FILE="$WINEPREFIX/drive_c/Program Files (x86)/saturn/Saturn.exe"
|
||||
try_remove_file "${PW_AUTOINSTALL_EXE}"
|
||||
try_remove_file "${PW_EXE_FILE}.ppdb"
|
||||
|
||||
@@ -23,8 +23,7 @@ PORT_IMG_PATH="$(dirname "$PORT_SCRIPTS_PATH")/img"
|
||||
PORT_CONF_PATH="$(dirname "$PORT_SCRIPTS_PATH")/conf"
|
||||
|
||||
if [[ -z "$PORT_DATA_PATH" ]] ; then
|
||||
if [[ -n "${FLATPAK_ID:-}" ]] \
|
||||
&& [[ -n "${XDG_DATA_HOME:-}" ]] ; then
|
||||
if [[ -n "${XDG_DATA_HOME:-}" ]] ; then
|
||||
PORT_DATA_PATH="$(dirname "${XDG_DATA_HOME}")"
|
||||
elif [[ -f "$HOME/.config/PortProtonQt.conf" ]] \
|
||||
&& grep "portdata_path" "$HOME/.config/PortProtonQt.conf" ; then
|
||||
@@ -37,8 +36,11 @@ if [[ -z "$PORT_DATA_PATH" ]] ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$HOME/.config/PortProtonQt.conf" ]] \
|
||||
&& grep "disable_runtime_download = True" "$HOME/.config/PortProtonQt.conf" ; then
|
||||
if [[ -n "${XDG_CONFIG_HOME:-}" ]] \
|
||||
&& grep -q "disable_runtime_download = True" "$XDG_CONFIG_HOME/PortProtonQt.conf" ; then
|
||||
export PW_DISABLE_RUNTIME_DOWNLOAD=1
|
||||
elif [[ -f "$HOME/.config/PortProtonQt.conf" ]] \
|
||||
&& grep -q "disable_runtime_download = True" "$HOME/.config/PortProtonQt.conf" ; then
|
||||
export PW_DISABLE_RUNTIME_DOWNLOAD=1
|
||||
fi
|
||||
|
||||
@@ -92,7 +94,7 @@ if [[ "${1,,}" =~ \.ppack$ ]] ; then
|
||||
export PW_NO_RESTART_PPDB="1"
|
||||
export PW_DISABLED_CREATE_DB="1"
|
||||
PW_EXE_FILE="$1"
|
||||
elif [[ "${1,,}" =~ \.(exe|bat|msi|reg|lnk)$ ]] ; then
|
||||
elif [[ "${1,,}" =~ \.(exe|bat|cmd|msi|reg|lnk)$ ]] ; then
|
||||
if [[ -f "$1" ]] ; then
|
||||
PW_EXE_FILE="$(realpath -s "$1")"
|
||||
elif [[ -f "$OLDPWD/$1" ]] ; then
|
||||
@@ -105,7 +107,7 @@ elif [[ "${1,,}" =~ \.(exe|bat|msi|reg|lnk)$ ]] ; then
|
||||
get_lnk "${PW_EXE_FILE}"
|
||||
PW_EXE_FILE="$(realpath "${link_path}" 2>/dev/null)"
|
||||
fi
|
||||
elif [[ "$1" =~ ^--(debug|launch|edit-db)$ && "${2,,}" =~ \.(exe|bat|msi|reg)$ ]] ; then
|
||||
elif [[ "$1" =~ ^--(debug|launch|edit-db)$ && "${2,,}" =~ \.(exe|bat|cmd|msi|reg)$ ]] ; then
|
||||
if [[ -f "$2" ]] ; then
|
||||
PW_EXE_FILE="$(realpath -s "$2")"
|
||||
elif [[ -f "$OLDPWD/$2" ]] ; then
|
||||
@@ -128,7 +130,7 @@ create_new_dir "${HOME}/.local/share/applications"
|
||||
create_new_dir "${PORT_DATA_PATH}/data/dist"
|
||||
IFS=$'\n'
|
||||
for dist_dir in $(lsbash "${PORT_DATA_PATH}/data/dist/") ; do
|
||||
dist_dir_new=$(echo "${dist_dir}" | awk '$1=$1' | sed -e s/[[:blank:]]/_/g)
|
||||
dist_dir_new=$(echo "${dist_dir}" | awk '$1=$1' | sed -e 's/[[:blank:]]*-[[:blank:]]*/-/g' -e 's/[[:blank:]]/_/g')
|
||||
if [[ ! -d "${PORT_DATA_PATH}/data/dist/${dist_dir_new^^}" ]] ; then
|
||||
mv -- "${PORT_DATA_PATH}/data/dist/$dist_dir" "${PORT_DATA_PATH}/data/dist/${dist_dir_new^^}"
|
||||
fi
|
||||
@@ -159,6 +161,8 @@ cd "${PORT_SCRIPTS_PATH}" || fatal
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "${PORT_SCRIPTS_PATH}/var"
|
||||
# HACK: Avoid inheriting a system scripts cwd in pressure-vessel.
|
||||
cd "${XDG_DATA_HOME:-$HOME}" || fatal
|
||||
|
||||
[[ ! -f "$VKBASALT_CONFIG_FILE" ]] && cp -f "${PORT_CONF_PATH}/vkBasalt.conf" "$VKBASALT_CONFIG_FILE"
|
||||
[[ ! -f "$DXVK_CONFIG_FILE" ]] && cp -f "${PORT_CONF_PATH}/dxvk.conf" "$DXVK_CONFIG_FILE"
|
||||
@@ -205,8 +209,8 @@ fi
|
||||
print_info "The first mirror in used: $MIRROR"
|
||||
|
||||
if check_gamescope_session
|
||||
then PW_TERM="env LANG=C python3 -m portprotonqt.easyterm --fullscreen -e"
|
||||
else PW_TERM="env LANG=C python3 -m portprotonqt.easyterm -e"
|
||||
then PW_TERM="env LANG=C python3 -m portprotonqt.scripts_utils.easyterm --fullscreen -e"
|
||||
else PW_TERM="env LANG=C python3 -m portprotonqt.scripts_utils.easyterm -e"
|
||||
fi
|
||||
|
||||
pw_cleanup () {
|
||||
@@ -269,6 +273,8 @@ case "$1" in
|
||||
--winecmd Open wine command prompt, requires WINE version and prefix name
|
||||
--winereg Open wine registry editor, requires WINE version and prefix name
|
||||
--wine_uninstaller Open wine uninstaller, requires WINE version and prefix name
|
||||
--winetricks-list-all List winetricks DLLs, fonts, and settings
|
||||
--winetricks-install Install winetricks items, requires WINE version and prefix name
|
||||
--clear_pfx Clear specified prefix, requires WINE version and prefix name
|
||||
--mangohud-preview Starts MangoHud preview in vkcube (optional argument: inline MangoHud config)
|
||||
--initial Initial setup command
|
||||
@@ -285,6 +291,8 @@ Usage examples:
|
||||
portproton cli --backup-prefix DEFAULT /path/to/backup/directory
|
||||
portproton cli --restore-prefix /path/to/backup/file.ppack
|
||||
portproton cli --winecfg WINE_LG DEFAULT
|
||||
portproton cli --winetricks-list-all WINE_LG DEFAULT
|
||||
portproton cli --winetricks-install WINE_LG DEFAULT --no-force corefonts
|
||||
portproton cli --mangohud-preview \"fps,frametime,cpu_temp,gpu_temp\"
|
||||
portproton cli --autoinstall [script_name_from_pw_autoinstall]
|
||||
"
|
||||
@@ -325,8 +333,15 @@ Usage examples:
|
||||
export PW_USE_D3D_EXTRAS=1
|
||||
export WINE_LARGE_ADDRESS_AWARE=0
|
||||
trap "stop_portproton" SIGTERM SIGINT
|
||||
# shellcheck source=/dev/null
|
||||
. "${PORT_SCRIPTS_PATH}/pw_autoinstall/${2}"
|
||||
if [[ "$2" =~ "/" ]] && [[ -f "$2" ]] ; then
|
||||
# shellcheck source=/dev/null
|
||||
. "${2}"
|
||||
elif [[ -f "${PORT_SCRIPTS_PATH}/pw_autoinstall/${2}" ]] ; then
|
||||
# shellcheck source=/dev/null
|
||||
. "${PORT_SCRIPTS_PATH}/pw_autoinstall/${2}"
|
||||
else
|
||||
fatal "Not found autoinstal script: $2"
|
||||
fi
|
||||
stop_portproton
|
||||
;;
|
||||
--debug)
|
||||
@@ -357,12 +372,12 @@ Usage examples:
|
||||
exit 0
|
||||
;;
|
||||
--show-ppdb)
|
||||
# --show-ppdb /полный/путь/до/файла.exe ИЛИ /полный/путь/до/файла.exe.ppdb
|
||||
# --show-ppdb /полный/путь/до/файла.(exe|bat|cmd|msi|reg) ИЛИ /полный/путь/до/файла.ext.ppdb
|
||||
input_path="$2"
|
||||
|
||||
case "$input_path" in
|
||||
*.ppdb) exe_path="${input_path%.ppdb}" ;;
|
||||
*.exe) exe_path="$input_path" ;;
|
||||
*.[bB][aA][tT]|*.[cC][mM][dD]|*.[eE][xX][eE]|*.[mM][sS][iI]|*.[rR][eE][gG]) exe_path="$input_path" ;;
|
||||
esac
|
||||
|
||||
ppdb_path="${exe_path}.ppdb"
|
||||
@@ -396,6 +411,11 @@ Usage examples:
|
||||
done < <(grep -E '^export ' "$USER_CONF" 2>/dev/null | sed -E 's/[[:space:]]*#.*$//' | sed '/^[[:space:]]*$/d')
|
||||
fi
|
||||
|
||||
[[ -n "${PW_DEFAULT_WINE_USE:-}" && -z "${all_vars[PW_WINE_USE]+x}" ]] && all_vars["PW_WINE_USE"]="$PW_DEFAULT_WINE_USE"
|
||||
[[ -n "${PW_DEFAULT_PREFIX_NAME:-}" && -z "${all_vars[PW_PREFIX_NAME]+x}" ]] && all_vars["PW_PREFIX_NAME"]="$PW_DEFAULT_PREFIX_NAME"
|
||||
[[ -n "${PW_DEFAULT_VULKAN_USE:-}" && -z "${all_vars[PW_VULKAN_USE]+x}" ]] && all_vars["PW_VULKAN_USE"]="$PW_DEFAULT_VULKAN_USE"
|
||||
all_vars["PW_PLUGINS_VER"]="$PW_PLUGINS_VER"
|
||||
|
||||
while IFS='=' read -r key val; do
|
||||
key="${key#export }"
|
||||
val="${val#\"}"
|
||||
@@ -403,7 +423,12 @@ Usage examples:
|
||||
if [[ -z "${all_vars[$key]+x}" ]]; then
|
||||
all_vars["$key"]="$val"
|
||||
fi
|
||||
done < <(grep -E '^export ' "$PORT_SCRIPTS_PATH/var" | sed -E 's/[[:space:]]*#.*$//' | sed '/^[[:space:]]*$/d')
|
||||
done < <(
|
||||
{
|
||||
grep -E '^export ' "$PORT_SCRIPTS_PATH/var"
|
||||
grep -E '^check_variables ' "$PORT_SCRIPTS_PATH/var" | sed -E 's/^check_variables ([^[:space:]]+) (.*)$/export \1=\2/'
|
||||
} | sed -E 's/[[:space:]]*#.*$//' | sed '/^[[:space:]]*$/d'
|
||||
)
|
||||
|
||||
for key in "${!all_vars[@]}"; do
|
||||
echo "${key}=\"${all_vars[$key]}\""
|
||||
@@ -454,6 +479,64 @@ Usage examples:
|
||||
pw_run uninstaller
|
||||
stop_portproton
|
||||
;;
|
||||
--winetricks-list-all)
|
||||
get_wine_and_pfx "$2" "$3"
|
||||
update_winetricks >&2
|
||||
sh -n "${PORT_WINE_TMP_PATH}/winetricks" >&2 || exit 1
|
||||
awk '
|
||||
function save_entry() {
|
||||
if (category == "") {
|
||||
return
|
||||
}
|
||||
count[category]++
|
||||
entries[category, count[category]] = name " " title
|
||||
category = ""
|
||||
name = ""
|
||||
title = ""
|
||||
}
|
||||
/^w_metadata[[:space:]]+/ {
|
||||
save_entry()
|
||||
if ($3 == "dlls" || $3 == "fonts" || $3 == "settings") {
|
||||
name = $2
|
||||
category = $3
|
||||
}
|
||||
next
|
||||
}
|
||||
category != "" && /^[[:space:]]*title="/ {
|
||||
title = $0
|
||||
sub(/^[[:space:]]*title="/, "", title)
|
||||
sub(/[[:space:]]*\\$/, "", title)
|
||||
sub(/"$/, "", title)
|
||||
}
|
||||
END {
|
||||
save_entry()
|
||||
split("dlls fonts settings", categories, " ")
|
||||
for (i = 1; i <= 3; i++) {
|
||||
category = categories[i]
|
||||
print "__PORTPROTONQT_WINETRICKS_SECTION__:" category
|
||||
for (j = 1; j <= count[category]; j++) {
|
||||
print entries[category, j]
|
||||
}
|
||||
}
|
||||
}
|
||||
' "${PORT_WINE_TMP_PATH}/winetricks"
|
||||
winetricks_status="$?"
|
||||
exit "$winetricks_status"
|
||||
;;
|
||||
--winetricks-install)
|
||||
get_wine_and_pfx "$2" "$3"
|
||||
winetricks_force="$4"
|
||||
shift 4
|
||||
start_portproton
|
||||
update_winetricks
|
||||
winetricks_args=("--unattended")
|
||||
[[ "$winetricks_force" == "--force" ]] && winetricks_args+=("--force")
|
||||
"${PORT_WINE_TMP_PATH}/winetricks" "${winetricks_args[@]}" "$@"
|
||||
winetricks_status=$?
|
||||
wait_wineserver
|
||||
stop_portproton
|
||||
exit "$winetricks_status"
|
||||
;;
|
||||
--clear_pfx)
|
||||
get_wine_and_pfx "$2" "$3"
|
||||
pw_clear_pfx
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
###BASE VARIABLES###
|
||||
export PW_PROTON_LG_VER="PROTON_LG_10-30"
|
||||
export PW_WINE_LG_VER="WINE_LG_11-1"
|
||||
export PW_WINE_LG_VER="WINE_LG_11-10"
|
||||
export PW_LIBS_VER="_v3.0.20260218"
|
||||
export PW_PLUGINS_VER="_v20"
|
||||
export DXVK_NEW_VER="2.7.1-509"
|
||||
@@ -14,6 +14,7 @@ export VKD3D_OLD_VER="2.14.1"
|
||||
export VKD3D_SAREK_VER="sarek-2.6.0"
|
||||
export PW_LSFG_VK_VER="2.0.0-dev25-p11"
|
||||
export PW_OPTISCALER_VER="v03-0.9.0-pre8"
|
||||
export PW_SPECIALK_VER="SK_26_5_15"
|
||||
export PW_GALLIUM_NINE_VER="0.10"
|
||||
export DOTPFX_VER="48v9"
|
||||
export DEFPFX_VER="v1"
|
||||
@@ -29,6 +30,7 @@ export PW_RT_MOUNT_RW=("/media" "/mnt" "/run/media/${USER}" "${PORT_DATA_PATH}")
|
||||
check_variables PW_WINE_USE "PROTON_LG"
|
||||
check_variables PROTON_USE_XALIA "0"
|
||||
check_variables PW_USE_RUNTIME "1"
|
||||
check_variables PW_USE_SPECIALK "0"
|
||||
check_variables PW_USE_ESYNC "0"
|
||||
check_variables PW_USE_FSYNC "0"
|
||||
check_variables PW_USE_NTSYNC "0"
|
||||
@@ -70,6 +72,7 @@ check_variables VKD3D_LIMIT_TESS_FACTORS "64"
|
||||
check_variables PW_VKBASALT "0"
|
||||
check_variables PW_VKBASALT_EFFECTS "Curves:cas"
|
||||
check_variables PW_VKBASALT_FFX_CAS "0.50"
|
||||
check_variables PW_VKBASALT_TOOGLE_KEY "Home"
|
||||
export VKBASALT_CONFIG_FILE="${PORT_DATA_PATH}/data/vkBasalt.conf"
|
||||
|
||||
###GAMESCOPE###
|
||||
@@ -80,7 +83,7 @@ export PW_GAMESCOPE_ARGS_NEW=" -f --sharpness 10 --hdr-sdr-content-nits 0"
|
||||
|
||||
###MANGOHUD###
|
||||
check_variables PW_MANGOHUD "0"
|
||||
export DEFAULT_MANGOHUD_CONFIG="arch,cpu_mhz,cpu_temp,engine_version,gamemode,gpu_core_clock,gpu_mem_clock,gpu_name,gpu_temp,ram,resolution,vkbasalt,vram,vulkan_driver,wine,winesync"
|
||||
export DEFAULT_MANGOHUD_CONFIG="arch,cpu_mhz,cpu_temp,engine_version,gamemode,gpu_core_clock,gpu_mem_clock,gpu_name,gpu_temp,ram,resolution,vkbasalt,vram,vulkan_driver,wine,winesync,display_server"
|
||||
|
||||
###DGVOODOO2###
|
||||
check_variables PW_DGVOODOO2 "0"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
"compatibilitytools"
|
||||
{
|
||||
"compat_tools"
|
||||
{
|
||||
"PortProtonQt"
|
||||
{
|
||||
"install_path" "."
|
||||
"display_name" "PortProtonQt"
|
||||
"from_oslist" "windows"
|
||||
"to_oslist" "linux"
|
||||
"Priority" "75"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
# Install PortProtonQt Steam compatibility tool
|
||||
install_data(
|
||||
'compatibilitytool.vdf',
|
||||
'toolmanifest.vdf',
|
||||
install_dir: prefix / get_option('datadir') / 'steam' / 'compatibilitytools.d' / 'PortProtonQt',
|
||||
)
|
||||
|
||||
install_data(
|
||||
'portproton',
|
||||
install_dir: prefix / get_option('datadir') / 'steam' / 'compatibilitytools.d' / 'PortProtonQt',
|
||||
install_mode: 'rwxr-xr-x',
|
||||
)
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Remove Steam Runtime libraries
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
export STEAM_COMPAT=1
|
||||
|
||||
exe_name="$(basename "$1")"
|
||||
exe_dir="$(dirname "$1")"
|
||||
|
||||
# Ignore specific executables
|
||||
if [[ "$exe_name" == "iscriptevaluator.exe" ]] || \
|
||||
[[ "$exe_name" == "d3ddriverquery64.exe" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Copy PPDB file from steam_scripts to exe directory if it exists
|
||||
# Steam passes the appid via SteamAppId environment variable
|
||||
if [[ -n "$SteamAppId" ]]; then
|
||||
# Get PortProton directory from config file or Flatpak defaults
|
||||
portproton_dir=""
|
||||
new_config_file="$HOME/.config/PortProtonQt.conf"
|
||||
legacy_config_file="$HOME/.config/PortProton.conf"
|
||||
new_flatpak_id="ru.linux_gaming.PortProtonQt"
|
||||
legacy_flatpak_id="ru.linux_gaming.PortProton"
|
||||
new_flatpak_config_file="$HOME/.var/app/$new_flatpak_id/config/PortProtonQt.conf"
|
||||
new_flatpak_dir="$HOME/.var/app/$new_flatpak_id/data"
|
||||
legacy_flatpak_dir="$HOME/.var/app/$legacy_flatpak_id/data"
|
||||
|
||||
if [[ -f "$new_config_file" ]]; then
|
||||
portproton_dir="$(awk -F'= ' '/^portdata_path/ {print $2; exit}' "$new_config_file" | tr -d '\n')"
|
||||
fi
|
||||
|
||||
if [[ -z "$portproton_dir" ]] || [[ ! -d "$portproton_dir" ]]; then
|
||||
if [[ -f "$legacy_config_file" ]]; then
|
||||
portproton_dir="$(cat "$legacy_config_file" | tr -d '\n')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$portproton_dir" ]] || [[ ! -d "$portproton_dir" ]]; then
|
||||
if [[ -f "$new_flatpak_config_file" ]]; then
|
||||
portproton_dir="$(awk -F'= ' '/^portdata_path/ {print $2; exit}' "$new_flatpak_config_file" | tr -d '\n')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$portproton_dir" ]] || [[ ! -d "$portproton_dir" ]]; then
|
||||
if [[ -d "$new_flatpak_dir" ]]; then
|
||||
portproton_dir="$new_flatpak_dir"
|
||||
elif [[ -d "$legacy_flatpak_dir" ]]; then
|
||||
portproton_dir="$legacy_flatpak_dir"
|
||||
fi
|
||||
fi
|
||||
|
||||
ppdb_source="$portproton_dir/steam_scripts/${SteamAppId}.exe.ppdb"
|
||||
ppdb_dest="$exe_dir/${exe_name}.ppdb"
|
||||
|
||||
if [[ -f "$ppdb_source" ]]; then
|
||||
cp "$ppdb_source" "$ppdb_dest"
|
||||
echo "Copied PPDB from $ppdb_source to $ppdb_dest"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Activate virtual environment if available
|
||||
if [[ -n "$PPQT_VENV_PATH" ]]; then
|
||||
cd "$PPQT_VENV_PATH" || exit 1
|
||||
source .venv/bin/activate || exit 1
|
||||
fi
|
||||
|
||||
# Use AppImage if specified, then Flatpak if installed, otherwise use portprotonqt from PATH
|
||||
if [[ -n "$PPQT_BIN_PATH" ]]; then
|
||||
"$PPQT_BIN_PATH" --silent --debug-level INFO "$@"
|
||||
elif command -v flatpak >/dev/null 2>&1 && flatpak info ru.linux_gaming.PortProtonQt >/dev/null 2>&1; then
|
||||
flatpak run ru.linux_gaming.PortProtonQt --silent --debug-level INFO "$@"
|
||||
else
|
||||
portprotonqt --silent --debug-level INFO "$@"
|
||||
fi
|
||||
@@ -1,4 +0,0 @@
|
||||
"manifest"
|
||||
{
|
||||
"commandline" "/portproton"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
portprotonqt (1.0) unstable; urgency=medium
|
||||
|
||||
* Add Debian packaging.
|
||||
|
||||
-- Ivan Mazhukin <vanyamajukin@outlook.com> Sat, 23 May 2026 13:32:57 +0300
|
||||
@@ -0,0 +1 @@
|
||||
completions/
|
||||
@@ -0,0 +1,70 @@
|
||||
Source: portprotonqt
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Ivan Mazhukin <vanyamajukin@outlook.com>
|
||||
Build-Depends:
|
||||
debhelper-compat (= 13),
|
||||
gettext,
|
||||
libvulkan-dev,
|
||||
meson,
|
||||
ninja-build,
|
||||
pkgconf,
|
||||
python3,
|
||||
Standards-Version: 4.7.0
|
||||
Homepage: https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: portprotonqt
|
||||
Architecture: linux-any
|
||||
Depends:
|
||||
7zip,
|
||||
cabextract,
|
||||
curl,
|
||||
file,
|
||||
gawk,
|
||||
jq,
|
||||
libimage-exiftool-perl,
|
||||
libsdl3-0,
|
||||
mesa-utils,
|
||||
pciutils,
|
||||
procps,
|
||||
psmisc,
|
||||
python3,
|
||||
python3-babel,
|
||||
python3-dbus-fast,
|
||||
python3-evdev,
|
||||
python3-libarchive-c,
|
||||
python3-orjson,
|
||||
python3-pefile,
|
||||
python3-pil,
|
||||
python3-psutil,
|
||||
python3-pygame,
|
||||
python3-pyside6.qtcore,
|
||||
python3-pyside6.qtgui,
|
||||
python3-pyside6.qtnetwork,
|
||||
python3-pyside6.qtsvg,
|
||||
python3-pyside6.qtwidgets,
|
||||
python3-qrcode,
|
||||
python3-rapidfuzz,
|
||||
python3-requests,
|
||||
python3-tqdm,
|
||||
python3-vdf,
|
||||
python3-websocket,
|
||||
qt6-image-formats-plugins,
|
||||
qt6-svg-plugins,
|
||||
unrar-free,
|
||||
unzip,
|
||||
xz-utils,
|
||||
zstd,
|
||||
${misc:Depends},
|
||||
${shlibs:Depends},
|
||||
Recommends:
|
||||
bluez,
|
||||
network-manager,
|
||||
pulseaudio-utils,
|
||||
squashfs-tools,
|
||||
udisks2,
|
||||
upower,
|
||||
Description: GUI for managing PortProton and Steam games
|
||||
PortProtonQt provides a graphical interface for managing and launching
|
||||
games from PortProton and Steam.
|
||||
@@ -0,0 +1,20 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: PortProtonQt
|
||||
Source: https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt
|
||||
|
||||
Files: *
|
||||
Copyright:
|
||||
2026 PortProtonQt contributors
|
||||
License: GPL-3.0
|
||||
|
||||
Files: debian/*
|
||||
Copyright:
|
||||
2026 PortProtonQt contributors
|
||||
License: GPL-3.0
|
||||
|
||||
License: GPL-3.0
|
||||
This package is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License version 3.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
version 3 can be found in /usr/share/common-licenses/GPL-3.
|
||||
@@ -0,0 +1,14 @@
|
||||
usr/bin/portprotonqt
|
||||
usr/bin/vk_gpu_info
|
||||
usr/lib/python3/dist-packages/portprotonqt/
|
||||
usr/lib/udev/rules.d/60-portprotonqt.rules
|
||||
usr/share/applications/ru.linux_gaming.PortProtonQt.desktop
|
||||
usr/share/bash-completion/completions/portprotonqt
|
||||
usr/share/fish/vendor_completions.d/portprotonqt.fish
|
||||
usr/share/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
|
||||
usr/share/locale/
|
||||
usr/share/metainfo/ru.linux_gaming.PortProtonQt.metainfo.xml
|
||||
usr/share/mime/packages/ru.linux_gaming.PortProtonQt.xml
|
||||
usr/share/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules
|
||||
usr/share/portproton/
|
||||
usr/share/zsh/site-functions/_portprotonqt
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=meson
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- \
|
||||
-Dpython_purelibdir=/usr/lib/python3/dist-packages \
|
||||
-Dudev_rulesdir=/usr/lib/udev/rules.d
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install --destdir=debian/tmp
|
||||
|
||||
execute_after_dh_auto_install:
|
||||
bash ./dev-scripts/generate-completions.sh
|
||||
install -Dpm 0644 completions/portprotonqt \
|
||||
debian/tmp/usr/share/bash-completion/completions/portprotonqt
|
||||
install -Dpm 0644 completions/_portprotonqt \
|
||||
debian/tmp/usr/share/zsh/site-functions/_portprotonqt
|
||||
install -Dpm 0644 completions/portprotonqt.fish \
|
||||
debian/tmp/usr/share/fish/vendor_completions.d/portprotonqt.fish
|
||||
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
||||
@@ -77,3 +77,5 @@ State
|
||||
HLTB
|
||||
Delete Folder
|
||||
PPQT
|
||||
SpecialK
|
||||
Reshade
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
remote="${1:-origin}"
|
||||
main_branch="${2:-main}"
|
||||
weblate_branch="${3:-weblate}"
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "Working tree is not clean."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git fetch "$remote" "$main_branch" "$weblate_branch"
|
||||
git switch "$main_branch"
|
||||
git merge --ff-only "$remote/$main_branch"
|
||||
|
||||
if git merge-base --is-ancestor "$remote/$weblate_branch" HEAD; then
|
||||
echo "No new Weblate changes to merge."
|
||||
elif git merge-base --is-ancestor HEAD "$remote/$weblate_branch"; then
|
||||
git merge --ff-only "$remote/$weblate_branch"
|
||||
else
|
||||
git switch -C "$weblate_branch" "$remote/$weblate_branch"
|
||||
git rebase "$main_branch"
|
||||
git switch "$main_branch"
|
||||
git merge --ff-only "$weblate_branch"
|
||||
fi
|
||||
|
||||
python dev-scripts/l10n.py
|
||||
|
||||
git push "$remote" "$main_branch"
|
||||
git push "$remote" "$main_branch:$weblate_branch"
|
||||
|
||||
echo "Weblate translations accepted and $weblate_branch synced to $main_branch."
|
||||
@@ -18,6 +18,7 @@ MESON_BUILD = BASE_DIR / "meson.build"
|
||||
APP_PY = BASE_DIR / "portprotonqt" / "app.py"
|
||||
GITEA_WORKFLOW = BASE_DIR / ".gitea" / "workflows" / "build.yml"
|
||||
CHANGELOG = BASE_DIR / "CHANGELOG.md"
|
||||
METAINFO = BASE_DIR / "build-aux" / "share" / "metainfo" / "ru.linux_gaming.PortProtonQt.metainfo.xml"
|
||||
|
||||
def bump_appimage(path: Path, old: str, new: str) -> bool:
|
||||
"""
|
||||
@@ -125,6 +126,48 @@ def bump_changelog(path: Path, old: str, new: str) -> bool:
|
||||
path.write_text(new_text, encoding='utf-8')
|
||||
return bool(count)
|
||||
|
||||
def bump_metainfo(path: Path, old: str, new: str) -> bool:
|
||||
"""
|
||||
Update releases in metainfo:
|
||||
1. Change old version's <description></description> to <description/>
|
||||
2. Prepend new version with <description></description>
|
||||
"""
|
||||
if not path.exists():
|
||||
return False
|
||||
text = path.read_text(encoding='utf-8')
|
||||
|
||||
# 1. Update the old version entry to use self-closing description
|
||||
# Match tag starting with <release version="OLD" and ending with >
|
||||
old_pattern = re.compile(
|
||||
r'(<release version="' + re.escape(old) + r'"[^>]*>)\s*<description></description>\s*(</release>)',
|
||||
re.DOTALL
|
||||
)
|
||||
|
||||
current_date = date.today().strftime('%Y-%m-%d')
|
||||
type_attr = ' type="development"' if new.startswith('0.') else ''
|
||||
new_entry = (
|
||||
f' <release version="{new}" date="{current_date}"{type_attr}>\n'
|
||||
f' <description></description>\n'
|
||||
f' </release>'
|
||||
)
|
||||
|
||||
# Replace old entry's description with self-closing
|
||||
new_text, count = old_pattern.subn(r'\1\n <description/>\n \2', text, count=1)
|
||||
|
||||
if count:
|
||||
# 2. Insert the new entry at the beginning of the <releases> tag
|
||||
new_text = new_text.replace('<releases>', f'<releases>\n{new_entry}')
|
||||
path.write_text(new_text, encoding='utf-8')
|
||||
return True
|
||||
|
||||
# Fallback if old version wasn't found with expected pattern
|
||||
if f'version="{new}"' not in text:
|
||||
new_text = text.replace('<releases>', f'<releases>\n{new_entry}')
|
||||
path.write_text(new_text, encoding='utf-8')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Bump project version in specific files')
|
||||
parser.add_argument('old', help='Old version string')
|
||||
@@ -141,7 +184,8 @@ def main():
|
||||
(MESON_BUILD, bump_meson),
|
||||
(APP_PY, bump_app_py),
|
||||
(GITEA_WORKFLOW, bump_workflow),
|
||||
(CHANGELOG, bump_changelog)
|
||||
(CHANGELOG, bump_changelog),
|
||||
(METAINFO, bump_metainfo)
|
||||
]
|
||||
|
||||
updated = []
|
||||
|
||||
@@ -654,13 +654,14 @@ class MesonLinter:
|
||||
def get_python_files_from_directory(directory):
|
||||
"""Get all Python files from a directory (excluding __pycache__ and other non-source files)"""
|
||||
python_files = []
|
||||
for file_path in Path(directory).iterdir():
|
||||
base_dir = Path(directory)
|
||||
for file_path in base_dir.rglob('*.py'):
|
||||
if (file_path.is_file() and
|
||||
file_path.suffix == '.py' and
|
||||
file_path.name != '__pycache__' and
|
||||
'__pycache__' not in file_path.parts and
|
||||
not file_path.name.startswith('.') and # Skip hidden files
|
||||
file_path.name != 'meson.build'): # Exclude meson.build itself
|
||||
python_files.append(file_path.name)
|
||||
python_files.append(file_path.relative_to(base_dir).as_posix())
|
||||
return sorted(python_files)
|
||||
|
||||
|
||||
@@ -747,8 +748,28 @@ def check_files(repo_root: Path) -> list[str]:
|
||||
|
||||
dir_files = get_python_files_from_directory(portprotonqt_dir)
|
||||
meson_files = get_files_from_meson_build(portprotonqt_meson)
|
||||
meson_subdirs = get_subdirs_from_meson_build(portprotonqt_meson)
|
||||
|
||||
return [f for f in dir_files if f not in meson_files]
|
||||
return [
|
||||
f for f in dir_files
|
||||
if f not in meson_files and
|
||||
not any(f == subdir or f.startswith(f"{subdir}/") for subdir in meson_subdirs)
|
||||
]
|
||||
|
||||
|
||||
def check_stale_files(repo_root: Path) -> list[str]:
|
||||
"""Check if meson.build lists Python files that no longer exist."""
|
||||
portprotonqt_dir = repo_root / 'portprotonqt'
|
||||
portprotonqt_meson = portprotonqt_dir / 'meson.build'
|
||||
|
||||
if not portprotonqt_meson.exists():
|
||||
return []
|
||||
|
||||
meson_files = get_files_from_meson_build(portprotonqt_meson)
|
||||
return [
|
||||
f for f in meson_files
|
||||
if f.endswith('.py') and not (portprotonqt_dir / f).exists()
|
||||
]
|
||||
|
||||
|
||||
def check_portproton_subdirs(repo_root: Path) -> list[str]:
|
||||
@@ -796,6 +817,7 @@ def main():
|
||||
# Check files inclusion (only if syntax is OK)
|
||||
if exit_code == 0:
|
||||
missing_files = check_files(repo_root)
|
||||
stale_files = check_stale_files(repo_root)
|
||||
missing_subdirs = check_portproton_subdirs(repo_root)
|
||||
|
||||
if missing_files:
|
||||
@@ -805,6 +827,13 @@ def main():
|
||||
print("\nPlease add these files to the install_data section in portprotonqt/meson.build")
|
||||
exit_code = 1
|
||||
|
||||
if stale_files:
|
||||
print("ERROR: The following files are listed in meson.build but do not exist:")
|
||||
for file in stale_files:
|
||||
print(f" - {file}")
|
||||
print("\nPlease remove or update these files in portprotonqt/meson.build")
|
||||
exit_code = 1
|
||||
|
||||
if missing_subdirs:
|
||||
print("ERROR: The following PortProton directories are missing from meson.build:")
|
||||
for directory in missing_subdirs:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
project('portprotonqt', 'c',
|
||||
version: '1.0',
|
||||
version: '1.2.0',
|
||||
meson_version: '>= 0.61.2',
|
||||
license: 'GPL-3.0',
|
||||
)
|
||||
@@ -33,7 +33,7 @@ bindir = prefix / get_option('bindir')
|
||||
|
||||
# Use custom purelibdir if specified, otherwise use default
|
||||
purelibdir_opt = get_option('python_purelibdir')
|
||||
pythondir = purelibdir_opt != '' ? purelibdir_opt : python3.get_install_dir(pure: true)
|
||||
pythondir = purelibdir_opt != '' ? purelibdir_opt: python3.get_install_dir(pure: true)
|
||||
pkgdatadir = pythondir / meson.project_name()
|
||||
|
||||
# C compiler for vk_gpu_info
|
||||
@@ -131,12 +131,9 @@ install_subdir(
|
||||
install_mode: 'rwxr-xr-x',
|
||||
)
|
||||
|
||||
# Install Steam compatibility tool
|
||||
subdir('build-aux/share/steam/compatibilitytools.d/PortProtonQt')
|
||||
|
||||
# Install udev rules
|
||||
udev_rulesdir_opt = get_option('udev_rulesdir')
|
||||
udevdir = udev_rulesdir_opt != '' ? udev_rulesdir_opt : prefix / 'lib' / 'udev' / 'rules.d'
|
||||
udevdir = udev_rulesdir_opt != '' ? udev_rulesdir_opt: prefix / 'lib' / 'udev' / 'rules.d'
|
||||
|
||||
install_data(
|
||||
'build-aux/lib/udev/rules.d/60-portprotonqt.rules',
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from portprotonqt.animations.detail_page import DetailPageAnimations
|
||||
from portprotonqt.animations.game_card import GameCardAnimations
|
||||
from portprotonqt.animations.library_controls import ExpandingSearchAnimation, LibraryControlsAnimation
|
||||
from portprotonqt.animations.virtual_keyboard import VirtualKeyboardAnimations
|
||||
|
||||
__all__ = [
|
||||
"DetailPageAnimations",
|
||||
"ExpandingSearchAnimation",
|
||||
"GameCardAnimations",
|
||||
"LibraryControlsAnimation",
|
||||
"VirtualKeyboardAnimations",
|
||||
]
|
||||
@@ -0,0 +1,117 @@
|
||||
from PySide6.QtCore import (
|
||||
QAbstractAnimation,
|
||||
QByteArray,
|
||||
QEasingCurve,
|
||||
QParallelAnimationGroup,
|
||||
QPropertyAnimation,
|
||||
)
|
||||
from PySide6.QtWidgets import QGraphicsOpacityEffect, QWidget
|
||||
|
||||
|
||||
class ExpandingSearchAnimation:
|
||||
def __init__(self, widget: QWidget, theme: object, fallback_duration: int):
|
||||
self.widget = widget
|
||||
self.theme = theme
|
||||
self.fallback_duration = fallback_duration
|
||||
self.animation = QPropertyAnimation(widget, QByteArray(b"maximumWidth"))
|
||||
|
||||
def setup(self, collapsed_width: int, expanded_width: int) -> None:
|
||||
self.collapsed_width = collapsed_width
|
||||
self.expanded_width = expanded_width
|
||||
self.widget.setMaximumWidth(collapsed_width)
|
||||
|
||||
def expand(self) -> None:
|
||||
self._start(self.expanded_width)
|
||||
|
||||
def collapse(self) -> None:
|
||||
if getattr(self.widget, "text", lambda: "")():
|
||||
return
|
||||
self._start(self.collapsed_width)
|
||||
|
||||
def _start(self, target_width: int) -> None:
|
||||
if self.animation.state() == QAbstractAnimation.State.Running:
|
||||
self.animation.stop()
|
||||
self.animation = QPropertyAnimation(self.widget, QByteArray(b"maximumWidth"))
|
||||
self.animation.setDuration(self._duration())
|
||||
self.animation.setStartValue(self.widget.maximumWidth())
|
||||
self.animation.setEndValue(target_width)
|
||||
self.animation.setEasingCurve(self._easing(target_width == self.expanded_width))
|
||||
self.animation.start()
|
||||
|
||||
def _duration(self) -> int:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
return animation_config.get("scale_anim_duration", self.fallback_duration)
|
||||
|
||||
def _easing(self, opening: bool) -> QEasingCurve:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
key = "scale_easing_curve" if opening else "scale_easing_curve_out"
|
||||
easing_name = animation_config.get(key, QEasingCurve.Type.InOutQuad.name)
|
||||
easing_type = getattr(QEasingCurve.Type, easing_name, QEasingCurve.Type.InOutQuad)
|
||||
return QEasingCurve(easing_type)
|
||||
|
||||
|
||||
class LibraryControlsAnimation:
|
||||
def __init__(self, widget: QWidget, theme: object, fallback_duration: int):
|
||||
self.widget = widget
|
||||
self.theme = theme
|
||||
self.fallback_duration = fallback_duration
|
||||
self.opacity_effect = QGraphicsOpacityEffect(widget)
|
||||
self.opacity_effect.setOpacity(0)
|
||||
self.widget.setGraphicsEffect(self.opacity_effect)
|
||||
self.group = QParallelAnimationGroup(widget)
|
||||
|
||||
def setup_hidden(self) -> None:
|
||||
self.widget.setVisible(False)
|
||||
self.widget.setMaximumHeight(0)
|
||||
|
||||
def toggle(self, opening: bool) -> None:
|
||||
if self.group.state() == QAbstractAnimation.State.Running:
|
||||
self.group.stop()
|
||||
if opening:
|
||||
self.widget.setVisible(True)
|
||||
|
||||
target_height = self.widget.sizeHint().height() if opening else 0
|
||||
self.group = QParallelAnimationGroup(self.widget)
|
||||
height_animation = self._create_widget_animation(
|
||||
b"maximumHeight",
|
||||
self.widget.maximumHeight(),
|
||||
target_height,
|
||||
)
|
||||
opacity_animation = self._create_opacity_animation(opening, height_animation.duration())
|
||||
self.group.addAnimation(height_animation)
|
||||
self.group.addAnimation(opacity_animation)
|
||||
if not opening:
|
||||
self.group.finished.connect(self.widget.hide)
|
||||
self.group.start()
|
||||
|
||||
def _create_widget_animation(
|
||||
self,
|
||||
property_name: bytes,
|
||||
start_value: object,
|
||||
end_value: object,
|
||||
) -> QPropertyAnimation:
|
||||
animation = QPropertyAnimation(self.widget, QByteArray(property_name))
|
||||
animation.setDuration(self._duration())
|
||||
animation.setStartValue(start_value)
|
||||
animation.setEndValue(end_value)
|
||||
animation.setEasingCurve(self._easing(end_value != 0))
|
||||
return animation
|
||||
|
||||
def _create_opacity_animation(self, opening: bool, duration: int) -> QPropertyAnimation:
|
||||
animation = QPropertyAnimation(self.opacity_effect, QByteArray(b"opacity"))
|
||||
animation.setDuration(duration)
|
||||
animation.setStartValue(self.opacity_effect.opacity())
|
||||
animation.setEndValue(1 if opening else 0)
|
||||
animation.setEasingCurve(self._easing(opening))
|
||||
return animation
|
||||
|
||||
def _duration(self) -> int:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
return animation_config.get("scale_anim_duration", self.fallback_duration)
|
||||
|
||||
def _easing(self, opening: bool) -> QEasingCurve:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
key = "scale_easing_curve" if opening else "scale_easing_curve_out"
|
||||
easing_name = animation_config.get(key, QEasingCurve.Type.InOutQuad.name)
|
||||
easing_type = getattr(QEasingCurve.Type, easing_name, QEasingCurve.Type.InOutQuad)
|
||||
return QEasingCurve(easing_type)
|
||||
@@ -3,6 +3,10 @@ import sys
|
||||
import os
|
||||
import importlib
|
||||
import subprocess
|
||||
import threading
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from urllib.parse import quote, unquote
|
||||
from logging import Logger
|
||||
|
||||
__app_id__ = "ru.linux_gaming.PortProtonQt"
|
||||
@@ -14,7 +18,7 @@ try:
|
||||
APP_VERSION = version_module.APP_VERSION
|
||||
except ImportError:
|
||||
APP_COMMIT = ""
|
||||
APP_VERSION = "1.0"
|
||||
APP_VERSION = "1.2.0"
|
||||
|
||||
__app_version__ = APP_VERSION
|
||||
|
||||
@@ -23,11 +27,13 @@ from PySide6.QtWidgets import QApplication, QMenu, QSystemTrayIcon
|
||||
from PySide6.QtGui import QAction, QIcon
|
||||
|
||||
from portprotonqt.config import (
|
||||
consume_download_counter_skip,
|
||||
display_config,
|
||||
get_portproton_start_command,
|
||||
get_portproton_location,
|
||||
save_portdata_path_to_config,
|
||||
ui_config,
|
||||
update_app_version,
|
||||
window_config,
|
||||
)
|
||||
from portprotonqt.logger import get_logger, setup_logger
|
||||
@@ -37,7 +43,6 @@ from portprotonqt.cli import (
|
||||
parse_portproton_url,
|
||||
is_launch_file,
|
||||
is_prefix_backup_file,
|
||||
is_exe_file,
|
||||
normalize_launch_path,
|
||||
add_steam_compat_tool,
|
||||
reinstall_steam_compat_tool,
|
||||
@@ -48,6 +53,8 @@ from portprotonqt.cli import (
|
||||
)
|
||||
from portprotonqt.localization import _, get_steam_language
|
||||
|
||||
COUNTER_DOWNLOAD_URL = "http://cloud.linux-gaming.ru:8081/api/download/{version}"
|
||||
|
||||
def get_version():
|
||||
if APP_COMMIT:
|
||||
return f"{__app_version__} ({APP_COMMIT})"
|
||||
@@ -142,6 +149,22 @@ def restore_prefix_backup(start_sh: list[str], backup_path: str) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
def notify_download_counter(app_version: str, logger: Logger) -> None:
|
||||
url = COUNTER_DOWNLOAD_URL.format(version=quote(app_version, safe=""))
|
||||
|
||||
def send_request() -> None:
|
||||
try:
|
||||
request = urllib.request.Request(url, method="GET")
|
||||
with urllib.request.urlopen(request, timeout=10):
|
||||
pass
|
||||
except urllib.error.URLError as e:
|
||||
logger.debug("Failed to notify download counter: %s", e)
|
||||
except OSError as e:
|
||||
logger.debug("Failed to notify download counter: %s", e)
|
||||
|
||||
threading.Thread(target=send_request, daemon=True).start()
|
||||
|
||||
|
||||
def create_prefix_backup(start_sh: list[str], prefix_name: str, backup_dir: str) -> int:
|
||||
"""Create a PortProton prefix backup."""
|
||||
logger = get_logger(__name__)
|
||||
@@ -203,19 +226,9 @@ def main():
|
||||
if start_sh is None:
|
||||
return
|
||||
|
||||
if parsed_args.create_backup:
|
||||
setup_logger(parsed_args.debug_level)
|
||||
prefix_name, backup_dir = parsed_args.create_backup
|
||||
backup_exit_code = create_prefix_backup(start_sh, prefix_name, backup_dir)
|
||||
sys.exit(backup_exit_code)
|
||||
|
||||
if parsed_args.restore_prefix and not parsed_args.file_or_url:
|
||||
setup_logger(parsed_args.debug_level)
|
||||
sys.exit(1)
|
||||
if is_restore_prefix_request(parsed_args):
|
||||
setup_logger(parsed_args.debug_level)
|
||||
restore_exit_code = restore_prefix_backup(start_sh, parsed_args.file_or_url)
|
||||
sys.exit(restore_exit_code)
|
||||
|
||||
# Handle Steam compatibility mode - launch game directly without GUI.
|
||||
if is_steam_compat:
|
||||
@@ -227,10 +240,11 @@ def main():
|
||||
logger = get_logger(__name__)
|
||||
setup_logger(parsed_args.debug_level)
|
||||
logger.info("Running in Steam compatibility mode, launching: %s", exe_path)
|
||||
logger.info("Steam compatibility launch arguments: %s", parsed_args.launch_args)
|
||||
|
||||
# Launch game via PortProton without GUI
|
||||
env_vars = os.environ.copy()
|
||||
cmd = start_sh + [exe_path]
|
||||
cmd = start_sh + [exe_path] + parsed_args.launch_args
|
||||
try:
|
||||
subprocess.run(cmd, env=env_vars)
|
||||
except Exception as e:
|
||||
@@ -254,9 +268,15 @@ def main():
|
||||
args = parsed_args
|
||||
setup_logger(args.debug_level)
|
||||
logger = get_logger(__name__)
|
||||
if consume_download_counter_skip():
|
||||
update_app_version(__app_version__)
|
||||
elif update_app_version(__app_version__):
|
||||
notify_download_counter(__app_version__, logger)
|
||||
|
||||
fullscreen = args.fullscreen or display_config.get_fullscreen()
|
||||
ipc_message = "show:fullscreen" if fullscreen else "show"
|
||||
backup_request = None
|
||||
restore_prefix_path = None
|
||||
resolution_from_args = None
|
||||
if args.resolution:
|
||||
resolution_from_args = parse_resolution(args.resolution)
|
||||
@@ -266,16 +286,23 @@ def main():
|
||||
window_config.set_geometry(resolution_from_args[0], resolution_from_args[1])
|
||||
logger.info("Saved window resolution: %sx%s", resolution_from_args[0], resolution_from_args[1])
|
||||
ipc_message = "noop"
|
||||
if is_silent_launch and args.file_or_url and is_exe_file(args.file_or_url):
|
||||
if args.create_backup:
|
||||
prefix_name, backup_dir = args.create_backup
|
||||
backup_request = (prefix_name, os.path.abspath(os.path.expanduser(backup_dir)))
|
||||
ipc_message = "backup:{}:{}".format(quote(prefix_name, safe=""), quote(backup_request[1], safe=""))
|
||||
elif is_silent_launch and args.file_or_url and is_launch_file(args.file_or_url):
|
||||
run_silent_tray(app, start_sh, normalize_launch_path(args.file_or_url))
|
||||
sys.exit(app.exec())
|
||||
elif is_restore_prefix_request(args):
|
||||
restore_prefix_path = normalize_launch_path(args.file_or_url)
|
||||
ipc_message = f"restore:{quote(restore_prefix_path, safe='')}"
|
||||
elif args.file_or_url and is_launch_file(args.file_or_url):
|
||||
ipc_message = f"open:{normalize_launch_path(args.file_or_url)}"
|
||||
|
||||
from PySide6.QtCore import QThread, Signal
|
||||
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
||||
from portprotonqt.main_window import MainWindow
|
||||
from portprotonqt.port_data_path_selector import ask_portdata_path
|
||||
from portprotonqt.port_data_path_selector import ask_portdata_path, is_portdata_path_read_write
|
||||
from portprotonqt.portproton_api import (
|
||||
PortProtonAPI,
|
||||
get_user_conf_setting,
|
||||
@@ -309,8 +336,14 @@ def main():
|
||||
logger.warning(f"Failed to start local server: {local_server.errorString()}")
|
||||
return
|
||||
|
||||
portdata_warning = None
|
||||
if portproton_location and not is_portdata_path_read_write(portproton_location):
|
||||
logger.warning("PORT_DATA_PATH is not readable/writable: %s", portproton_location)
|
||||
portdata_warning = _("PortProton data folder is not readable and writable. Choose another folder for PortProton data.")
|
||||
portproton_location = None
|
||||
|
||||
if not portproton_location:
|
||||
portproton_location = ask_portdata_path()
|
||||
portproton_location = ask_portdata_path(portdata_warning, bool(portdata_warning))
|
||||
if not portproton_location:
|
||||
logger.error("PORT_DATA_PATH is not configured, startup aborted")
|
||||
return
|
||||
@@ -319,7 +352,7 @@ def main():
|
||||
logger.warning("Failed to persist PORT_DATA_PATH in PortProtonQt config")
|
||||
|
||||
# Check if we have a portproton:// URL or launch file to handle
|
||||
if args.file_or_url:
|
||||
if args.file_or_url and not restore_prefix_path:
|
||||
if is_portproton_url(args.file_or_url):
|
||||
# Parse the portproton:// URL to get the full download URL
|
||||
download_url = parse_portproton_url(args.file_or_url)
|
||||
@@ -366,6 +399,14 @@ def main():
|
||||
def handle_launch_exe():
|
||||
window.handle_launch_exe(exe_path)
|
||||
QTimer.singleShot(0, handle_launch_exe)
|
||||
elif backup_request:
|
||||
def handle_create_backup():
|
||||
window._perform_backup(backup_request[1], backup_request[0])
|
||||
QTimer.singleShot(0, handle_create_backup)
|
||||
elif restore_prefix_path:
|
||||
def handle_restore_prefix():
|
||||
window._perform_restore(restore_prefix_path)
|
||||
QTimer.singleShot(0, handle_restore_prefix)
|
||||
|
||||
# --- Handle incoming connections ---
|
||||
def handle_new_connection():
|
||||
@@ -380,7 +421,12 @@ def main():
|
||||
|
||||
def restore_window():
|
||||
try:
|
||||
if msg.startswith("show") or msg.startswith("open:"):
|
||||
if (
|
||||
msg.startswith("show")
|
||||
or msg.startswith("open:")
|
||||
or msg.startswith("restore:")
|
||||
or msg.startswith("backup:")
|
||||
):
|
||||
# Ensure the window is visible and not minimized
|
||||
window.setWindowState(window.windowState() & ~Qt.WindowState.WindowMinimized)
|
||||
window.show()
|
||||
@@ -407,6 +453,22 @@ def main():
|
||||
window.handle_launch_exe(launch_path)
|
||||
else:
|
||||
logger.warning("Invalid launch file via IPC: %s", launch_path)
|
||||
elif msg.startswith("restore:"):
|
||||
backup_path = unquote(msg[8:].strip())
|
||||
if backup_path and is_prefix_backup_file(backup_path):
|
||||
logger.info("Restoring prefix backup via IPC: %s", backup_path)
|
||||
window._perform_restore(backup_path)
|
||||
else:
|
||||
logger.warning("Invalid prefix backup via IPC: %s", backup_path)
|
||||
elif msg.startswith("backup:"):
|
||||
parts = msg.split(":", 2)
|
||||
if len(parts) == 3:
|
||||
prefix_name = unquote(parts[1])
|
||||
backup_dir = unquote(parts[2])
|
||||
logger.info("Creating prefix backup via IPC: %s", prefix_name)
|
||||
window._perform_backup(backup_dir, prefix_name)
|
||||
else:
|
||||
logger.warning("Invalid prefix backup request via IPC: %s", msg)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to restore window: {e}")
|
||||
|
||||
@@ -419,12 +481,20 @@ def main():
|
||||
|
||||
# --- Initial fullscreen state ---
|
||||
launch_fullscreen = args.fullscreen or display_config.get_fullscreen()
|
||||
launch_auto_fullscreen = (
|
||||
display_config.get_auto_fullscreen_gamepad()
|
||||
and not launch_fullscreen
|
||||
and getattr(window.input_manager, "gamepad", None) is not None
|
||||
)
|
||||
launch_minimized = (
|
||||
display_config.get_start_minimized()
|
||||
and not args.fullscreen
|
||||
and not launch_auto_fullscreen
|
||||
and window_resolution is None
|
||||
and exe_path is None
|
||||
)
|
||||
and backup_request is None
|
||||
and restore_prefix_path is None
|
||||
)
|
||||
if launch_minimized:
|
||||
logger.info("Launching in tray")
|
||||
window.hide()
|
||||
@@ -434,6 +504,10 @@ def main():
|
||||
)
|
||||
display_config.set_fullscreen(True)
|
||||
window.showFullScreen()
|
||||
elif launch_auto_fullscreen:
|
||||
logger.info("Launching in fullscreen mode (gamepad)")
|
||||
window.input_manager.handle_fullscreen_slot(True)
|
||||
window.updateControlHints("force")
|
||||
elif window_resolution:
|
||||
logger.info(f"Launching with resolution: {window_resolution[0]}x{window_resolution[1]}")
|
||||
window.resize(window_resolution[0], window_resolution[1])
|
||||
|
||||
@@ -2,11 +2,12 @@ import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from portprotonqt.steam_api import get_steam_home
|
||||
|
||||
LAUNCH_FILE_EXTENSIONS = ('.exe', '.iso', '.mdf')
|
||||
LAUNCH_FILE_EXTENSIONS = ('.exe', '.bat', '.cmd', '.msi', '.reg', '.iso', '.mdf', '.nrg')
|
||||
PREFIX_BACKUP_EXTENSION = '.ppack'
|
||||
|
||||
|
||||
@@ -65,7 +66,7 @@ def parse_args():
|
||||
parser.add_argument(
|
||||
"--silent",
|
||||
action="store_true",
|
||||
help="Launch .exe file in tray without showing the main window"
|
||||
help="Launch supported Windows file in tray without showing the main window"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--restore-prefix",
|
||||
@@ -82,9 +83,25 @@ def parse_args():
|
||||
parser.add_argument(
|
||||
'file_or_url',
|
||||
nargs='?',
|
||||
help="Launch file path (.exe/.iso/.mdf) or portproton:// URL"
|
||||
help="Launch file path (.exe/.bat/.cmd/.msi/.reg/.iso/.mdf/.nrg) or portproton:// URL"
|
||||
)
|
||||
return parser.parse_args()
|
||||
if os.environ.get("STEAM_COMPAT") == "1" and "--" in sys.argv[1:]:
|
||||
separator_index = sys.argv.index("--")
|
||||
args = parser.parse_args(sys.argv[1:separator_index])
|
||||
steam_args = sys.argv[separator_index + 1:]
|
||||
if steam_args:
|
||||
args.file_or_url = steam_args[0]
|
||||
launch_args = steam_args[1:]
|
||||
else:
|
||||
launch_args = []
|
||||
elif os.environ.get("STEAM_COMPAT") == "1":
|
||||
args, launch_args = parser.parse_known_args()
|
||||
else:
|
||||
args = parser.parse_args()
|
||||
launch_args = []
|
||||
|
||||
args.launch_args = launch_args
|
||||
return args
|
||||
|
||||
|
||||
def add_steam_compat_tool(force_install: bool = False) -> bool:
|
||||
@@ -156,13 +173,41 @@ unset LD_LIBRARY_PATH
|
||||
|
||||
export STEAM_COMPAT=1
|
||||
|
||||
launch_args=("$@")
|
||||
case "${1:-}" in
|
||||
run|waitforexitandrun)
|
||||
launch_args=("${@:2}")
|
||||
;;
|
||||
esac
|
||||
|
||||
exe_name="$(basename "$1")"
|
||||
exe_dir="$(dirname "$1")"
|
||||
launch_target="${launch_args[0]:-}"
|
||||
exe_name="$(basename "$launch_target")"
|
||||
exe_dir="$(dirname "$launch_target")"
|
||||
|
||||
# Ignore specific executables
|
||||
if [[ "$exe_name" == "iscriptevaluator.exe" ]] || \\
|
||||
[[ "$exe_name" == "d3ddriverquery64.exe" ]]; then
|
||||
if [[ "$exe_name" == "iscriptevaluator.exe" ]] \\
|
||||
|| [[ "$exe_name" == "d3ddriverquery64.exe" ]]
|
||||
then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Activate virtual environment if available
|
||||
if [[ -n "$PPQT_VENV_PATH" ]]; then
|
||||
cd "$PPQT_VENV_PATH" || exit 1
|
||||
source .venv/bin/activate || exit 1
|
||||
fi
|
||||
|
||||
if [[ "$UPDATE_CT_PPQT" != "1" ]]; then
|
||||
export UPDATE_CT_PPQT="1"
|
||||
full_command_line=("$(realpath "$0")" "$@")
|
||||
if [[ -n "$PPQT_BIN_PATH" ]]; then
|
||||
"$PPQT_BIN_PATH" --reinstall-steam-compat-tool
|
||||
elif command -v flatpak >/dev/null 2>&1 && flatpak info ru.linux_gaming.PortProtonQt >/dev/null 2>&1; then
|
||||
flatpak run ru.linux_gaming.PortProtonQt --reinstall-steam-compat-tool
|
||||
else
|
||||
portprotonqt --reinstall-steam-compat-tool
|
||||
fi
|
||||
exec "${full_command_line[@]}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -212,19 +257,13 @@ if [[ -n "$SteamAppId" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Activate virtual environment if available
|
||||
if [[ -n "$PPQT_VENV_PATH" ]]; then
|
||||
cd "$PPQT_VENV_PATH" || exit 1
|
||||
source .venv/bin/activate || exit 1
|
||||
fi
|
||||
|
||||
# Use AppImage if specified, then Flatpak if installed, otherwise use portprotonqt from PATH
|
||||
if [[ -n "$PPQT_BIN_PATH" ]]; then
|
||||
"$PPQT_BIN_PATH" --debug-level INFO "$@"
|
||||
"$PPQT_BIN_PATH" --debug-level INFO -- "${launch_args[@]}"
|
||||
elif command -v flatpak >/dev/null 2>&1 && flatpak info ru.linux_gaming.PortProtonQt >/dev/null 2>&1; then
|
||||
flatpak run ru.linux_gaming.PortProtonQt --debug-level INFO "$@"
|
||||
flatpak run ru.linux_gaming.PortProtonQt --debug-level INFO -- "${launch_args[@]}"
|
||||
else
|
||||
portprotonqt --debug-level INFO "$@"
|
||||
portprotonqt --debug-level INFO -- "${launch_args[@]}"
|
||||
fi
|
||||
''')
|
||||
|
||||
@@ -290,9 +329,22 @@ def clear_cache() -> bool:
|
||||
|
||||
def reset_settings() -> bool:
|
||||
"""Reset PortProtonQt settings."""
|
||||
from portprotonqt.config import reset_main_config
|
||||
from portprotonqt.config import (
|
||||
get_portproton_location,
|
||||
reset_main_config,
|
||||
)
|
||||
|
||||
portproton_location = get_portproton_location()
|
||||
reset_main_config()
|
||||
if portproton_location:
|
||||
user_conf_path = Path(portproton_location) / "data" / "user.conf"
|
||||
try:
|
||||
if user_conf_path.is_file():
|
||||
user_conf_path.unlink()
|
||||
except OSError as e:
|
||||
print(f"Failed to delete user.conf: {e}")
|
||||
return False
|
||||
|
||||
print("PortProtonQt settings reset")
|
||||
return True
|
||||
|
||||
|
||||
@@ -4,17 +4,23 @@ from portprotonqt.config.base import (
|
||||
CACHE_DIR,
|
||||
CONFIG_DIR,
|
||||
CONFIG_FILE,
|
||||
COUNTER_SKIP_FILE,
|
||||
PORTPROTON_CONFIG_FILE,
|
||||
THEMES_DIRS,
|
||||
consume_download_counter_skip,
|
||||
reset_main_config,
|
||||
update_app_version,
|
||||
)
|
||||
from portprotonqt.config.cache import CacheConfig, CacheManager
|
||||
from portprotonqt.config.display import DisplayConfig, apply_xdg_autostart
|
||||
from portprotonqt.config.favorites import FavoritesConfig, FavoritesFoldersConfig
|
||||
from portprotonqt.config.favorites import ExeSettingsFavoritesConfig, FavoritesConfig, FavoritesFoldersConfig
|
||||
from portprotonqt.config.game import GameConfig
|
||||
from portprotonqt.config.gamepad import GamepadConfig
|
||||
from portprotonqt.config.portproton import (
|
||||
PortProtonConfig,
|
||||
LAUNCH_FILE_EXTENSIONS,
|
||||
THEMED_LAUNCH_ICON_NAMES,
|
||||
WINDOWS_LAUNCH_EXTENSIONS,
|
||||
create_desktop_file,
|
||||
extract_exec_target_path,
|
||||
find_game_by_exe,
|
||||
@@ -40,6 +46,7 @@ display_config = DisplayConfig()
|
||||
window_config = MainWindowConfig()
|
||||
favorites_config = FavoritesConfig()
|
||||
favorites_folders_config = FavoritesFoldersConfig()
|
||||
exe_settings_favorites_config = ExeSettingsFavoritesConfig()
|
||||
portproton_config = PortProtonConfig()
|
||||
|
||||
__all__ = [
|
||||
@@ -54,8 +61,13 @@ __all__ = [
|
||||
"MainWindowConfig",
|
||||
"FavoritesConfig",
|
||||
"FavoritesFoldersConfig",
|
||||
"ExeSettingsFavoritesConfig",
|
||||
"PortProtonConfig",
|
||||
"LAUNCH_FILE_EXTENSIONS",
|
||||
"THEMED_LAUNCH_ICON_NAMES",
|
||||
"WINDOWS_LAUNCH_EXTENSIONS",
|
||||
"CONFIG_FILE",
|
||||
"COUNTER_SKIP_FILE",
|
||||
"PORTPROTON_CONFIG_FILE",
|
||||
"CONFIG_DIR",
|
||||
"THEMES_DIRS",
|
||||
@@ -70,6 +82,7 @@ __all__ = [
|
||||
"window_config",
|
||||
"favorites_config",
|
||||
"favorites_folders_config",
|
||||
"exe_settings_favorites_config",
|
||||
"portproton_config",
|
||||
"load_theme_metainfo",
|
||||
"apply_xdg_autostart",
|
||||
@@ -83,5 +96,7 @@ __all__ = [
|
||||
"extract_exec_target_path",
|
||||
"find_game_by_exe",
|
||||
"create_desktop_file",
|
||||
"consume_download_counter_skip",
|
||||
"reset_main_config",
|
||||
"update_app_version",
|
||||
]
|
||||
@@ -13,6 +13,7 @@ __all__ = ["configparser"]
|
||||
# Configuration file paths
|
||||
CONFIG_DIR = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
||||
CONFIG_FILE = CONFIG_DIR / "PortProtonQt.conf"
|
||||
COUNTER_SKIP_FILE = CONFIG_DIR / "PortProtonQt.counter.skip"
|
||||
PORTPROTON_CONFIG_FILE = CONFIG_DIR / "PortProton.conf"
|
||||
|
||||
# Theme directories
|
||||
@@ -32,6 +33,7 @@ _config_mtime: dict[str, float] = {}
|
||||
|
||||
def reset_main_config() -> None:
|
||||
"""Delete main config file and invalidate cache."""
|
||||
_mark_download_counter_skip()
|
||||
if not CONFIG_FILE.exists():
|
||||
return
|
||||
try:
|
||||
@@ -46,6 +48,60 @@ def reset_main_config() -> None:
|
||||
_config_mtime.pop(config_path, None)
|
||||
|
||||
|
||||
def _mark_download_counter_skip() -> None:
|
||||
"""Skip the next download counter request."""
|
||||
try:
|
||||
COUNTER_SKIP_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
COUNTER_SKIP_FILE.write_text("1", encoding="utf-8")
|
||||
except OSError as error:
|
||||
logger.warning("Failed to save counter skip marker: %s", error)
|
||||
|
||||
|
||||
def consume_download_counter_skip() -> bool:
|
||||
"""Return True once after settings reset."""
|
||||
if not COUNTER_SKIP_FILE.exists():
|
||||
return False
|
||||
try:
|
||||
COUNTER_SKIP_FILE.unlink()
|
||||
except OSError as error:
|
||||
logger.warning("Failed to delete counter skip marker: %s", error)
|
||||
return True
|
||||
|
||||
|
||||
def update_app_version(app_version: str) -> bool:
|
||||
"""Store current app version and report whether it changed."""
|
||||
cp = configparser.ConfigParser()
|
||||
config_existed = CONFIG_FILE.exists()
|
||||
if config_existed:
|
||||
try:
|
||||
cp.read(CONFIG_FILE, encoding="utf-8")
|
||||
except configparser.Error as e:
|
||||
logger.warning("Invalid configuration file format: %s", e)
|
||||
return False
|
||||
|
||||
section = "Application"
|
||||
old_version = cp.get(section, "version", fallback="")
|
||||
if config_existed and old_version == app_version:
|
||||
return False
|
||||
|
||||
if section not in cp:
|
||||
cp[section] = {}
|
||||
cp[section]["version"] = app_version
|
||||
|
||||
try:
|
||||
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
|
||||
cp.write(config_file)
|
||||
except OSError as error:
|
||||
logger.warning("Failed to save application version: %s", error)
|
||||
return False
|
||||
|
||||
config_path = str(CONFIG_FILE)
|
||||
_config_cache.pop(config_path, None)
|
||||
_config_mtime.pop(config_path, None)
|
||||
return True
|
||||
|
||||
|
||||
class BaseConfig:
|
||||
"""Base configuration class providing common functionality."""
|
||||
|
||||
|
||||
@@ -68,3 +68,34 @@ class FavoritesFoldersConfig(BaseConfig):
|
||||
fav_str = ", ".join([os.path.normpath(f) for f in folders])
|
||||
cp[self._section]["folders"] = f'"{fav_str}"'
|
||||
self._save_config(cp)
|
||||
|
||||
|
||||
class ExeSettingsFavoritesConfig(BaseConfig):
|
||||
"""Favorite executable settings configuration."""
|
||||
|
||||
_section = "ExeSettingsFavorites"
|
||||
|
||||
def get_keys(self) -> list[str]:
|
||||
"""Get list of favorite executable setting keys."""
|
||||
cp = self._read_config()
|
||||
if cp is None or not cp.has_section(self._section):
|
||||
return []
|
||||
|
||||
keys = cp.get(self._section, "keys", fallback="").strip()
|
||||
if keys.startswith('"') and keys.endswith('"'):
|
||||
keys = keys[1:-1]
|
||||
return [key.strip() for key in keys.split(",") if key.strip()]
|
||||
|
||||
def set_keys(self, keys: list[str]) -> None:
|
||||
"""Set list of favorite executable setting keys."""
|
||||
if not isinstance(keys, list):
|
||||
raise ValidationError("keys must be a list", "keys", keys)
|
||||
for key in keys:
|
||||
validate_string(key, "key", min_len=1, max_len=255)
|
||||
|
||||
cp = self._read_config() or configparser.ConfigParser()
|
||||
if self._section not in cp:
|
||||
cp[self._section] = {}
|
||||
fav_str = ", ".join(keys)
|
||||
cp[self._section]["keys"] = f'"{fav_str}"'
|
||||
self._save_config(cp)
|
||||
@@ -1,7 +1,24 @@
|
||||
"""Gamepad configuration settings."""
|
||||
from portprotonqt.config.base import BaseConfig
|
||||
|
||||
|
||||
class GamepadConfig(BaseConfig):
|
||||
"""Gamepad configuration settings."""
|
||||
|
||||
_section = "Gamepad"
|
||||
|
||||
def get_gamepad_type(self) -> str:
|
||||
"""Get configured gamepad type."""
|
||||
value = self._get_str("type", "auto").lower()
|
||||
if value in ("auto", "xbox", "playstation"):
|
||||
return value
|
||||
if value in ("ps", "sony"):
|
||||
return "playstation"
|
||||
return "auto"
|
||||
|
||||
def set_gamepad_type(self, gamepad_type: str) -> None:
|
||||
"""Set configured gamepad type."""
|
||||
value = gamepad_type.lower()
|
||||
if value not in ("auto", "xbox", "playstation"):
|
||||
value = "auto"
|
||||
self._save_value("type", value, "str")
|
||||
@@ -3,7 +3,10 @@ import configparser
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from PySide6.QtCore import QStandardPaths
|
||||
from portprotonqt.config.base import (
|
||||
BaseConfig,
|
||||
CONFIG_FILE,
|
||||
@@ -143,17 +146,27 @@ def save_portdata_path_to_config(portdata_path: str) -> bool:
|
||||
|
||||
def get_portproton_scripts_path() -> str | None:
|
||||
"""Return PortProton scripts directory path."""
|
||||
appdir_prefix = os.getenv("APPDIR")
|
||||
sharun_prefix = os.getenv("SHARUN_DIR")
|
||||
prefixes = [("system package", Path("/usr")), ("Flatpak package", Path("/app"))]
|
||||
appimage_path = os.getenv("APPIMAGE", "").strip()
|
||||
prefixes = []
|
||||
if appdir_prefix:
|
||||
prefixes.append(("AppImage appdir", Path(appdir_prefix)))
|
||||
if sharun_prefix:
|
||||
prefixes.append(("AppImage", Path(sharun_prefix)))
|
||||
if appimage_path:
|
||||
appimage_root = Path(sys.executable).resolve().parent.parent
|
||||
prefixes.append(("AppImage executable", appimage_root))
|
||||
if os.getenv("FLATPAK_ID"):
|
||||
prefixes.append(("Flatpak package", Path("/app")))
|
||||
prefixes.append(("system package", Path("/usr")))
|
||||
|
||||
scripts_dirs = (
|
||||
("repository", Path.cwd() / "build-aux" / "share" / "portproton" / "scripts"),
|
||||
*[(source, prefix / "share" / "portproton" / "scripts") for source, prefix in prefixes],
|
||||
)
|
||||
for source, scripts_dir in scripts_dirs:
|
||||
if scripts_dir.exists():
|
||||
if (scripts_dir / "start.sh").is_file():
|
||||
logger.info("Using PortProton scripts from %s: %s", source, scripts_dir)
|
||||
return str(scripts_dir)
|
||||
logger.info(
|
||||
@@ -178,6 +191,82 @@ def get_portproton_start_command() -> list[str] | None:
|
||||
return None
|
||||
|
||||
|
||||
def _get_current_launcher_command() -> list[str] | None:
|
||||
"""Return the current launcher command for shortcut migration."""
|
||||
appimage_path = os.getenv("APPIMAGE", "").strip()
|
||||
if appimage_path and os.path.isfile(appimage_path):
|
||||
return [appimage_path, "--silent"]
|
||||
|
||||
flatpak_id = os.getenv("FLATPAK_ID", "").strip()
|
||||
if flatpak_id:
|
||||
return ["flatpak", "run", flatpak_id, "--silent"]
|
||||
|
||||
scripts_path = get_portproton_scripts_path()
|
||||
if scripts_path:
|
||||
return [os.path.join(scripts_path, "start.sh")]
|
||||
|
||||
if shutil.which("portprotonqt"):
|
||||
return ["portprotonqt", "--silent"]
|
||||
return None
|
||||
|
||||
|
||||
def _extract_launcher_tail(parts: list[str]) -> list[str] | None:
|
||||
"""Return shortcut args after the launcher command."""
|
||||
if not parts:
|
||||
return None
|
||||
|
||||
command_name = os.path.basename(parts[0])
|
||||
if parts[0].lower().endswith(".appimage") or command_name == "portprotonqt" or command_name == "start.sh":
|
||||
tail = parts[1:]
|
||||
if tail[:1] == ["--silent"]:
|
||||
tail = tail[1:]
|
||||
return tail
|
||||
|
||||
if parts[0] == "flatpak" and len(parts) >= 3 and parts[1] == "run":
|
||||
tail = parts[3:]
|
||||
if tail[:1] == ["--silent"]:
|
||||
tail = tail[1:]
|
||||
return tail
|
||||
return None
|
||||
|
||||
|
||||
def _migrate_launcher_line(line: str, launcher_command: list[str] | None) -> str:
|
||||
"""Update launcher command in desktop Exec or shell script line."""
|
||||
if not launcher_command:
|
||||
return line
|
||||
|
||||
prefix = "Exec=" if line.startswith("Exec=") else ""
|
||||
command = line[len(prefix):].strip() if prefix else line.strip()
|
||||
if not command or command.startswith("#"):
|
||||
return line
|
||||
|
||||
try:
|
||||
parts = shlex.split(command)
|
||||
except ValueError:
|
||||
return line
|
||||
|
||||
tail = _extract_launcher_tail(parts)
|
||||
if tail is None:
|
||||
return line
|
||||
|
||||
updated_parts = launcher_command + tail
|
||||
if not prefix and updated_parts[-1:] == ["$@"]:
|
||||
return f'{shlex.join(updated_parts[:-1])} "$@"'
|
||||
return f"{prefix}{shlex.join(updated_parts)}"
|
||||
|
||||
|
||||
def _get_desktop_paths(desktop_dir: str | None) -> tuple[str, ...]:
|
||||
"""Return desktop directories to scan for shortcuts."""
|
||||
if desktop_dir:
|
||||
return (desktop_dir,)
|
||||
|
||||
desktop_path = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DesktopLocation)
|
||||
if desktop_path:
|
||||
return (desktop_path,)
|
||||
|
||||
return (os.path.join(os.path.expanduser("~"), "Desktop"),)
|
||||
|
||||
|
||||
def migrate_legacy_shortcut(portproton_path: str, desktop_dir: str | None = None) -> int:
|
||||
"""Migrate legacy PortProton shortcuts in known desktop directories."""
|
||||
flatpak_id = os.getenv("FLATPAK_ID", "").strip()
|
||||
@@ -197,11 +286,11 @@ def migrate_legacy_shortcut(portproton_path: str, desktop_dir: str | None = None
|
||||
except OSError as error:
|
||||
logger.warning("Failed to read legacy PortProton config %s: %s", PORTPROTON_CONFIG_FILE, error)
|
||||
|
||||
desktop_path = desktop_dir or os.path.join(os.path.expanduser("~"), "Desktop")
|
||||
launcher_command = _get_current_launcher_command()
|
||||
desktop_paths = (
|
||||
portproton_path,
|
||||
os.path.join(os.path.expanduser("~"), ".local", "share", "applications"),
|
||||
desktop_path,
|
||||
os.path.join(user_home, ".local", "share", "applications"),
|
||||
*_get_desktop_paths(desktop_dir),
|
||||
)
|
||||
migrated = 0
|
||||
for current_path in desktop_paths:
|
||||
@@ -281,6 +370,9 @@ def migrate_legacy_shortcut(portproton_path: str, desktop_dir: str | None = None
|
||||
if "--silent" not in parts:
|
||||
updated_line = f'Exec={shlex.join(["portprotonqt", "--silent", *parts[2:]])}'
|
||||
|
||||
if updated_line.startswith("Exec="):
|
||||
updated_line = _migrate_launcher_line(updated_line, launcher_command)
|
||||
|
||||
if updated_line == line_content:
|
||||
continue
|
||||
|
||||
@@ -295,6 +387,40 @@ def migrate_legacy_shortcut(portproton_path: str, desktop_dir: str | None = None
|
||||
migrated += 1
|
||||
except OSError as error:
|
||||
logger.warning("Failed to update desktop file %s: %s", entry.path, error)
|
||||
|
||||
steam_scripts_path = os.path.join(portproton_path, "steam_scripts")
|
||||
if not os.path.isdir(steam_scripts_path):
|
||||
return migrated
|
||||
|
||||
for entry in os.scandir(steam_scripts_path):
|
||||
if not entry.name.endswith(".sh") or not entry.is_file():
|
||||
continue
|
||||
|
||||
try:
|
||||
lines = Path(entry.path).read_text(encoding="utf-8").splitlines(keepends=True)
|
||||
except OSError as error:
|
||||
logger.warning("Failed to read steam script %s: %s", entry.path, error)
|
||||
continue
|
||||
|
||||
changed = False
|
||||
for idx, line in enumerate(lines):
|
||||
line_end = "\r\n" if line.endswith("\r\n") else "\n"
|
||||
line_content = line[:-len(line_end)] if line.endswith(("\n", "\r\n")) else line
|
||||
updated_line = _migrate_launcher_line(line_content, launcher_command)
|
||||
if updated_line == line_content:
|
||||
continue
|
||||
|
||||
lines[idx] = f"{updated_line}{line_end}"
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
continue
|
||||
|
||||
try:
|
||||
Path(entry.path).write_text("".join(lines), encoding="utf-8")
|
||||
migrated += 1
|
||||
except OSError as error:
|
||||
logger.warning("Failed to update steam script %s: %s", entry.path, error)
|
||||
return migrated
|
||||
|
||||
|
||||
@@ -307,6 +433,17 @@ def parse_desktop_entry(file_path: str) -> configparser.SectionProxy | None:
|
||||
return cp["Desktop Entry"]
|
||||
|
||||
|
||||
WINDOWS_LAUNCH_EXTENSIONS = (".exe", ".bat", ".cmd", ".msi", ".reg")
|
||||
DISC_IMAGE_EXTENSIONS = (".iso", ".mdf", ".nrg")
|
||||
LAUNCH_FILE_EXTENSIONS = WINDOWS_LAUNCH_EXTENSIONS + DISC_IMAGE_EXTENSIONS
|
||||
THEMED_LAUNCH_ICON_NAMES = {
|
||||
".bat": "bat",
|
||||
".cmd": "bat",
|
||||
".msi": "msi",
|
||||
".reg": "reg",
|
||||
}
|
||||
|
||||
|
||||
def extract_exec_target_path(exec_value: str | list[str]) -> str | None:
|
||||
"""Extract target executable or image path from a desktop Exec value."""
|
||||
if isinstance(exec_value, str):
|
||||
@@ -327,7 +464,7 @@ def extract_exec_target_path(exec_value: str | list[str]) -> str | None:
|
||||
return None
|
||||
|
||||
for part in reversed(parts):
|
||||
if part.lower().endswith((".exe", ".iso", ".mdf")):
|
||||
if part.lower().endswith(LAUNCH_FILE_EXTENSIONS):
|
||||
return os.path.expanduser(part)
|
||||
|
||||
if parts[0] in ("env", "flatpak"):
|
||||
@@ -383,12 +520,15 @@ def create_desktop_file(
|
||||
os.makedirs(os.path.dirname(icon_path), exist_ok=True)
|
||||
|
||||
flatpak_id = os.getenv("FLATPAK_ID")
|
||||
appimage_path = os.getenv("APPIMAGE", "").strip()
|
||||
if flatpak_id:
|
||||
exec_str = f'flatpak run {flatpak_id} --silent "{exe_path}"'
|
||||
elif appimage_path and os.path.isfile(appimage_path):
|
||||
exec_str = shlex.join([appimage_path, "--silent", exe_path])
|
||||
else:
|
||||
exec_str = f'portprotonqt --silent "{exe_path}"'
|
||||
|
||||
comment = _('Launch game "{name}" with PortProton').format(name=game_name)
|
||||
comment = _('Launch "{name}" with PortProton').format(name=game_name)
|
||||
desktop_entry = (
|
||||
"[Desktop Entry]\n"
|
||||
f"Name={game_name}\n"
|
||||
|
||||
@@ -124,39 +124,53 @@ class ContextMenuManager:
|
||||
|
||||
return hasattr(self.parent, 'target_exe') and self.parent.target_exe == current_exe
|
||||
|
||||
def show_folder_context_menu(self, file_explorer, pos):
|
||||
"""Shows the context menu for a folder in FileExplorer."""
|
||||
def show_file_explorer_context_menu(self, file_explorer, pos):
|
||||
"""Shows the context menu for a file or folder in FileExplorer."""
|
||||
try:
|
||||
item = file_explorer.file_list.itemAt(pos)
|
||||
if not item:
|
||||
logger.debug("No folder selected at position %s", pos)
|
||||
logger.debug("No item selected at position %s", pos)
|
||||
return
|
||||
|
||||
# Ensure the item is visually selected
|
||||
file_explorer.file_list.setCurrentItem(item)
|
||||
file_explorer.handle_item_click(item)
|
||||
|
||||
selected = item.text()
|
||||
if not selected.endswith("/"):
|
||||
logger.debug("Selected item is not a folder: %s", selected)
|
||||
return # Only for folders
|
||||
full_path = os.path.normpath(os.path.join(file_explorer.current_path, selected.rstrip("/")))
|
||||
if not os.path.isdir(full_path):
|
||||
logger.debug("Path is not a directory: %s", full_path)
|
||||
if selected in ("../", "./"):
|
||||
return
|
||||
|
||||
is_folder = selected.endswith("/")
|
||||
full_path = os.path.normpath(os.path.join(file_explorer.current_path, selected.rstrip("/")))
|
||||
|
||||
menu = QMenu(file_explorer)
|
||||
menu.setWindowFlags(menu.windowFlags() | Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint)
|
||||
menu.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
menu.setStyleSheet(self.theme.CONTEXT_MENU_STYLE)
|
||||
menu.setParent(file_explorer, Qt.WindowType.Popup) # Set transientParent for Wayland
|
||||
|
||||
favorite_folders = favorites_folders_config.get_folders()
|
||||
is_favorite = full_path in favorite_folders
|
||||
action_text = _("Remove from Favorites") if is_favorite else _("Add to Favorites")
|
||||
favorite_action = menu.addAction(self._get_safe_icon("star" if is_favorite else "star_full"), action_text)
|
||||
favorite_action.triggered.connect(lambda: self.toggle_favorite_folder(file_explorer, full_path, not is_favorite))
|
||||
if is_folder:
|
||||
favorite_folders = favorites_folders_config.get_folders()
|
||||
is_favorite = full_path in favorite_folders
|
||||
action_text = _("Remove from Favorites") if is_favorite else _("Add to Favorites")
|
||||
favorite_action = menu.addAction(self._get_safe_icon("star" if is_favorite else "star_full"), action_text)
|
||||
favorite_action.triggered.connect(lambda: self.toggle_favorite_folder(file_explorer, full_path, not is_favorite))
|
||||
|
||||
create_folder_action = menu.addAction(self._get_safe_icon("folder"), _("Create Folder"))
|
||||
create_folder_action.triggered.connect(lambda: self.create_folder(file_explorer))
|
||||
rename_folder_action = menu.addAction(self._get_safe_icon("edit"), _("Rename Folder"))
|
||||
rename_folder_action.triggered.connect(lambda: self.rename_folder(file_explorer, full_path))
|
||||
delete_folder_action = menu.addAction(self._get_safe_icon("delete"), _("Delete Folder"))
|
||||
delete_folder_action.triggered.connect(lambda: self.delete_folder(file_explorer, full_path))
|
||||
create_folder_action.triggered.connect(
|
||||
lambda: self.create_folder(file_explorer, file_explorer.current_path)
|
||||
)
|
||||
|
||||
if is_folder:
|
||||
rename_action = menu.addAction(self._get_safe_icon("edit"), _("Rename Folder"))
|
||||
rename_action.triggered.connect(lambda: self.rename_folder(file_explorer, full_path))
|
||||
delete_action = menu.addAction(self._get_safe_icon("delete"), _("Delete Folder"))
|
||||
delete_action.triggered.connect(lambda: self.delete_folder(file_explorer, full_path))
|
||||
else:
|
||||
rename_action = menu.addAction(self._get_safe_icon("edit"), _("Rename File"))
|
||||
rename_action.triggered.connect(lambda: self.rename_file(file_explorer, full_path))
|
||||
delete_action = menu.addAction(self._get_safe_icon("delete"), _("Delete File"))
|
||||
delete_action.triggered.connect(lambda: self.delete_file(file_explorer, full_path))
|
||||
|
||||
# Disconnect file_list signals to prevent navigation during menu interaction
|
||||
try:
|
||||
@@ -184,7 +198,7 @@ class ContextMenuManager:
|
||||
global_pos = file_explorer.file_list.mapToGlobal(pos)
|
||||
menu.exec(global_pos)
|
||||
except Exception as e:
|
||||
logger.error("Error displaying folder context menu: %s", e)
|
||||
logger.error("Error displaying file explorer context menu: %s", e)
|
||||
|
||||
def toggle_favorite_folder(self, file_explorer, folder_path, add):
|
||||
"""Adds or removes a folder from favorites."""
|
||||
@@ -280,12 +294,12 @@ class ContextMenuManager:
|
||||
if hasattr(parent, "file_list") and parent.file_list:
|
||||
parent.file_list.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
|
||||
def create_folder(self, file_explorer) -> None:
|
||||
def create_folder(self, file_explorer, parent_path: str | None = None) -> None:
|
||||
"""Create a new folder in the current directory."""
|
||||
folder_name = self._prompt_folder_name(file_explorer, _("Create Folder"), _("Folder Name:"))
|
||||
if not folder_name:
|
||||
return
|
||||
target_path = os.path.join(file_explorer.current_path, folder_name)
|
||||
target_path = os.path.join(parent_path or file_explorer.current_path, folder_name)
|
||||
if os.path.exists(target_path):
|
||||
self.signals.show_warning_dialog.emit(_("Error"), _("Folder already exists: {folder_name}").format(folder_name=folder_name))
|
||||
return
|
||||
@@ -352,6 +366,49 @@ class ContextMenuManager:
|
||||
_("Failed to delete folder: {error}").format(error=str(e))
|
||||
)
|
||||
|
||||
def delete_file(self, file_explorer, file_path: str) -> None:
|
||||
"""Delete selected file after confirmation."""
|
||||
file_name = os.path.basename(file_path)
|
||||
msg_box = QMessageBox(file_explorer)
|
||||
msg_box.setWindowTitle(_("Confirm Deletion"))
|
||||
msg_box.setText(
|
||||
_("Are you sure you want to delete file '{file_name}'? This action cannot be undone.")
|
||||
.format(file_name=file_name)
|
||||
)
|
||||
msg_box.setIcon(QMessageBox.Icon.Warning)
|
||||
msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||
msg_box.setButtonText(QMessageBox.StandardButton.Yes, _("Yes"))
|
||||
msg_box.setButtonText(QMessageBox.StandardButton.No, _("No"))
|
||||
if msg_box.exec() != QMessageBox.StandardButton.Yes:
|
||||
return
|
||||
try:
|
||||
os.remove(file_path)
|
||||
file_explorer.update_file_list()
|
||||
except OSError as e:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Failed to delete file: {error}").format(error=str(e))
|
||||
)
|
||||
|
||||
def rename_file(self, file_explorer, file_path: str) -> None:
|
||||
"""Rename selected file."""
|
||||
current_name = os.path.basename(file_path)
|
||||
new_name = self._prompt_folder_name(file_explorer, _("Rename File"), _("New File Name:"), current_name)
|
||||
if not new_name or new_name == current_name:
|
||||
return
|
||||
target_path = os.path.join(os.path.dirname(file_path), new_name)
|
||||
if os.path.exists(target_path):
|
||||
self.signals.show_warning_dialog.emit(_("Error"), _("File already exists: {file_name}").format(file_name=new_name))
|
||||
return
|
||||
try:
|
||||
os.rename(file_path, target_path)
|
||||
file_explorer.update_file_list()
|
||||
except OSError as e:
|
||||
self.signals.show_warning_dialog.emit(
|
||||
_("Error"),
|
||||
_("Failed to rename file: {error}").format(error=str(e))
|
||||
)
|
||||
|
||||
def _get_safe_icon(self, icon_name: str) -> QIcon:
|
||||
"""Returns a QIcon, ensuring it is valid."""
|
||||
icon = self.theme_manager.get_icon(icon_name)
|
||||
|
||||
|
Before Width: | Height: | Size: 499 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Albion Online
|
||||
description_ru=Многопользовательская песочница в жанре MMORPG, где игроки могут исследовать открытый мир, заниматься ремеслом, добычей ресурсов и сражаться с другими игроками. Игра предлагает уникальную систему классов, позволяющую игрокам изменять свои роли в зависимости от выбранного снаряжения.
|
||||
description_en=A multiplayer sandbox MMORPG where players can explore an open world, engage in crafting, gather resources, and battle against other players. The game features a unique class system that allows players to change their roles based on the gear they equip.
|
||||
|
Before Width: | Height: | Size: 147 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Ankama Launcher
|
||||
description_ru=Лаунчер для игр Ankama.
|
||||
description_en=Launcher for Ankama studio games.
|
||||
|
Before Width: | Height: | Size: 720 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Anomaly Zone
|
||||
description_ru=Экшен-игра про сталкеров, где игроки исследуют таинственные миры и сражаются с разнообразными противниками. Игра предлагает захватывающий сюжет и возможность улучшать персонажа, открывая новые способности и снаряжение.
|
||||
description_en=An action game about stalkers, where players explore mysterious worlds and fight against a variety of opponents. The game offers an exciting storyline and the opportunity to improve the character by unlocking new abilities and equipment.
|
||||
|
Before Width: | Height: | Size: 352 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Arizona Games Launcher
|
||||
description_ru=Лаунчер для игры Arizona Role Play.
|
||||
description_en=Launcher for the Arizona Role Play game.
|
||||
|
Before Width: | Height: | Size: 121 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Battle.net Launcher
|
||||
description_ru=Лаунчер для игр Activision и Blizzard.
|
||||
description_en=Launcher for Activision and Blizzard studio games.
|
||||
|
Before Width: | Height: | Size: 528 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Black Desert Online (RU)
|
||||
description_ru=Многопользовательская ролевая игра с открытым миром, известная своей потрясающей графикой и глубокой системой кастомизации персонажей. Игроки могут исследовать обширные земли, выполнять задания, участвовать в PvP-сражениях и заниматься различными ремеслами.
|
||||
description_en=A massively multiplayer online role-playing game set in an open world, renowned for its stunning graphics and deep character customization system. Players can explore vast lands, complete quests, engage in PvP battles, and participate in various crafting activities.
|
||||
|
Before Width: | Height: | Size: 655 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Blood and Soul
|
||||
description_ru=Многопользовательская ролевая игра с богатой системой боя и яркой графикой, вдохновленная восточной мифологией. Игроки могут выбирать из различных классов, исследовать уникальные локации и сражаться с могущественными врагами.
|
||||
description_en=A multiplayer role-playing game featuring a rich combat system and vibrant graphics, inspired by Eastern mythology. Players can choose from various classes, explore unique locations, and battle powerful foes.
|
||||
|
Before Width: | Height: | Size: 660 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Chicken Invaders Universe
|
||||
description_ru=Захватывающая аркадная игра, в которой игроки сражаются с агрессивными курицами из космоса, защищая свою планету. Игра предлагает множество уровней, кооперативный режим и возможность улучшения космического корабля.
|
||||
description_en=An exciting arcade game where players battle aggressive space chickens to defend their planet. The game features multiple levels, a cooperative mode, and the ability to upgrade their spaceship.
|
||||
|
Before Width: | Height: | Size: 324 KiB |
@@ -1,3 +0,0 @@
|
||||
name=CONTRACT WARS
|
||||
description_ru=Многопользовательский шутер от первого лица, где игроки участвуют в противостоянии между двумя командами на разнообразных картах. Игра предлагает широкий выбор оружия и возможностей для настройки персонажей, что делает каждый матч уникальным.
|
||||
description_en=A multiplayer first-person shooter where players engage in battles between two teams on various maps. The game offers a wide selection of weapons and character customization options, making each match unique.
|
||||
|
Before Width: | Height: | Size: 870 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Age of Empires Online
|
||||
description_ru=Многопользовательская стратегия в реальном времени, где игроки строят свои цивилизации, собирают ресурсы и сражаются с противниками. Игра предлагает уникальную экономическую систему и возможность развивать свои города с помощью различных миссий и задач.
|
||||
description_en=A multiplayer real-time strategy game where players build their civilizations, gather resources, and battle against opponents. The game features a unique economic system and the ability to develop cities through various missions and quests.
|
||||
|
Before Width: | Height: | Size: 276 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Cemu
|
||||
description_ru=Эмулятор Wii U, который позволяет пользователям запускать и играть в игры, выпущенные на этой консоли, с высокой производительностью и улучшенной графикой.
|
||||
description_en=A Wii U emulator that allows users to launch and play games released on this console with high performance and enhanced graphics.
|
||||
|
Before Width: | Height: | Size: 764 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Secret World Legends (ENG)
|
||||
description_ru=MMORPG с уникальной историей и сеттингом, основанная на мифах, легендах и тайных обществах, позволяющая игрокам исследовать современный мир, полный сверхъестественных существ и загадок. Игра предлагает свободу в выборе навыков и построении персонажей, а также захватывающие квесты и глубокий сюжет.
|
||||
description_en=An MMORPG with a unique story and setting based on myths, legends, and secret societies, allowing players to explore a modern world filled with supernatural beings and mysteries. The game offers freedom in skill selection and character building, along with engaging quests and a rich narrative.
|
||||
|
Before Width: | Height: | Size: 395 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Broken Ranks (ENG)
|
||||
description_ru=MMORPG игра, предлагающая глубокий сон и уникальную боевую систему, которая ориентирует внимание на стратегию и деятельность персоны. Игроки исследуют мрачный мир, полный интриг и опасностей, развивая своих героев и принимая ключевые решения, влияющие на ход истории.
|
||||
description_en=An MMORPG game offering deep sleep and a unique combat system that focuses on the strategy and activities of the person. Players explore a dark world full of intrigue and danger, developing their characters and making key decisions that affect the course of history.
|
||||
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Dolphin 5.0
|
||||
description_ru=Мощный эмулятор для игровых консолей Nintendo GameCube и Wii, который позволяет запускать игры на ПК с улучшенной графикой и производительностью. Он поддерживает широкий спектр функций, включая HD-разрешение, множество настройек управления и возможность использования модификаций.
|
||||
description_en=A powerful emulator for Nintendo GameCube and Wii consoles that allows users to play games on their PCs with enhanced graphics and performance. It supports a wide range of features, including HD resolution, numerous control configurations, and the ability to use modifications.
|
||||
|
Before Width: | Height: | Size: 736 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Doomsday
|
||||
description_ru=Стратегическая игра в реальном времени, где игроки управляют отрядом выживших в постапокалиптическом мире, стремясь восстановить цивилизацию и защититься от различных угроз. Игра предлагает элементы строительства базы, тактические сражения и глубокую проработку сюжета.
|
||||
description_en=A real-time strategy game where players manage a group of survivors in a post-apocalyptic world, aiming to rebuild civilization and defend against various threats. The game features base-building elements, tactical combat, and a deep narrative experience.
|
||||
|
Before Width: | Height: | Size: 523 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Eldevin (ENG)
|
||||
description_ru=MMORPG с красочной графикой, где игроки исследуют обширный фэнтезийный мир, выполняют квесты и сражаются с врагами, чтобы развивать свои персонажи. Игра предлагает разнообразные классы и навыки, а также системы крафта и группового взаимодействия.
|
||||
description_en=An MMORPG with vibrant graphics where players explore a vast fantasy world, complete quests, and battle enemies to develop their characters. The game features diverse classes and skills, as well as crafting and group interaction systems.
|
||||
|
Before Width: | Height: | Size: 267 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Epic Games Launcher
|
||||
description_ru=Лаунчер для библиотеки игр Epic Games.
|
||||
description_en=Launcher for the Epic Games game library.
|
||||
|
Before Width: | Height: | Size: 824 KiB |
@@ -1,3 +0,0 @@
|
||||
name=STALCRAFT
|
||||
description_ru=Многопользовательская игра с открытым миром, вдохновленная вселенной S.T.A.L.K.E.R., где игроки исследуют заброшенные зоны, сражаются с мутантами и другими сталкерами, а также выполняют различные квесты. Игра сочетает элементы выживания, RPG и шутера от первого лица, предлагая уникальный опыт в постапокалиптическом мире.
|
||||
description_en=A multiplayer open-world game inspired by the S.T.A.L.K.E.R. universe, where players explore abandoned zones, battle mutants and other stalkers, and complete various quests. The game combines elements of survival, RPG, and first-person shooter, offering a unique experience in a post-apocalyptic world.
|
||||
|
Before Width: | Height: | Size: 634 KiB |
@@ -1,3 +0,0 @@
|
||||
name=ExoTanks
|
||||
description_ru=Многопользовательская боевая игра, в которой игроки управляют экзоскелетами и сражаются в различных аренах, используя мощное вооружение и стратегический подход. Игра предлагает как командные, так и одиночные режимы, а также возможность кастомизации своего экзоскелета для уникального стиля игры.
|
||||
description_en=A multiplayer battle game where players control exoskeletons and fight in various arenas using powerful weapons and strategic gameplay. The game offers both team and solo modes, along with the ability to customize their exoskeleton for a unique playing style.
|
||||
|
Before Width: | Height: | Size: 854 KiB |
@@ -1,3 +0,0 @@
|
||||
name=Farlight 84
|
||||
description_ru=Многопользовательская игра в жанре королевская битва, которая проходит в красочном и футуристическом мире, где игроки сражаются друг с другом с использованием уникальных навыков и оружия. Игра предлагает захватывающий геймплей с элементами строительства, а также возможность использовать различные транспортные средства для перемещения по карте.
|
||||
description_en=A multiplayer battle royale game set in a colorful and futuristic world where players fight against each other using unique skills and weapons. The game features exciting gameplay with building elements, as well as the ability to utilize various vehicles to navigate the map.
|
||||