diff --git a/portprotonqt/config_utils.py b/portprotonqt/config_utils.py index cf40558..2066d2c 100644 --- a/portprotonqt/config_utils.py +++ b/portprotonqt/config_utils.py @@ -322,6 +322,41 @@ def save_favorites(favorites): with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: cp.write(configfile) +def read_rumble_config(): + """ + Читает настройку виброотдачи геймпада из секции [Gamepad]. + Если параметр отсутствует, сохраняет и возвращает 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_rumble_config(False) + return False + if not cp.has_section("Gamepad") or not cp.has_option("Gamepad", "rumble_enabled"): + save_rumble_config(False) + return False + return cp.getboolean("Gamepad", "rumble_enabled", fallback=False) + return False + +def save_rumble_config(rumble_enabled): + """ + Сохраняет настройку виброотдачи геймпада в секцию [Gamepad]. + """ + 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 "Gamepad" not in cp: + cp["Gamepad"] = {} + cp["Gamepad"]["rumble_enabled"] = str(rumble_enabled) + with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: + cp.write(configfile) + def ensure_default_proxy_config(): """ Проверяет наличие секции [Proxy] в конфигурационном файле. @@ -342,7 +377,6 @@ def ensure_default_proxy_config(): with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: cp.write(configfile) - def read_proxy_config(): """ Читает настройки прокси из секции [Proxy] конфигурационного файла. @@ -421,8 +455,6 @@ def save_fullscreen_config(fullscreen): with open(CONFIG_FILE, "w", encoding="utf-8") as configfile: cp.write(configfile) - - def read_window_geometry() -> tuple[int, int]: """ Читает ширину и высоту окна из секции [MainWindow] конфигурационного файла. diff --git a/portprotonqt/input_manager.py b/portprotonqt/input_manager.py index 3640bfe..9713485 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, read_auto_fullscreen_gamepad +from portprotonqt.config_utils import read_fullscreen_config, read_window_geometry, save_window_geometry, read_auto_fullscreen_gamepad, read_rumble_config logger = get_logger(__name__) @@ -128,6 +128,8 @@ class InputManager(QObject): def trigger_rumble(self, duration_ms: int = 200, strong_magnitude: int = 0x8000, weak_magnitude: int = 0x8000) -> None: """Trigger a rumble effect on the gamepad if supported.""" + if not read_rumble_config(): + return if not self.gamepad: return try: diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py index bc0dd50..46e0414 100644 --- a/portprotonqt/main_window.py +++ b/portprotonqt/main_window.py @@ -26,7 +26,7 @@ from portprotonqt.config_utils import ( 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 + clear_cache, read_auto_fullscreen_gamepad, save_auto_fullscreen_gamepad, read_rumble_config, save_rumble_config ) from portprotonqt.localization import _ from portprotonqt.logger import get_logger @@ -997,6 +997,17 @@ class MainWindow(QMainWindow): self.autoFullscreenGamepadCheckBox.setChecked(current_auto_fullscreen) formLayout.addRow(self.autoFullscreenGamepadTitle, self.autoFullscreenGamepadCheckBox) + # 7. Gamepad haptic feedback config + self.gamepadRumbleCheckBox = QCheckBox(_("Gamepad haptic feedback")) + self.gamepadRumbleCheckBox.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + self.gamepadRumbleCheckBox.setStyleSheet(self.theme.SETTINGS_CHECKBOX_STYLE) + self.gamepadRumbleTitle = QLabel(_("Gamepad haptic feedback:")) + self.gamepadRumbleTitle.setStyleSheet(self.theme.PARAMS_TITLE_STYLE) + self.gamepadRumbleTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus) + current_rumble_state = read_rumble_config() + self.gamepadRumbleCheckBox.setChecked(current_rumble_state) + formLayout.addRow(self.gamepadRumbleTitle, self.gamepadRumbleCheckBox) + layout.addLayout(formLayout) # Кнопки @@ -1117,6 +1128,10 @@ class MainWindow(QMainWindow): auto_fullscreen_gamepad = self.autoFullscreenGamepadCheckBox.isChecked() save_auto_fullscreen_gamepad(auto_fullscreen_gamepad) + # Сохранение настройки виброотдачи геймпада + rumble_enabled = self.gamepadRumbleCheckBox.isChecked() + save_rumble_config(rumble_enabled) + for card in self.game_card_cache.values(): card.update_badge_visibility(filter_key)