From 8b2b2c88f59ec5728023c67a8d6fc62358f04280 Mon Sep 17 00:00:00 2001
From: Boris Yumankulov <boria138@altlinux.org>
Date: Fri, 6 Jun 2025 19:59:41 +0500
Subject: [PATCH] feat(input-manager): toggle fullscreen mode with gamepad
 Select button

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
---
 portprotonqt/config_utils.py  | 35 ++++++++++++++++++++++++++++++
 portprotonqt/input_manager.py | 18 +++++++++++-----
 portprotonqt/main_window.py   | 40 +++++++++++++++++++++++------------
 3 files changed, 74 insertions(+), 19 deletions(-)

diff --git a/portprotonqt/config_utils.py b/portprotonqt/config_utils.py
index 453b192..3c9ca86 100644
--- a/portprotonqt/config_utils.py
+++ b/portprotonqt/config_utils.py
@@ -482,3 +482,38 @@ def clear_cache():
             logger.info("Кэш PortProtonQT удалён: %s", cache_dir)
         except Exception as e:
             logger.error("Ошибка при удалении кэша: %s", e)
+
+def read_auto_fullscreen_gamepad():
+    """
+    Читает настройку автоматического полноэкранного режима при подключении геймпада из секции [Display].
+    Если параметр отсутствует, сохраняет и возвращает False по умолчанию.
+    """
+    cp = configparser.ConfigParser()
+    if os.path.exists(CONFIG_FILE):
+        try:
+            cp.read(CONFIG_FILE, encoding="utf-8")
+        except Exception as e:
+            logger.error("Ошибка чтения конфигурационного файла: %s", e)
+            save_auto_fullscreen_gamepad(False)
+            return False
+        if not cp.has_section("Display") or not cp.has_option("Display", "auto_fullscreen_gamepad"):
+            save_auto_fullscreen_gamepad(False)
+            return False
+        return cp.getboolean("Display", "auto_fullscreen_gamepad", fallback=False)
+    return False
+
+def save_auto_fullscreen_gamepad(auto_fullscreen):
+    """
+    Сохраняет настройку автоматического полноэкранного режима при подключении геймпада в секцию [Display].
+    """
+    cp = configparser.ConfigParser()
+    if os.path.exists(CONFIG_FILE):
+        try:
+            cp.read(CONFIG_FILE, encoding="utf-8")
+        except (configparser.DuplicateSectionError, configparser.DuplicateOptionError) as e:
+            logger.error("Ошибка чтения конфигурационного файла: %s", e)
+    if "Display" not in cp:
+        cp["Display"] = {}
+    cp["Display"]["auto_fullscreen_gamepad"] = str(auto_fullscreen)
+    with open(CONFIG_FILE, "w", encoding="utf-8") as configfile:
+        cp.write(configfile)
diff --git a/portprotonqt/input_manager.py b/portprotonqt/input_manager.py
index 28becc8..daaafbc 100644
--- a/portprotonqt/input_manager.py
+++ b/portprotonqt/input_manager.py
@@ -10,7 +10,7 @@ from portprotonqt.logger import get_logger
 from portprotonqt.image_utils import FullscreenDialog
 from portprotonqt.custom_widgets import NavLabel
 from portprotonqt.game_card import GameCard
-from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry
+from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad
 
 logger = get_logger(__name__)
 
@@ -41,7 +41,7 @@ BUTTONS = {
     'next_tab':  {ecodes.BTN_TR, ecodes.BTN_TRIGGER_HAPPY5},
     'confirm_stick': {ecodes.BTN_THUMBL, ecodes.BTN_THUMBR},
     'context_menu': {ecodes.BTN_START},
-    'menu':      {ecodes.BTN_SELECT, ecodes.BTN_MODE},
+    'menu':      {ecodes.BTN_SELECT},
 }
 
 class InputManager(QObject):
@@ -595,8 +595,11 @@ class InputManager(QObject):
                     self.gamepad_thread.join()
                 self.gamepad_thread = threading.Thread(target=self.monitor_gamepad, daemon=True)
                 self.gamepad_thread.start()
-                # Signal to enter fullscreen mode
-                self.toggle_fullscreen.emit(True)
+                # Отправляем сигнал для полноэкранного режима только если:
+                # 1. auto_fullscreen_gamepad включено
+                # 2. fullscreen выключено (чтобы не конфликтовать с основной настройкой)
+                if read_auto_fullscreen_gamepad() and not read_fullscreen_config():
+                    self.toggle_fullscreen.emit(True)
         except Exception as e:
             logger.error(f"Error checking gamepad: {e}", exc_info=True)
 
@@ -623,7 +626,12 @@ class InputManager(QObject):
                     continue
                 now = time.time()
                 if event.type == ecodes.EV_KEY and event.value == 1:
