5 Commits

Author SHA1 Message Date
b8773f3b68 chore(build): added Debian
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-12-03 21:26:55 +05:00
f4275dd465 fix(get_portproton_start_command): Check if flatpak command exists before trying to run it
All checks were successful
Code check / Check code (push) Successful in 1m7s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-12-02 18:44:47 +05:00
c8b91c4687 fix(settings): update keyboard navigation
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-12-02 18:40:27 +05:00
4aaeb2e809 fix: dont start game by Enter
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-12-02 18:23:49 +05:00
b6ea9350fa fix: fix gamecard refrefresh regression after 0889aa8
All checks were successful
Code check / Check code (push) Successful in 1m13s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2025-12-02 17:52:19 +05:00
15 changed files with 239 additions and 27 deletions

View File

@@ -8,6 +8,36 @@ env:
PACKAGE: "portprotonqt"
jobs:
build-deb:
name: Build Debian Package
runs-on: ubuntu-22.04
steps:
- name: Install build dependencies
run: |
apt-get update
apt-get install -y build-essential git python3-dev python3-pip python3-wheel debhelper dh-python \
devscripts python3-all bash-completion python3-setuptools
- name: Checkout repo
uses: https://gitea.com/actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- name: Build Debian package
run: |
# Copy the debian directory to the root for proper packaging
cp -r build-aux/debian .
# Build the package
dpkg-buildpackage -us -uc -b
mv ../*.deb .
- name: Upload Debian package
uses: https://gitea.com/actions/gitea-upload-artifact@v4
with:
name: PortProtonQt-Debian
path: "*.deb"
build-appimage:
name: Build AppImage
runs-on: ubuntu-22.04

View File

@@ -147,9 +147,37 @@ jobs:
name: PortProtonQt-RPM-Fedora-${{ matrix.fedora_version }}
path: /home/rpmbuild/RPMS/**/*.rpm
build-deb:
name: Build Debian Package
runs-on: ubuntu-22.04
steps:
- name: Install build dependencies
run: |
apt-get update
apt-get install -y build-essential git python3-dev python3-pip python3-wheel debhelper dh-python \
devscripts python3-all bash-completion python3-setuptools
- name: Checkout repo
uses: https://gitea.com/actions/checkout@v4
- name: Build Debian package
run: |
# Copy the debian directory to the root for proper packaging
cp -r build-aux/debian .
# Build the package
dpkg-buildpackage -us -uc -b
- name: Upload Debian package
uses: https://gitea.com/actions/gitea-upload-artifact@v4
with:
name: PortProtonQt-Debian
path: "*.deb"
release:
name: Create and Publish Release
needs: [build-appimage, build-arch, build-fedora]
needs: [build-appimage, build-arch, build-fedora, build-deb]
runs-on: ubuntu-latest
steps:
- uses: https://gitea.com/actions/checkout@v4

View File

@@ -16,7 +16,7 @@ source .venv/bin/activate
### Установка (release)
Выберите подходящий пакет для вашей системы или AppImage.
Выберите подходящий пакет для вашей системы (deb, rpm, arch) или AppImage.
Запуск производится по команде portprotonqt или по ярлыку в меню

View File

@@ -0,0 +1,5 @@
portprotonqt (0.1.9-1) unstable; urgency=medium
* Initial release for Debian packaging
-- Boris Yumankulov <boria138@altlinux.org> Wed, 03 Dec 2025 12:00:00 +0000

44
build-aux/debian/control Normal file
View File

@@ -0,0 +1,44 @@
Source: portprotonqt
Section: python
Priority: optional
Maintainer: Boris Yumankulov <boria138@altlinux.org>
Build-Depends: debhelper-compat (= 13),
dh-python,
python3-all,
python3-setuptools,
python3-pip
Standards-Version: 4.6.0
Homepage: https://git.linux-gaming.ru/Boria138/PortProtonQt
Rules-Requires-Root: no
X-Python3-Version: >= 3.10
Package: portprotonqt
Architecture: all
Depends: ${python3:Depends}, ${misc:Depends},
python3-babel,
python3-beautifulsoup4,
python3-evdev,
python3-icoextract,
python3-numpy,
python3-orjson,
python3-pillow,
python3-psutil,
python3-pyside6,
python3-pyudev,
python3-rapidfuzz,
python3-requests,
python3-tqdm,
python3-vdf,
python3-websocket-client,
perl-image-exiftool,
xdg-utils,
cabextract,
curl,
gzip,
unzip,
unrar
Description: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store.
It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization.
Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers.
Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.

