From 18c4a5860af24770d3f304ea5421d2579143ed5a Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 8 Aug 2018 12:44:40 +0200 Subject: [PATCH] Further changes to message loop issues on Mac (#442). Minor fix to CefAppProtocol implementation, see CEF Forum topic for details. Updated Qt example so that message loop work is not called in a timer when message pump is enabled. This is not required. In wxPython apps it is still required to enable both message pump and message looper timer work. --- api/ApplicationSettings.md | 6 ++++-- docs/Migration-guide.md | 26 ++++++++++++++++---------- examples/qt.py | 12 +++++++----- examples/wxpython.py | 33 ++++++++++++++++++--------------- src/cefpython.pyx | 8 +++++--- src/client_handler/util_mac.h | 1 + src/client_handler/util_mac.mm | 24 +++++++++++++++++++++--- src/extern/mac.pxd | 1 + 8 files changed, 73 insertions(+), 38 deletions(-) diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index 82ca5b23..a9ad20f1 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -201,8 +201,10 @@ on these platforms. See [Issue #246](https://github.com/cztomczak/cefpython/issu for more details. IMPORTANT: Currently there are issues on Mac with both message loop work - and external message pump. The working solution is to call - a message loop work in a timer and enable external message pump + and external message pump. In Qt apps calling message loop + work in a timer doesn't work anymore, you have to use external + message pump. In wxPython apps it is required to call a message + loop work in a timer and enable external message pump both at the same time (an incorrect approach, but it works). This is just a temporary solution and how this affects performance was not tested. See [Issue #442](../../../issues/442) diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index b211f355..3063ada1 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -45,7 +45,7 @@ Table of contents: * [v66+ Threads removed: TID_DB, TID_PROCESS_LAUNCHER, TID_CACHE](#v66-threads-removed-tid_db-tid_process_launcher-tid_cache) * [v66+ cef.Request.Flags changed](#v66-cefrequestflags-changed) * [v66+ RequestHandler.GetCookieManager not getting called in some cases](#v66-requesthandlergetcookiemanager-not-getting-called-in-some-cases) -* [v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loops-qt-wxpython) +* [v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython)](#v66-changes-to-mac-apps-that-integrate-into-existing-message-loop-qt-wxpython) @@ -386,18 +386,24 @@ callback is not getting called due to a race condition. This bug is to be fixed in Issue [#429](../../../issues/429). -## v66+ Changes to Mac apps that integrate into existing message loops (Qt, wxPython) +## v66+ Changes to Mac apps that integrate into existing message loop (Qt, wxPython) -The `qt.py` and `wxpython.py` examples were modified to set +In Qt apps calling message loop work in a timer doesn't work anymore. +You have to enable external message pump by setting ApplicationSettings.[external_message_pump](../api/ApplicationSettings.md#external_message_pump) -to `True`. Due to Issue [#442](../../../issues/442) it is required -to implement both approaches to integrating with existing message -loops at the same time: +to `True`. The `qt.py` example was updated to disable calling +message loop work in a timer. External message pump +is a recommended way over calling message loop work in a timer on Mac, +so this should make Qt apps work smoothly. + +In wxPython apps you have to implement both approaches for +integrating with existing message loop at the same time: 1. Call `cef.DoMessageLoopWork` in a 10ms timer 2. Set `ApplicationSettings.external_message_pump` to True -This is not a correct approach and is only a temporary fix. More -testing is required to check if that resolves all the issues with message -loop freezing. Only basic testing was performed. It was not tested of -how this change affects performance. +This is not a correct approach and is only a temporary fix for wxPython +apps. More testing is required to check if that resolves all the issues +with message loop freezing. Only basic testing was performed. It was not +tested of how this change affects performance. +See Issue [#442](../../../issues/442) for more details on the issues. diff --git a/examples/qt.py b/examples/qt.py index dc8f944a..d0ed3300 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -79,10 +79,10 @@ def main(): settings = {} if MAC: # Issue #442 requires enabling message pump on Mac - # and calling message loop work in a timer both at - # the same time. This is an incorrect approach - # and only a temporary solution. + # in Qt example. Calling cef.DoMessageLoopWork in a timer + # doesn't work anymore. settings["external_message_pump"] = True + cef.Initialize(settings) app = CefApplication(sys.argv) main_window = MainWindow() @@ -90,7 +90,8 @@ def main(): main_window.activateWindow() main_window.raise_() app.exec_() - app.stopTimer() + if not cef.GetAppSetting("external_message_pump"): + app.stopTimer() del main_window # Just to be safe, similarly to "del app" del app # Must destroy app object before calling Shutdown cef.Shutdown() @@ -266,7 +267,8 @@ def resizeEvent(self, event): class CefApplication(QApplication): def __init__(self, args): super(CefApplication, self).__init__(args) - self.timer = self.createTimer() + if not cef.GetAppSetting("external_message_pump"): + self.timer = self.createTimer() self.setupIcon() def createTimer(self): diff --git a/examples/wxpython.py b/examples/wxpython.py index bafcdaec..91adea2d 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -21,6 +21,17 @@ LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") +if MAC: + try: + # noinspection PyUnresolvedReferences + from AppKit import NSApp + except ImportError: + print("[wxpython.py] Error: PyObjC package is missing, " + "cannot fix Issue #371") + print("[wxpython.py] To install PyObjC type: " + "pip install -U pyobjc") + sys.exit(1) + # Configuration WIDTH = 800 HEIGHT = 600 @@ -37,7 +48,7 @@ def main(): # Issue #442 requires enabling message pump on Mac # and calling message loop work in a timer both at # the same time. This is an incorrect approach - # and only a temporary solution. + # and only a temporary fix. settings["external_message_pump"] = True if WINDOWS: # noinspection PyUnresolvedReferences, PyArgumentList @@ -87,20 +98,12 @@ def __init__(self): self.browser_panel.Bind(wx.EVT_SIZE, self.OnSize) if MAC: - try: - # noinspection PyUnresolvedReferences - from AppKit import NSApp - # Make the content view for the window have a layer. - # This will make all sub-views have layers. This is - # necessary to ensure correct layer ordering of all - # child views and their layers. This fixes Window - # glitchiness during initial loading on Mac (Issue #371). - NSApp.windows()[0].contentView().setWantsLayer_(True) - except ImportError: - print("[wxpython.py] Warning: PyObjC package is missing, " - "cannot fix Issue #371") - print("[wxpython.py] To install PyObjC type: " - "pip install -U pyobjc") + # Make the content view for the window have a layer. + # This will make all sub-views have layers. This is + # necessary to ensure correct layer ordering of all + # child views and their layers. This fixes Window + # glitchiness during initial loading on Mac (Issue #371). + NSApp.windows()[0].contentView().setWantsLayer_(True) if LINUX: # On Linux must show before embedding browser, so that handle diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 369f8f2e..05a12217 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -499,9 +499,7 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): Debug("Initialize() called") - # Mac initialization. Need to call NSApplication.sharedApplication() - # and do NSApplication methods swizzling to implement - # CrAppControlProtocol. See Issue 156. + # Additional initialization on Mac, see util_mac.mm. IF UNAME_SYSNAME == "Darwin": MacInitialize() @@ -944,6 +942,10 @@ def Shutdown(): with nogil: CefShutdown() + # Additional cleanup on Mac, see util_mac.mm. + IF UNAME_SYSNAME == "Darwin": + MacShutdown() + def SetOsModalLoop(py_bool modalLoop): cdef cpp_bool cefModalLoop = bool(modalLoop) with nogil: diff --git a/src/client_handler/util_mac.h b/src/client_handler/util_mac.h index 37ec7266..63ee1dff 100644 --- a/src/client_handler/util_mac.h +++ b/src/client_handler/util_mac.h @@ -11,6 +11,7 @@ #include "include/cef_browser.h" void MacInitialize(); +void MacShutdown(); void MacSetWindowTitle(CefRefPtr browser, char* title); #endif // CEFPYTHON_UTIL_MAC_H_ diff --git a/src/client_handler/util_mac.mm b/src/client_handler/util_mac.mm index f2e14cd1..4916b253 100644 --- a/src/client_handler/util_mac.mm +++ b/src/client_handler/util_mac.mm @@ -2,6 +2,16 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +// Copyright (c) 2015 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +// Some code was copied from here: +// java-cef: src/master/native/util_mac.mm +// upstream cef: src/tests/ceftests/run_all_unittests_mac.mm +// upstream cef: src/tests/cefclient/cefclient_mac.mm +// upstream cef: src/tests/cefsimple/cefsimple_mac.mm + #import "util_mac.h" #import #include @@ -11,13 +21,14 @@ namespace { +// static NSAutoreleasePool* g_autopool = nil; BOOL g_handling_send_event = false; } // namespace -// Add the necessary CrAppControlProtocol -// functionality to NSApplication using categories and swizzling. -@interface NSApplication (CEFPythonApplication) +// Add the necessary CefAppProtocol functionality to NSApplication +// using categories and swizzling (Issue #442, Issue #156). +@interface NSApplication (CEFPythonApplication) - (BOOL)isHandlingSendEvent; - (void)setHandlingSendEvent:(BOOL)handlingSendEvent; @@ -63,9 +74,16 @@ - (void)_swizzled_terminate:(id)sender { @end void MacInitialize() { + // OFF: it's causing a crash during shutdown release + // g_autopool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; } +void MacShutdown() { + // OFF: it's causing a crash during shutdown release + // [g_autopool release]; +} + void MacSetWindowTitle(CefRefPtr browser, char* title) { NSView* view = browser->GetHost()->GetWindowHandle(); NSString* nstitle = [NSString stringWithFormat:@"%s" , title]; diff --git a/src/extern/mac.pxd b/src/extern/mac.pxd index cca038b9..e23b4b98 100644 --- a/src/extern/mac.pxd +++ b/src/extern/mac.pxd @@ -7,4 +7,5 @@ from cef_browser cimport CefBrowser cdef extern from "client_handler/util_mac.h": void MacInitialize() + void MacShutdown() void MacSetWindowTitle(CefRefPtr[CefBrowser] browser, char* title)