Compare commits
3 Commits
6555b42839
...
e162dc3c17
Author | SHA1 | Date | |
---|---|---|---|
e162dc3c17 | |||
f47dce1fb7 | |||
dbf1340f88
|
@ -199,7 +199,7 @@ class GameCard(QFrame):
|
|||||||
icon_size=16,
|
icon_size=16,
|
||||||
icon_space=3,
|
icon_space=3,
|
||||||
)
|
)
|
||||||
self.anticheatLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
|
self.anticheatLabel.setStyleSheet(self.theme.get_anticheat_badge_style(anticheat_status))
|
||||||
self.anticheatLabel.setFixedWidth(int(card_width * 2/3))
|
self.anticheatLabel.setFixedWidth(int(card_width * 2/3))
|
||||||
anticheat_visible = True
|
anticheat_visible = True
|
||||||
else:
|
else:
|
||||||
|
@ -261,19 +261,25 @@ class MainWindow(QMainWindow):
|
|||||||
self.update_status_message.emit
|
self.update_status_message.emit
|
||||||
)
|
)
|
||||||
elif display_filter == "favorites":
|
elif display_filter == "favorites":
|
||||||
def on_all_games(portproton_games, steam_games):
|
def on_all_games(portproton_games, steam_games, epic_games):
|
||||||
games = [game for game in portproton_games + steam_games if game[0] in favorites]
|
games = [game for game in portproton_games + steam_games + epic_games if game[0] in favorites]
|
||||||
self.games_loaded.emit(games)
|
self.games_loaded.emit(games)
|
||||||
self._load_portproton_games_async(
|
self._load_portproton_games_async(
|
||||||
lambda pg: self._load_steam_games_async(
|
lambda pg: self._load_steam_games_async(
|
||||||
lambda sg: on_all_games(pg, sg)
|
lambda sg: load_egs_games_async(
|
||||||
|
self.legendary_path,
|
||||||
|
lambda eg: on_all_games(pg, sg, eg),
|
||||||
|
self.downloader,
|
||||||
|
self.update_progress.emit,
|
||||||
|
self.update_status_message.emit
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
def on_all_games(portproton_games, steam_games):
|
def on_all_games(portproton_games, steam_games, epic_games):
|
||||||
seen = set()
|
seen = set()
|
||||||
games = []
|
games = []
|
||||||
for game in portproton_games + steam_games:
|
for game in portproton_games + steam_games + epic_games:
|
||||||
# Уникальный ключ: имя + exec_line
|
# Уникальный ключ: имя + exec_line
|
||||||
key = (game[0], game[4])
|
key = (game[0], game[4])
|
||||||
if key not in seen:
|
if key not in seen:
|
||||||
@ -282,7 +288,13 @@ class MainWindow(QMainWindow):
|
|||||||
self.games_loaded.emit(games)
|
self.games_loaded.emit(games)
|
||||||
self._load_portproton_games_async(
|
self._load_portproton_games_async(
|
||||||
lambda pg: self._load_steam_games_async(
|
lambda pg: self._load_steam_games_async(
|
||||||
lambda sg: on_all_games(pg, sg)
|
lambda sg: load_egs_games_async(
|
||||||
|
self.legendary_path,
|
||||||
|
lambda eg: on_all_games(pg, sg, eg),
|
||||||
|
self.downloader,
|
||||||
|
self.update_progress.emit,
|
||||||
|
self.update_status_message.emit
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
@ -927,7 +939,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# 3. Games display_filter
|
# 3. Games display_filter
|
||||||
self.filter_keys = ["all", "steam", "portproton", "favorites", "epic"]
|
self.filter_keys = ["all", "steam", "portproton", "favorites", "epic"]
|
||||||
self.filter_labels = [_("all"), "steam", "portproton", _("favorites")]
|
self.filter_labels = [_("all"), "steam", "portproton", _("favorites"), "epic games store"]
|
||||||
self.gamesDisplayCombo = QComboBox()
|
self.gamesDisplayCombo = QComboBox()
|
||||||
self.gamesDisplayCombo.addItems(self.filter_labels)
|
self.gamesDisplayCombo.addItems(self.filter_labels)
|
||||||
self.gamesDisplayCombo.setStyleSheet(self.theme.SETTINGS_COMBO_STYLE)
|
self.gamesDisplayCombo.setStyleSheet(self.theme.SETTINGS_COMBO_STYLE)
|
||||||
@ -1008,6 +1020,37 @@ class MainWindow(QMainWindow):
|
|||||||
self.gamepadRumbleCheckBox.setChecked(current_rumble_state)
|
self.gamepadRumbleCheckBox.setChecked(current_rumble_state)
|
||||||
formLayout.addRow(self.gamepadRumbleTitle, self.gamepadRumbleCheckBox)
|
formLayout.addRow(self.gamepadRumbleTitle, self.gamepadRumbleCheckBox)
|
||||||
|
|
||||||
|
# 8. Legendary Authentication
|
||||||
|
self.legendaryAuthButton = AutoSizeButton(
|
||||||
|
_("Open Legendary Login"),
|
||||||
|
icon=self.theme_manager.get_icon("login")
|
||||||
|
)
|
||||||
|
self.legendaryAuthButton.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
||||||
|
self.legendaryAuthButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.legendaryAuthButton.clicked.connect(self.openLegendaryLogin)
|
||||||
|
self.legendaryAuthTitle = QLabel(_("Legendary Authentication:"))
|
||||||
|
self.legendaryAuthTitle.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
||||||
|
self.legendaryAuthTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||||
|
formLayout.addRow(self.legendaryAuthTitle, self.legendaryAuthButton)
|
||||||
|
|
||||||
|
self.legendaryCodeEdit = QLineEdit()
|
||||||
|
self.legendaryCodeEdit.setPlaceholderText(_("Enter Legendary Authorization Code"))
|
||||||
|
self.legendaryCodeEdit.setStyleSheet(self.theme.PROXY_INPUT_STYLE)
|
||||||
|
self.legendaryCodeEdit.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.legendaryCodeTitle = QLabel(_("Authorization Code:"))
|
||||||
|
self.legendaryCodeTitle.setStyleSheet(self.theme.PARAMS_TITLE_STYLE)
|
||||||
|
self.legendaryCodeTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||||
|
formLayout.addRow(self.legendaryCodeTitle, self.legendaryCodeEdit)
|
||||||
|
|
||||||
|
self.submitCodeButton = AutoSizeButton(
|
||||||
|
_("Submit Code"),
|
||||||
|
icon=self.theme_manager.get_icon("save")
|
||||||
|
)
|
||||||
|
self.submitCodeButton.setStyleSheet(self.theme.ACTION_BUTTON_STYLE)
|
||||||
|
self.submitCodeButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
|
self.submitCodeButton.clicked.connect(self.submitLegendaryCode)
|
||||||
|
formLayout.addRow(QLabel(""), self.submitCodeButton)
|
||||||
|
|
||||||
layout.addLayout(formLayout)
|
layout.addLayout(formLayout)
|
||||||
|
|
||||||
# Кнопки
|
# Кнопки
|
||||||
@ -1058,6 +1101,37 @@ class MainWindow(QMainWindow):
|
|||||||
logger.error(f"Failed to open Legendary login page: {e}")
|
logger.error(f"Failed to open Legendary login page: {e}")
|
||||||
self.statusBar().showMessage(_("Failed to open Legendary login page"), 3000)
|
self.statusBar().showMessage(_("Failed to open Legendary login page"), 3000)
|
||||||
|
|
||||||
|
def submitLegendaryCode(self):
|
||||||
|
"""Submits the Legendary authorization code using the legendary CLI."""
|
||||||
|
auth_code = self.legendaryCodeEdit.text().strip()
|
||||||
|
if not auth_code:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Please enter an authorization code"))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Execute legendary auth command
|
||||||
|
result = subprocess.run(
|
||||||
|
[self.legendary_path, "auth", "--code", auth_code],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
logger.info("Legendary authentication successful: %s", result.stdout)
|
||||||
|
self.statusBar().showMessage(_("Successfully authenticated with Legendary"), 3000)
|
||||||
|
self.legendaryCodeEdit.clear()
|
||||||
|
# Reload Epic Games Store games after successful authentication
|
||||||
|
self.games = self.loadGames()
|
||||||
|
self.updateGameGrid()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
logger.error("Legendary authentication failed: %s", e.stderr)
|
||||||
|
self.statusBar().showMessage(_("Legendary authentication failed: {0}").format(e.stderr), 5000)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error("Legendary executable not found at %s", self.legendary_path)
|
||||||
|
self.statusBar().showMessage(_("Legendary executable not found"), 5000)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Unexpected error during Legendary authentication: %s", str(e))
|
||||||
|
self.statusBar().showMessage(_("Unexpected error during authentication"), 5000)
|
||||||
|
|
||||||
def resetSettings(self):
|
def resetSettings(self):
|
||||||
"""Сбрасывает настройки и перезапускает приложение."""
|
"""Сбрасывает настройки и перезапускает приложение."""
|
||||||
reply = QMessageBox.question(
|
reply = QMessageBox.question(
|
||||||
@ -1492,7 +1566,7 @@ class MainWindow(QMainWindow):
|
|||||||
icon_size=16,
|
icon_size=16,
|
||||||
icon_space=3,
|
icon_space=3,
|
||||||
)
|
)
|
||||||
anticheatLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
|
anticheatLabel.setStyleSheet(self.theme.get_anticheat_badge_style(anticheat_status))
|
||||||
anticheatLabel.setFixedWidth(badge_width)
|
anticheatLabel.setFixedWidth(badge_width)
|
||||||
anticheatLabel.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(f"https://areweanticheatyet.com/game/{name.lower().replace(' ', '-')}")))
|
anticheatLabel.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(f"https://areweanticheatyet.com/game/{name.lower().replace(' ', '-')}")))
|
||||||
anticheat_visible = True
|
anticheat_visible = True
|
||||||
@ -1748,11 +1822,67 @@ class MainWindow(QMainWindow):
|
|||||||
self.target_exe = None
|
self.target_exe = None
|
||||||
|
|
||||||
def toggleGame(self, exec_line, button=None):
|
def toggleGame(self, exec_line, button=None):
|
||||||
|
# Обработка Steam-игр
|
||||||
if exec_line.startswith("steam://"):
|
if exec_line.startswith("steam://"):
|
||||||
url = QUrl(exec_line)
|
url = QUrl(exec_line)
|
||||||
QDesktopServices.openUrl(url)
|
QDesktopServices.openUrl(url)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Обработка EGS-игр
|
||||||
|
if exec_line.startswith("legendary:launch:"):
|
||||||
|
# Извлекаем app_name из exec_line
|
||||||
|
app_name = exec_line.split("legendary:launch:")[1]
|
||||||
|
legendary_path = self.legendary_path # Путь к legendary
|
||||||
|
|
||||||
|
# Формируем переменные окружения
|
||||||
|
env_vars = os.environ.copy()
|
||||||
|
env_vars['START_FROM_STEAM'] = '1'
|
||||||
|
env_vars['LEGENDARY_CONFIG_PATH'] = self.legendary_config_path
|
||||||
|
|
||||||
|
wrapper = "flatpak run ru.linux_gaming.PortProton"
|
||||||
|
if self.portproton_location is not None and ".var" not in self.portproton_location:
|
||||||
|
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
|
||||||
|
wrapper = start_sh
|
||||||
|
|
||||||
|
# Формируем команду
|
||||||
|
cmd = [
|
||||||
|
legendary_path, "launch", app_name, "--no-wine", "--wrapper", wrapper
|
||||||
|
]
|
||||||
|
|
||||||
|
current_exe = os.path.basename(legendary_path)
|
||||||
|
if self.game_processes and self.target_exe is not None and self.target_exe != current_exe:
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Cannot launch game while another game is running"))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Обновляем кнопку
|
||||||
|
update_button = button if button is not None else self.current_play_button
|
||||||
|
self.current_running_button = update_button
|
||||||
|
self.target_exe = current_exe
|
||||||
|
exe_name = app_name # Используем app_name для EGS-игр
|
||||||
|
|
||||||
|
# Запускаем процесс
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(cmd, env=env_vars, shell=False, preexec_fn=os.setsid)
|
||||||
|
self.game_processes.append(process)
|
||||||
|
save_last_launch(exe_name, datetime.now())
|
||||||
|
if update_button:
|
||||||
|
update_button.setText(_("Launching"))
|
||||||
|
icon = self.theme_manager.get_icon("stop")
|
||||||
|
if isinstance(icon, str):
|
||||||
|
icon = QIcon(icon)
|
||||||
|
elif icon is None:
|
||||||
|
icon = QIcon()
|
||||||
|
update_button.setIcon(icon)
|
||||||
|
|
||||||
|
self.checkProcessTimer = QTimer(self)
|
||||||
|
self.checkProcessTimer.timeout.connect(self.checkTargetExe)
|
||||||
|
self.checkProcessTimer.start(500)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to launch EGS game {app_name}: {e}")
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Failed to launch game: {0}").format(str(e)))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Обработка PortProton-игр
|
||||||
entry_exec_split = shlex.split(exec_line)
|
entry_exec_split = shlex.split(exec_line)
|
||||||
if entry_exec_split[0] == "env":
|
if entry_exec_split[0] == "env":
|
||||||
if len(entry_exec_split) < 3:
|
if len(entry_exec_split) < 3:
|
||||||
@ -1766,18 +1896,20 @@ class MainWindow(QMainWindow):
|
|||||||
file_to_check = entry_exec_split[3]
|
file_to_check = entry_exec_split[3]
|
||||||
else:
|
else:
|
||||||
file_to_check = entry_exec_split[0]
|
file_to_check = entry_exec_split[0]
|
||||||
|
|
||||||
if not os.path.exists(file_to_check):
|
if not os.path.exists(file_to_check):
|
||||||
QMessageBox.warning(self, _("Error"), _("File not found: {0}").format(file_to_check))
|
QMessageBox.warning(self, _("Error"), _("File not found: {0}").format(file_to_check))
|
||||||
return
|
return
|
||||||
current_exe = os.path.basename(file_to_check)
|
|
||||||
|
|
||||||
|
current_exe = os.path.basename(file_to_check)
|
||||||
if self.game_processes and self.target_exe is not None and self.target_exe != current_exe:
|
if self.game_processes and self.target_exe is not None and self.target_exe != current_exe:
|
||||||
QMessageBox.warning(self, _("Error"), _("Cannot launch game while another game is running"))
|
QMessageBox.warning(self, _("Error"), _("Cannot launch game while another game is running"))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Обновляем кнопку
|
||||||
update_button = button if button is not None else self.current_play_button
|
update_button = button if button is not None else self.current_play_button
|
||||||
|
|
||||||
# Если игра уже запущена для этого exe – останавливаем её по нажатию кнопки
|
# Если игра уже запущена для этого exe – останавливаем её
|
||||||
if self.game_processes and self.target_exe == current_exe:
|
if self.game_processes and self.target_exe == current_exe:
|
||||||
for proc in self.game_processes:
|
for proc in self.game_processes:
|
||||||
try:
|
try:
|
||||||
@ -1811,17 +1943,16 @@ class MainWindow(QMainWindow):
|
|||||||
self.current_running_button = None
|
self.current_running_button = None
|
||||||
self.target_exe = None
|
self.target_exe = None
|
||||||
self._gameLaunched = False
|
self._gameLaunched = False
|
||||||
#self._uninhibit_screensaver()
|
return
|
||||||
else:
|
|
||||||
# Сохраняем ссылку на кнопку для сброса после завершения игры
|
# Запускаем игру
|
||||||
self.current_running_button = update_button
|
self.current_running_button = update_button
|
||||||
self.target_exe = current_exe
|
self.target_exe = current_exe
|
||||||
exe_name = os.path.splitext(current_exe)[0]
|
exe_name = os.path.splitext(current_exe)[0]
|
||||||
env_vars = os.environ.copy()
|
env_vars = os.environ.copy()
|
||||||
if entry_exec_split[0] == "env" and len(entry_exec_split) > 1 and 'data/scripts/start.sh' in entry_exec_split[1]:
|
env_vars['START_FROM_STEAM'] = '1'
|
||||||
env_vars['START_FROM_STEAM'] = '1'
|
|
||||||
elif entry_exec_split[0] == "flatpak":
|
try:
|
||||||
env_vars['START_FROM_STEAM'] = '1'
|
|
||||||
process = subprocess.Popen(entry_exec_split, env=env_vars, shell=False, preexec_fn=os.setsid)
|
process = subprocess.Popen(entry_exec_split, env=env_vars, shell=False, preexec_fn=os.setsid)
|
||||||
self.game_processes.append(process)
|
self.game_processes.append(process)
|
||||||
save_last_launch(exe_name, datetime.now())
|
save_last_launch(exe_name, datetime.now())
|
||||||
@ -1837,6 +1968,9 @@ class MainWindow(QMainWindow):
|
|||||||
self.checkProcessTimer = QTimer(self)
|
self.checkProcessTimer = QTimer(self)
|
||||||
self.checkProcessTimer.timeout.connect(self.checkTargetExe)
|
self.checkProcessTimer.timeout.connect(self.checkTargetExe)
|
||||||
self.checkProcessTimer.start(500)
|
self.checkProcessTimer.start(500)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to launch game {exe_name}: {e}")
|
||||||
|
QMessageBox.warning(self, _("Error"), _("Failed to launch game: {0}").format(str(e)))
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
for proc in self.game_processes:
|
for proc in self.game_processes:
|
||||||
|
@ -416,6 +416,26 @@ def get_protondb_badge_style(tier):
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def get_anticheat_badge_style(status):
|
||||||
|
status = status.lower()
|
||||||
|
status_colors = {
|
||||||
|
"supported": {"background": "rgba(102, 168, 15, 0.7)", "color": "black"},
|
||||||
|
"running": {"background": "rgba(25, 113, 194, 0.7)", "color": "black"},
|
||||||
|
"planned": {"background": "rgba(156, 54, 181, 0.7)", "color": "black"},
|
||||||
|
"broken": {"background": "rgba(232, 89, 12, 0.7)", "color": "black"},
|
||||||
|
"denied": {"background": "rgba(224, 49, 49, 0.7)", "color": "black"}
|
||||||
|
}
|
||||||
|
colors = status_colors.get(status, {"background": "rgba(0, 0, 0, 0.5)", "color": "white"})
|
||||||
|
return f"""
|
||||||
|
qproperty-alignment: AlignCenter;
|
||||||
|
background-color: {colors["background"]};
|
||||||
|
color: {colors["color"]};
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-weight: bold;
|
||||||
|
"""
|
||||||
|
|
||||||
# СТИЛИ БЕЙДЖА STEAM
|
# СТИЛИ БЕЙДЖА STEAM
|
||||||
STEAM_BADGE_STYLE= """
|
STEAM_BADGE_STYLE= """
|
||||||
qproperty-alignment: AlignCenter;
|
qproperty-alignment: AlignCenter;
|
||||||
|
@ -478,6 +478,27 @@ def get_protondb_badge_style(tier):
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# СТИЛИ БЕЙДЖА WEANTICHEATYET
|
||||||
|
def get_anticheat_badge_style(status):
|
||||||
|
status = status.lower()
|
||||||
|
status_colors = {
|
||||||
|
"supported": {"background": "rgba(102, 168, 15, 0.7)", "color": "black"},
|
||||||
|
"running": {"background": "rgba(25, 113, 194, 0.7)", "color": "black"},
|
||||||
|
"planned": {"background": "rgba(156, 54, 181, 0.7)", "color": "black"},
|
||||||
|
"broken": {"background": "rgba(232, 89, 12, 0.7)", "color": "black"},
|
||||||
|
"denied": {"background": "rgba(224, 49, 49, 0.7)", "color": "black"}
|
||||||
|
}
|
||||||
|
colors = status_colors.get(status, {"background": "rgba(0, 0, 0, 0.5)", "color": "white"})
|
||||||
|
return f"""
|
||||||
|
qproperty-alignment: AlignCenter;
|
||||||
|
background-color: {colors["background"]};
|
||||||
|
color: {colors["color"]};
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: 'Play';
|
||||||
|
font-weight: bold;
|
||||||
|
"""
|
||||||
|
|
||||||
# СТИЛИ БЕЙДЖА STEAM
|
# СТИЛИ БЕЙДЖА STEAM
|
||||||
STEAM_BADGE_STYLE= """
|
STEAM_BADGE_STYLE= """
|
||||||
qproperty-alignment: AlignCenter;
|
qproperty-alignment: AlignCenter;
|
||||||
|
Reference in New Issue
Block a user