View File

@@ -0,0 +1,24 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: PortProtonQt
Source: https://git.linux-gaming.ru/Boria138/PortProtonQt
Files: *
Copyright: 2023-2025 Boria138, BlackSnaker, Mikhail Tergoev(Castro-Fidel)
License: GPL-3.0+
License: GPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GPL-3.0+ license
can be found in /usr/share/common-licenses/GPL-3.

25
build-aux/debian/rules Executable file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/make -f
%:
dh $@ --with python3
override_dh_auto_build:
python3 -m pip install --user --no-deps --ignore-installed .
override_dh_auto_install:
# Install Python modules
python3 -m pip install --no-deps --ignore-installed --prefix=$(CURDIR)/debian/portprotonqt/usr .
# Install desktop files and icons
install -d $(CURDIR)/debian/portprotonqt/usr/share/applications
install -m 644 $(CURDIR)/../build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop \
$(CURDIR)/debian/portprotonqt/usr/share/applications/
install -d $(CURDIR)/debian/portprotonqt/usr/share/icons/hicolor/scalable/apps
install -m 644 $(CURDIR)/../build-aux/share/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg \
$(CURDIR)/debian/portprotonqt/usr/share/icons/hicolor/scalable/apps/
# Install metainfo file
install -d $(CURDIR)/debian/portprotonqt/usr/share/metainfo
install -m 644 $(CURDIR)/../build-aux/share/metainfo/ru.linux_gaming.PortProtonQt.metainfo.xml \
$(CURDIR)/debian/portprotonqt/usr/share/metainfo/

View File

@@ -0,0 +1 @@
3.0 (quilt)

View File

@@ -0,0 +1,7 @@
extend-diff-ignore = "^[^/]*[.]egg-info/"
compression = "gzip"
tar-ignore = "dev-scripts"
tar-ignore = ".*"
tar-ignore = "__pycache__"
tar-ignore = "build-aux"
tar-ignore = "data"

View File

@@ -0,0 +1,2 @@
"""PortProtonQt - A project to rewrite PortProton (PortWINE) using PySide."""
__version__ = "0.1.9"

View File

