summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorFaugus2024-03-16 23:18:45 -0300
committerFaugus2024-03-16 23:18:45 -0300
commitb86662a7d7d5656124c8295c31629f5059722420 (patch)
tree8f9ada28f4252105b88f96edae2ba2ddb3e4c1a2
parent947aec8891eaf1edde2535c03b3f9f5a182a971f (diff)
downloadaur-b86662a7d7d5656124c8295c31629f5059722420.tar.gz
Initial release
-rwxr-xr-xfaugus-launcher.py656
-rwxr-xr-xulwgl-faugus153
2 files changed, 809 insertions, 0 deletions
diff --git a/faugus-launcher.py b/faugus-launcher.py
new file mode 100755
index 000000000000..9675ac544461
--- /dev/null
+++ b/faugus-launcher.py
@@ -0,0 +1,656 @@
+#!/usr/bin/env python3
+
+import os
+import subprocess
+import re
+import gi
+import sys
+import shutil
+import signal
+
+# Importa os módulos necessários do Gtk
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+from gi.repository import Gtk, Gdk
+
+# Classe para caixa de diálogo de confirmação
+class ConfirmacaoDialog(Gtk.Dialog):
+ def __init__(self, parent, mensagem):
+ super().__init__(title="Confirmação", parent=parent, flags=0)
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_default_size(300, 100)
+
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ self.get_content_area().add(box)
+
+ # Adiciona espaçamento e uma etiqueta com a mensagem de confirmação
+ box.pack_start(Gtk.Label(), True, True, 0)
+ label = Gtk.Label(label=mensagem)
+ label.set_line_wrap(True)
+ box.pack_start(label, True, True, 0)
+ box.pack_start(Gtk.Label(), True, True, 0)
+
+ # Adiciona botões para "Não" e "Sim"
+ buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
+ box.pack_start(buttons_box, True, True, 0)
+
+ box.pack_start(Gtk.Label(), True, True, 0)
+
+ btn_no = Gtk.Button()
+ btn_no.add(Gtk.Image.new_from_icon_name("window-close-symbolic", Gtk.IconSize.BUTTON))
+ btn_no.connect("clicked", lambda x: self.response(Gtk.ResponseType.NO))
+ buttons_box.pack_start(btn_no, True, True, 0)
+
+ btn_yes = Gtk.Button()
+ btn_yes.add(Gtk.Image.new_from_icon_name("gtk-ok", Gtk.IconSize.BUTTON))
+ btn_yes.connect("clicked", lambda x: self.response(Gtk.ResponseType.YES))
+ buttons_box.pack_start(btn_yes, True, True, 0)
+
+ # Exibe todos os elementos
+ self.show_all()
+
+# Classe para caixa de diálogo de adição/edição de jogo
+class AdicionarJogoDialog(Gtk.Dialog):
+ def __init__(self, parent):
+ super().__init__(title="Adicionar/Editar Jogo", parent=parent)
+ self.set_default_size(640, 480)
+ self.set_resizable(False)
+ self.set_modal(True) # Torna a janela de diálogo modal
+
+ 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)
+
+ # Widgets para a entrada de dados do jogo
+ self.nome_label = Gtk.Label(label="Nome:")
+ self.nome_entry = Gtk.Entry()
+
+ self.caminho_label = Gtk.Label(label="Caminho:")
+ self.caminho_entry = Gtk.Entry()
+ self.procurar_button = Gtk.Button()
+ self.procurar_button.set_image(Gtk.Image.new_from_icon_name("system-search", Gtk.IconSize.BUTTON))
+ self.procurar_button.connect("clicked", self.on_procurar_clicked)
+
+ # Adicionar o novo campo Prefix
+ self.prefix_label = Gtk.Label(label="Prefix:")
+ self.prefix_entry = Gtk.Entry()
+
+ self.argumentos_label = Gtk.Label(label="Argumentos:")
+ self.argumentos_entry = Gtk.Entry()
+
+ self.sufixo_label = Gtk.Label(label="Sufixo:")
+ self.sufixo_entry = Gtk.Entry()
+
+
+
+ # Dropdown menu
+ self.dropdown_label = Gtk.Label(label="Compatibilidade:")
+ self.dropdown_menu = Gtk.ComboBoxText()
+ self.dropdown_menu.connect("changed", self.on_dropdown_changed)
+
+ self.populate_dropdown()
+
+ # Checkboxes
+ self.mangohud_checkbox = Gtk.CheckButton(label="MangoHud")
+ self.gamemode_checkbox = Gtk.CheckButton(label="Game Mode")
+
+ # Button
+ self.winetricks_button = Gtk.Button(label="Winetricks")
+ self.winetricks_button.connect("clicked", self.on_winetricks_clicked)
+
+ # Conecta o sinal de mudança no campo Nome para atualizar dinamicamente o campo Prefix
+ self.nome_entry.connect("changed", self.atualizar_prefix_entry)
+
+
+ # Adiciona os widgets ao grid
+ grid.attach(self.nome_label, 0, 0, 1, 1)
+ grid.attach(self.nome_entry, 1, 0, 3, 1)
+
+ grid.attach(self.caminho_label, 0, 1, 1, 1)
+ grid.attach(self.caminho_entry, 1, 1, 2, 1)
+ self.caminho_entry.set_hexpand(True)
+ grid.attach(self.procurar_button, 3, 1, 1, 1)
+
+ # Adicionar o novo campo Prefix
+ grid.attach(self.prefix_label, 0, 2, 1, 1)
+ grid.attach(self.prefix_entry, 1, 2, 3, 1)
+
+ grid.attach(self.argumentos_label, 0, 3, 1, 1)
+ grid.attach(self.argumentos_entry, 1, 3, 3, 1)
+
+ grid.attach(self.sufixo_label, 0, 4, 1, 1)
+ grid.attach(self.sufixo_entry, 1, 4, 3, 1)
+
+ grid.attach(self.dropdown_label, 0, 5, 1, 1)
+ grid.attach(self.dropdown_menu, 1, 5, 3, 1)
+
+ # Adiciona as checkboxes e o botão ao grid
+ grid.attach(self.mangohud_checkbox, 0, 6, 1, 1)
+ grid.attach(self.gamemode_checkbox, 1, 6, 1, 1)
+ grid.attach(self.winetricks_button, 2, 6, 2, 1)
+
+ # Adiciona o grid à caixa de diálogo
+ self.box.add(grid)
+
+ # Adiciona botões de cancelar e OK
+ cancel_button = Gtk.Button()
+ cancel_button.add(Gtk.Image.new_from_icon_name("window-close-symbolic", Gtk.IconSize.BUTTON))
+ self.add_action_widget(cancel_button, Gtk.ResponseType.CANCEL)
+
+ ok_button = Gtk.Button()
+ ok_button.add(Gtk.Image.new_from_icon_name("gtk-ok", Gtk.IconSize.BUTTON))
+ self.add_action_widget(ok_button, Gtk.ResponseType.OK)
+
+ # Verificação para MangoHud
+ mangohud_enabled = os.path.exists("/usr/bin/mangohud")
+ # self.mangohud_checkbox = Gtk.CheckButton(label="MangoHud")
+ if not mangohud_enabled:
+ self.mangohud_checkbox.set_sensitive(False) # Torna o checkbox insensível
+ self.mangohud_checkbox.set_active(False) # Desmarca o checkbox
+
+ # Verificação para Game Mode
+ gamemode_enabled = os.path.exists("/usr/games/gamemoderun")
+ if not gamemode_enabled:
+ self.gamemode_checkbox.set_sensitive(False) # Torna o checkbox insensível
+ self.gamemode_checkbox.set_active(False) # Desmarca o checkbox
+
+ # Verificação para Winetricks
+ winetricks_enabled = os.path.exists("/usr/bin/winetricks")
+ if not winetricks_enabled:
+ self.winetricks_button.set_sensitive(False) # Torna o botão insensível
+
+
+ # Exibe todos os elementos
+ self.show_all()
+
+ # Método para atualizar dinamicamente o campo Prefix
+ def atualizar_prefix_entry(self, widget):
+ nome_jogo_formatado = re.sub(r'[^a-zA-Z0-9\s]', '', self.nome_entry.get_text())
+ nome_jogo_formatado = nome_jogo_formatado.replace(' ', '-')
+ nome_jogo_formatado = '-'.join(nome_jogo_formatado.lower().split())
+ prefix = os.path.expanduser("~/.config/faugus-launcher/prefixes/") + nome_jogo_formatado
+ self.prefix_entry.set_text(prefix)
+
+ def carregar_estado_checkboxes(self, mangohud, gamemode):
+ # Define o estado dos checkboxes com base nos valores fornecidos
+ self.mangohud_checkbox.set_active(mangohud)
+ self.gamemode_checkbox.set_active(gamemode)
+
+ def on_winetricks_clicked(self, widget):
+ # Callback para o botão de Winetricks
+ nome_jogo_formatado = re.sub(r'[^a-zA-Z0-9\s]', '', self.nome_entry.get_text())
+ nome_jogo_formatado = nome_jogo_formatado.replace(' ', '-')
+ nome_jogo_formatado = '-'.join(nome_jogo_formatado.lower().split())
+
+ # Obtém o texto do campo Prefixo
+ prefixo_jogo = self.prefix_entry.get_text()
+
+ comando = (
+ f'WINEPREFIX={prefixo_jogo} '
+ f'{os.path.expanduser("/usr/bin/winetricks")} '
+
+ #f'/usr/bin/winetricks'
+
+ )
+
+ subprocess.Popen(["/bin/bash", "-c", comando])
+
+ def populate_dropdown(self):
+ # Verifica se os diretórios existem e adiciona opções ao dropdown
+ if os.path.exists(os.path.expanduser("~/.steam/steam/steamapps/common/Proton - Experimental")):
+ self.dropdown_menu.append_text("Proton Experimental")
+
+ compatibility_tools_dir = os.path.expanduser("~/.steam/steam/compatibilitytools.d/")
+ if os.path.exists(compatibility_tools_dir):
+ for item in os.listdir(compatibility_tools_dir):
+ self.dropdown_menu.append_text(item)
+
+ #if os.path.exists("/usr/bin/wine64"):
+ # self.dropdown_menu.append_text("Wine")
+
+ def on_dropdown_changed(self, widget):
+ # Este método será chamado sempre que houver uma mudança no dropdown
+ pass
+
+ def on_procurar_clicked(self, widget):
+ # Callback para o botão de procurar
+ dialog = Gtk.FileChooserDialog(
+ title="Selecione o arquivo .exe",
+ parent=self,
+ action=Gtk.FileChooserAction.OPEN,
+ )
+
+ dialog.set_current_folder("/mnt/data/Games")
+ dialog.add_buttons(
+ Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK
+ )
+
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ self.caminho_entry.set_text(dialog.get_filename())
+
+ dialog.destroy()
+
+ def validar_campos(self):
+ # Validação dos campos antes de adicionar/editar um jogo
+ nome = self.nome_entry.get_text()
+ caminho = self.caminho_entry.get_text()
+
+ if not nome or not caminho:
+ self.exibir_mensagem_aviso("Preencha os campos Nome e Caminho.")
+ return False
+
+ return True
+
+ def exibir_mensagem_aviso(self, mensagem):
+ # Exibe uma mensagem de aviso
+ dialog = Gtk.MessageDialog(
+ transient_for=self,
+ flags=0,
+ message_type=Gtk.MessageType.WARNING,
+ buttons=Gtk.ButtonsType.OK,
+ text=mensagem,
+ )
+ dialog.run()
+ dialog.destroy()
+
+ def on_delete_event(self, widget, event):
+ return True
+
+# Classe para representar um jogo
+class Jogo:
+ def __init__(self, nome, caminho, prefixo, argumentos, sufixo, compatibilidade, diretorio, mangohud, gamemode):
+ self.nome = nome
+ self.caminho = caminho
+ self.argumentos = argumentos
+ self.sufixo = sufixo
+ self.compatibilidade = compatibilidade
+ self.diretorio = diretorio # Adiciona o diretório correspondente ao jogo
+ self.mangohud = mangohud # Adiciona o status do MangoHud
+ self.gamemode = gamemode # Adiciona o status do Game Mode
+ self.prefixo = prefixo # Adiciona o status do Game Mode
+
+
+# Classe principal da aplicação
+class JogoApp(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self, title="Faugus Launcher")
+ self.set_default_size(640, 480)
+
+ self.jogo_em_execucao = None # Variável para armazenar o jogo em execução
+ self.botao_executar = None # Variável para armazenar o botão de executar
+
+ # Cria o diretório se não existir e define como diretório de trabalho
+ diretorio_faugus_launcher = os.path.expanduser("~/.config/faugus-launcher/")
+ if not os.path.exists(diretorio_faugus_launcher):
+ os.makedirs(diretorio_faugus_launcher)
+
+ self.diretorio_trabalho = diretorio_faugus_launcher
+ os.chdir(self.diretorio_trabalho)
+
+ # Obtém o caminho absoluto para o diretório temporário do PyInstaller
+ script_dir = os.path.dirname(os.path.realpath(__file__))
+
+ # Obtém o caminho absoluto para o arquivo "ulwgl-faugus"
+ ulwgl_run_path = os.path.join(script_dir, 'ulwgl-faugus')
+
+ # Define o caminho de destino onde você deseja copiar a pasta ulwgl/bin
+ destino_dir = os.path.expanduser('~/.config/faugus-launcher/')
+
+ # Imprime o caminho de origem e destino
+ # print(f"Caminho de origem: {ulwgl_dir}")
+ print(f"Caminho de destino: {destino_dir}")
+
+ # Verifica se a pasta de destino já existe
+ if os.path.exists(os.path.join(destino_dir, 'ulwgl-faugus')):
+ print("O arquivo ulwgl-faugus já existe no destino. Não é necessário copiar.")
+ else:
+ # Copia o arquivo ulwgl-faugus para o diretório de destino
+ shutil.copy(ulwgl_run_path, destino_dir)
+ print("Arquivo ulwgl-faugus copiado com sucesso!")
+
+ self.jogos = []
+ self.listbox = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
+ self.carregar_jogos()
+
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+
+ # Configuração da janela com uma lista e um botão de adicionar
+ scrolled_window = Gtk.ScrolledWindow()
+ scrolled_window.set_vexpand(True)
+ scrolled_window.add(self.listbox)
+
+ self.btn_adicionar = Gtk.Button()
+ self.btn_adicionar.set_image(Gtk.Image.new_from_icon_name("list-add", Gtk.IconSize.BUTTON))
+ self.btn_adicionar.connect("clicked", self.on_btn_adicionar_clicked)
+
+ box.pack_start(scrolled_window, True, True, 0)
+ box.pack_end(self.btn_adicionar, False, False, 0)
+
+ self.add(box)
+ self.show_all()
+
+ # Conecta o sinal SIGCHLD para capturar o evento de fechamento do processo filho
+ signal.signal(signal.SIGCHLD, self.on_child_process_closed)
+
+ def on_child_process_closed(self, signum, frame):
+ # Este método será chamado quando um processo filho terminar
+ if self.jogo_em_execucao and self.jogo_em_execucao.poll() is not None:
+ # Se o jogo em execução foi encerrado, redefine o estado do botão de executar
+ if isinstance(self.botao_executar.get_child(), Gtk.Image):
+ image = self.botao_executar.get_child()
+ image.set_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)
+ else:
+ self.botao_executar.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON))
+ self.jogo_em_execucao = None
+
+ # Reativa os outros botões "Executar"
+ for row in self.listbox.get_children():
+ hbox = row.get_child()
+ btn_executar = hbox.get_children()[3]
+ btn_executar.set_sensitive(True)
+
+ def exibir_mensagem_aviso(self, mensagem):
+ # Exibe uma mensagem de aviso
+ dialog = Gtk.MessageDialog(
+ transient_for=self,
+ flags=0,
+ message_type=Gtk.MessageType.WARNING,
+ buttons=Gtk.ButtonsType.OK,
+ text=mensagem,
+ )
+ dialog.run()
+ dialog.destroy()
+
+ # Alteração no método carregar_jogos
+ def carregar_jogos(self):
+ # Carrega jogos do arquivo para a lista
+ try:
+ with open("jogos.txt", "r") as arquivo:
+ for linha in arquivo:
+ dados = linha.strip().split(";")
+ # Verifica se há pelo menos 7 campos na linha
+ if len(dados) >= 7:
+ # Extrai os primeiros 7 campos
+ nome, caminho, prefixo, argumentos, sufixo, compatibilidade, diretorio = dados[:7]
+ # Verifica se há campos adicionais para mangohud e gamemode
+ if len(dados) >= 9:
+ mangohud = dados[7]
+ gamemode = dados[8]
+ else:
+ mangohud = ""
+ gamemode = ""
+ jogo = Jogo(nome, caminho, prefixo, argumentos, sufixo, compatibilidade, diretorio, mangohud, gamemode)
+ self.jogos.append(jogo)
+ self.adicionar_item_lista(jogo)
+ else:
+ print(f"A linha '{linha}' não possui dados suficientes.")
+ except FileNotFoundError:
+ pass
+
+
+ # Alterações no método salvar_jogos
+ def salvar_jogos(self):
+ # Salva a lista de jogos no arquivo
+ with open("jogos.txt", "w") as arquivo:
+ for jogo in self.jogos:
+ mangohud_value = "MANGOHUD=1" if jogo.mangohud else ""
+ gamemode_value = "gamemoderun" if jogo.gamemode else ""
+ linha = f"{jogo.nome};{jogo.caminho};{jogo.prefixo};{jogo.argumentos};{jogo.sufixo};{jogo.compatibilidade};{jogo.diretorio};{mangohud_value};{gamemode_value}\n"
+ arquivo.write(linha)
+
+
+
+ def adicionar_item_lista(self, jogo):
+ # Adiciona um item à lista de jogos na interface
+ hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
+
+ label_jogo = Gtk.Label.new(jogo.nome)
+ hbox.pack_start(label_jogo, True, True, 0)
+
+ btn_excluir = Gtk.Button()
+ btn_excluir.set_image(Gtk.Image.new_from_icon_name("edit-delete-symbolic", Gtk.IconSize.BUTTON))
+ btn_excluir.connect('clicked', self.on_btn_excluir_clicked, jogo)
+ hbox.pack_start(btn_excluir, False, False, 0)
+
+ btn_editar = Gtk.Button()
+ btn_editar.set_image(Gtk.Image.new_from_icon_name("document-edit-symbolic", Gtk.IconSize.BUTTON))
+ btn_editar.connect('clicked', self.on_btn_editar_clicked, jogo)
+ hbox.pack_start(btn_editar, False, False, 0)
+
+ btn_executar = Gtk.Button()
+ btn_executar.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON))
+ btn_executar.connect('clicked', self.on_btn_executar_clicked, jogo)
+ hbox.pack_start(btn_executar, False, False, 0)
+
+ listbox_row = Gtk.ListBoxRow()
+ listbox_row.add(hbox)
+ listbox_row.set_activatable(False)
+ listbox_row.set_can_focus(False)
+ listbox_row.set_selectable(False)
+ self.listbox.add(listbox_row)
+
+ def on_btn_adicionar_clicked(self, widget):
+ # Callback para o botão de adicionar jogo
+ adicionar_jogo_dialog = AdicionarJogoDialog(self)
+ adicionar_jogo_dialog.connect("response", self.on_dialog_response, adicionar_jogo_dialog)
+ adicionar_jogo_dialog.show()
+ adicionar_jogo_dialog.dropdown_menu.set_active(0) # Aqui estamos definindo o primeiro item (índice 0) como ativo
+
+ def on_dialog_response(self, dialog, response_id, adicionar_jogo_dialog):
+ # Callback para a resposta da caixa de diálogo de adição/edição de jogo
+ if response_id == Gtk.ResponseType.OK:
+ if adicionar_jogo_dialog.validar_campos():
+ nome_jogo = adicionar_jogo_dialog.nome_entry.get_text()
+ caminho_jogo = adicionar_jogo_dialog.caminho_entry.get_text()
+ argumentos_jogo = adicionar_jogo_dialog.argumentos_entry.get_text()
+ sufixo_jogo = adicionar_jogo_dialog.sufixo_entry.get_text()
+ compatibilidade_jogo = adicionar_jogo_dialog.dropdown_menu.get_active_text()
+ diretorio_jogo = self.get_diretorio_compatibilidade(compatibilidade_jogo)
+ # Grava o campo "Prefix"
+ prefixo_jogo = adicionar_jogo_dialog.prefix_entry.get_text()
+
+ # Monta a linha com os valores dos campos
+ jogo_info = f"{nome_jogo};{caminho_jogo};{prefixo_jogo};{argumentos_jogo};{sufixo_jogo};{compatibilidade_jogo};{diretorio_jogo}"
+
+ # Verifica se MangoHud está marcado
+ mangohud = "MANGOHUD=1" if adicionar_jogo_dialog.mangohud_checkbox.get_active() else ""
+
+ # Verifica se Game Mode está marcado
+ gamemode = "gamemoderun" if adicionar_jogo_dialog.gamemode_checkbox.get_active() else ""
+
+ # Adiciona MangoHud e Game Mode à linha
+ jogo_info += f";{mangohud};{gamemode}\n"
+
+ # Adiciona o jogo à lista e salva no arquivo
+ with open("jogos.txt", "a") as arquivo:
+ arquivo.write(jogo_info)
+
+ jogo = Jogo(nome_jogo, caminho_jogo, prefixo_jogo, argumentos_jogo, sufixo_jogo, compatibilidade_jogo, diretorio_jogo, mangohud, gamemode)
+ self.jogos.append(jogo)
+ self.adicionar_item_lista(jogo)
+ self.atualizar_lista()
+ else:
+ return True
+
+ adicionar_jogo_dialog.destroy()
+
+
+
+
+ def get_diretorio_compatibilidade(self, compatibilidade):
+ # Função para obter o diretório correspondente ao item selecionado no dropdown
+ if compatibilidade == "Proton Experimental":
+ return "~/.steam/steam/steamapps/common/Proton - Experimental"
+ else:
+ return os.path.expanduser(f"~/.steam/steam/compatibilitytools.d/{compatibilidade}")
+ #elif compatibilidade == "Wine":
+ # return "/usr/bin/wine64"
+
+
+ def on_btn_excluir_clicked(self, widget, jogo):
+ # Callback para o botão de excluir jogo
+ confirmacao_dialog = ConfirmacaoDialog(self, "Tem certeza que deseja excluir este jogo?")
+ response = confirmacao_dialog.run()
+
+ if response == Gtk.ResponseType.YES:
+ self.jogos.remove(jogo)
+ self.salvar_jogos()
+ self.atualizar_lista()
+
+ confirmacao_dialog.destroy()
+
+ # Alteração no método on_btn_editar_clicked
+ def on_btn_editar_clicked(self, widget, jogo):
+ # Callback para o botão de editar jogo
+ editar_jogo_dialog = AdicionarJogoDialog(self)
+ editar_jogo_dialog.connect("response", self.on_edit_dialog_response, editar_jogo_dialog, jogo)
+
+ # Carregar os campos do jogo no diálogo de edição
+ editar_jogo_dialog.nome_entry.set_text(jogo.nome)
+ editar_jogo_dialog.caminho_entry.set_text(jogo.caminho)
+ editar_jogo_dialog.prefix_entry.set_text(jogo.prefixo) # Correção aqui
+ editar_jogo_dialog.argumentos_entry.set_text(jogo.argumentos)
+ editar_jogo_dialog.sufixo_entry.set_text(jogo.sufixo)
+
+ texto_predefinido = jogo.compatibilidade
+ modelo = editar_jogo_dialog.dropdown_menu.get_model()
+
+ for i, row in enumerate(modelo):
+ if row[0] == texto_predefinido:
+ editar_jogo_dialog.dropdown_menu.set_active(i)
+ break
+
+ # Verificar o estado do mangohud e gamemode no arquivo jogos.txt
+ mangohud_status = False # Define como False por padrão
+ gamemode_status = False # Define como False por padrão
+ with open("jogos.txt", "r") as arquivo:
+ for linha in arquivo:
+ campos = linha.strip().split(";")
+ if len(campos) >= 9 and campos[0] == jogo.nome:
+ mangohud_status = campos[7] == "MANGOHUD=1" # Verifica se o campo do mangohud está marcado
+ gamemode_status = campos[8] == "gamemoderun" # Verifica se o campo do gamemode está marcado
+
+ # Definir o estado dos checkboxes com base no status lido
+ editar_jogo_dialog.mangohud_checkbox.set_active(mangohud_status)
+ editar_jogo_dialog.gamemode_checkbox.set_active(gamemode_status)
+
+ editar_jogo_dialog.show()
+
+ def on_edit_dialog_response(self, dialog, response_id, editar_jogo_dialog, jogo):
+ # Callback para a resposta da caixa de diálogo de edição de jogo
+ if response_id == Gtk.ResponseType.OK:
+ nome = editar_jogo_dialog.nome_entry.get_text()
+ caminho = editar_jogo_dialog.caminho_entry.get_text()
+
+ if not nome or not caminho:
+ editar_jogo_dialog.exibir_mensagem_aviso("Preencha os campos Nome e Caminho.")
+ return True
+
+ # Atualiza todos os campos do objeto jogo
+ jogo.nome = nome
+ jogo.caminho = caminho
+ jogo.prefixo = editar_jogo_dialog.prefix_entry.get_text()
+ jogo.argumentos = editar_jogo_dialog.argumentos_entry.get_text()
+ jogo.sufixo = editar_jogo_dialog.sufixo_entry.get_text()
+ jogo.compatibilidade = editar_jogo_dialog.dropdown_menu.get_active_text()
+ jogo.diretorio = self.get_diretorio_compatibilidade(jogo.compatibilidade)
+ jogo.mangohud = editar_jogo_dialog.mangohud_checkbox.get_active()
+ jogo.gamemode = editar_jogo_dialog.gamemode_checkbox.get_active()
+
+ # Atualiza o arquivo jogos.txt com as informações do jogo editado
+ self.salvar_jogos()
+
+ self.atualizar_lista()
+
+ editar_jogo_dialog.destroy()
+
+
+
+
+ def on_btn_executar_clicked(self, widget, jogo):
+ if self.jogo_em_execucao is None:
+ # Inicia o jogo
+ nome_jogo_formatado = re.sub(r'[^a-zA-Z0-9\s]', '', jogo.nome)
+ nome_jogo_formatado = nome_jogo_formatado.replace(' ', '-')
+ nome_jogo_formatado = '-'.join(nome_jogo_formatado.lower().split())
+
+ argumentos_jogo = jogo.argumentos
+ caminho_jogo = jogo.caminho
+ prefixo_jogo = jogo.prefixo
+ sufixo_jogo = jogo.sufixo
+ compatibilidade_jogo = jogo.compatibilidade
+ diretorio_jogo = jogo.diretorio
+ mangohud_jogo = jogo.mangohud
+ gamemode_jogo = jogo.gamemode
+
+ #f'WINEPREFIX={os.path.expanduser("~/.config/faugus-launcher/prefixes/")}{nome_jogo_formatado} '
+
+ comando = (
+ f'{mangohud_jogo} '
+ f'WINEPREFIX={prefixo_jogo} '
+ f'GAMEID={nome_jogo_formatado} '
+ f'PROTONPATH=\'{os.path.expanduser(diretorio_jogo)}\' '
+ f'{argumentos_jogo} '
+ f'{gamemode_jogo} '
+ f'{os.path.expanduser("~/.config/faugus-launcher/ulwgl-faugus")} "{caminho_jogo}" "{sufixo_jogo}"'
+ )
+
+ print(comando)
+
+ self.jogo_em_execucao = subprocess.Popen(["/bin/bash", "-c", comando])
+ self.botao_executar = widget
+ if isinstance(self.botao_executar.get_child(), Gtk.Image):
+ image = self.botao_executar.get_child()
+ image.set_from_icon_name("media-playback-stop-symbolic", Gtk.IconSize.BUTTON)
+ else:
+ self.botao_executar.set_image(Gtk.Image.new_from_icon_name("media-playback-stop-symbolic", Gtk.IconSize.BUTTON))
+
+ # Desativa os outros botões "Executar"
+ for row in self.listbox.get_children():
+ hbox = row.get_child()
+ btn_executar = hbox.get_children()[3]
+ if btn_executar != widget:
+ btn_executar.set_sensitive(False)
+ else:
+ # Interrompe o jogo em execução
+ subprocess.run("ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | perl -pe 's;^.*/proc/(\d+)/exe.*$;$1;g;' | xargs -n 1 kill | killall -s9 winedevice.exe tee", shell=True)
+ self.jogo_em_execucao.wait() # Espera pelo processo ser encerrado completamente
+ self.jogo_em_execucao = None
+ if isinstance(self.botao_executar.get_child(), Gtk.Image):
+ image = self.botao_executar.get_child()
+ image.set_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON)
+ else:
+ self.botao_executar.set_image(Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON))
+
+ # Reativa os outros botões "Executar"
+ for row in self.listbox.get_children():
+ hbox = row.get_child()
+ btn_executar = hbox.get_children()[3]
+ btn_executar.set_sensitive(True)
+
+
+ def atualizar_lista(self):
+ # Atualiza a lista de jogos na interface
+ for row in self.listbox.get_children():
+ self.listbox.remove(row)
+
+ self.jogos.clear()
+
+ self.carregar_jogos()
+ self.show_all()
+
+if __name__ == "__main__":
+ # Instancia e inicia a aplicação Gtk
+ app = JogoApp()
+ app.connect("destroy", Gtk.main_quit)
+ app.show_all()
+ Gtk.main()
+
diff --git a/ulwgl-faugus b/ulwgl-faugus
new file mode 100755
index 000000000000..d52bb855dfe5
--- /dev/null
+++ b/ulwgl-faugus
@@ -0,0 +1,153 @@
+#!/bin/sh
+
+# use for debug only.
+# set -x
+
+if [ -z "$1" ] || [ -z "$WINEPREFIX" ] || [ -z "$GAMEID" ]; then
+ echo "Usage: WINEPREFIX=<wine-prefix-path> GAMEID=<ulwgl-id> PROTONPATH=<proton-version-path> ./gamelauncher.sh <executable-path> <arguments>"
+ echo "Ex:"
+ echo "WINEPREFIX=$HOME/Games/epic-games-store GAMEID=egs PROTONPATH=\"$HOME/.steam/steam/compatibilitytools.d/GE-Proton8-28\" ./gamelauncher.sh \"$HOME/Games/epic-games-store/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/EpicGamesLauncher.exe\" \"-opengl -SkipBuildPatchPrereq\""
+ exit 1
+fi
+
+ULWGL_PROTON_VER="ULWGL-Proton-8.0-5"
+ULWGL_LAUNCHER_VER="0.1-RC3"
+
+me="$(readlink -f "$0")"
+#here="${me%/*}"
+
+# Self-update
+# In flatpak it will check for /app/share/ULWGL/ULWGL-launcher.tar.gz and check version
+# In distro package it will check for /usr/share/ULWGL/ULWGL-launcher.tar.gz and check version
+# If tarball does not exist it will just download it.
+if [ ! -d "$HOME"/.local/share/ULWGL/ ]; then
+ if [ -f "${me%/*/*}"/share/ULWGL/ULWGL-launcher.tar.gz ]; then
+ tar -zxvf "${me%/*/*}"/share/ULWGL/ULWGL-launcher.tar.gz --one-top-level="$HOME"/.local/share/ULWGL
+ else
+ wget https://github.com/Open-Wine-Components/ULWGL-launcher/releases/download/$ULWGL_LAUNCHER_VER/ULWGL-launcher.tar.gz
+ tar -zxvf ULWGL-launcher.tar.gz --one-top-level="$HOME"/.local/share/ULWGL
+ fi
+else
+ if [ "$ULWGL_LAUNCHER_VER" != "$(cat "$HOME"/.local/share/ULWGL/ULWGL-VERSION)" ]; then
+ rm -Rf "$HOME"/.local/share/ULWGL/
+ if [ -f "${me%/*/*}"/share/ULWGL/ULWGL-launcher.tar.gz ]; then
+ tar -zxvf "${me%/*/*}"/share/ULWGL/ULWGL-launcher.tar.gz --one-top-level="$HOME"/.local/share/ULWGL
+ else
+ wget https://github.com/Open-Wine-Components/ULWGL-launcher/releases/download/$ULWGL_LAUNCHER_VER/ULWGL-launcher.tar.gz
+ tar -zxvf ULWGL-launcher.tar.gz --one-top-level="$HOME"/.local/share/ULWGL
+ fi
+ fi
+fi
+
+if [ "$WINEPREFIX" ]; then
+ if [ ! -d "$WINEPREFIX" ]; then
+ mkdir -p "$WINEPREFIX"
+ export PROTON_DLL_COPY="*"
+ fi
+ if [ ! -d "$WINEPREFIX"/pfx ]; then
+ ln -s "$WINEPREFIX" "$WINEPREFIX"/pfx > log 2>&1
+ fi
+ if [ ! -f "$WINEPREFIX"/tracked_files ]; then
+ touch "$WINEPREFIX"/tracked_files
+ fi
+ if [ ! -f "$WINEPREFIX/dosdevices/" ]; then
+ mkdir -p "$WINEPREFIX"/dosdevices
+ ln -s "../drive_c" "$WINEPREFIX/dosdevices/c:" > log 2>&1
+ fi
+fi
+if [ -n "$PROTONPATH" ]; then
+ if [ ! -d "$PROTONPATH" ]; then
+ echo "ERROR: $PROTONPATH is invalid, aborting!" exit 1
+ exit 1
+ fi
+fi
+if [ -z "$PROTONPATH" ]; then
+ if [ ! -d "$HOME"/.local/share/Steam/compatibilitytools.d/$ULWGL_PROTON_VER ]; then
+ wget https://github.com/Open-Wine-Components/ULWGL-Proton/releases/download/$ULWGL_PROTON_VER/$ULWGL_PROTON_VER.tar.gz
+ wget https://github.com/Open-Wine-Components/ULWGL-Proton/releases/download/$ULWGL_PROTON_VER/$ULWGL_PROTON_VER.sha512sum
+ checksum=$(sha512sum $ULWGL_PROTON_VER.tar.gz)
+ if [ "$checksum" = "$(cat $ULWGL_PROTON_VER.sha512sum)" ]; then
+ tar -zxvf $ULWGL_PROTON_VER.tar.gz --one-top-level="$HOME"/.local/share/Steam/compatibilitytools.d/
+ rm $ULWGL_PROTON_VER.tar.gz
+ rm $ULWGL_PROTON_VER.sha512sum
+ else
+ echo "ERROR: $ULWGL_PROTON_VER.tar.gz checksum does not match $ULWGL_PROTON_VER.sha512sum, aborting!"
+ rm $ULWGL_PROTON_VER.tar.gz
+ rm $ULWGL_PROTON_VER.sha512sum
+ exit 1
+ fi
+ fi
+ PROTONPATH="$HOME"/.local/share/Steam/compatibilitytools.d/$ULWGL_PROTON_VER
+else
+ export PROTONPATH="$PROTONPATH"
+fi
+export ULWGL_ID="$GAMEID"
+export STEAM_COMPAT_APP_ID="0"
+numcheck='^[0-9]+$'
+if echo "$ULWGL_ID" | cut -d "-" -f 2 | grep -Eq "$numcheck"; then
+ STEAM_COMPAT_APP_ID=$(echo "$ULWGL_ID" | cut -d "-" -f 2)
+ export STEAM_COMPAT_APP_ID
+fi
+export SteamAppId="$STEAM_COMPAT_APP_ID"
+export SteamGameId="$STEAM_COMPAT_APP_ID"
+
+# TODO: Ideally this should be the main game install path, which is often, but not always the path of the game's executable.
+if [ -z "$STEAM_COMPAT_INSTALL_PATH" ]; then
+ exepath="$(readlink -f "$1")"
+ gameinstallpath="${exepath%/*}"
+ export STEAM_COMPAT_INSTALL_PATH="$gameinstallpath"
+fi
+
+compat_lib_path=$(findmnt -T "$STEAM_COMPAT_INSTALL_PATH" | tail -n 1 | awk '{ print $1 }')
+if [ "$compat_lib_path" != "/" ]; then
+ export STEAM_COMPAT_LIBRARY_PATHS="${STEAM_COMPAT_LIBRARY_PATHS:+"${STEAM_COMPAT_LIBRARY_PATHS}:"}$compat_lib_path"
+fi
+
+if [ -z "$STEAM_RUNTIME_LIBRARY_PATH" ]; then
+ # The following info taken from steam ~/.local/share/ubuntu12_32/steam-runtime/run.sh
+ host_library_paths=
+ exit_status=0
+ ldconfig_output=$(/sbin/ldconfig -XNv 2> /dev/null; exit $?) || exit_status=$?
+ if [ $exit_status != 0 ]; then
+ echo "Warning: An unexpected error occurred while executing \"/sbin/ldconfig -XNv\", the exit status was $exit_status"
+ fi
+
+ echo "$ldconfig_output" | while IFS= read -r line; do
+ # If line starts with a leading / and contains :, it's a new path prefix
+ case "$line" in
+ /*:*)
+ library_path_prefix=$(echo "$line" | cut -d: -f1)
+ host_library_paths="$host_library_paths$library_path_prefix:"
+ ;;
+ esac
+ done
+
+ host_library_paths="${LD_LIBRARY_PATH:+"${LD_LIBRARY_PATH}:"}$host_library_paths"
+ steam_runtime_library_paths="${STEAM_COMPAT_INSTALL_PATH}:${host_library_paths}"
+ export STEAM_RUNTIME_LIBRARY_PATH="$steam_runtime_library_paths"
+fi
+
+if [ -z "$PROTON_VERB" ]; then
+ export PROTON_VERB="waitforexitandrun"
+fi
+
+export STEAM_COMPAT_CLIENT_INSTALL_PATH=''
+export STEAM_COMPAT_DATA_PATH="$WINEPREFIX"
+export STEAM_COMPAT_SHADER_PATH="$STEAM_COMPAT_DATA_PATH"/shadercache
+
+export PROTON_CRASH_REPORT_DIR='/tmp/ULWGL_crashreports'
+export FONTCONFIG_PATH=''
+
+export EXE="$1"
+if [ "$EXE" = "createprefix" ]; then
+ # Hack, leave empty.
+ # forces proton to create a prefix without actually running anything.
+ EXE=""
+fi
+shift 1
+
+export STEAM_COMPAT_TOOL_PATHS="$PROTONPATH:$HOME"/.local/share/ULWGL
+export STEAM_COMPAT_MOUNTS="$PROTONPATH:$HOME"/.local/share/ULWGL
+
+"$HOME"/.local/share/ULWGL/ULWGL "--verb=$PROTON_VERB" -- "$PROTONPATH"/proton "$PROTON_VERB" "$EXE" "$@"
+