From 28d3e981c541d5126ced716a5b0cc550e4ca22f4 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Fri, 12 May 2023 09:44:44 -0600 Subject: [PATCH] GUI: Implement Pulse() work-around for non-Metal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASB I beg of you, please fix this. This hack is so painful… --- resources/macos_installer_handler.py | 1 + resources/wx_gui/gui_install_oc.py | 9 +- .../wx_gui/gui_macos_installer_download.py | 8 +- resources/wx_gui/gui_macos_installer_flash.py | 33 +++++-- resources/wx_gui/gui_main_menu.py | 12 +++ resources/wx_gui/gui_support.py | 91 +++++++++++++++++++ resources/wx_gui/gui_sys_patch.py | 11 ++- 7 files changed, 153 insertions(+), 12 deletions(-) diff --git a/resources/macos_installer_handler.py b/resources/macos_installer_handler.py index 2632b481d..c6a6ce10e 100644 --- a/resources/macos_installer_handler.py +++ b/resources/macos_installer_handler.py @@ -585,6 +585,7 @@ class LocalInstallerCatalog: "Build": app_sdk, "Path": application, "Minimum Host OS": min_required, + "OS": kernel } }) diff --git a/resources/wx_gui/gui_install_oc.py b/resources/wx_gui/gui_install_oc.py index e6011e830..e5cc8aabf 100644 --- a/resources/wx_gui/gui_install_oc.py +++ b/resources/wx_gui/gui_install_oc.py @@ -21,6 +21,8 @@ class InstallOCFrame(wx.Frame): self.available_disks: dict = None self.stock_output = logging.getLogger().handlers[0].stream + self.progress_bar_animation: gui_support.GaugePulseCallback = None + self.hyperlink_colour = (25, 179, 231) self._generate_elements() @@ -55,7 +57,11 @@ class InstallOCFrame(wx.Frame): # 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() + + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() + + self.progress_bar_animation = progress_bar_animation self.progress_bar = progress_bar @@ -77,6 +83,7 @@ class InstallOCFrame(wx.Frame): wx.Yield() continue + self.progress_bar_animation.stop_pulse() self.progress_bar.Hide() # Create wxDialog for disk selection diff --git a/resources/wx_gui/gui_macos_installer_download.py b/resources/wx_gui/gui_macos_installer_download.py index c4b711bff..1b86b6c4f 100644 --- a/resources/wx_gui/gui_macos_installer_download.py +++ b/resources/wx_gui/gui_macos_installer_download.py @@ -81,7 +81,8 @@ class macOSInstallerFrame(wx.Frame): # Progress bar progress_bar = wx.Gauge(self, range=100, pos=(-1, title_label.GetPosition()[1] + title_label.GetSize()[1] + 5), size=(250, 30)) progress_bar.Center(wx.HORIZONTAL) - progress_bar.Pulse() + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() # Set size of frame self.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 40)) @@ -100,6 +101,7 @@ class macOSInstallerFrame(wx.Frame): while thread.is_alive(): wx.Yield() + progress_bar_animation.stop_pulse() progress_bar.Hide() self._display_available_installers() @@ -233,7 +235,8 @@ class macOSInstallerFrame(wx.Frame): chunk_label.SetLabel("May take a few minutes...") chunk_label.Center(wx.HORIZONTAL) - progress_bar.Pulse() + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() # Start thread to extract installer self.result = False @@ -250,6 +253,7 @@ class macOSInstallerFrame(wx.Frame): while thread.is_alive(): wx.Yield() + progress_bar_animation.stop_pulse() progress_bar.Hide() chunk_label.SetLabel("Successfully extracted macOS installer" if self.result is True else "Failed to extract macOS installer") chunk_label.Center(wx.HORIZONTAL) diff --git a/resources/wx_gui/gui_macos_installer_flash.py b/resources/wx_gui/gui_macos_installer_flash.py index 392155018..0cd5116ba 100644 --- a/resources/wx_gui/gui_macos_installer_flash.py +++ b/resources/wx_gui/gui_macos_installer_flash.py @@ -8,7 +8,7 @@ import tempfile from pathlib import Path -from resources.wx_gui import gui_main_menu, gui_build +from resources.wx_gui import gui_main_menu, gui_build, gui_support from resources import ( constants, macos_installer_handler, @@ -31,6 +31,8 @@ class macOSInstallerFlashFrame(wx.Frame): self.available_disks: dict = {} self.prepare_result: bool = False + self.progress_bar_animation: gui_support.GaugePulseCallback = None + self.frame_modal: wx.Dialog = None self._generate_elements() @@ -54,7 +56,10 @@ class macOSInstallerFlashFrame(wx.Frame): # Progress bar progress_bar = wx.Gauge(self, range=100, pos=(-1, 30), size=(200, 30)) progress_bar.Center(wx.HORIZONTAL) - progress_bar.Pulse() + + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() + self.progress_bar_animation = progress_bar_animation # Set size of frame self.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 40)) @@ -105,6 +110,8 @@ class macOSInstallerFlashFrame(wx.Frame): # Set size of frame frame_modal.SetSize((-1, cancel_button.GetPosition()[1] + cancel_button.GetSize()[1] + 40)) + self.progress_bar_animation.stop_pulse() + frame_modal.ShowWindowModal() self.frame_modal = frame_modal @@ -123,7 +130,9 @@ class macOSInstallerFlashFrame(wx.Frame): # Progress bar progress_bar = wx.Gauge(self, range=100, pos=(-1, 30), size=(200, 30)) progress_bar.Center(wx.HORIZONTAL) - progress_bar.Pulse() + + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() # Set size of frame self.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 40)) @@ -179,6 +188,9 @@ class macOSInstallerFlashFrame(wx.Frame): # Set size of frame self.frame_modal.SetSize((-1, cancel_button.GetPosition()[1] + cancel_button.GetSize()[1] + 40)) + + progress_bar_animation.stop_pulse() + self.frame_modal.ShowWindowModal() @@ -217,7 +229,9 @@ class macOSInstallerFlashFrame(wx.Frame): # Progress bar progress_bar = wx.Gauge(self, range=100, pos=(-1, bytes_written_label.GetPosition()[1] + bytes_written_label.GetSize()[1] + 5), size=(300, 30)) progress_bar.Center(wx.HORIZONTAL) - progress_bar.Pulse() + + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() # Set size of frame self.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 40)) @@ -232,10 +246,11 @@ class macOSInstallerFlashFrame(wx.Frame): # Base Size estimated_size = 16000 # AutoPkg (700MB~) - estimated_size += 700 if self.constants.detected_os >= os_data.os_data.big_sur else 0 + estimated_size += 700 if installer['OS'] >= os_data.os_data.big_sur else 0 # KDK (700MB~, and overhead for copying to installer) - estimated_size += 700 * 2 if self.constants.detected_os >= os_data.os_data.ventura else 0 + estimated_size += 700 * 2 if installer['OS'] >= os_data.os_data.ventura else 0 + progress_bar_animation.stop_pulse() progress_bar.SetRange(estimated_size) root_disk = disk['identifier'][5:] @@ -259,10 +274,14 @@ class macOSInstallerFlashFrame(wx.Frame): return # Next verify the installer - progress_bar.Pulse() + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() + bytes_written_label.SetLabel("Validating Installer Integrity...") error_message = self._validate_installer_pkg(disk['identifier']) + progress_bar_animation.stop_pulse() + if error_message != "": progress_bar.SetValue(0) wx.MessageBox(f"Failed to validate installer, cannot continue.\n This can generally happen due to a faulty USB drive, as flashing is an intensive process that can trigger hardware faults not normally seen. \n\n{error_message}", "Corrupted Installer!", wx.OK | wx.ICON_ERROR) diff --git a/resources/wx_gui/gui_main_menu.py b/resources/wx_gui/gui_main_menu.py index dff901c56..158553316 100644 --- a/resources/wx_gui/gui_main_menu.py +++ b/resources/wx_gui/gui_main_menu.py @@ -8,6 +8,7 @@ from resources.wx_gui import ( gui_settings, ) from resources import constants +from data import model_array, os_data class MainMenu(wx.Frame): def __init__(self, parent: wx.Frame, title: str, global_constants: constants.Constants, screen_location: tuple = None): @@ -16,6 +17,9 @@ class MainMenu(wx.Frame): self.constants: constants.Constants = global_constants self.title: str = title + self.model_label: wx.StaticText = None + self.build_button: wx.Button = None + self._generate_elements() self.SetPosition(screen_location) if screen_location else self.Centre() @@ -64,6 +68,14 @@ class MainMenu(wx.Frame): button.Center(wx.HORIZONTAL) button_y += 30 + if button_name == "Build and Install OpenCore": + self.build_button = button + if gui_support.CheckProperties(self.constants).host_can_build() is False: + button.Disable() + elif button_name == "Post-Install Root Patch": + if self.constants.detected_os < os_data.os_data.big_sur: + button.Disable() + # 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")) diff --git a/resources/wx_gui/gui_support.py b/resources/wx_gui/gui_support.py index 535be2013..bb91f14da 100644 --- a/resources/wx_gui/gui_support.py +++ b/resources/wx_gui/gui_support.py @@ -4,10 +4,12 @@ import sys import time import logging import subprocess +import threading from pathlib import Path from resources import constants +from data import model_array, os_data class GenerateMenubar: @@ -21,6 +23,95 @@ class GenerateMenubar: return self.menubar + +class GaugePulseCallback: + """ + Uses an alternative Pulse() method for wx.Gauge() on macOS Monterey+ + Dirty hack, however better to display some form of animation than none at all + """ + + def __init__(self, global_constants: constants.Constants, gauge: wx.Gauge) -> None: + self.gauge: wx.Gauge = gauge + + self.pulse_thread: threading.Thread = None + self.pulse_thread_active: bool = False + + self.gauge_value: int = 0 + self.pulse_forward: bool = True + + self.non_metal_alternative: bool = CheckProperties(global_constants).host_is_non_metal() + + + def start_pulse(self) -> None: + if self.non_metal_alternative is False: + self.gauge.Pulse() + return + self.pulse_thread_active = True + self.pulse_thread = threading.Thread(target=self._pulse) + self.pulse_thread.start() + + + def stop_pulse(self) -> None: + if self.non_metal_alternative is False: + return + self.pulse_thread_active = False + self.pulse_thread.join() + + + def _pulse(self) -> None: + while self.pulse_thread_active: + if self.gauge_value == 0: + self.pulse_forward = True + + elif self.gauge_value == 100: + self.pulse_forward = False + + if self.pulse_forward: + self.gauge_value += 1 + else: + self.gauge_value -= 1 + + wx.CallAfter(self.gauge.SetValue, self.gauge_value) + time.sleep(0.005) + + +class CheckProperties: + + def __init__(self, global_constants: constants.Constants) -> None: + self.constants: constants.Constants = global_constants + + def host_can_build(self): + """ + Check if host supports building OpenCore configs + """ + if self.constants.host_is_hackintosh is True: + return False + if self.constants.allow_oc_everywhere is True: + return True + if self.constants.custom_model: + return True + if self.constants.computer.real_model in model_array.SupportedSMBIOS: + return True + + return False + + + def host_is_non_metal(self): + """ + Check if host is non-metal + Primarily for wx.Gauge().Pulse() workaround (where animation doesn't work on Monterey+) + """ + + if self.constants.detected_os < os_data.os_data.monterey: + return False + if not Path("/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLightOld.dylib").exists(): + # SkyLight stubs are only used on non-Metal + return False + + return True + + + class PayloadMount: def __init__(self, global_constants: constants.Constants, frame: wx.Frame) -> None: diff --git a/resources/wx_gui/gui_sys_patch.py b/resources/wx_gui/gui_sys_patch.py index 098caf16d..57d187b9d 100644 --- a/resources/wx_gui/gui_sys_patch.py +++ b/resources/wx_gui/gui_sys_patch.py @@ -61,7 +61,9 @@ class SysPatchMenu(wx.Frame): progress_bar = wx.Gauge(frame, range=100, pos=(-1, subheader.GetPosition()[1] + subheader.GetSize()[1] + 5), size=(250, 20)) progress_bar.Center(wx.HORIZONTAL) - progress_bar.Pulse() + + progress_bar_animation = gui_support.GaugePulseCallback(self.constants, progress_bar) + progress_bar_animation.start_pulse() # Set size of frame frame.SetSize((-1, progress_bar.GetPosition()[1] + progress_bar.GetSize()[1] + 35)) @@ -76,10 +78,10 @@ class SysPatchMenu(wx.Frame): kdk_thread.start() while kdk_thread.is_alive(): - progress_bar.Pulse() wx.GetApp().Yield() if self.kdk_obj.success is False: + progress_bar_animation.stop_pulse() progress_bar.SetValue(0) wx.MessageBox(f"KDK download failed: {self.kdk_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR) return False @@ -107,13 +109,18 @@ class SysPatchMenu(wx.Frame): subheader.Center(wx.HORIZONTAL) wx.GetApp().Yield() + progress_bar_animation.stop_pulse() + if self.kdk_obj.validate_kdk_checksum() is False: + progress_bar.SetValue(0) logging.error("KDK checksum validation failed") logging.error(self.kdk_obj.error_msg) msg = wx.MessageDialog(frame, f"KDK checksum validation failed: {self.kdk_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR) msg.ShowModal() return False + progress_bar.SetValue(100) + logging.info("KDK download complete") return True