@@ -183,23 +183,39 @@ def get_portproton_start_command():
if not portproton_path:
return None
# Check if flatpak command exists before trying to run it
try:
result = subprocess.run(
["flatpak", "list"],
subprocess.run(
["flatpak", "--version"],
capture_output=True,
text=True,
check=False,
timeout=10
timeout=5
)
if "ru.linux_gaming.PortProton" in result.stdout:
logger.info("Detected Flatpak installation")
return ["flatpak", "run", "ru.linux_gaming.PortProton"]
except subprocess.TimeoutExpired:
logger.warning("Flatpak list command timed out")
return None
except Exception as e:
logger.warning(f"Error checking flatpak list: {e}")
pass
flatpak_available = True
except FileNotFoundError:
flatpak_available = False
except Exception:
flatpak_available = False
if flatpak_available:
try:
result = subprocess.run(
["flatpak", "list"],
capture_output=True,
text=True,
check=False,
timeout=10
)
if "ru.linux_gaming.PortProton" in result.stdout:
logger.info("Detected Flatpak installation")
return ["flatpak", "run", "ru.linux_gaming.PortProton"]
except subprocess.TimeoutExpired:
logger.warning("Flatpak list command timed out")
return None
except Exception as e:
logger.warning(f"Error checking flatpak list: {e}")
pass
start_sh_path = os.path.join(portproton_path, "data", "scripts", "start.sh")
if os.path.exists(start_sh_path):

View File

@@ -505,17 +505,25 @@ class GameLibraryManager:
"""Clears all widgets from the layout."""
if layout is None:
return
# Remove all widgets from the layout and clean up caches
while layout.count():
child = layout.takeAt(0)
if child.widget():
widget = child.widget()
# Clean up cache if widget exists in it
for key, card in list(self.game_card_cache.items()):
if card == widget:
del self.game_card_cache[key]
if key in self.pending_images:
del self.pending_images[key]
break
# Always schedule widget for deletion regardless of cache state
widget.deleteLater()
# Also clear the cache completely if needed (in case layout wasn't in sync)
self.game_card_cache.clear()
self.pending_images.clear()
def set_games(self, games: list[tuple]):
"""Sets the games list and updates the filtered games."""
self.games = games

View File

@@ -1939,8 +1939,10 @@ class InputManager(QObject):
active_win.show_next()
return True # Consume event to prevent tab switching
# Handle tab switching with Left/Right arrow keys when not in GameCard focus or QLineEdit
if key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and not isinstance(focused, GameCard | QLineEdit) and not self.file_explorer:
# Handle tab switching with Left/Right arrow keys when not in GameCard focus or QLineEdit or QTableWidget or AutoSizeButton
if (key in (Qt.Key.Key_Left, Qt.Key.Key_Right) and
not isinstance(focused, GameCard | QLineEdit | QTableWidget | AutoSizeButton) and
not self.file_explorer):
idx = self._parent.stackedWidget.currentIndex()
total = len(self._parent.tabButtons)
if key == Qt.Key.Key_Left:
@@ -1976,12 +1978,6 @@ class InputManager(QObject):
self.dpad_moved.emit(dpad_code, dpad_value, now)
return True
# Launch/stop game on detail page
if self._parent.currentDetailPage and key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
if self._parent.current_exec_line:
self._parent.toggleGame(self._parent.current_exec_line, None)
return True
# Context menu for GameCard
if isinstance(focused, GameCard):
if key == Qt.Key.Key_F10 and modifiers & Qt.KeyboardModifier.ShiftModifier:
@@ -1991,6 +1987,18 @@ class InputManager(QObject):
# General actions: Activate, Back, Add
if key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
# Special handling for table widgets with checkboxes
if isinstance(focused, QTableWidget):
current_row = focused.currentRow()
current_col = focused.currentColumn()
if current_row >= 0 and current_col >= 0:
# Check if the cell contains a checkbox
item = focused.item(current_row, current_col)
if item and (item.flags() & Qt.ItemFlag.ItemIsUserCheckable):
# Toggle the checkbox state
new_state = Qt.CheckState.Checked if item.checkState() == Qt.CheckState.Unchecked else Qt.CheckState.Unchecked
item.setCheckState(new_state)
return True
self._parent.activateFocusedWidget()
return True
elif key in (Qt.Key.Key_Escape, Qt.Key.Key_Backspace):

View File

@@ -1199,7 +1199,7 @@ class MainWindow(QMainWindow):
self.progress_bar.setRange(0, 0) # Indeterminate
self.update_status_message.emit(_("Refreshing game library..."), 0)
# Clear the game card cache to force reload of custom data
# Clear the game card cache and layout to force reload of custom data
if hasattr(self, 'game_library_manager') and self.game_library_manager:
# Clear the cache to ensure custom data is reloaded
self.game_library_manager.game_card_cache.clear()
@@ -1209,6 +1209,19 @@ class MainWindow(QMainWindow):
# Mark for full rebuild of search indices
self.game_library_manager.dirty = True # Force full update
# Also clear the layout to ensure old widgets are removed
if (hasattr(self.game_library_manager, 'gamesListLayout') and
self.game_library_manager.gamesListLayout and
hasattr(self.game_library_manager, 'gamesListWidget') and
self.game_library_manager.gamesListWidget):
# Remove all widgets from the layout
self.game_library_manager.clear_layout(self.game_library_manager.gamesListLayout)
# Force layout update to ensure UI changes are visible
self.game_library_manager.gamesListWidget.updateGeometry()
if hasattr(self.game_library_manager, 'gamesListLayout'):
self.game_library_manager.gamesListLayout.update()
# Reload games using the existing loadGames functionality
# Use a small delay to allow UI to update before starting the refresh
QTimer.singleShot(50, lambda: self.loadGames())

View File

@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools >= 77.0.3"]
requires = ["setuptools >= 77.0.3", "wheel"]
build-backend = "setuptools.build_meta"
[project]
@@ -46,12 +46,13 @@ dependencies = [
[project.scripts]
portprotonqt = "portprotonqt.app:main"
[tool.setuptools]
packages = ["portprotonqt"]
include-package-data = true
[tool.setuptools.package-data]
"portprotonqt" = ["themes/**/*", "locales/**/*"]
[tool.setuptools.packages.find]
exclude = ["build-aux", "dev-scripts", "documentation", "data"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors