diff --git a/faugus-launcher-flatpak.py b/faugus-launcher-flatpak.py deleted file mode 100644 index dbfe4d6..0000000 --- a/faugus-launcher-flatpak.py +++ /dev/null @@ -1,1483 +0,0 @@ -#!/usr/bin/env python3 - -import os -import re -import shutil -import signal -import subprocess -import sys -import threading - -import gi - -gi.require_version('Gtk', '3.0') -gi.require_version('Gdk', '3.0') - -from gi.repository import Gtk, Gdk, GdkPixbuf, GLib - - -class Main(Gtk.Window): - def __init__(self): - # Initialize the main window with title and default size - Gtk.Window.__init__(self, title="Faugus Launcher") - self.set_default_size(580, 580) - - self.game_running = None - - self.games = [] - self.processos = {} - - # Define the configuration path - config_path = os.path.expanduser("~/.config/faugus-launcher/") - # Create the configuration directory if it doesn't exist - if not os.path.exists(config_path): - os.makedirs(config_path) - self.working_directory = config_path - os.chdir(self.working_directory) - - config_file = os.path.join(self.working_directory, 'config.ini') - if not os.path.exists(config_file): - self.save_config("False", "~/.config/faugus-launcher/prefixes") - - self.games = [] - - # Create main box and its components - box_main = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - box_top = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_left = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - box_right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - box_right.set_border_width(10) - box_bottom = Gtk.Box() - - # Create buttons for adding, editing, and deleting games - self.button_add = Gtk.Button(label="New") - self.button_add.connect("clicked", self.on_button_add_clicked) - self.button_add.set_size_request(50, 50) - self.button_add.set_margin_top(10) - self.button_add.set_margin_start(10) - self.button_add.set_margin_end(10) - - self.button_edit = Gtk.Button(label="Edit") - self.button_edit.connect("clicked", self.on_button_edit_clicked) - self.button_edit.set_size_request(50, 50) - self.button_edit.set_margin_top(10) - self.button_edit.set_margin_start(10) - self.button_edit.set_margin_end(10) - - self.button_delete = Gtk.Button(label="Del") - self.button_delete.connect("clicked", self.on_button_delete_clicked) - self.button_delete.set_size_request(50, 50) - self.button_delete.set_margin_top(10) - self.button_delete.set_margin_start(10) - self.button_delete.set_margin_end(10) - - # Create button for killing processes - button_kill = Gtk.Button(label="Kill") - button_kill.connect("clicked", self.on_button_kill_clicked) - button_kill.set_tooltip_text("Force close all running games") - button_kill.set_size_request(50, 50) - button_kill.set_margin_top(10) - button_kill.set_margin_end(10) - button_kill.set_margin_bottom(10) - - # Create button for settings - button_settings = Gtk.Button() - button_settings.connect("clicked", self.on_button_settings_clicked) - button_settings.set_size_request(50, 50) - button_settings.set_image(Gtk.Image.new_from_icon_name("applications-system-symbolic", Gtk.IconSize.BUTTON)) - button_settings.set_margin_top(10) - button_settings.set_margin_start(10) - button_settings.set_margin_bottom(10) - - # Create button for launching games - self.button_play = Gtk.Button() - self.button_play.connect("clicked", self.on_button_play_clicked) - self.button_play.set_size_request(150, 50) - self.button_play.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)) - self.button_play.set_margin_top(10) - self.button_play.set_margin_end(10) - self.button_play.set_margin_bottom(10) - - # Create scrolled window for game list - scroll_box = Gtk.ScrolledWindow() - scroll_box.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) - scroll_box.set_margin_top(10) - scroll_box.set_margin_end(10) - - self.last_clicked_row = None - self.last_click_time = 0 - - # Create list box for displaying games - self.game_list = Gtk.ListBox(halign=Gtk.Align.START, valign=Gtk.Align.START) - self.game_list.connect("button-release-event", self.on_button_release_event) - scroll_box.add(self.game_list) - self.load_games() - - # Pack buttons and other components into the bottom box - box_bottom.pack_start(button_settings, False, False, 0) - box_bottom.pack_end(self.button_play, False, False, 0) - box_bottom.pack_end(button_kill, False, False, 0) - - # Pack buttons into the left box - box_left.pack_start(self.button_add, False, False, 0) - box_left.pack_start(self.button_edit, False, False, 0) - box_left.pack_start(self.button_delete, False, False, 0) - - # Pack left and scrolled box into the top box - box_top.pack_start(box_left, False, True, 0) - box_top.pack_start(scroll_box, True, True, 0) - - # Pack top and bottom boxes into the main box - box_main.pack_start(box_top, True, True, 0) - box_main.pack_end(box_bottom, False, True, 0) - self.add(box_main) - - self.button_edit.set_sensitive(False) - self.button_delete.set_sensitive(False) - self.button_play.set_sensitive(False) - - self.game_running2 = False - - # Set signal handler for child process termination - signal.signal(signal.SIGCHLD, self.on_child_process_closed) - - def load_close_onlaunch(self): - config_file = os.path.expanduser('~/.config/faugus-launcher/config.ini') - close_onlaunch = False - if os.path.exists(config_file): - with open(config_file, 'r') as f: - config_data = f.read().splitlines() - config_dict = dict(line.split('=') for line in config_data) - close_onlaunch_value = config_dict.get('close-onlaunch', '').strip('"') - if close_onlaunch_value.lower() == 'true': - close_onlaunch = True - return close_onlaunch - - def on_button_settings_clicked(self, widget): - # Handle add button click event - settings_dialog = Settings(self) - settings_dialog.connect("response", self.on_settings_dialog_response, settings_dialog) - - settings_dialog.show() - - def on_settings_dialog_response(self, dialog, response_id, settings_dialog): - - self.checkbox_close_after_launch = settings_dialog.checkbox_close_after_launch - self.entry_default_prefix = settings_dialog.entry_default_prefix - - checkbox_state = self.checkbox_close_after_launch.get_active() - default_prefix = self.entry_default_prefix.get_text() - - # Handle dialog response - if response_id == Gtk.ResponseType.OK: - self.save_config(checkbox_state, default_prefix) - settings_dialog.destroy() - - else: - settings_dialog.destroy() - - # Ensure the dialog is destroyed when canceled - settings_dialog.destroy() - - def on_button_play_clicked(self, widget): - if not (listbox_row := self.game_list.get_selected_row()): - return - # Get the selected game's title - hbox = listbox_row.get_child() - game_label = hbox.get_children()[0] - title = game_label.get_text() - # Find the selected game object - game = next((j for j in self.games if j.title == title), None) - if game: - # Format the title for command execution - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', game.title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - # Extract game launch information - launch_arguments = game.launch_arguments - path = game.path - prefix = game.prefix - game_arguments = game.game_arguments - mangohud = game.mangohud - sc_controller = game.sc_controller - - gamemode_enabled = os.path.exists("/usr/bin/gamemoderun") - gamemode = game.gamemode if gamemode_enabled else "" - - # Get the directory containing the executable - game_directory = os.path.dirname(path) - # self.working_directory = game_directory - - command_parts = [] - - # Add command parts if they are not empty - if mangohud: - command_parts.append(f'--env=WINEPREFIX={mangohud}') - if sc_controller: - command_parts.append(sc_controller) - if prefix: - command_parts.append(f'--env=WINEPREFIX={prefix}') - if title_formatted: - command_parts.append(f'--env=GAMEID={title_formatted}') - if gamemode: - command_parts.append(gamemode) - if launch_arguments: - command_parts.append(launch_arguments) - - # Add the fixed command and remaining arguments - command_parts.append('"org.openwinecomponents.umu.umu-launcher"') - if path: - command_parts.append(f'"{path}"') - if game_arguments: - command_parts.append(f'"{game_arguments}"') - - # Join all parts into a single command - command = ' '.join(command_parts) - print(command) - - # faugus-run path - faugus_run_path = "/app/bin/faugus-run" - - - # Launch the game with subprocess - if self.load_close_onlaunch(): - subprocess.Popen([sys.executable, faugus_run_path, command], stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, cwd=game_directory) - sys.exit() - else: - processo = subprocess.Popen([sys.executable, faugus_run_path, command], cwd=game_directory) - self.processos[title] = processo - self.button_play.set_sensitive(False) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-stop-symbolic", Gtk.IconSize.BUTTON)) - - def on_button_kill_clicked(self, widget): - # Handle kill button click event - subprocess.run(r"ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | perl " - r"-pe 's;^.*/proc/(\d+)/exe.*$;$1;g;' | xargs -n 1 kill | killall -s9 winedevice.exe tee", - shell=True) - self.game_running = None - self.game_running2 = False - - def on_button_add_clicked(self, widget): - # Handle add button click event - add_game_dialog = AddGame(self, self.game_running2) - add_game_dialog.connect("response", self.on_dialog_response, add_game_dialog) - - add_game_dialog.show() - - def on_button_edit_clicked(self, widget): - if not (listbox_row := self.game_list.get_selected_row()): - return - hbox = listbox_row.get_child() - game_label = hbox.get_children()[0] - title = game_label.get_text() - if game := next((j for j in self.games if j.title == title), None): - if game.title in self.processos: - self.game_running2 = True - else: - self.game_running2 = False - edit_game_dialog = AddGame(self, self.game_running2) - edit_game_dialog.connect("response", self.on_edit_dialog_response, edit_game_dialog, game) - edit_game_dialog.entry_title.set_text(game.title) - edit_game_dialog.entry_path.set_text(game.path) - edit_game_dialog.entry_prefix.set_text(game.prefix) - edit_game_dialog.entry_launch_arguments.set_text(game.launch_arguments) - edit_game_dialog.entry_game_arguments.set_text(game.game_arguments) - - mangohud_status = False - gamemode_status = False - sc_controller_status = False - with open("games.txt", "r") as file: - for line in file: - fields = line.strip().split(";") - if len(fields) >= 8 and fields[0] == game.title: - mangohud_status = fields[5] == "MANGOHUD=1" - gamemode_status = fields[6] == "gamemoderun" - sc_controller_status = fields[7] == "SC_CONTROLLER=1" - - mangohud_enabled = os.path.exists("/usr/bin/mangohud") - if mangohud_enabled: - edit_game_dialog.checkbox_mangohud.set_active(mangohud_status) - gamemode_enabled = os.path.exists("/usr/bin/gamemoderun") - if gamemode_enabled: - edit_game_dialog.checkbox_gamemode.set_active(gamemode_status) - sc_controller_enabled = os.path.exists("/usr/bin/sc-controller") or os.path.exists( - "/usr/local/bin/sc-controller") - if sc_controller_enabled: - edit_game_dialog.checkbox_sc_controller.set_active(sc_controller_status) - edit_game_dialog.check_existing_shortcut() - - image = self.set_image_shortcut_icon(game.title) - edit_game_dialog.button_shortcut_icon.set_image(image) - edit_game_dialog.entry_title.set_sensitive(False) - - if self.game_running2: - edit_game_dialog.button_winecfg.set_sensitive(False) - edit_game_dialog.button_winecfg.set_tooltip_text(f'{game.title} is running. Please close it first.') - edit_game_dialog.button_winetricks.set_sensitive(False) - edit_game_dialog.button_winetricks.set_tooltip_text(f'{game.title} is running. Please close it first.') - edit_game_dialog.button_run.set_sensitive(False) - edit_game_dialog.button_run.set_tooltip_text(f'{game.title} is running. Please close it first.') - - edit_game_dialog.show() - - def set_image_shortcut_icon(self, title): - - # Handle the click event of the Create Shortcut button - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - # Check if the icon file exists - icons_path = os.path.expanduser("~/.config/faugus-launcher/icons/") - new_icon_path = os.path.join(icons_path, f"{title_formatted}.ico") - - if os.path.exists(new_icon_path): - image_path = f"{icons_path}{title_formatted}.ico" - - pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) - scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_file(image_path) - image.set_from_pixbuf(scaled_pixbuf) - - if not os.path.exists(new_icon_path): - image_path = "/app/share/icons/hicolor/128x128/apps/com.faugus.launcher.png" - - pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) - scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_file(image_path) - image.set_from_pixbuf(scaled_pixbuf) - - return image - - def on_button_delete_clicked(self, widget): - if not (listbox_row := self.game_list.get_selected_row()): - return - # Retrieve the selected game's title - hbox = listbox_row.get_child() - game_label = hbox.get_children()[0] - title = game_label.get_text() - if game := next((j for j in self.games if j.title == title), None): - # Display confirmation dialog - confirmation_dialog = ConfirmationDialog(self) - response = confirmation_dialog.run() - - if response == Gtk.ResponseType.YES: - # Remove game and associated files if required - if confirmation_dialog.get_remove_prefix_state(): - game_prefix = game.prefix - prefix_path = os.path.expanduser(game_prefix) - try: - shutil.rmtree(prefix_path) - except FileNotFoundError: - pass - - # Remove the shortcut - self.remove_shortcut(game) - - self.games.remove(game) - self.save_games() - self.update_list() - - self.button_edit.set_sensitive(False) - self.button_delete.set_sensitive(False) - self.button_play.set_sensitive(False) - - confirmation_dialog.destroy() - - def on_dialog_response(self, dialog, response_id, add_game_dialog): - # Handle dialog response - if response_id == Gtk.ResponseType.OK: - if not add_game_dialog.validate_fields(): - # If fields are not validated, return and keep the dialog open - return True - # Proceed with adding the game - # Get game information from dialog fields - title = add_game_dialog.entry_title.get_text() - path = add_game_dialog.entry_path.get_text() - launch_arguments = add_game_dialog.entry_launch_arguments.get_text() - game_arguments = add_game_dialog.entry_game_arguments.get_text() - prefix = add_game_dialog.entry_prefix.get_text() - - # Concatenate game information - game_info = (f"{title};{path};{prefix};{launch_arguments};{game_arguments}") - - # Determine mangohud and gamemode status - mangohud = "MANGOHUD=1" if add_game_dialog.checkbox_mangohud.get_active() else "" - gamemode = "gamemoderun" if add_game_dialog.checkbox_gamemode.get_active() else "" - sc_controller = "SC_CONTROLLER=1" if add_game_dialog.checkbox_sc_controller.get_active() else "" - - game_info += f";{mangohud};{gamemode};{sc_controller}\n" - - # Write game info to file - with open("games.txt", "a") as file: - file.write(game_info) - - # Create Game object and update UI - game = Game(title, path, prefix, launch_arguments, game_arguments, mangohud, gamemode, sc_controller) - self.games.append(game) - self.add_item_list(game) - self.update_list() - - # Determine the state of the shortcut checkbox - shortcut_state = add_game_dialog.checkbox_shortcut.get_active() - - # Call add_remove_shortcut method - self.add_shortcut(game, shortcut_state) - - # Select the added game - self.select_game_by_title(title) - - else: - add_game_dialog.destroy() - - # Ensure the dialog is destroyed when canceled - add_game_dialog.destroy() - - def select_game_by_title(self, title): - # Select an item from the list based on title - for row in self.game_list.get_children(): - hbox = row.get_child() - game_label = hbox.get_children()[0] - if game_label.get_text() == title: - self.game_list.select_row(row) - break - self.button_edit.set_sensitive(True) - self.button_delete.set_sensitive(True) - self.button_play.set_sensitive(True) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)) - - def on_edit_dialog_response(self, dialog, response_id, edit_game_dialog, game): - # Handle edit dialog response - if response_id == Gtk.ResponseType.OK: - if not edit_game_dialog.validate_fields(): - # If fields are not validated, return and keep the dialog open - return True - # Update game object with new information - game.title = edit_game_dialog.entry_title.get_text() - game.path = edit_game_dialog.entry_path.get_text() - game.prefix = edit_game_dialog.entry_prefix.get_text() - game.launch_arguments = edit_game_dialog.entry_launch_arguments.get_text() - game.game_arguments = edit_game_dialog.entry_game_arguments.get_text() - game.mangohud = edit_game_dialog.checkbox_mangohud.get_active() - game.gamemode = edit_game_dialog.checkbox_gamemode.get_active() - game.sc_controller = edit_game_dialog.checkbox_sc_controller.get_active() - - # Save changes and update UI - self.save_games() - self.update_list() - - # Determine the state of the shortcut checkbox - shortcut_state = edit_game_dialog.checkbox_shortcut.get_active() - - # Call add_remove_shortcut method - self.add_shortcut(game, shortcut_state) - - # Select the game that was edited - self.select_game_by_title(game.title) - - edit_game_dialog.destroy() - - def add_shortcut(self, game, shortcut_state): - # Check if the shortcut checkbox is checked - if not shortcut_state: - # Remove existing shortcut if it exists - self.remove_shortcut(game) - return - - # Handle the click event of the Create Shortcut button - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', game.title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - prefix = game.prefix - path = game.path - launch_arguments = game.launch_arguments - game_arguments = game.game_arguments - - mangohud = "--env=MANGOHUD=1" if game.mangohud else "" - gamemode = "gamemoderun" if game.gamemode else "" - sc_controller = "SC_CONTROLLER=1" if game.sc_controller else "" - # Check if the icon file exists - icons_path = os.path.expanduser("~/.config/faugus-launcher/icons/") - new_icon_path = os.path.join(icons_path, f"{title_formatted}.ico") - if not os.path.exists(new_icon_path): - new_icon_path = "/var/lib/flatpak/exports/share/icons/hicolor/128x128/apps/com.faugus.launcher.png" - - # Get the directory containing the executable - game_directory = os.path.dirname(path) - - # self.working_directory = game_directory - - command_parts = [] - - # Add command parts if they are not empty - if mangohud: - command_parts.append(mangohud) - if sc_controller: - command_parts.append(sc_controller) - if prefix: - command_parts.append(f'--env=WINEPREFIX={prefix}') - if title_formatted: - command_parts.append(f'--env=GAMEID={title_formatted}') - if gamemode: - command_parts.append(gamemode) - if launch_arguments: - command_parts.append(launch_arguments) - - # Add the fixed command and remaining arguments - command_parts.append('"org.openwinecomponents.umu.umu-launcher"') - if path: - command_parts.append(f'"{path}"') - if game_arguments: - command_parts.append(f'"{game_arguments}"') - - # Join all parts into a single command - command = ' '.join(command_parts) - - # Create a .desktop file - desktop_file_content = f"""[Desktop Entry] - Name={game.title} - Exec=flatpak run --command=faugus-run com.faugus.launcher '{command}' - Icon={new_icon_path} - Type=Application - Categories=Game; - Path={game_directory} - """ - - # Check if the destination directory exists and create if it doesn't - applications_directory = os.path.expanduser("~/.local/share/applications/") - if not os.path.exists(applications_directory): - os.makedirs(applications_directory) - - desktop_directory = os.path.expanduser("~/Desktop") - if not os.path.exists(desktop_directory): - os.makedirs(desktop_directory) - - applications_shortcut_path = os.path.expanduser(f"~/.local/share/applications/{title_formatted}.desktop") - - with open(applications_shortcut_path, 'w') as desktop_file: - desktop_file.write(desktop_file_content) - - # Make the .desktop file executable - os.chmod(applications_shortcut_path, 0o755) - - # Copy the shortcut to Desktop - desktop_shortcut_path = os.path.expanduser(f"~/Desktop/{title_formatted}.desktop") - shutil.copy(applications_shortcut_path, desktop_shortcut_path) - - def update_preview(self, dialog): - if file_path := dialog.get_preview_filename(): - try: - # Create an image widget for the thumbnail - pixbuf = GdkPixbuf.Pixbuf.new_from_file(file_path) - - # Resize the thumbnail if it's too large, maintaining the aspect ratio - max_width = 400 - max_height = 400 - width = pixbuf.get_width() - height = pixbuf.get_height() - - if width > max_width or height > max_height: - # Calculate the new width and height while maintaining the aspect ratio - ratio = min(max_width / width, max_height / height) - new_width = int(width * ratio) - new_height = int(height * ratio) - pixbuf = pixbuf.scale_simple(new_width, new_height, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_pixbuf(pixbuf) - dialog.set_preview_widget(image) - dialog.set_preview_widget_active(True) - dialog.get_preview_widget().set_size_request(max_width, max_height) - except GLib.Error: - dialog.set_preview_widget_active(False) - else: - dialog.set_preview_widget_active(False) - - def remove_shortcut(self, game): - # Remove existing shortcut if it exists - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', game.title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - desktop_file_path = os.path.expanduser(f"~/.local/share/applications/{title_formatted}.desktop") - - if os.path.exists(desktop_file_path): - os.remove(desktop_file_path) - - # Remove shortcut from Desktop if exists - desktop_shortcut_path = os.path.expanduser(f"~/Desktop/{title_formatted}.desktop") - if os.path.exists(desktop_shortcut_path): - os.remove(desktop_shortcut_path) - - # Remove icon file - icon_file_path = os.path.expanduser(f"~/.config/faugus-launcher/icons/{title_formatted}.ico") - if os.path.exists(icon_file_path): - os.remove(icon_file_path) - - def remove_desktop_entry(self, game): - # Remove the .desktop file from ~/.local/share/applications/ - desktop_file_path = os.path.expanduser(f"~/.local/share/applications/{game.title}.desktop") - - if os.path.exists(desktop_file_path): - os.remove(desktop_file_path) - - def remove_shortcut_from_desktop(self, game): - # Remove the shortcut from the desktop if it exists - desktop_link_path = os.path.expanduser(f"~/Desktop/{game.title}.desktop") - - if os.path.exists(desktop_link_path): - os.remove(desktop_link_path) - - def add_item_list(self, game): - # Add a game item to the list - hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) - hbox.set_border_width(10) - hbox.set_size_request(500, -1) - - game_label = Gtk.Label.new(game.title) - hbox.pack_start(game_label, True, True, 0) - - listbox_row = Gtk.ListBoxRow() - listbox_row.add(hbox) - listbox_row.set_activatable(False) - listbox_row.set_can_focus(False) - listbox_row.set_selectable(True) - self.game_list.add(listbox_row) - - hbox.set_halign(Gtk.Align.CENTER) - listbox_row.set_valign(Gtk.Align.START) - - def update_list(self): - # Update the game list - for row in self.game_list.get_children(): - self.game_list.remove(row) - - self.games.clear() - self.load_games() - self.show_all() - - def on_child_process_closed(self, signum, frame): - for title, processo in list(self.processos.items()): - retcode = processo.poll() - if retcode is not None: - del self.processos[title] - - listbox_row = self.game_list.get_selected_row() - if listbox_row: - hbox = listbox_row.get_child() - game_label = hbox.get_children()[0] - selected_title = game_label.get_text() - - if selected_title not in self.processos: - self.button_play.set_sensitive(True) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)) - else: - self.button_play.set_sensitive(False) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-stop-symbolic", Gtk.IconSize.BUTTON)) - - def load_games(self): - # Load games from file - try: - with open("games.txt", "r") as file: - for line in file: - data = line.strip().split(";") - if len(data) >= 5: - title, path, prefix, launch_arguments, game_arguments = data[:5] - if len(data) >= 8: - mangohud = data[5] - gamemode = data[6] - sc_controller = data[7] - else: - mangohud = "" - gamemode = "" - sc_controller = "" - game = Game(title, path, prefix, launch_arguments, game_arguments, mangohud, gamemode, - sc_controller) - self.games.append(game) - self.games = sorted(self.games, key=lambda x: x.title.lower()) - self.game_list.foreach(Gtk.Widget.destroy) - for game in self.games: - self.add_item_list(game) - except FileNotFoundError: - pass - - def save_games(self): - # Save game information to file - with open("games.txt", "w") as file: - for game in self.games: - # Determine mangohud and gamemode values - mangohud_value = "MANGOHUD=1" if game.mangohud else "" - gamemode_value = "gamemoderun" if game.gamemode else "" - sc_controller_value = "SC_CONTROLLER=1" if game.sc_controller else "" - # Construct line with game information - line = (f"{game.title};{game.path};{game.prefix};{game.launch_arguments};{game.game_arguments};" - f"{mangohud_value};{gamemode_value};{sc_controller_value}\n") - file.write(line) - - def show_warning_message(self, message): - # Show a warning message dialog - dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.WARNING, - buttons=Gtk.ButtonsType.OK, text=message, ) - dialog.run() - dialog.destroy() - - def save_config(self, checkbox_state, default_prefix): - # Save configuration to file - config_file = os.path.join(self.working_directory, 'config.ini') - - with open(config_file, 'w') as f: - f.write(f'close-onlaunch={checkbox_state}\n') - f.write(f'default-prefix="{default_prefix}"\n') - - def on_button_release_event(self, listbox, event): - # Handle button release event - if event.type == Gdk.EventType.BUTTON_RELEASE and event.button == Gdk.BUTTON_PRIMARY: - current_row = listbox.get_row_at_y(event.y) - current_time = event.time - if current_row == self.last_clicked_row and current_time - self.last_click_time < 500: - # Double-click detected - if current_row: - hbox = current_row.get_child() - game_label = hbox.get_children()[0] - title = game_label.get_text() - - # Check if the game is already running - if title not in self.processos: - widget = self.button_play - self.on_button_play_clicked(widget) - else: - dialog = Gtk.MessageDialog(title=title, text=f"'{title}' is already running.", - buttons=Gtk.ButtonsType.OK, parent=self) - dialog.set_resizable(False) - dialog.set_modal(True) - dialog.run() - dialog.destroy() - else: - # Single-click, update last click details and enable buttons - self.last_clicked_row = current_row - self.last_click_time = current_time - - self.button_edit.set_sensitive(True) - self.button_delete.set_sensitive(True) - - if current_row: - hbox = current_row.get_child() - game_label = hbox.get_children()[0] - title = game_label.get_text() - - if title in self.processos: - self.button_play.set_sensitive(False) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-stop-symbolic", Gtk.IconSize.BUTTON)) - else: - self.button_play.set_sensitive(True) - self.button_play.set_image( - Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)) - - -class Settings(Gtk.Dialog): - def __init__(self, parent): - # Initialize the Settings dialog - super().__init__(title="Settings", parent=parent) - self.set_resizable(False) - self.set_modal(True) - self.set_default_size(300, 100) - - self.parent = parent - - # Create checkbox for 'Close after launch' option - self.checkbox_close_after_launch = Gtk.CheckButton(label="Close after launch") - self.checkbox_close_after_launch.set_active(False) - - # Widgets for prefix - self.label_default_prefix = Gtk.Label(label="Default prefixes location") - self.label_default_prefix.set_halign(Gtk.Align.START) - self.entry_default_prefix = Gtk.Entry() - self.entry_default_prefix.set_tooltip_text("/path/to/the/prefix") - self.button_search_prefix = Gtk.Button() - self.button_search_prefix.set_image(Gtk.Image.new_from_icon_name("system-search-symbolic", Gtk.IconSize.BUTTON)) - self.button_search_prefix.connect("clicked", self.on_button_search_prefix_clicked) - - # Button Cancel - self.button_cancel = Gtk.Button(label="Cancel") - self.button_cancel.connect("clicked", lambda widget: self.response(Gtk.ResponseType.CANCEL)) - self.button_cancel.set_size_request(150, -1) - self.button_cancel.set_halign(Gtk.Align.CENTER) - - # Button Ok - self.button_ok = Gtk.Button(label="Ok") - self.button_ok.connect("clicked", lambda widget: self.response(Gtk.ResponseType.OK)) - self.button_ok.set_size_request(150, -1) - self.button_ok.set_halign(Gtk.Align.CENTER) - - self.box = self.get_content_area() - - grid = Gtk.Grid() - grid.set_row_spacing(10) - grid.set_column_spacing(10) - grid.set_margin_start(10) - grid.set_margin_end(10) - grid.set_margin_top(10) - grid.set_margin_bottom(10) - - grid2 = Gtk.Grid() - grid2.set_row_spacing(10) - grid2.set_column_spacing(10) - grid2.set_margin_start(10) - grid2.set_margin_end(10) - grid2.set_margin_top(10) - grid2.set_margin_bottom(10) - - # Attach widgets to the grid layout - grid.attach(self.label_default_prefix, 0, 0, 4, 1) - grid.attach(self.entry_default_prefix, 0, 1, 3, 1) - self.entry_default_prefix.set_hexpand(True) - grid.attach(self.button_search_prefix, 3, 1, 1, 1) - grid.attach(self.checkbox_close_after_launch, 0, 2, 4, 1) - - grid2.attach(self.button_cancel, 1, 1, 1, 1) - grid2.attach(self.button_ok, 2, 1, 1, 1) - - self.load_config() - - self.box.add(grid) - self.box.add(grid2) - self.show_all() - - def on_button_search_prefix_clicked(self, widget): - # Handle the click event of the search button to select the game's .exe - dialog = Gtk.FileChooserDialog(title="Select a prefix location", parent=self, - action=Gtk.FileChooserAction.SELECT_FOLDER) - dialog.set_current_folder(os.path.expanduser(self.default_prefix)) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - - response = dialog.run() - if response == Gtk.ResponseType.OK: - self.entry_default_prefix.set_text(dialog.get_filename()) - - dialog.destroy() - - def load_config(self): - # Load configuration from file - # config_file = os.path.expanduser("~/.config/faugus-launcher/config.ini") - config_file = os.path.join(self.parent.working_directory, 'config.ini') - if os.path.exists(config_file): - with open(config_file, 'r') as f: - config_data = f.read().splitlines() - config_dict = dict(line.split('=') for line in config_data) - close_on_launch = config_dict.get('close-onlaunch', 'False') == 'True' - self.default_prefix = config_dict.get('default-prefix', '').strip('"') - - self.checkbox_close_after_launch.set_active(close_on_launch) - self.entry_default_prefix.set_text(self.default_prefix) - else: - # Save default configuration if file does not exist - print("else") - self.parent.save_config(False, '') - - -class Game: - def __init__(self, title, path, prefix, launch_arguments, game_arguments, mangohud, gamemode, sc_controller): - # Initialize a Game object with various attributes - self.title = title # Title of the game - self.path = path # Path to the game executable - self.launch_arguments = launch_arguments # Arguments to launch the game - self.game_arguments = game_arguments # Arguments specific to the game - self.mangohud = mangohud # Boolean indicating whether Mangohud is enabled - self.gamemode = gamemode # Boolean indicating whether Gamemode is enabled - self.prefix = prefix # Prefix for Wine games - self.sc_controller = sc_controller # Boolean indicating whether SC Controller is enabled - - -class ConfirmationDialog(Gtk.Dialog): - def __init__(self, parent): - # Initialize the ConfirmationDialog - Gtk.Dialog.__init__(self, title="Remove Game", parent=parent, modal=True) - - # Configure dialog properties - self.set_decorated(False) - self.set_resizable(False) - self.set_default_size(300, 100) - - # Create a grid layout for the dialog - grid = Gtk.Grid() - grid.set_column_homogeneous(True) - grid.set_row_homogeneous(True) - grid.set_row_spacing(10) - grid.set_column_spacing(10) - grid.set_border_width(10) - - # Add grid to dialog's content area - self.get_content_area().add(grid) - - # Create a label - label = Gtk.Label() - label.set_label("Are you sure?") - label.set_halign(Gtk.Align.CENTER) - grid.attach(label, 0, 0, 2, 1) - - # Create "No" button - button_no = Gtk.Button(label="Cancel") - button_no.connect("clicked", lambda x: self.response(Gtk.ResponseType.NO)) - grid.attach(button_no, 0, 1, 1, 1) - - # Create "Yes" button - button_yes = Gtk.Button(label="Confirm") - button_yes.connect("clicked", lambda x: self.response(Gtk.ResponseType.YES)) - grid.attach(button_yes, 1, 1, 1, 1) - - # Create a checkbox to optionally remove the prefix - self.checkbox = Gtk.CheckButton(label="Also remove the prefix") - self.checkbox.set_halign(Gtk.Align.CENTER) - grid.attach(self.checkbox, 0, 2, 2, 1) - - # Display all widgets - self.show_all() - - def get_remove_prefix_state(self): - # Get the state of the checkbox - return self.checkbox.get_active() - - -class AddGame(Gtk.Dialog): - def __init__(self, parent, game_running2): - # Initialize the AddGame dialog - super().__init__(title="Add/Edit Game", parent=parent) - # self.set_default_size(500, -1) - self.set_resizable(False) - self.set_modal(True) - self.parent_window = parent - - # Create the content area - self.box = self.get_content_area() - grid = Gtk.Grid() - grid.set_row_spacing(10) - grid.set_column_spacing(10) - grid.set_margin_start(10) - grid.set_margin_end(10) - grid.set_margin_top(10) - grid.set_margin_bottom(10) - - grid2 = Gtk.Grid() - grid2.set_row_spacing(10) - grid2.set_column_spacing(10) - grid2.set_margin_start(10) - grid2.set_margin_end(10) - grid2.set_margin_top(10) - grid2.set_margin_bottom(10) - - grid3 = Gtk.Grid() - grid3.set_row_spacing(10) - grid3.set_column_spacing(10) - grid3.set_margin_start(10) - grid3.set_margin_end(10) - grid3.set_margin_top(10) - grid3.set_margin_bottom(10) - - # Widgets for title - self.label_title = Gtk.Label(label="Title") - self.label_title.set_halign(Gtk.Align.START) - self.entry_title = Gtk.Entry() - self.entry_title.set_tooltip_text("Game Title") - - # Widgets for path - self.label_path = Gtk.Label(label="Path") - self.label_path.set_halign(Gtk.Align.START) - self.entry_path = Gtk.Entry() - self.entry_path.set_tooltip_text("/path/to/the/exe") - self.button_search = Gtk.Button() - self.button_search.set_image(Gtk.Image.new_from_icon_name("system-search-symbolic", Gtk.IconSize.BUTTON)) - self.button_search.connect("clicked", self.on_button_search_clicked) - - # Widgets for prefix - self.label_prefix = Gtk.Label(label="Prefix") - self.label_prefix.set_halign(Gtk.Align.START) - self.entry_prefix = Gtk.Entry() - self.entry_prefix.set_tooltip_text("/path/to/the/prefix") - self.button_search_prefix = Gtk.Button() - self.button_search_prefix.set_image(Gtk.Image.new_from_icon_name("system-search-symbolic", Gtk.IconSize.BUTTON)) - self.button_search_prefix.connect("clicked", self.on_button_search_prefix_clicked) - - # Widgets for launch arguments - self.label_launch_arguments = Gtk.Label(label="Launch Arguments") - self.label_launch_arguments.set_halign(Gtk.Align.START) - self.entry_launch_arguments = Gtk.Entry() - self.entry_launch_arguments.set_tooltip_text("e.g.: PROTON_USE_WINED3D=1 gamescope -W 2560 -H 1440") - - # Widgets for game arguments - self.label_game_arguments = Gtk.Label(label="Game's Arguments") - self.label_game_arguments.set_halign(Gtk.Align.START) - self.entry_game_arguments = Gtk.Entry() - self.entry_game_arguments.set_tooltip_text("e.g.: -d3d11 -fullscreen") - - # Checkboxes for optional features - self.checkbox_mangohud = Gtk.CheckButton(label="MangoHud") - self.checkbox_mangohud.set_tooltip_text( - "Shows an overlay for monitoring FPS, temperatures, CPU/GPU load and more.") - self.checkbox_gamemode = Gtk.CheckButton(label="GameMode") - self.checkbox_gamemode.set_tooltip_text("Tweaks your system to improve performance.") - self.checkbox_sc_controller = Gtk.CheckButton(label="SC Controller") - self.checkbox_sc_controller.set_tooltip_text( - "Emulates a Xbox controller if the game doesn't support yours. Put the profile at ~/.config/faugus-launcher/controller.sccprofile.") - - # Button for Winecfg - self.button_winecfg = Gtk.Button(label="Winecfg") - self.button_winecfg.set_size_request(120, -1) - self.button_winecfg.connect("clicked", self.on_button_winecfg_clicked) - - # Button for Winetricks - self.button_winetricks = Gtk.Button(label="Winetricks") - self.button_winetricks.set_size_request(120, -1) - self.button_winetricks.connect("clicked", self.on_button_winetricks_clicked) - - # Button for Run - self.button_run = Gtk.Button(label="Run") - self.button_run.set_size_request(120, -1) - self.button_run.connect("clicked", self.on_button_run_clicked) - self.button_run.set_tooltip_text("Run a file inside the prefix") - - # Button for creating shortcut - self.checkbox_shortcut = Gtk.CheckButton(label="Create Shortcut") - self.checkbox_shortcut.connect("toggled", self.on_checkbox_toggled) - - # Button for selection shortcut icon - self.button_shortcut_icon = Gtk.Button() - self.button_shortcut_icon.connect("clicked", self.on_button_shortcut_icon_clicked) - self.button_shortcut_icon.set_tooltip_text("Select an icon for the shortcut") - self.button_shortcut_icon.set_sensitive(False) # Initially disable the button - - # Button Cancel - self.button_cancel = Gtk.Button(label="Cancel") - self.button_cancel.connect("clicked", lambda widget: self.response(Gtk.ResponseType.CANCEL)) - self.button_cancel.set_size_request(150, -1) - self.button_cancel.set_halign(Gtk.Align.CENTER) - - # Button Ok - self.button_ok = Gtk.Button(label="Ok") - self.button_ok.connect("clicked", lambda widget: self.response(Gtk.ResponseType.OK)) - self.button_ok.set_size_request(150, -1) - self.button_ok.set_halign(Gtk.Align.CENTER) - - # Add vertical space - space_label = Gtk.Label() - - # Event handlers - self.default_prefix = self.load_default_prefix() - self.entry_title.connect("changed", self.update_prefix_entry) - - # Attach widgets to the grid layout - grid.attach(self.label_title, 0, 0, 4, 1) - grid.attach(self.entry_title, 0, 1, 4, 1) - - grid.attach(self.label_path, 0, 2, 4, 1) - grid.attach(self.entry_path, 0, 3, 3, 1) - self.entry_path.set_hexpand(True) - grid.attach(self.button_search, 3, 3, 1, 1) - - grid.attach(self.label_prefix, 0, 4, 4, 1) - grid.attach(self.entry_prefix, 0, 5, 3, 1) - self.entry_prefix.set_hexpand(True) - grid.attach(self.button_search_prefix, 3, 5, 1, 1) - - grid.attach(self.label_launch_arguments, 0, 6, 4, 1) - grid.attach(self.entry_launch_arguments, 0, 7, 4, 1) - - grid.attach(self.label_game_arguments, 0, 8, 4, 1) - grid.attach(self.entry_game_arguments, 0, 9, 4, 1) - - self.box.add(grid) - - grid2.attach(self.checkbox_mangohud, 0, 0, 1, 1) - self.checkbox_mangohud.set_hexpand(True) - grid2.attach(self.checkbox_gamemode, 0, 1, 1, 1) - grid2.attach(self.checkbox_sc_controller, 0, 2, 1, 1) - - grid2.attach(self.button_winecfg, 2, 0, 1, 1) - grid2.attach(self.button_winetricks, 2, 1, 1, 1) - grid2.attach(self.button_run, 2, 2, 1, 1) - - # grid2.attach(space_label, 2, 3, 1, 1) - - grid2.attach(self.button_shortcut_icon, 2, 3, 1, 1) - grid2.attach(self.checkbox_shortcut, 0, 3, 1, 1) - - self.box.add(grid2) - - grid3.attach(self.button_cancel, 1, 1, 1, 1) - grid3.attach(self.button_ok, 2, 1, 1, 1) - - self.box.add(grid3) - - # Check if optional features are available and enable/disable accordingly - self.mangohud_enabled = os.path.exists("/usr/bin/mangohud") - if not self.mangohud_enabled: - self.checkbox_mangohud.set_sensitive(False) - self.checkbox_mangohud.set_active(False) - self.checkbox_mangohud.set_tooltip_text( - "Shows an overlay for monitoring FPS, temperatures, CPU/GPU load and more. NOT INSTALLED.") - - self.gamemode_enabled = os.path.exists("/usr/bin/gamemoderun") - if not self.gamemode_enabled: - self.checkbox_gamemode.set_sensitive(False) - self.checkbox_gamemode.set_active(False) - self.checkbox_gamemode.set_tooltip_text("Tweaks your system to improve performance. NOT INSTALLED.") - - self.sc_controller_enabled = os.path.exists("/usr/bin/sc-controller") or os.path.exists( - "/usr/local/bin/sc-controller") - if not self.sc_controller_enabled: - self.checkbox_sc_controller.set_sensitive(False) - self.checkbox_sc_controller.set_active(False) - self.checkbox_sc_controller.set_tooltip_text( - "Emulates a Xbox controller if the game doesn't support yours. Put the profile at ~/.config/faugus-launcher/controller.sccprofile. NOT INSTALLED.") - - # self.create_remove_shortcut(self) - self.button_shortcut_icon.set_image(self.set_image_shortcut_icon()) - self.show_all() - - def load_default_prefix(self): - config_file = os.path.expanduser('~/.config/faugus-launcher/config.ini') - default_prefix = "" - if os.path.exists(config_file): - with open(config_file, 'r') as f: - config_data = f.read().splitlines() - config_dict = dict(line.split('=') for line in config_data) - default_prefix = config_dict.get('default-prefix', '').strip('"') - return default_prefix - - def on_button_run_clicked(self, widget): - self.set_sensitive(False) - # Handle the click event of the Run button - validation_result = self.validate_fields() - if not validation_result: - return - - dialog = Gtk.FileChooserDialog(title="Select a file to run inside the prefix", - action=Gtk.FileChooserAction.OPEN) - dialog.set_current_folder(os.path.expanduser("~/")) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - - response = dialog.run() - if response == Gtk.ResponseType.OK: - title = self.entry_title.get_text() - prefix = self.entry_prefix.get_text() - - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - file_run = dialog.get_filename() - run_command2 = (f'--env=WINEPREFIX={prefix} ' - f'--env=GAMEID={title_formatted} ' - f'"org.openwinecomponents.umu.umu-launcher" "{file_run}"') - print(run_command2) - - # faugus-run path - faugus_run_path = "/app/bin/faugus-run" - - def run_command(): - process = subprocess.Popen([sys.executable, faugus_run_path, run_command2, "winecfg"]) - process.wait() - GLib.idle_add(self.set_sensitive, True) - GLib.idle_add(self.parent_window.set_sensitive, True) - GLib.idle_add(self.blocking_window.destroy) - - self.blocking_window = Gtk.Window() - self.blocking_window.set_transient_for(self.parent_window) - self.blocking_window.set_decorated(False) - self.blocking_window.set_modal(True) - - command_thread = threading.Thread(target=run_command) - command_thread.start() - - else: - self.set_sensitive(True) - dialog.destroy() - - def on_checkbox_toggled(self, checkbox): - # Enable or disable the button based on the checkbox state - self.button_shortcut_icon.set_sensitive(checkbox.get_active()) - - def set_image_shortcut_icon(self): - - image_path = "/app/share/icons/hicolor/128x128/apps/com.faugus.launcher.png" - - pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) - scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_file(image_path) - image.set_from_pixbuf(scaled_pixbuf) - - return image - - def on_button_shortcut_icon_clicked(self, widget): - self.set_sensitive(False) - - # Handle the click event of the Winetricks button - validation_result = self.validate_fields2() - if not validation_result: - return - - title = self.entry_title.get_text() - path = self.entry_path.get_text() - - # Handle the click event of the Create Shortcut button - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - # Check if the icon file exists - icons_path = os.path.expanduser("~/.config/faugus-launcher/icons/") - - # Check if the icon directory exists and create if it doesn't - icon_directory = os.path.expanduser(f"~/.config/faugus-launcher/icons/{title_formatted}/") - if not os.path.exists(icon_directory): - os.makedirs(icon_directory) - - # Execute 7z command to extract icon - os.system(f'7z e "{path}" -o{icon_directory} -r -aoa') - - # Open file dialog to select .ico file - dialog = Gtk.FileChooserDialog(title="Select an icon for the shortcut", action=Gtk.FileChooserAction.OPEN) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - - # Add a filter to limit selection to .ico files - filter_ico = Gtk.FileFilter() - filter_ico.set_name("Image files") - filter_ico.add_mime_type("image/*") # Other image formats - dialog.add_filter(filter_ico) - - # Set the initial directory to the icon directory - dialog.set_current_folder(icon_directory) - - # Connect signal to update preview widget when file selection changes - dialog.connect("update-preview", self.update_preview) - - response = dialog.run() - if response == Gtk.ResponseType.OK: - file_path = dialog.get_filename() - # Move and rename the icon file - shutil.move(file_path, os.path.expanduser(f"{icons_path}{title_formatted}.ico")) - - image_path = f"{icons_path}{title_formatted}.ico" - - pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_path) - scaled_pixbuf = pixbuf.scale_simple(50, 50, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_file(image_path) - image.set_from_pixbuf(scaled_pixbuf) - - self.button_shortcut_icon.set_image(image) - - # Delete the folder after the icon is moved - shutil.rmtree(icon_directory) - dialog.destroy() - self.set_sensitive(True) - - else: - # Delete the folder - shutil.rmtree(icon_directory) - dialog.destroy() - self.set_sensitive(True) - return - - def update_preview(self, dialog): - if file_path := dialog.get_preview_filename(): - try: - # Create an image widget for the thumbnail - pixbuf = GdkPixbuf.Pixbuf.new_from_file(file_path) - - # Resize the thumbnail if it's too large, maintaining the aspect ratio - max_width = 400 - max_height = 400 - width = pixbuf.get_width() - height = pixbuf.get_height() - - if width > max_width or height > max_height: - # Calculate the new width and height while maintaining the aspect ratio - ratio = min(max_width / width, max_height / height) - new_width = int(width * ratio) - new_height = int(height * ratio) - pixbuf = pixbuf.scale_simple(new_width, new_height, GdkPixbuf.InterpType.BILINEAR) - - image = Gtk.Image.new_from_pixbuf(pixbuf) - dialog.set_preview_widget(image) - dialog.set_preview_widget_active(True) - dialog.get_preview_widget().set_size_request(max_width, max_height) - except GLib.Error: - dialog.set_preview_widget_active(False) - else: - dialog.set_preview_widget_active(False) - - def check_existing_shortcut(self): - # Check if the shortcut already exists and mark or unmark the checkbox - title = self.entry_title.get_text().strip() - if not title: - return # If there's no title, there's no shortcut to check - - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', title).replace(' ', '-').lower() - desktop_file_path = os.path.expanduser(f"~/.local/share/applications/{title_formatted}.desktop") - - # Check if the shortcut file exists - shortcut_exists = os.path.exists(desktop_file_path) - - # Mark the checkbox if the shortcut exists - self.checkbox_shortcut.set_active(shortcut_exists) - - def update_prefix_entry(self, entry): - # Update the prefix entry based on the title and self.default_prefix - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', entry.get_text()) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - prefix = os.path.expanduser(self.default_prefix) + "/" + title_formatted - self.entry_prefix.set_text(prefix) - - def on_button_winecfg_clicked(self, widget): - self.set_sensitive(False) - # Handle the click event of the Winetricks button - validation_result = self.validate_fields() - if not validation_result: - return - - title = self.entry_title.get_text() - prefix = self.entry_prefix.get_text() - - title_formatted = re.sub(r'[^a-zA-Z0-9\s]', '', title) - title_formatted = title_formatted.replace(' ', '-') - title_formatted = '-'.join(title_formatted.lower().split()) - - # Open Winetricks for the specified Wine prefix - winecfg_command = (f'--env=WINEPREFIX={prefix} ' - f'--env=GAMEID={title_formatted} ' - f'"org.openwinecomponents.umu.umu-launcher" "winecfg"') - print(winecfg_command) - - # faugus-run path - faugus_run_path = "/app/bin/faugus-run" - - def run_command(): - process = subprocess.Popen([sys.executable, faugus_run_path, winecfg_command, "winecfg"]) - process.wait() - GLib.idle_add(self.set_sensitive, True) - GLib.idle_add(self.parent_window.set_sensitive, True) - GLib.idle_add(self.blocking_window.destroy) - - self.blocking_window = Gtk.Window() - self.blocking_window.set_transient_for(self.parent_window) - self.blocking_window.set_decorated(False) - self.blocking_window.set_modal(True) - - command_thread = threading.Thread(target=run_command) - command_thread.start() - - def on_button_winetricks_clicked(self, widget): - self.set_sensitive(False) - # Handle the click event of the Winetricks button - validation_result = self.validate_fields() - if not validation_result: - return - - prefix = self.entry_prefix.get_text() - - # Open Winetricks for the specified Wine prefix - winetricks_command = (f'--env=WINEPREFIX={prefix} ' - f'--env=GAMEID=winetricks-gui ' - f'--env=STORE="none" ' - f'"org.openwinecomponents.umu.umu-launcher" ""') - print(winetricks_command) - - # faugus-run path - faugus_run_path = "/app/bin/faugus-run" - - def run_command(): - process = subprocess.Popen([sys.executable, faugus_run_path, winetricks_command, "winetricks"]) - process.wait() - GLib.idle_add(self.set_sensitive, True) - GLib.idle_add(self.parent_window.set_sensitive, True) - GLib.idle_add(self.blocking_window.destroy) - - self.blocking_window = Gtk.Window() - self.blocking_window.set_transient_for(self.parent_window) - self.blocking_window.set_decorated(False) - self.blocking_window.set_modal(True) - - command_thread = threading.Thread(target=run_command) - command_thread.start() - - def on_button_search_clicked(self, widget): - # Handle the click event of the search button to select the game's .exe - dialog = Gtk.FileChooserDialog(title="Select the game's .exe", parent=self, action=Gtk.FileChooserAction.OPEN) - if not self.entry_path.get_text(): - dialog.set_current_folder(os.path.expanduser("~/")) - else: - dialog.set_current_folder(os.path.dirname(self.entry_path.get_text())) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - - response = dialog.run() - if response == Gtk.ResponseType.OK: - self.entry_path.set_text(dialog.get_filename()) - - dialog.destroy() - - def on_button_search_prefix_clicked(self, widget): - dialog = Gtk.FileChooserDialog(title="Select a prefix location", parent=self, - action=Gtk.FileChooserAction.SELECT_FOLDER) - - if not self.entry_prefix.get_text(): - dialog.set_current_folder(os.path.expanduser(self.default_prefix)) - else: - dialog.set_current_folder(self.entry_prefix.get_text()) - dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) - - response = dialog.run() - if response == Gtk.ResponseType.OK: - new_prefix = dialog.get_filename() - self.default_prefix = new_prefix - self.entry_title.emit("changed") - - dialog.destroy() - - def validate_fields(self): - # Validate the input fields for title and path - title = self.entry_title.get_text() - prefix = self.entry_prefix.get_text() - - if not title or not prefix: - # Show a warning message if title or path is empty - self.show_warning_message("Title and Prefix need to be filled") - return False - - return True - - def validate_fields2(self): - # Validate the input fields for title and path - title = self.entry_title.get_text() - path = self.entry_path.get_text() - - if not title or not path: - # Show a warning message if title or path is empty - self.show_warning_message("Title and Path need to be filled") - return False - - return True - - def show_warning_message(self, message): - # Display a warning message dialog - dialog = Gtk.MessageDialog(transient_for=self, flags=0, message_type=Gtk.MessageType.WARNING, - buttons=Gtk.ButtonsType.OK, text=message) - dialog.run() - dialog.destroy() - - -if __name__ == "__main__": - app = Main() - app.connect("destroy", Gtk.main_quit) - app.show_all() - Gtk.main()