Compare commits

...
60 Commits
Author SHA1 Message Date
Boria138 d0d1dff778 bump to 1.1.0 (hotfix)
Code check / Check code (push) Successful in 54s
Build AppImage, Arch and Fedora Packages / Build AppImage (push) Successful in 6m53s
Build AppImage, Arch and Fedora Packages / Build Arch Package (push) Successful in 2m8s
Build AppImage, Arch and Fedora Packages / Build Fedora RPM (42) (push) Successful in 1m9s
Build AppImage, Arch and Fedora Packages / Build Fedora RPM (43) (push) Successful in 1m3s
Build AppImage, Arch and Fedora Packages / Build Fedora RPM (44) (push) Successful in 1m9s
Build AppImage, Arch and Fedora Packages / Build Fedora RPM (rawhide) (push) Successful in 1m20s
Build AppImage, Arch and Fedora Packages / Create and Publish Release (push) Successful in 17s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:48:45 +05:00
Boria138 07e07dbe09 chore(locale): update
Code check / Check code (push) Successful in 54s
Check Translations / check-translations (push) Successful in 10s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:44:38 +05:00
Boria138 004916a82b fix: filter invalid GPU entries in settings
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:44:15 +05:00
Codeberg TranslateandBoria138 ae2115ad0a Translated using Weblate (Russian)
Check Translations / check-translations (push) Successful in 15s
Code check / Check code (push) Successful in 55s
Currently translated at 100.0% (665 of 665 strings)

Co-authored-by: Boria138 <boria138@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/portprotonqt/portprotonqt/ru/
Translation: PortProtonQt/PortProtonQt
2026-05-26 06:33:50 +00:00
Boria138 357a0dcf42 chore(locale): update
Check Translations / check-translations (push) Failing after 20s
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:31:45 +05:00
Boria138 e5df48b9b0 chore: move gamepad fullscreen option in settings
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:31:10 +05:00
Boria138 48217b93f5 feat: validate PORT_DATA_PATH read-write access
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:27:29 +05:00
Boria138 cc2372107a feat: add version-based download counter
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 11:12:26 +05:00
Boria138 a32d63f5e5 refactor: centralize script D-Bus calls
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 10:58:35 +05:00
Boria138 cbf29005ff fix: remove obsolete PROGRAMS prefix
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-26 10:35:39 +05:00
Boria138 8d10bae8f2 fix: avoid duplicate library refresh
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:42:38 +05:00
Boria138 28d0417ede chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:32:47 +05:00
Boria138 7efb0aadec fix: reset PortProton user config from CLI
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:18:17 +05:00
Boria138 d4d7e9c044 fix: always show pinned prefixes
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:15:06 +05:00
Boria138 eb18178958 fix: reset PortProton user config with app settings
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:10:51 +05:00
Boria138 979e208969 fix: update PortProton dependency progress from live output
Code check / Check code (push) Successful in 57s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 21:03:32 +05:00
Boria138 8704ba00e9 fix: keep Wine settings tab populated without dist directory
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 20:31:58 +05:00
Mikhail Tergoev d9d8242475 add check write access to the game directory
Code check / Check code (push) Successful in 55s
2026-05-25 18:12:59 +03:00
Codeberg TranslateandBoria138 64e626b2b2 Translated using Weblate (Russian)
Check Translations / check-translations (push) Successful in 17s
Code check / Check code (push) Successful in 53s
Currently translated at 100.0% (664 of 664 strings)

