mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-21 18:40:16 +10:00
Push experimental build
This commit is contained in:
112
resources/wx_gui/gui_build.py
Normal file
112
resources/wx_gui/gui_build.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import wx
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from resources.wx_gui import gui_main_menu, gui_install_oc, gui_support
|
||||
from resources.build import build
|
||||
from resources import constants
|
||||
|
||||
|
||||
class BuildFrame(wx.Frame):
|
||||
"""
|
||||
Create a frame for building OpenCore
|
||||
Uses a Modal Dialog for smoother transition from other frames
|
||||
"""
|
||||
def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None):
|
||||
super(BuildFrame, self).__init__(parent, title=title, size=(350, 200))
|
||||
|
||||
self.install_button: wx.Button = None
|
||||
self.text_box: wx.TextCtrl = None
|
||||
self.frame_modal: wx.Dialog = None
|
||||
|
||||
self.constants: constants.Constants = global_constants
|
||||
self.title: str = title
|
||||
self.stock_output = logging.getLogger().handlers[0].stream
|
||||
|
||||
self.frame_modal = wx.Dialog(self, title=title, size=(400, 200))
|
||||
|
||||
self._generate_elements(self.frame_modal)
|
||||
|
||||
self.SetPosition(screen_location) if screen_location else self.Centre()
|
||||
self.frame_modal.ShowWindowModal()
|
||||
|
||||
self._invoke_build()
|
||||
|
||||
|
||||
def _generate_elements(self, frame=None) -> None:
|
||||
"""
|
||||
Generate UI elements for build frame
|
||||
|
||||
Format:
|
||||
- Title label: Build and Install OpenCore
|
||||
- Text: Model: {Build or Host Model}
|
||||
- Button: Install OpenCore
|
||||
- Read-only text box: {empty}
|
||||
- Button: Return to Main Menu
|
||||
"""
|
||||
frame = self if not frame else frame
|
||||
|
||||
title_label = wx.StaticText(frame, label="Build and Install OpenCore", pos=(-1,5))
|
||||
title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
title_label.Center(wx.HORIZONTAL)
|
||||
|
||||
model_label = wx.StaticText(frame, label=f"Model: {self.constants.custom_model or self.constants.computer.real_model}", pos=(-1,30))
|
||||
model_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
model_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Button: Install OpenCore
|
||||
install_button = wx.Button(frame, label="🔩 Install OpenCore", pos=(-1, model_label.GetPosition()[1] + model_label.GetSize()[1]), size=(150, 30))
|
||||
install_button.Bind(wx.EVT_BUTTON, self.on_install)
|
||||
install_button.Center(wx.HORIZONTAL)
|
||||
install_button.Disable()
|
||||
self.install_button = install_button
|
||||
|
||||
# Read-only text box: {empty}
|
||||
text_box = wx.TextCtrl(frame, value="", pos=(-1, install_button.GetPosition()[1] + install_button.GetSize()[1] + 10), size=(400, 350), style=wx.TE_READONLY | wx.TE_MULTILINE | wx.TE_RICH2)
|
||||
text_box.Center(wx.HORIZONTAL)
|
||||
self.text_box = text_box
|
||||
|
||||
# Button: Return to Main Menu
|
||||
return_button = wx.Button(frame, label="Return to Main Menu", pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 5), size=(200, 30))
|
||||
return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
|
||||
return_button.Center(wx.HORIZONTAL)
|
||||
|
||||
# Adjust window size to fit all elements
|
||||
frame.SetSize((-1, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
|
||||
|
||||
|
||||
def _invoke_build(self):
|
||||
"""
|
||||
Calls build function and redirects stdout to the text box
|
||||
"""
|
||||
logging.getLogger().handlers[0].stream = gui_support.RedirectText(self.text_box, False)
|
||||
build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants)
|
||||
logging.getLogger().handlers[0].stream = self.stock_output
|
||||
self.install_button.Enable()
|
||||
|
||||
|
||||
def on_return_to_main_menu(self, event):
|
||||
self.frame_modal.Hide()
|
||||
main_menu_frame = gui_main_menu.MainMenu(
|
||||
None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetScreenPosition()
|
||||
)
|
||||
main_menu_frame.Show()
|
||||
self.frame_modal.Destroy()
|
||||
self.Destroy()
|
||||
|
||||
|
||||
def on_install(self, event):
|
||||
self.frame_modal.Destroy()
|
||||
self.Destroy()
|
||||
install_oc_frame = gui_install_oc.InstallOCFrame(
|
||||
None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetScreenPosition()
|
||||
)
|
||||
install_oc_frame.Show()
|
||||
|
||||
|
||||
70
resources/wx_gui/gui_entry.py
Normal file
70
resources/wx_gui/gui_entry.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import wx
|
||||
import sys
|
||||
import logging
|
||||
import atexit
|
||||
|
||||
from resources import constants
|
||||
from resources.wx_gui import (
|
||||
gui_main_menu,
|
||||
gui_build,
|
||||
gui_menubar,
|
||||
gui_install_oc
|
||||
)
|
||||
|
||||
class SupportedEntryPoints:
|
||||
"""
|
||||
Enum for supported entry points
|
||||
"""
|
||||
MAIN_MENU = gui_main_menu.MainMenu
|
||||
BUILD_OC = gui_build.BuildFrame
|
||||
INSTALL_OC = gui_install_oc.InstallOCFrame
|
||||
|
||||
class EntryPoint:
|
||||
|
||||
def __init__(self, global_constants: constants.Constants) -> None:
|
||||
self.app: wx.App = None
|
||||
self.main_menu_frame: gui_main_menu.MainMenu = None
|
||||
self.constants: constants.Constants = global_constants
|
||||
|
||||
self.constants.gui_mode = True
|
||||
|
||||
|
||||
def _generate_base_data(self) -> None:
|
||||
self.app = wx.App()
|
||||
|
||||
|
||||
def start(self, entry: SupportedEntryPoints = gui_main_menu.MainMenu) -> None:
|
||||
"""
|
||||
Launches entry point for the wxPython GUI
|
||||
"""
|
||||
self._generate_base_data()
|
||||
self.frame: wx.Frame = entry(
|
||||
None,
|
||||
title=f"{self.constants.patcher_name} ({self.constants.patcher_version})",
|
||||
global_constants=self.constants,
|
||||
screen_location=None
|
||||
)
|
||||
self.frame.SetMenuBar(gui_menubar.GenerateMenubar().generate())
|
||||
atexit.register(self.OnCloseFrame)
|
||||
|
||||
self.app.MainLoop()
|
||||
|
||||
|
||||
def OnCloseFrame(self, event=None):
|
||||
"""
|
||||
Closes the wxPython GUI
|
||||
"""
|
||||
|
||||
if not self.frame:
|
||||
return
|
||||
|
||||
logging.info("- Cleaning up wxPython GUI")
|
||||
|
||||
self.frame.SetTransparent(0)
|
||||
wx.GetApp().Yield()
|
||||
|
||||
self.frame.DestroyChildren()
|
||||
self.frame.Destroy()
|
||||
self.app.ExitMainLoop()
|
||||
|
||||
sys.exit()
|
||||
277
resources/wx_gui/gui_install_oc.py
Normal file
277
resources/wx_gui/gui_install_oc.py
Normal file
@@ -0,0 +1,277 @@
|
||||
import wx
|
||||
import threading
|
||||
import logging
|
||||
|
||||
from resources.wx_gui import gui_main_menu, gui_support
|
||||
from resources import constants, install
|
||||
|
||||
|
||||
class InstallOCFrame(wx.Frame):
|
||||
"""
|
||||
Create a frame for installing OpenCore to disk
|
||||
"""
|
||||
def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None):
|
||||
super(InstallOCFrame, self).__init__(parent, title=title, size=(300, 120))
|
||||
|
||||
self.constants: constants.Constants = global_constants
|
||||
self.title: str = title
|
||||
|
||||
self.available_disks: dict = None
|
||||
self.stock_output = logging.getLogger().handlers[0].stream
|
||||
|
||||
self._generate_elements()
|
||||
|
||||
self.SetPosition(screen_location) if screen_location else self.Centre()
|
||||
self.Show()
|
||||
|
||||
self._display_disks()
|
||||
|
||||
|
||||
def _generate_elements(self) -> None:
|
||||
"""
|
||||
Display indeterminate progress bar while collecting disk information
|
||||
|
||||
Format:
|
||||
- Title label: Install OpenCore
|
||||
- Text: Fetching information on local disks...
|
||||
- Progress bar: {indeterminate}
|
||||
"""
|
||||
|
||||
# Title label: Install OpenCore
|
||||
title_label = wx.StaticText(self, label="Install OpenCore", pos=(-1,5))
|
||||
title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
title_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Text: Parsing local disks...
|
||||
text_label = wx.StaticText(self, label="Fetching information on local disks...", pos=(-1,30))
|
||||
text_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
text_label.Center(wx.HORIZONTAL)
|
||||
self.text_label = text_label
|
||||
|
||||
# Progress bar: {indeterminate}
|
||||
progress_bar = wx.Gauge(self, range=100, pos=(-1, text_label.GetPosition()[1] + text_label.GetSize()[1]), size=(150, 30), style=wx.GA_HORIZONTAL | wx.GA_SMOOTH)
|
||||
progress_bar.Center(wx.HORIZONTAL)
|
||||
progress_bar.Pulse()
|
||||
self.progress_bar = progress_bar
|
||||
|
||||
|
||||
def _fetch_disks(self) -> None:
|
||||
"""
|
||||
Fetch information on local disks
|
||||
"""
|
||||
self.available_disks = install.tui_disk_installation(self.constants).list_disks()
|
||||
|
||||
|
||||
def _display_disks(self) -> None:
|
||||
"""
|
||||
Display disk selection dialog
|
||||
"""
|
||||
thread = threading.Thread(target=self._fetch_disks)
|
||||
thread.start()
|
||||
|
||||
while thread.is_alive():
|
||||
wx.Yield()
|
||||
continue
|
||||
|
||||
self.progress_bar.Hide()
|
||||
|
||||
# Create wxDialog for disk selection
|
||||
dialog = wx.Dialog(self, title=self.title, size=(370, -1))
|
||||
|
||||
# Title label: Install OpenCore
|
||||
title_label = wx.StaticText(dialog, label="Install OpenCore", pos=(-1,5))
|
||||
title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
title_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Text: select disk to install OpenCore onto
|
||||
text_label = wx.StaticText(dialog, label="Select disk to install OpenCore onto:", pos=(-1, title_label.GetPosition()[1] + title_label.GetSize()[1] + 5))
|
||||
text_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
text_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Add note: "Missing disks? Ensure they're FAT32 or formatted as GUID/GPT"
|
||||
gpt_note = wx.StaticText(dialog, label="Missing disks? Ensure they're FAT32 or formatted as GUID/GPT", pos=(-1, text_label.GetPosition()[1] + text_label.GetSize()[1] + 5))
|
||||
gpt_note.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
gpt_note.Center(wx.HORIZONTAL)
|
||||
|
||||
# Add buttons for each disk
|
||||
if self.available_disks is None:
|
||||
# Text: Failed to find any applicable disks
|
||||
disk_label = wx.StaticText(dialog, label="Failed to find any applicable disks", pos=(-1, gpt_note.GetPosition()[1] + gpt_note.GetSize()[1] + 5))
|
||||
disk_label.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
disk_label.Center(wx.HORIZONTAL)
|
||||
else:
|
||||
disk_root = self.constants.booted_oc_disk
|
||||
if disk_root:
|
||||
# disk6s1 -> disk6
|
||||
disk_root = self.constants.booted_oc_disk.strip("disk")
|
||||
disk_root = "disk" + disk_root.split("s")[0]
|
||||
|
||||
# Add buttons for each disk
|
||||
for disk in self.available_disks:
|
||||
# Create a button for each disk
|
||||
logging.info(f"- {self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}")
|
||||
disk_button = wx.Button(dialog, label=f"{self.available_disks[disk]['disk']} - {self.available_disks[disk]['name']} - {self.available_disks[disk]['size']}", size=(300,30), pos=(-1, gpt_note.GetPosition()[1] + gpt_note.GetSize()[1] + 5))
|
||||
disk_button.Center(wx.HORIZONTAL)
|
||||
disk_button.Bind(wx.EVT_BUTTON, lambda event, disk=disk: self._display_volumes(disk, self.available_disks))
|
||||
if disk_root == self.available_disks[disk]['disk']:
|
||||
disk_button.SetForegroundColour((25, 179, 231))
|
||||
|
||||
|
||||
if disk_root:
|
||||
# Add note: "Note: Blue represent the disk OpenCore is currently booted from"
|
||||
disk_label = wx.StaticText(dialog, label="Note: Blue represent the disk OpenCore is currently booted from", pos=(-1, disk_button.GetPosition()[1] + disk_button.GetSize()[1] + 5))
|
||||
disk_label.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
disk_label.Center(wx.HORIZONTAL)
|
||||
else:
|
||||
disk_label = wx.StaticText(dialog, label="", pos=(-1, disk_button.GetPosition()[1] + 15))
|
||||
disk_label.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
|
||||
# Add button: Search for disks again
|
||||
search_button = wx.Button(dialog, label="Search for disks again", size=(200,30), pos=(-1, disk_label.GetPosition()[1] + disk_label.GetSize()[1] + 5))
|
||||
search_button.Center(wx.HORIZONTAL)
|
||||
search_button.Bind(wx.EVT_BUTTON, self._reload_frame)
|
||||
|
||||
# Add button: Return to main menu
|
||||
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, search_button.GetPosition()[1] + 25))
|
||||
return_button.Center(wx.HORIZONTAL)
|
||||
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu)
|
||||
|
||||
# Set size
|
||||
dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
|
||||
dialog.ShowWindowModal()
|
||||
self.dialog = dialog
|
||||
|
||||
|
||||
def _display_volumes(self, disk: str, dataset: dict) -> None:
|
||||
"""
|
||||
List volumes on disk
|
||||
"""
|
||||
|
||||
self.dialog.Close()
|
||||
|
||||
# Create dialog
|
||||
dialog = wx.Dialog(
|
||||
self,
|
||||
title=f"Volumes on {disk}",
|
||||
style=wx.CAPTION | wx.CLOSE_BOX,
|
||||
size=(370, 300)
|
||||
)
|
||||
|
||||
# Add text: "Volumes on {disk}"
|
||||
text_label = wx.StaticText(dialog, label=f"Volumes on {disk}", pos=(-1, 10))
|
||||
text_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
text_label.Center(wx.HORIZONTAL)
|
||||
|
||||
partitions = install.tui_disk_installation(self.constants).list_partitions(disk, dataset)
|
||||
for partition in partitions:
|
||||
logging.info(f"- {partitions[partition]['partition']} - {partitions[partition]['name']} - {partitions[partition]['size']}")
|
||||
disk_button = wx.Button(dialog, label=f"{partitions[partition]['partition']} - {partitions[partition]['name']} - {partitions[partition]['size']}", size=(300,30), pos=(-1, text_label.GetPosition()[1] + text_label.GetSize()[1] + 5))
|
||||
disk_button.Center(wx.HORIZONTAL)
|
||||
disk_button.Bind(wx.EVT_BUTTON, lambda event, partition=partition: self._install_oc_process(partition))
|
||||
|
||||
# Add button: Return to main menu
|
||||
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, disk_button.GetPosition()[1] + disk_button.GetSize()[1]))
|
||||
return_button.Center(wx.HORIZONTAL)
|
||||
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu)
|
||||
|
||||
# Set size
|
||||
dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
|
||||
|
||||
# Show dialog
|
||||
dialog.ShowWindowModal()
|
||||
self.dialog = dialog
|
||||
|
||||
|
||||
def _install_oc_process(self, partition: dict) -> None:
|
||||
"""
|
||||
Install OpenCore to disk
|
||||
"""
|
||||
self.dialog.Close()
|
||||
|
||||
# Create dialog
|
||||
dialog = wx.Dialog(
|
||||
self,
|
||||
title=f"Installing OpenCore to {partition}",
|
||||
style=wx.CAPTION | wx.CLOSE_BOX,
|
||||
size=(370, 200)
|
||||
)
|
||||
|
||||
# Add text: "Installing OpenCore to {partition}"
|
||||
text_label = wx.StaticText(dialog, label=f"Installing OpenCore to {partition}", pos=(-1, 10))
|
||||
text_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
text_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Read-only text box: {empty}
|
||||
text_box = wx.TextCtrl(dialog, value="", pos=(-1, text_label.GetPosition()[1] + text_label.GetSize()[1] + 10), size=(370, 200), style=wx.TE_READONLY | wx.TE_MULTILINE | wx.TE_RICH2)
|
||||
text_box.Center(wx.HORIZONTAL)
|
||||
self.text_box = text_box
|
||||
|
||||
# Add button: Return to main menu
|
||||
return_button = wx.Button(dialog, label="Return to main menu", size=(200,30), pos=(-1, text_box.GetPosition()[1] + text_box.GetSize()[1] + 10))
|
||||
return_button.Center(wx.HORIZONTAL)
|
||||
return_button.Bind(wx.EVT_BUTTON, self._return_to_main_menu)
|
||||
return_button.Disable()
|
||||
|
||||
# Set size
|
||||
dialog.SetSize((370, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40))
|
||||
|
||||
# Show dialog
|
||||
dialog.ShowWindowModal()
|
||||
self.dialog = dialog
|
||||
|
||||
# Install OpenCore
|
||||
self._install_oc(partition)
|
||||
return_button.Enable()
|
||||
|
||||
|
||||
def _install_oc(self, partition: dict) -> None:
|
||||
"""
|
||||
Install OpenCore to disk
|
||||
"""
|
||||
logging.info(f"- Installing OpenCore to {partition}")
|
||||
logging.getLogger().handlers[0].stream = gui_support.RedirectText(self.text_box, False)
|
||||
result = install.tui_disk_installation(self.constants).install_opencore(partition)
|
||||
logging.getLogger().handlers[0].stream = self.stock_output
|
||||
|
||||
if result is True:
|
||||
if not self.constants.custom_model:
|
||||
gui_support.RestartHost(self).restart(message="OpenCore has finished installing to disk.\n\nYou will need to reboot and hold the Option key and select OpenCore/Boot EFI's option.\n\nWould you like to reboot?")
|
||||
else:
|
||||
popup_message = wx.MessageDialog(
|
||||
self,
|
||||
f"OpenCore has finished installing to disk.\n\nYou can eject the drive, insert it into the {self.constants.custom_model}, reboot, hold the Option key and select OpenCore/Boot EFI's option.", "Success",
|
||||
wx.OK
|
||||
)
|
||||
popup_message.ShowModal()
|
||||
|
||||
|
||||
def _reload_frame(self, event) -> None:
|
||||
"""
|
||||
Reload frame
|
||||
"""
|
||||
self.Destroy()
|
||||
frame = InstallOCFrame(
|
||||
None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetScreenPosition()
|
||||
)
|
||||
frame.Show()
|
||||
|
||||
|
||||
def _return_to_main_menu(self, event: wx.Event) -> None:
|
||||
"""
|
||||
Return to main menu
|
||||
"""
|
||||
main_menu_frame = gui_main_menu.MainMenu(
|
||||
None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetScreenPosition()
|
||||
)
|
||||
main_menu_frame.Show()
|
||||
self.Destroy()
|
||||
|
||||
|
||||
|
||||
|
||||
99
resources/wx_gui/gui_main_menu.py
Normal file
99
resources/wx_gui/gui_main_menu.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import wx
|
||||
from resources.wx_gui import (
|
||||
gui_build,
|
||||
gui_sys_patch,
|
||||
gui_support,
|
||||
)
|
||||
from resources import constants
|
||||
|
||||
class MainMenu(wx.Frame):
|
||||
def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None):
|
||||
super(MainMenu, self).__init__(parent, title=title, size=(350, 300))
|
||||
|
||||
self.constants: constants.Constants = global_constants
|
||||
self.title: str = title
|
||||
|
||||
self._generate_elements()
|
||||
|
||||
self.SetPosition(screen_location) if screen_location else self.Centre()
|
||||
self.Show()
|
||||
|
||||
|
||||
def _generate_elements(self) -> None:
|
||||
"""
|
||||
Generate UI elements for the main menu
|
||||
|
||||
Format:
|
||||
- Title label: OpenCore Legacy Patcher v{X.Y.Z}
|
||||
- Text: Model: {Build or Host Model}
|
||||
- Buttons:
|
||||
- Build and Install OpenCore
|
||||
- Post-Install Root Patch
|
||||
- Create macOS Installer
|
||||
- Settings
|
||||
- Help
|
||||
- Text: Copyright
|
||||
"""
|
||||
|
||||
# Title label: OpenCore Legacy Patcher v{X.Y.Z}
|
||||
title_label = wx.StaticText(self, label=f"OpenCore Legacy Patcher v{self.constants.patcher_version}", pos=(-1,1))
|
||||
title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
title_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Text: Model: {Build or Host Model}
|
||||
model_label = wx.StaticText(self, label=f"Model: {self.constants.custom_model or self.constants.computer.real_model}", pos=(-1,30))
|
||||
model_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
model_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Buttons:
|
||||
menu_buttons = {
|
||||
"Build and Install OpenCore": self.on_build_and_install,
|
||||
"Post-Install Root Patch": self.on_post_install_root_patch,
|
||||
"Create macOS Installer": self.on_create_macos_installer,
|
||||
"Settings": self.on_settings,
|
||||
"Help": self.on_help
|
||||
}
|
||||
button_y = model_label.GetPosition()[1] + 20
|
||||
for button_name, button_function in menu_buttons.items():
|
||||
button = wx.Button(self, label=button_name, pos=(-1, button_y), size=(200, 30))
|
||||
button.Bind(wx.EVT_BUTTON, button_function)
|
||||
button.Center(wx.HORIZONTAL)
|
||||
button_y += 30
|
||||
|
||||
# Text: Copyright
|
||||
copy_label = wx.StaticText(self, label=self.constants.copyright_date, pos=(-1, button_y + 10))
|
||||
copy_label.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
copy_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Set window size
|
||||
self.SetSize((350, copy_label.GetPosition()[1] + 50))
|
||||
|
||||
|
||||
def on_build_and_install(self, event):
|
||||
gui_build.BuildFrame(
|
||||
parent=None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetPosition()
|
||||
)
|
||||
self.Destroy()
|
||||
|
||||
|
||||
def on_post_install_root_patch(self, event):
|
||||
gui_sys_patch.SysPatchMenu(
|
||||
parent=None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetPosition()
|
||||
)
|
||||
self.Destroy()
|
||||
|
||||
|
||||
def on_create_macos_installer(self, event):
|
||||
pass
|
||||
|
||||
def on_settings(self, event):
|
||||
pass
|
||||
|
||||
def on_help(self, event):
|
||||
pass
|
||||
14
resources/wx_gui/gui_menubar.py
Normal file
14
resources/wx_gui/gui_menubar.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Generates menubar for wxPython to use
|
||||
|
||||
import wx
|
||||
|
||||
class GenerateMenubar:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.menubar: wx.MenuBar = None
|
||||
|
||||
|
||||
def generate(self) -> wx.MenuBar:
|
||||
self.menubar = wx.MenuBar()
|
||||
|
||||
return self.menubar
|
||||
139
resources/wx_gui/gui_support.py
Normal file
139
resources/wx_gui/gui_support.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import wx
|
||||
import time
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
|
||||
import subprocess
|
||||
|
||||
from resources import constants
|
||||
|
||||
class RedirectText(object):
|
||||
"""
|
||||
Redirects stdout to a wxPython TextCtrl
|
||||
"""
|
||||
|
||||
def __init__(self, aWxTextCtrl: wx.TextCtrl, sleep: bool):
|
||||
self.out = aWxTextCtrl
|
||||
self.sleep = sleep
|
||||
|
||||
def write(self,string):
|
||||
self.out.WriteText(string)
|
||||
wx.GetApp().Yield()
|
||||
if self.sleep:
|
||||
time.sleep(0.01)
|
||||
|
||||
def fileno(self):
|
||||
return 1
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
class RestartHost:
|
||||
"""
|
||||
Restarts the host machine
|
||||
"""
|
||||
|
||||
def __init__(self, frame) -> None:
|
||||
self.frame: wx.Frame = frame
|
||||
|
||||
|
||||
def restart(self, event: wx.Event = None, message: str = ""):
|
||||
self.popup = wx.MessageDialog(
|
||||
self.frame,
|
||||
message,
|
||||
"Reboot to apply?",
|
||||
wx.YES_NO | wx.ICON_INFORMATION
|
||||
)
|
||||
self.popup.SetYesNoLabels("Reboot", "Ignore")
|
||||
answer = self.popup.ShowModal()
|
||||
if answer == wx.ID_YES:
|
||||
# Reboots with Count Down prompt (user can still dismiss if needed)
|
||||
self.frame.Hide()
|
||||
wx.GetApp().Yield()
|
||||
subprocess.call(['osascript', '-e', 'tell app "loginwindow" to «event aevtrrst»'])
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class RelaunchApplicationAsRoot:
|
||||
"""
|
||||
Relaunches the application as root
|
||||
"""
|
||||
|
||||
def __init__(self, frame: wx.Frame, global_constants: constants.Constants) -> None:
|
||||
self.constants = global_constants
|
||||
self.frame: wx.Frame = frame
|
||||
|
||||
def relaunch(self, event: wx.Event):
|
||||
|
||||
self.dialog = wx.MessageDialog(
|
||||
self.frame,
|
||||
"OpenCore Legacy Patcher needs to relaunch as admin to continue. You will be prompted to enter your password.",
|
||||
"Relaunch as root?",
|
||||
wx.YES_NO | wx.ICON_QUESTION
|
||||
)
|
||||
|
||||
# Show Dialog Box
|
||||
if self.dialog.ShowModal() != wx.ID_YES:
|
||||
logging.info("User cancelled relaunch")
|
||||
return
|
||||
|
||||
|
||||
timer: int = 5
|
||||
program_arguments: str = ""
|
||||
|
||||
if event:
|
||||
if event.GetEventObject() != wx.Menu:
|
||||
try:
|
||||
if event.GetEventObject().GetLabel() in ["Start Root Patching", "Reinstall Root Patches"]:
|
||||
program_arguments = " --gui_patch"
|
||||
elif event.GetEventObject().GetLabel() == "Revert Root Patches":
|
||||
program_arguments = " --gui_unpatch"
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
if self.constants.launcher_script is None:
|
||||
program_arguments = f"'{self.constants.launcher_binary}'{program_arguments}"
|
||||
else:
|
||||
program_arguments = f"{self.constants.launcher_binary} {self.constants.launcher_script}{program_arguments}"
|
||||
|
||||
# Relaunch as root
|
||||
args = [
|
||||
"osascript",
|
||||
"-e",
|
||||
f'''do shell script "{program_arguments}"'''
|
||||
' with prompt "OpenCore Legacy Patcher needs administrator privileges to relaunch as admin."'
|
||||
" with administrator privileges"
|
||||
" without altering line endings",
|
||||
]
|
||||
|
||||
self.frame.DestroyChildren()
|
||||
self.frame.SetSize(400, 300)
|
||||
|
||||
# Header
|
||||
header = wx.StaticText(self.frame, label="Relaunching as root")
|
||||
header.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
header.Centre(wx.HORIZONTAL)
|
||||
|
||||
# Add count down label
|
||||
countdown_label = wx.StaticText(self.frame, label=f"Closing old process in {timer} seconds", pos=(0, header.GetPosition().y + header.GetSize().height + 3))
|
||||
countdown_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
countdown_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Set size of frame
|
||||
self.frame.SetSize((-1, countdown_label.GetPosition().y + countdown_label.GetSize().height + 40))
|
||||
|
||||
wx.GetApp().Yield()
|
||||
|
||||
logging.info(f"- Relaunching as root with command: {program_arguments}")
|
||||
subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
wx.GetApp().Yield()
|
||||
countdown_label.SetLabel(f"Closing old process in {timer} seconds")
|
||||
time.sleep(1)
|
||||
timer -= 1
|
||||
if timer == 0:
|
||||
break
|
||||
sys.exit(0)
|
||||
202
resources/wx_gui/gui_sys_patch.py
Normal file
202
resources/wx_gui/gui_sys_patch.py
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
import wx
|
||||
import logging
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
|
||||
from resources import constants
|
||||
from resources.sys_patch import (
|
||||
sys_patch,
|
||||
sys_patch_detect
|
||||
)
|
||||
|
||||
from resources.wx_gui import (
|
||||
gui_main_menu
|
||||
)
|
||||
|
||||
class SysPatchMenu(wx.Frame):
|
||||
"""
|
||||
Create a frame for building OpenCore
|
||||
Uses a Modal Dialog for smoother transition from other frames
|
||||
"""
|
||||
def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None):
|
||||
super(SysPatchMenu, self).__init__(parent, title=title, size=(350, 250))
|
||||
|
||||
self.title = title
|
||||
self.constants: constants.Constants = global_constants
|
||||
self.frame_modal: wx.Dialog = None
|
||||
|
||||
self.frame_modal = wx.Dialog(self, title=title, size=(370, 200))
|
||||
|
||||
self._generate_elements(self.frame_modal)
|
||||
|
||||
self.SetPosition(screen_location) if screen_location else self.Centre()
|
||||
self.frame_modal.ShowWindowModal()
|
||||
|
||||
|
||||
|
||||
def _generate_elements(self, frame=None) -> None:
|
||||
"""
|
||||
Generate UI elements for root patching frame
|
||||
|
||||
Format:
|
||||
- Title label: Post-Install Menu
|
||||
- Label: Available patches:
|
||||
- Labels: {patch name}
|
||||
- Button: Start Root Patching
|
||||
- Button: Revert Root Patches
|
||||
- Button: Return to Main Menu
|
||||
"""
|
||||
frame = self if not frame else frame
|
||||
|
||||
title_label = wx.StaticText(frame, label="Post-Install Menu", pos=(-1, 10))
|
||||
title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
title_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Label: Available patches:
|
||||
available_label = wx.StaticText(frame, label="Available patches for your system:", pos=(-1, title_label.GetPosition()[1] + title_label.GetSize()[1] + 10))
|
||||
available_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
available_label.Center(wx.HORIZONTAL)
|
||||
|
||||
# Labels: {patch name}
|
||||
patches: dict = sys_patch_detect.DetectRootPatch(self.constants.computer.real_model, self.constants).detect_patch_set()
|
||||
can_unpatch: bool = patches["Validation: Unpatching Possible"]
|
||||
|
||||
if not any(not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True for patch in patches):
|
||||
logging.info("- No applicable patches available")
|
||||
patches = []
|
||||
|
||||
# Check if OCLP has already applied the same patches
|
||||
no_new_patches = not self._check_if_new_patches_needed(patches) if patches else False
|
||||
|
||||
|
||||
if not patches:
|
||||
# Prompt user with no patches found
|
||||
patch_label = wx.StaticText(frame, label="No patches needed", pos=(-1, available_label.GetPosition()[1] + 20))
|
||||
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
patch_label.Center(wx.HORIZONTAL)
|
||||
|
||||
else:
|
||||
# Add Label for each patch
|
||||
i = 0
|
||||
if no_new_patches is True:
|
||||
patch_label = wx.StaticText(frame, label="No new patches needed", pos=(-1, 50))
|
||||
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
patch_label.Center(wx.HORIZONTAL)
|
||||
i = i + 10
|
||||
else:
|
||||
for patch in patches:
|
||||
if (not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True):
|
||||
logging.info(f"- Adding patch: {patch} - {patches[patch]}")
|
||||
patch_label = wx.StaticText(frame, label=f"- {patch}", pos=(available_label.GetPosition()[0]
|
||||
, available_label.GetPosition()[1] + 20 + i))
|
||||
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
i = i + 5
|
||||
|
||||
|
||||
if patches["Validation: Patching Possible"] is False:
|
||||
# Cannot patch due to the following reasons:
|
||||
i = i + 10
|
||||
patch_label = wx.StaticText(frame, label="Cannot patch due to the following reasons:", pos=(-1, patch_label.GetPosition().y + i + 10))
|
||||
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
patch_label.Center(wx.HORIZONTAL)
|
||||
|
||||
|
||||
for patch in patches:
|
||||
if not patch.startswith("Validation"):
|
||||
continue
|
||||
if patches[patch] is False:
|
||||
continue
|
||||
if patch == "Validation: Unpatching Possible":
|
||||
continue
|
||||
|
||||
patch_label = wx.StaticText(frame, label=f"- {patch.split('Validation: ')[1]}", pos=(available_label.GetPosition().x - 10, patch_label.GetPosition().y + 20))
|
||||
patch_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
# patch_label.Center(wx.HORIZONTAL)
|
||||
|
||||
|
||||
# Button: Start Root Patching
|
||||
start_button = wx.Button(frame, label="Start Root Patching", pos=(10, patch_label.GetPosition().y + 40), size=(170, 30))
|
||||
start_button.Bind(wx.EVT_BUTTON, lambda event: self._start_root_patching(frame, patches, no_new_patches))
|
||||
start_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
start_button.Center(wx.HORIZONTAL)
|
||||
|
||||
# Button: Revert Root Patches
|
||||
revert_button = wx.Button(frame, label="Revert Root Patches", pos=(10, start_button.GetPosition().y + start_button.GetSize().height - 5
|
||||
), size=(170, 30))
|
||||
revert_button.Bind(wx.EVT_BUTTON, lambda event: self._revert_root_patching(frame, patches, can_unpatch))
|
||||
revert_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
revert_button.Center(wx.HORIZONTAL)
|
||||
|
||||
# Button: Return to Main Menu
|
||||
return_button = wx.Button(frame, label="Return to Main Menu", pos=(10, revert_button.GetPosition().y + revert_button.GetSize().height + 10), size=(150, 30))
|
||||
return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu)
|
||||
return_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont"))
|
||||
return_button.Center(wx.HORIZONTAL)
|
||||
|
||||
# Set frame size
|
||||
frame.SetSize((-1, return_button.GetPosition().y + return_button.GetSize().height + 40))
|
||||
|
||||
|
||||
|
||||
def _start_root_patching(self, frame: wx.Frame, patches: dict, no_new_patches: bool):
|
||||
pass
|
||||
|
||||
|
||||
def _revert_root_patching(self, frame: wx.Frame, patches: dict, can_unpatch: bool):
|
||||
pass
|
||||
|
||||
def on_return_to_main_menu(self, event):
|
||||
self.frame_modal.Hide()
|
||||
main_menu_frame = gui_main_menu.MainMenu(
|
||||
None,
|
||||
title=self.title,
|
||||
global_constants=self.constants,
|
||||
screen_location=self.GetScreenPosition()
|
||||
)
|
||||
main_menu_frame.Show()
|
||||
self.frame_modal.Destroy()
|
||||
self.Destroy()
|
||||
|
||||
|
||||
|
||||
|
||||
def _check_if_new_patches_needed(self, patches: dict) -> bool:
|
||||
"""
|
||||
Checks if any new patches are needed for the user to install
|
||||
Newer users will assume the root patch menu will present missing patches.
|
||||
Thus we'll need to see if the exact same OCLP build was used already
|
||||
"""
|
||||
|
||||
if self.constants.commit_info[0] in ["Running from source", "Built from source"]:
|
||||
return True
|
||||
|
||||
if self.constants.computer.oclp_sys_url != self.constants.commit_info[2]:
|
||||
# If commits are different, assume patches are as well
|
||||
return True
|
||||
|
||||
oclp_plist = "/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist"
|
||||
if not Path(oclp_plist).exists():
|
||||
# If it doesn't exist, no patches were ever installed
|
||||
# ie. all patches applicable
|
||||
return True
|
||||
|
||||
oclp_plist_data = plistlib.load(open(oclp_plist, "rb"))
|
||||
for patch in patches:
|
||||
if (not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True):
|
||||
# Patches should share the same name as the plist key
|
||||
# See sys_patch_dict.py for more info
|
||||
patch_installed = False
|
||||
for key in oclp_plist_data:
|
||||
if "Display Name" not in oclp_plist_data[key]:
|
||||
continue
|
||||
if oclp_plist_data[key]["Display Name"] == patch:
|
||||
patch_installed = True
|
||||
break
|
||||
|
||||
if patch_installed is False:
|
||||
logging.info(f"- Patch {patch} not installed")
|
||||
return True
|
||||
|
||||
logging.info("- No new patches detected for system")
|
||||
return False
|
||||
Reference in New Issue
Block a user