Push experimental build

This commit is contained in:
Mykola Grymalyuk
2023-05-06 19:49:29 -06:00
parent a6e0c142ca
commit f3e2dfc4de
9 changed files with 917 additions and 1 deletions

View 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()

View 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()

View 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()

View 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

View 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

View 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)

View 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