-                    self.button_pressed.emit(event.code)
+                    # Обработка кнопки Select для переключения полноэкранного режима
+                    if event.code in BUTTONS['menu']:
+                        # Переключаем полноэкранный режим
+                        self.toggle_fullscreen.emit(not self._is_fullscreen)
+                    else:
+                        self.button_pressed.emit(event.code)
                 elif event.type == ecodes.EV_ABS:
                     self.dpad_moved.emit(event.code, event.value, now)
         except OSError as e:
diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py
index 964f5a3..b31f398 100644
--- a/portprotonqt/main_window.py
+++ b/portprotonqt/main_window.py
@@ -20,9 +20,12 @@ from portprotonqt.egs_api import load_egs_games_async
 from portprotonqt.theme_manager import ThemeManager, load_theme_screenshots, load_logo
 from portprotonqt.time_utils import save_last_launch, get_last_launch, parse_playtime_file, format_playtime, get_last_launch_timestamp, format_last_launch
 from portprotonqt.config_utils import (
-    get_portproton_location, read_theme_from_config, save_theme_to_config, parse_desktop_entry, load_theme_metainfo, read_time_config, read_card_size, save_card_size,
-    read_sort_method, read_display_filter, read_favorites, save_favorites, save_time_config, save_sort_method, save_display_filter, save_proxy_config, read_proxy_config,
-    read_fullscreen_config, save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config, clear_cache
+    get_portproton_location, read_theme_from_config, save_theme_to_config, parse_desktop_entry,
+    load_theme_metainfo, read_time_config, read_card_size, save_card_size, read_sort_method,
+    read_display_filter, read_favorites, save_favorites, save_time_config, save_sort_method,
+    save_display_filter, save_proxy_config, read_proxy_config, read_fullscreen_config,
+    save_fullscreen_config, read_window_geometry, save_window_geometry, reset_config,
+    clear_cache, read_auto_fullscreen_gamepad, save_auto_fullscreen_gamepad
 )
 from portprotonqt.localization import _
 from portprotonqt.logger import get_logger
@@ -976,7 +979,17 @@ class MainWindow(QMainWindow):
         self.fullscreenCheckBox.setChecked(current_fullscreen)
         formLayout.addRow(self.fullscreenTitle, self.fullscreenCheckBox)
 
-        # 6. Legendary Authentication
+        # 6. Automatic fullscreen on gamepad connection
+        self.autoFullscreenGamepadCheckBox = QCheckBox(_("Auto Fullscreen on Gamepad connected"))
+        self.autoFullscreenGamepadCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+        self.autoFullscreenGamepadTitle = QLabel(_("Auto Fullscreen on Gamepad connected:"))
+        self.autoFullscreenGamepadTitle.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
+        self.autoFullscreenGamepadTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
+        current_auto_fullscreen = read_auto_fullscreen_gamepad()
+        self.autoFullscreenGamepadCheckBox.setChecked(current_auto_fullscreen)
+        formLayout.addRow(self.autoFullscreenGamepadTitle, self.autoFullscreenGamepadCheckBox)
+
+        # 7. Legendary Authentication
         self.legendaryAuthButton = AutoSizeButton(
             _("Open Legendary Login"),
             icon=self.theme_manager.get_icon("login")
@@ -1155,6 +1168,9 @@ class MainWindow(QMainWindow):
         fullscreen = self.fullscreenCheckBox.isChecked()
         save_fullscreen_config(fullscreen)
 
+        auto_fullscreen_gamepad = self.autoFullscreenGamepadCheckBox.isChecked()
+        save_auto_fullscreen_gamepad(auto_fullscreen_gamepad)
+
         for card in self.game_card_cache.values():
             card.update_badge_visibility(filter_key)
 
@@ -1170,18 +1186,14 @@ class MainWindow(QMainWindow):
 
         self.settings_saved.emit()
 
-        if fullscreen:
+        # Управление полноэкранным режимом
+        gamepad_connected = self.input_manager.find_gamepad() is not None
+        if fullscreen or (auto_fullscreen_gamepad and gamepad_connected):
             self.showFullScreen()
         else:
-            if self.isFullScreen():
-                # Переходим в нормальный режим и восстанавливаем сохраненные размеры
-                width, height = read_window_geometry()
-                self.showNormal()
-                if width > 0 and height > 0:
-                    self.resize(width, height)
-            # Сохраняем геометрию только если окно не в полноэкранном режиме
-            if not self.isFullScreen():
-                save_window_geometry(self.width(), self.height())
+            # Если обе галочки сняты и геймпад не подключен, возвращаем нормальное состояние
+            self.showNormal()
+            self.resize(*read_window_geometry())  # Восстанавливаем сохраненные размеры окна
 
         self.statusBar().showMessage(_("Settings saved"), 3000)