Co-authored-by: Boria138 <boria138@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/portprotonqt/portprotonqt/ru/
Translation: PortProtonQt/PortProtonQt
2026-05-25 05:14:31 +00:00
Boria138 8af84460eb fix: improve library toolbar focus navigation
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 10:03:38 +05:00
Boria138 b7e83ad1d2 fix: handle gamepad fullscreen startup
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 09:56:21 +05:00
Boria138 ffff6efa68 fix: keep Wine and LG builds in dist
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 09:46:53 +05:00
Boria138 fabf14fc46 chore(localization): update
Check Translations / check-translations (push) Failing after 22s
Code check / Check code (push) Successful in 53s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 09:35:55 +05:00
Boria138 88db436b94 fix: keep mouse emulation available after launcher start
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 09:35:18 +05:00
Boria138 592d24c9c6 ui: depersonalize interface by using generic terminology
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 09:02:54 +05:00
Boria138 c472c785cc feat(ui): add quick launch button
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 08:45:03 +05:00
Boria138 759e931dfc ui: remove redundant tab titles
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 08:30:01 +05:00
Boria138 93f5ea49d4 chore(build): replace steam compat
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-25 08:15:04 +05:00
Mikhail Tergoev d9c55bed61 Merge branch 'main' of git.linux-gaming.ru:Linux-Gaming/PortProtonQt
Code check / Check code (push) Successful in 55s
2026-05-24 21:35:19 +03:00
Mikhail Tergoev 64bbabd0f1 fix auto update compat tools 2026-05-24 21:33:59 +03:00
Boria138 c3e9433f9d fix: defer empty library refresh until window shows
Code check / Check code (push) Successful in 54s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 17:26:48 +05:00
Boria138 c4274bbef6 fix: restore legacy squashfs via easyterm
Code check / Check code (push) Successful in 54s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 17:20:59 +05:00
Boria138 cdfd983580 fix: avoid prefix restore on theme restart
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 17:13:44 +05:00
Boria138 749113b48c fix: use classic autoinstall covers for list themes
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 15:03:05 +05:00
Boria138 5fdba88c5c fix: refresh empty library after silent launch
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 14:54:30 +05:00
Boria138 a20887da5b fix: refresh library after all PortProton shortcut creation
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 14:44:40 +05:00
Boria138 c9b8871fb1 fix: fallback to exe icon when cover download fails
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 14:38:48 +05:00
Boria138 6db27ef7af feat: add GUI support for bat msi reg launch files
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 14:25:55 +05:00
Boria138 97de53303c fix: force 96 DPI on prefix initialization
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 11:02:59 +05:00
Simple16 07ac67e934 Updating the Russian localization
Check Translations / check-translations (push) Successful in 15s
Code check / Check code (push) Successful in 51s
2026-05-24 05:27:33 +00:00
Simple16 66e1e437f8 Updating the Russian localization 2026-05-24 05:27:33 +00:00
Boria138 12c98adbd7 fix: resolve pyright type errors in wine_extractor, downloader, and prefix_backup
Code check / Check code (push) Successful in 53s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 10:27:14 +05:00
Boria138 d7de29008e fix(deps): update python dependencies
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 10:21:03 +05:00
Boria138 4d99a66e84 chore: finally migrate custom data to https://git.linux-gaming.ru/Linux-Gaming/PortProtonQt-Custom-Metadata
Code check / Check code (push) Successful in 53s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 09:55:35 +05:00
Simple16 288242195b Updating the English localization
Code check / Check code (pull_request) Successful in 56s
Code check / Check code (push) Successful in 56s
2026-05-24 09:26:37 +05:00
Boria138 892124efba fix: normalize Proton dist hyphen spacing
Code check / Check code (push) Successful in 54s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-24 03:24:49 +00:00
Renovate Bot ae7bd3e5ff fix(deps): update dependency dbus-fast to v5
Code check / Check code (push) Successful in 56s
2026-05-24 03:19:56 +00:00
Renovate Bot 042b90d663 chore(deps): update ghcr.io/renovatebot/renovate:latest docker digest to 47421f2
Code check / Check code (pull_request) Successful in 55s
Code check / Check code (push) Successful in 57s
2026-05-24 00:00:43 +00:00
Boria138 4fdaf0115e feat: rework prefix backups with libarchive progress UI
Code check / Check code (push) Successful in 56s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 16:31:16 +00:00
Boria138 b00f62776c feat: add svg cover image support
Code check / Check code (push) Successful in 57s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 21:29:43 +05:00
Boria138 69674b5c75 fix: check moved Python files in meson hook
Code check / Check code (push) Successful in 58s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 21:14:37 +05:00
Boria138 eaa3467410 chore(changelog): update
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 21:07:19 +05:00
Boria138 47a949fe6e chore(scripts): fix easyterm location
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 20:41:22 +05:00
Mikhail Tergoev 8902369fe7 fix create desktop and choose ppdb for setup-msi
Code check / Check code (push) Successful in 57s
2026-05-23 16:41:08 +03:00
Mikhail Tergoev c0529ac9fd move easyterm to scripts_utils 2026-05-23 13:49:36 +03:00
Mikhail Tergoev a9ed377b00 add runner terminal function: python_term 2026-05-23 13:40:08 +03:00
Mikhail Tergoev bc523762b6 add runner python3 function: python_module 2026-05-23 13:20:50 +03:00
Boria138 2f5159129c fix: include check_variables defaults in --show-ppdb output
Code check / Check code (push) Successful in 59s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 14:10:47 +05:00
Boria138 eef8c7d496 chore(locale): rm zh_Hans
Check Translations / check-translations (push) Successful in 13s
Code check / Check code (push) Successful in 55s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 11:08:38 +05:00
Boria138 64cad59d53 chore(locale): rename zh_Hans to zh_CN for glibc compatibility
Code check / Check code (push) Successful in 57s
Check Translations / check-translations (push) Failing after 17s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-05-23 11:03:51 +05:00
219 changed files with 2902 additions and 1728 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ on:
env:
# Common version, will be used for tagging the release
VERSION: 1.0
VERSION: 1.1.0
PKGDEST: "/tmp/portprotonqt"
PACKAGE: "portprotonqt"
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
+1 -1
View File
@@ -8,7 +8,7 @@ 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
+5
View File
@@ -785,6 +785,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
+48 -1
View File
@@ -3,6 +3,53 @@
Все заметные изменения в этом проекте фиксируются в этом файле.
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
## [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 +106,7 @@
- Улучшенно качестве иконок из `exe` файлов
- В трей добавлены компактный и детальный режимы меню
- Автоустановки теперь можно остановить
- Префиксы `DEFAULT`, `DOTNET` и `PROGRAMS` теперь всегда отображаются вверху списков префиксов
- Префиксы `DEFAULT` и `DOTNET` теперь всегда отображаются вверху списков префиксов
- Из лога удалена версия скриптов, так как скрипты теперь внутри PortProtonQt
- Из downloader удалена отдельная проверка сети через Yandex и Google
- Выбор видеокарты теперь не отображается в настройках PortProtonQt если видеокарта всего одна
+7 -17
View File
@@ -1,13 +1,16 @@
pkgname=('portprotonqt' 'portprotonqt-steam-compat')
pkgver=1.0
pkgname=('portprotonqt')
pkgver=1.1.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')
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
}
+6 -16
View File
@@ -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')
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
}
+8 -15
View File
@@ -1,5 +1,5 @@
%global pypi_name portprotonqt
%global pypi_version 1.0
%global pypi_version 1.1.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
@@ -63,7 +63,6 @@ Requires: pciutils
Requires: vulkan-loader
Requires: procps-ng
Requires: psmisc
Requires: squashfs-tools
Requires: 7zip
Requires: python3-dbus-fast
@@ -74,18 +73,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 +121,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
+8 -15
View File
@@ -1,5 +1,5 @@
%global pypi_name portprotonqt
%global pypi_version 1.0
%global pypi_version 1.1.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
@@ -60,7 +60,6 @@ Requires: pciutils
Requires: vulkan-loader
Requires: procps-ng
Requires: psmisc
Requires: squashfs-tools
Requires: 7zip
Requires: python3-dbus-fast
@@ -71,18 +70,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 +119,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-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;
@@ -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>
@@ -4,24 +4,31 @@
########################################################################
$PW_DEBUG
process_log () {
local filter_word="mono|gecko"
echo "$@" | sed -r "/$filter_word/Id" >> "$PW_TMPFS_PATH/process.log"
}
export -f process_log
print_error () { printf "\E[31m%s Error: $@ %s\e[0m\n" ; process_log "$@" ;}
print_error () { printf "\E[31m%s Error: $@ %s\e[0m\n" ;}
export -f print_error
print_warning () { printf "\E[33m%s Warning: $@ %s\e[0m\n" ; process_log "$@" ;}
print_warning () { printf "\E[33m%s Warning: $@ %s\e[0m\n" ;}
export -f print_warning
print_info () { printf "\E[36m%s Info: $@ %s\e[0m\n" ; process_log "$@" ;}
print_info () { printf "\E[36m%s Info: $@ %s\e[0m\n" ;}
export -f print_info
print_ok () { printf "\E[35m%s OK: $@ %s\e[0m\n" ; process_log "$@" ;}
print_ok () { printf "\E[35m%s OK: $@ %s\e[0m\n" ;}
export -f print_ok
python_module () {
local python3_module="$1"
shift
print_info "Running python3 module: $python3_module $@" >&2
python3 -m portprotonqt.scripts_utils.$python3_module "$@"
}
python_term () {
print_info "Running: easyterm $@"
python_module easyterm -e "$@"
[[ "$?" == "0" ]] && return 0 || return 1
}
print_wrapped () {
local text="$1"
local a="0"
@@ -162,6 +169,32 @@ pw_is_process_active() {
return 1
}
pw_power_profile_state_file() {
echo "${PW_TMPFS_PATH}/power_profile"
}
pw_save_power_profile() {
[[ -n "$1" ]] || return 1
printf "%s\n" "$1" > "$(pw_power_profile_state_file)"
}
pw_restore_power_profile() {
local current_power_profile saved_power_profile state_file
state_file="$(pw_power_profile_state_file)"
saved_power_profile="$PW_POWERPROFILECTL_PROFILE"
if [[ -z "$saved_power_profile" && -f "$state_file" ]] ; then
saved_power_profile="$(head -n 1 "$state_file")"
fi
[[ -n "$saved_power_profile" ]] || return 0
current_power_profile=$(python_module power_profiles get)
if [[ "$current_power_profile" != "$saved_power_profile" ]] ; then
python_module power_profiles set "$saved_power_profile" &>/dev/null
fi
rm -f "$state_file"
}
try_copy_file () {
if [[ ! -f "$1" ]] ; then print_info "file $1 not found for copy" && return 1
elif [[ -z "$2" ]] ; then print_error "no way to copy file $1" && return 1
@@ -462,7 +495,7 @@ try_download () {
# set -o pipefail
curl -f -# -A 'Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)' -H 'Cache-Control: no-cache, no-store' \
-H 'Pragma: no-cache' -L "${FIRST_URL[@]}" -o "$dest" 2>&1 \
| tr '\r' '\n' | sed -ur 's|[# ]+||g;s|100||g;s|0-||g;s|.*=.*||g' >> "$PW_TMPFS_PATH/process.log"
| tr '\r' '\n' | sed -ur 's|[# ]+||g;s|100||g;s|0-||g;s|.*=.*||g'
#TODO: check download errror
# [[ "${PIPESTATUS[0]}" != 0 ]] && local curl_status="error"
@@ -512,47 +545,43 @@ var_ld_library_path_update () {
# GUI NOTIFY SEND
pw_notify_send () {
if command -v gdbus &>/dev/null ; then
local icon title body app timeout OPTIND
app="PortProton"
timeout="5000"
OPTIND="1"
while getopts a:i:t: opt ; do
case "$opt" in
a)
app=$OPTARG
;;
i)
case "$OPTARG" in
info) local OPTARG="${PORT_IMG_PATH}/notify/info.svg";;
warning) local OPTARG="${PORT_IMG_PATH}/notify/warning.svg";;
error) local OPTARG="${PORT_IMG_PATH}/notify/error.svg";;
esac
icon=$OPTARG
;;
t)
timeout=$OPTARG
;;
*)
echo "usage: $0
[-a] name application (PortProton default)
[-i] info, warning, error (none default)
[-t] timeout in ms (5000 default)" >&2
return 1
;;
esac
done
shift $(( OPTIND - 1 ))
[[ "$1" == "--" ]] && shift
title="$1" ; shift
body="$*"
local icon title body app timeout OPTIND
app="PortProtonQt"
timeout="5000"
OPTIND="1"
while getopts a:i:t: opt ; do
case "$opt" in
a)
app=$OPTARG
;;
i)
case "$OPTARG" in
info) local OPTARG="${PORT_IMG_PATH}/notify/info.svg";;
warning) local OPTARG="${PORT_IMG_PATH}/notify/warning.svg";;
error) local OPTARG="${PORT_IMG_PATH}/notify/error.svg";;
esac
icon=$OPTARG
;;
t)
timeout=$OPTARG
;;
*)
echo "usage: $0
[-a] name application (PortProtonQt default)
[-i] info, warning, error (none default)
[-t] timeout in ms (5000 default)" >&2
return 1
;;
esac
done
shift $(( OPTIND - 1 ))
[[ "$1" == "--" ]] && shift
title="$1" ; shift
body="$*"
gdbus call --session --dest org.freedesktop.Notifications \
--object-path /org/freedesktop/Notifications \
--method org.freedesktop.Notifications.Notify \
"$app" 0 "$icon" "$title" "$body" "[]" "{}" "$timeout" &>/dev/null
return 0
fi
python_module dbus_tools notify \
-a "$app" -i "$icon" -t "$timeout" -- "$title" "$body" &>/dev/null
return 0
}
export -f pw_notify_send
@@ -671,7 +700,7 @@ unpack () {
$PW_TERM "echo ; echo ; echo \"Unpacking file: $1. Please wait...\" ; $command \"$1\" -C \"$2\""
[[ "$?" != 0 ]] && return 1 || return 0
elif [[ "$silent" != "true" ]] ; then
process_log "Unpacking file: $1. Please wait..."
print_info "Unpacking file: $(basename "$1") Please wait..."
set -o pipefail
$command "$1" -C "$2" 2>/dev/null
[[ "${PIPESTATUS[0]}" != 0 ]] && print_error "File $1 unpacking error." && return 1 || return 0
@@ -1195,7 +1224,7 @@ init_wine_ver () {
if [[ -d "${PORT_DATA_PATH}/data/dist" ]] ; then
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
@@ -1294,7 +1323,7 @@ pw_init_runtime () {
export PW_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${PW_PLUGINS_PATH}/portable/lib/lib64:${PW_PLUGINS_PATH}/portable/lib/lib32"
export PW_VK_LAYER_PATH="${PW_PLUGINS_PATH}/portable/share/vulkan/implicit_layer.d"
if [[ -z "$PW_TERM" ]] ; then
export PW_TERM="env LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\" python3 -m portprotonqt.easyterm -e"
export PW_TERM="env LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\" python3 -m portprotonqt.scripts_utils.easyterm -e"
fi
export pw_runtime="env"
return 0
@@ -1337,6 +1366,10 @@ ${PW_PV_OVERRIDES}/i386-linux-gnu/aliases:\
export PW_PATH="${PW_PLUGINS_PATH}/portable/bin${PATH:+:$PATH}"
if [[ "$PW_LOG" == "1" ]] ; then
PW_ADD_TO_ARGS_IN_RUNTIME="--verbose $PW_ADD_TO_ARGS_IN_RUNTIME"
fi
if [[ -n "${PW_SANDBOX_HOME_PATH}" && -d "${PW_SANDBOX_HOME_PATH}" ]] ; then
export pw_runtime="${PW_WINELIB}/pressure-vessel/bin/pressure-vessel-unruntime \
--unshare-home \
@@ -1381,7 +1414,7 @@ ${PW_PV_OVERRIDES}/i386-linux-gnu/aliases:\
for PWRTMRW in "${PW_RT_MOUNT_RW[@]}" ; do
export PRESSURE_VESSEL_FILESYSTEMS_RW="${PRESSURE_VESSEL_FILESYSTEMS_RW:+$PRESSURE_VESSEL_FILESYSTEMS_RW:}${PWRTMRW}"
done
export PW_TERM="env LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\" python3 -m portprotonqt.easyterm -e"
export PW_TERM="env LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\" python3 -m portprotonqt.scripts_utils.easyterm -e"
print_info "RUNTIME is enabled"
}
@@ -1623,20 +1656,16 @@ stop_portproton () {
elif [[ "${DESKTOP_SESSION}" =~ "cinnamon" ]] ; then
gsettings set org.cinnamon.muffin unredirect-fullscreen-windows false
elif [[ "${DESKTOP_SESSION}" =~ "deepin" ]] ; then
dbus-send --session --dest=com.deepin.WMSwitcher --type=method_call /com/deepin/WMSwitcher com.deepin.WMSwitcher.RequestSwitchWM
python_module dbus_tools deepin-switch-wm &>/dev/null
fi
fi
kill_portwine
try_remove_dir "${PW_WINELIB}/var"
[[ -n $PW_WINELIB ]] && try_remove_dir "${PW_WINELIB}/var"
find "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/drive_c/" -maxdepth 1 -type f -name "*.tmp" -delete
pw_auto_create_shortcut
add_in_stop_portproton
if [[ -n "$PW_POWERPROFILECTL_PROFILE" ]] \
&& [[ "$(python3 -m portprotonqt.scripts_utils.power_profiles get)" != "$PW_POWERPROFILECTL_PROFILE" ]]
then
python3 -m portprotonqt.scripts_utils.power_profiles set "$PW_POWERPROFILECTL_PROFILE" &>/dev/null
fi
pw_restore_power_profile
if [[ $PW_LOG != 1 ]] && [[ -n $START_PW_TIME_IN_GAME ]] ; then
debug_timer --end -s "PW_TIME_IN_GAME"
@@ -1906,8 +1935,13 @@ pw_init_db () {
fi
else
IFS=$'\n'
#TODO: msi
PW_FIND_DB_FILE=$(grep -ilw "#${PORTWINE_DB}.exe" "${PORT_SCRIPTS_PATH}/portwine_db"/* | sed s/".exe"/""/gi)
if [[ "${PORTWINE_DB,,}" =~ (setup|install|.msi$) ]] ; then
print_info "Force used setup.ppdb files!"
PW_FIND_DB_FILE="${PORT_SCRIPTS_PATH}/portwine_db/Setup.ppdb"
else
PW_FIND_DB_FILE=$(grep -ilw "#${PORTWINE_DB}.exe" "${PORT_SCRIPTS_PATH}/portwine_db"/* | sed s/".exe"/""/gi)
fi
if [[ $(echo "$PW_FIND_DB_FILE" | wc -l) -gt 1 ]] ; then
fatal "More than one ppdb file found!"
elif [[ $(echo "$PW_FIND_DB_FILE" | wc -l) -eq 1 ]] ; then
@@ -1953,7 +1987,7 @@ pw_init_db () {
echo "#Author: ${USER}"
echo "#${PORTWINE_DB}.exe"
echo "#Rating=1-5"
} > "${PW_EXE_FILE}".ppdb
} > "${PW_EXE_FILE}".ppdb || fatal "Read only: $(dirname "${PW_EXE_FILE}")"
export PORTWINE_DB_FILE="${PW_EXE_FILE}".ppdb
fi
if [[ "${PW_EXE_FILE}" =~ "/prefixes/" ]] && \
@@ -1968,11 +2002,6 @@ pw_init_db () {
fi
fi
if [[ -z "${PATH_TO_GAME}" ]] || [[ ! -d "${PATH_TO_GAME}" ]] ; then
PATH_TO_GAME="$( cd "$( dirname "${PW_EXE_FILE}" )" >/dev/null 2>&1 && pwd )"
export PATH_TO_GAME
fi
if [[ "${PW_WINE_CPU_TOPOLOGY}" == "disabled" ]] && [[ -n "${WINE_CPU_TOPOLOGY}" ]] ; then
export PW_WINE_CPU_TOPOLOGY="${WINE_CPU_TOPOLOGY}"
fi
@@ -1989,6 +2018,13 @@ pw_init_db () {
fi
fi
if [[ -z "${PATH_TO_GAME}" ]] || [[ ! -d "$(realpath "${PATH_TO_GAME}")" ]] ; then
if [[ -f "${PW_EXE_FILE}" ]] ; then
PATH_TO_GAME="$(dirname "$(realpath "${PW_EXE_FILE}")")"
export PATH_TO_GAME
fi
fi
return 0
}
@@ -2702,6 +2738,7 @@ start_portproton () {
pw_wineboot -r
echo "based_on_default" >> "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/winetricks.log"
fi
get_and_set_reg_file --add 'Control Panel\Desktop' 'LogPixels' 'REG_DWORD' "96" "user"
else
check_dirs_and_files_in_pfx
if [[ ! -f "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/.wine_ver" ]] \
@@ -2904,11 +2941,12 @@ start_portproton () {
export GAMEMODERUN=0
export PW_GAMEMODERUN_SLR=""
local current_power_profile
current_power_profile=$(python3 -m portprotonqt.scripts_utils.power_profiles get)
current_power_profile=$(python_module power_profiles get)
if [[ -n "$current_power_profile" && "$current_power_profile" != "performance" ]] \
&& python3 -m portprotonqt.scripts_utils.power_profiles set performance &>/dev/null
&& python_module power_profiles set performance &>/dev/null
then
export PW_POWERPROFILECTL_PROFILE="$current_power_profile"
pw_save_power_profile "$current_power_profile"
print_info "Gamemode replaced by power-profiles-daemon/tuned-ppd/tlp-pd via D-Bus to avoid conflicts"
else
export PW_POWERPROFILECTL_PROFILE=""
@@ -3662,7 +3700,7 @@ start_portproton () {
elif [[ "${DESKTOP_SESSION}" =~ "cinnamon" ]] ; then
gsettings set org.cinnamon.muffin unredirect-fullscreen-windows true
elif [[ "${DESKTOP_SESSION}" =~ "deepin" ]] ; then
dbus-send --session --dest=com.deepin.WMSwitcher --type=method_call /com/deepin/WMSwitcher com.deepin.WMSwitcher.RequestSwitchWM
python_module dbus_tools deepin-switch-wm &>/dev/null
fi
fi
@@ -3689,14 +3727,14 @@ pw_run () {
if [[ -n "${PATH_TO_GAME}" ]] \
&& [[ -d "${PATH_TO_GAME}" ]]
then
cd "${PATH_TO_GAME}" || :
:
elif [[ -f "$PW_EXE_FILE" ]]
then
PATH_TO_GAME="$( cd "$( dirname "${PW_EXE_FILE}" )" >/dev/null 2>&1 && pwd )"
cd "${PATH_TO_GAME}" || :
else
cd "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/drive_c" || :
PATH_TO_GAME="${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/drive_c" || :
fi
cd "${PATH_TO_GAME}" || fatal "${PATH_TO_GAME} not found"
if file "$PW_EXE_FILE" | grep -q "x86-64" ; then
check_variables WINE_LARGE_ADDRESS_AWARE "0"
@@ -3715,6 +3753,8 @@ pw_run () {
if [[ "$PW_USE_RUNTIME" == 1 ]] \
&& [[ "$PW_WINE_USE" != "USE_SYSTEM_WINE" ]]
then
unset PORT_SCRIPTS_PATH
export OLDPWD="$(realpath "$PATH_TO_GAME")"
if [[ "${PW_LOG}" == 1 ]] ; then
echo "export PW_BASE_PFX=$PW_BASE_PFX" >> "$PW_LOG_FILE"
echo "WINEDLLOVERRIDES=${WINEDLLOVERRIDES}" >> "${PW_LOG_FILE}"
@@ -4584,7 +4624,10 @@ pw_auto_create_shortcut () {
unset LINKS
print_info "checking *.lnk files"
for link_file in "${PORT_DATA_PATH}"/data/prefixes/*/drive_c/users/${USER}/Desktop/*.lnk ; do
for link_file in "${PORT_DATA_PATH}"/data/prefixes/*/drive_c/users/${USER}/Desktop/*.lnk \
"${PORT_DATA_PATH}"/data/prefixes/*/drive_c/users/Public/Desktop/*.lnk
do
[[ ! -f $link_file ]] && continue
link_file=$(readlink -f "$link_file")
LINKS+=("${link_file// /#@_@#}")
done
@@ -4647,65 +4690,25 @@ pw_create_prefix_backup_cli () {
PW_PREFIX_NAME="$1"
PW_PREFIX_TO_BACKUP="$2"
if [[ -n "$(grep "/${PW_PREFIX_NAME}/" "${PORT_DATA_PATH}"/*.desktop )" ]] ; then
try_remove_file "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/.create_shortcut"
grep "/${PW_PREFIX_NAME}/" "${PORT_DATA_PATH}"/*.desktop | awk -F"/${PW_PREFIX_NAME}/" '{print $2}' \
| awk -F\" '{print $1}' > "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/.create_shortcut"
fi
unset PW_SANDBOX_HOME_PATH
pw_init_runtime
if check_flatpak; then
TMP_ALL_PATH=""
else
TMP_ALL_PATH="LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\""
fi
chmod -R u+w "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}"
echo "#!/usr/bin/env bash
${TMP_ALL_PATH} \"${PW_PLUGINS_PATH}/portable/bin/mksquashfs\" \"${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}\" \
\"${PW_PREFIX_TO_BACKUP}/${PW_PREFIX_NAME}.ppack.part\" -comp zstd \
|| rm -f \"${PW_PREFIX_TO_BACKUP}/${PW_PREFIX_NAME}.ppack.part\"
" > "${PORT_WINE_TMP_PATH}/pp_pfx_backup.sh"
chmod u+x "${PORT_WINE_TMP_PATH}/pp_pfx_backup.sh"
${pw_runtime} ${PW_TERM} "${PORT_WINE_TMP_PATH}/pp_pfx_backup.sh"
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_backup.sh"
if [[ -f "${PW_PREFIX_TO_BACKUP}/${PW_PREFIX_NAME}.ppack.part" ]]; then
mv -f "${PW_PREFIX_TO_BACKUP}/${PW_PREFIX_NAME}.ppack.part" \
"${PW_PREFIX_TO_BACKUP}/${PW_PREFIX_NAME}.ppack"
return 0
else
return 1
fi
python3 -m portprotonqt.scripts_utils.prefix_backup --port-data-path "${PORT_DATA_PATH}" \
backup "${PW_PREFIX_NAME}" "${PW_PREFIX_TO_BACKUP}"
return $?
}
pw_unpack_prefix () {
PW_BACKUP_FILE="$1"
pw_is_legacy_prefix_backup () {
[[ "$(head -c 4 "$1" 2>/dev/null)" == "hsqs" ]]
}
pw_unpack_legacy_prefix () {
PW_PREFIX_NAME=$(basename "${PW_BACKUP_FILE}")
PW_PREFIX_NAME="${PW_PREFIX_NAME%.[Pp][Pp][Aa][Cc][Kk]}"
unset PW_SANDBOX_HOME_PATH
pw_init_runtime
if check_flatpak; then
TMP_ALL_PATH=""
else
TMP_ALL_PATH="LD_LIBRARY_PATH=\"${PW_LD_LIBRARY_PATH}\""
fi
if check_selinux; then
NO_XATTRS_NEED="-no-xattrs"
else
NO_XATTRS_NEED=""
fi
PW_PREFIX_NAME=$(basename "${PW_BACKUP_FILE^^}" .PPACK)
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack_error"
echo "#!/usr/bin/env bash
${TMP_ALL_PATH} \"${PW_PLUGINS_PATH}/portable/bin/unsquashfs\" $NO_XATTRS_NEED -f -d \
unsquashfs -no-xattrs -f -d \
\"${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}\" \"$PW_BACKUP_FILE\" \
|| echo \"ERROR\" > \"${PORT_WINE_TMP_PATH}/pp_pfx_unpack_error\"
" > "${PORT_WINE_TMP_PATH}/pp_pfx_unpack.sh"
@@ -4717,15 +4720,31 @@ pw_unpack_prefix () {
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack_error"
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack.sh"
return 1
else
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack.sh"
if [[ -f "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/.create_shortcut" ]] ; then
while read -r line
do
export PW_EXE_FILE="$PORT_DATA_PATH/data/prefixes/$PW_PREFIX_NAME/$line"
portwine_output_yad_shortcut
done < "$PORT_DATA_PATH/data/prefixes/$PW_PREFIX_NAME/.create_shortcut"
fi
return 0
fi
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack_error"
try_remove_file "${PORT_WINE_TMP_PATH}/pp_pfx_unpack.sh"
if [[ -f "${PORT_DATA_PATH}/data/prefixes/${PW_PREFIX_NAME}/.create_shortcut" ]] ; then
while read -r line
do
export PW_EXE_FILE="$PORT_DATA_PATH/data/prefixes/$PW_PREFIX_NAME/$line"
portwine_output_yad_shortcut
done < "$PORT_DATA_PATH/data/prefixes/$PW_PREFIX_NAME/.create_shortcut"
fi
return 0
}
pw_unpack_prefix () {
PW_BACKUP_FILE="$1"
PW_PREFIX_NAME=$(basename "${PW_BACKUP_FILE}")
PW_PREFIX_NAME="${PW_PREFIX_NAME%.[Pp][Pp][Aa][Cc][Kk]}"
if pw_is_legacy_prefix_backup "${PW_BACKUP_FILE}" ; then
pw_unpack_legacy_prefix
return $?
fi
python3 -m portprotonqt.scripts_utils.prefix_backup --port-data-path "${PORT_DATA_PATH}" \
restore "${PW_BACKUP_FILE}"
return $?
}
+9 -4
View File
@@ -128,7 +128,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
@@ -205,8 +205,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 () {
@@ -403,7 +403,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]}\""
@@ -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"
}
+33 -4
View File
@@ -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 -4
View File
@@ -1,5 +1,5 @@
project('portprotonqt', 'c',
version: '1.0',
version: '1.1.0',
meson_version: '>= 0.61.2',
license: 'GPL-3.0',
)
@@ -131,9 +131,6 @@ 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'
+91 -18
View File
@@ -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.1.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:
@@ -254,9 +267,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 +285,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 +335,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 +351,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 +398,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 +420,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 +452,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 +480,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 +503,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])
+40 -13
View File
@@ -6,7 +6,7 @@ from pathlib import Path
from portprotonqt.steam_api import get_steam_home
LAUNCH_FILE_EXTENSIONS = ('.exe', '.iso', '.mdf')
LAUNCH_FILE_EXTENSIONS = ('.exe', '.bat', '.msi', '.reg', '.iso', '.mdf')
PREFIX_BACKUP_EXTENSION = '.ppack'
@@ -65,7 +65,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,7 +82,7 @@ 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/.msi/.reg/.iso/.mdf) or portproton:// URL"
)
return parser.parse_args()
@@ -156,13 +156,33 @@ 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
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,12 +232,6 @@ 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 "$@"
@@ -290,9 +304,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
+12
View File
@@ -4,9 +4,12 @@ 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
@@ -15,6 +18,9 @@ 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,
@@ -55,7 +61,11 @@ __all__ = [
"FavoritesConfig",
"FavoritesFoldersConfig",
"PortProtonConfig",
"LAUNCH_FILE_EXTENSIONS",
"THEMED_LAUNCH_ICON_NAMES",
"WINDOWS_LAUNCH_EXTENSIONS",
"CONFIG_FILE",
"COUNTER_SKIP_FILE",
"PORTPROTON_CONFIG_FILE",
"CONFIG_DIR",
"THEMES_DIRS",
@@ -83,5 +93,7 @@ __all__ = [
"extract_exec_target_path",
"find_game_by_exe",
"create_desktop_file",
"consume_download_counter_skip",
"reset_main_config",
"update_app_version",
]
+56
View File
@@ -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."""
+12 -2
View File
@@ -307,6 +307,16 @@ def parse_desktop_entry(file_path: str) -> configparser.SectionProxy | None:
return cp["Desktop Entry"]
WINDOWS_LAUNCH_EXTENSIONS = (".exe", ".bat", ".msi", ".reg")
DISC_IMAGE_EXTENSIONS = (".iso", ".mdf")
LAUNCH_FILE_EXTENSIONS = WINDOWS_LAUNCH_EXTENSIONS + DISC_IMAGE_EXTENSIONS
THEMED_LAUNCH_ICON_NAMES = {
".bat": "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 +337,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"):
@@ -388,7 +398,7 @@ def create_desktop_file(
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"
Binary file not shown.

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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

@@ -1,3 +0,0 @@
name=Ankama Launcher
description_ru=Лаунчер для игр Ankama.
description_en=Launcher for Ankama studio games.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 KiB

@@ -1,3 +0,0 @@
name=Fractured Online (ENG)
description_ru=Fractured Online — это первая массовая многопользовательская ролевая онлайн-игра с открытым миром, сочетающая динамичные сражения с полностью интерактивным окружением. Она одинаково понравится любителям соревновательного и кооперативного игрового процесса. С самого первого дня погрузитесь в бой. Побеждайте врагов благодаря собственным навыкам и смекалке, а не снаряжению или уровню. Собирайте ресурсы, создавайте предметы, торгуйте и отправляйтесь в легендарные путешествия в одиночку или создайте поселение со своей гильдией и превратите его в следующую империю.
description_en=Fractured Online is the first open-world sandbox MMORPG mixing action combat with fully interactable environments, appealing equally to lovers of competitive and cooperative gameplay. Jump right into the fray from day one. Defeat your enemies through your own skill and cleverness, not equipment or level. Gather resources, craft, trade and venture into legendary travels as a solitary hero, or start a settlement with your guild and grow it into the next empire.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

@@ -1,3 +0,0 @@
name=Goose Goose Duck
description_ru=Многопользовательская игра в жанре социальной дедукции, где игроки выступают в роли уток или гусей, пытаясь выполнить задания и выявить среди них "уток" — предателей. Игра сочетает в себе элементы стратегии и общения, требуя от игроков координации и способности распознавать обман.
description_en=A multiplayer social deduction game where players take on the roles of ducks or geese, trying to complete tasks and identify the "ducks" — the impostors among them. The game combines elements of strategy and communication, requiring players to coordinate and recognize deception.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 KiB

@@ -1,3 +0,0 @@
name=GOG Galaxy Launcher
description_ru=Лаунчер для библиотеки игр GOG.
description_en=Launcher for the GOG game library.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

@@ -1,3 +0,0 @@
name=Guild Wars 2
description_ru=MMORPG с ярким миром и уникальной системой динамических событий, где игроки могут свободно исследовать просторы Тираи и участвовать в масштабных сражениях. Игра предлагает разнообразие рас и классов, а также акцент на совместной игре и взаимодействии между игроками.
description_en=An MMORPG with a vibrant world and a unique system of dynamic events, where players can freely explore the realms of Tyria and engage in large-scale battles. The game offers a variety of races and classes, with an emphasis on cooperative play and player interaction.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

@@ -1,3 +0,0 @@
name=Indiegala Client
description_ru=Лаунчер для библиотеки игр Indiegala.
description_en=Launcher for the Indiegala game library.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 833 KiB

-3
View File
@@ -1,3 +0,0 @@
name=Last Chaos
description_ru=Last Chaos – классическая MMORPG с шестью классами, осадами замков, корейским гриндом и километрами подземелий. Противостояние Апполона и Эреса набирает обороты, так что спешите принять одну из сторон.
description_en=Last Chaos is a classic MMORPG with six classes, castle sieges, a Korean grind and kilometers of dungeons. The confrontation between Apollo and Eres is gaining momentum, so hurry up to take one of the sides.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 KiB

@@ -1,3 +0,0 @@
name=DC Universe Online (ENG)
description_ru=MMORPG, в которой игроки создают собственных супергероев или суперзлодеев во вселенной DC Comics и участвуют в эпических битвах с известными персонажами, такими как Супермен и Бэтмен. Игра предлагает обширные квесты, захватывающие PvP-режимы и возможность совместной игры с другими игроками.
description_en=An MMORPG where players create their own superheroes or supervillains in the DC Comics universe and engage in epic battles alongside iconic characters like Superman and Batman. The game features extensive quests, exciting PvP modes, and the ability to team up with other players.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 KiB

@@ -1,3 +0,0 @@
name=The Lord of the Rings Online (ENG)
description_ru=MMORPG, основанная на произведениях Дж. Р. Р. Толкиена, позволяющая игрокам исследовать Средиземье, принимать участие в эпических квестах и сражениях с известными персонажами и созданиями из вселенной Властелина колец. Игра предлагает глубокую кастомизацию персонажей, богатый сюжет и множество возможностей для игры в одиночку или в группе.
description_en=An MMORPG based on the works of J. R. R. Tolkien, allowing players to explore Middle-earth, take part in epic quests and battles with famous characters and creatures from the Lord of the Rings universe. The game offers deep customization of characters, a rich plot and many opportunities to play alone or in a group.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 KiB

@@ -1,3 +0,0 @@
name=Metal War Online
description_ru=Многопользовательская онлайн-игра в жанре экшен с элементами стратегии, где игроки управляют боевыми роботами и сражаются на различных аренах. Игра предлагает богатую кастомизацию техники и разнообразные игровые режимы, позволяя каждому игроку создавать уникальный стиль боя.
description_en=A multiplayer online action game with strategic elements, where players control combat robots and battle on various arenas. The game offers extensive customization options for the machines and a variety of game modes, allowing each player to create a unique fighting style.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 KiB

@@ -1,3 +0,0 @@
name=Mini World
description_ru=Песочница, в которой игроки могут исследовать, строить и создавать свои уникальные миры в 3D-окружении. Игра предлагает разнообразные режимы, включая выживание и творчество, а также возможность взаимодействовать с другими игроками в многопользовательском формате.
description_en=A sandbox game where players can explore, build, and create their unique worlds in a 3D environment. The game offers various modes, including survival and creative, as well as the ability to interact with other players in a multiplayer format.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 KiB

@@ -1,3 +0,0 @@
name=The World of Nifty Craft (ENG)
description_ru=Многопользовательская онлайн-игра в жанре песочницы, где игроки могут строить, исследовать и развиваться в ярком и разнообразном мире, наполненном приключениями. Игра предлагает систему крафта, уникальные возможности для творчества и взаимодействия с другими игроками.
description_en=A multiplayer online sandbox game where players can build, explore, and progress in a vibrant and diverse world filled with adventures. The game features a crafting system, unique opportunities for creativity, and interaction with other players.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

@@ -1,3 +0,0 @@
name=OSU
description_ru=Ритм-игра, в которой игроки нажимают кружки, следуя музыке и ритму. Игра предлагает различные режимы и комьюнити, где пользователи могут создавать и делиться собственными картами.
description_en=A rhythm game where players click circles in time with music and rhythm. The game features various modes and a community where users can create and share their own maps.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

@@ -1,3 +0,0 @@
name=PPSSPP
description_ru=Эмулятор портативной игровой консоли PlayStation Portable (PSP), позволяющий запускать игры PSP на компьютерах и мобильных устройствах. Он поддерживает улучшенную графику и различные настройки, что позволяет наслаждаться классическими играми с высоким качеством.
description_en=An emulator for the PlayStation Portable (PSP) handheld gaming console, enabling users to play PSP games on computers and mobile devices. It supports enhanced graphics and various settings, allowing players to enjoy classic games at high quality.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 814 KiB

@@ -1,3 +0,0 @@
name=Panzar
description_ru=Многопользовательская онлайн-игра в жанре экшен, где игроки сражаются в командах, используя различные классы персонажей и уникальные способности. Игра предлагает тактические сражения на больших картах с разрушительным взаимодействием окружения.
description_en=A multiplayer online action game where players battle in teams using various character classes and unique abilities. The game features tactical battles on large maps with destructible environments.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 KiB

@@ -1,3 +0,0 @@
name=Path of Exile
description_ru=Бесплатная онлайн-игра в жанре action RPG, в которой игроки исследуют мрачный мир, сражаются с монстрами и развивают свои персонажи через глубокую систему навыков и предметов. Игра известна своим сложным геймплеем и постоянно обновляемым контентом, включая сезонные лиги и уникальные механики.
description_en=A free online action RPG where players explore a dark world, fight monsters, and develop their characters through a deep skill and item system. The game is known for its challenging gameplay and constantly updated content, including seasonal leagues and unique mechanics.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

@@ -1,3 +0,0 @@
name=Plarium Play
description_ru=Лаунчер для игр Plarium.
description_en=Launcher for Plarium studio games.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

@@ -1,3 +0,0 @@
name=Project64
description_ru=Эмулятор Nintendo 64, который позволяет играть в игры этой консоли на современных устройствах. Поддерживает различные настройки графики и управления, обеспечивая высококачественный игровой опыт.
description_en=The Nintendo 64 emulator, which allows you to play games of this console on modern devices. Supports various graphics and control settings, providing a high-quality gaming experience.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

@@ -1,3 +0,0 @@
name=Russian Fishing 4
description_ru=Реалистичный симулятор рыбалки, который предлагает игрокам погрузиться в увлекательный процесс ловли рыбы в различных красивых водоемах России. Игра включает в себя широкий выбор снастей, видов рыб и возможностей для соревнований с другими рыбаками.
description_en=A realistic fishing simulator that immerses players in the enjoyable process of catching fish in various beautiful bodies of water across Russia. The game features a wide selection of tackle, fish species, and opportunities for competition with other anglers.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 KiB

Loaded 100 of 219 files, more files were not shown because too many files have changed in this diff. Show more