From 77c506b7c337a83aabda51f00df9f6a17c47d75e Mon Sep 17 00:00:00 2001 From: cztomczak Date: Sat, 25 Mar 2017 13:18:47 +0100 Subject: [PATCH] Fix Printing in the hello_world.py and tkinter_.py examples. Minor fixes. Added some code for testing CEF views. --- docs/Migration-guide.md | 6 +- examples/gtk2.py | 10 +-- src/browser.pyx | 1 - src/cefpython.pyx | 62 ++++++++++++++++- src/client_handler/dialog_handler_gtk.cpp | 4 ++ src/common/cefpython_public_api.h | 12 ++-- src/compile_time_constants.pxi | 4 +- src/extern/cef/cef_types.pxd | 9 +++ src/extern/cef/cef_views.pxd | 84 +++++++++++++++++++++++ src/frame.pyx | 1 - src/handlers/request_handler.pyx | 9 +++ src/linux/binaries_64bit/kivy_.py | 6 +- src/settings.pyx | 5 +- src/subprocess/cefpython_app.cpp | 13 ++++ src/subprocess/cefpython_app.h | 4 -- src/window_info.pyx | 7 +- 16 files changed, 208 insertions(+), 29 deletions(-) create mode 100644 src/extern/cef/cef_views.pxd diff --git a/docs/Migration-guide.md b/docs/Migration-guide.md index 0ee6ca99..3bab17bc 100644 --- a/docs/Migration-guide.md +++ b/docs/Migration-guide.md @@ -27,7 +27,7 @@ Table of contents: * [v50+ Build instructions and build tools](#v50-build-instructions-and-build-tools) * [v51+ Off-screen-rendering: new option "windowless_rendering_enabled"](#v51-off-screen-rendering-new-option-windowless_rendering_enabled) * [v51+ Remove LifespanHandler.RunModal](#v51-remove-lifespanhandlerrunmodal) -* [v51+: BrowserSettings options removed](#v51-browsersettings-options-removed) +* [v51+ BrowserSettings options removed](#v51-browsersettings-options-removed) * [v51+ cef.Request.Flags changed](#v51-cefrequestflags-changed) * [v51+ Request.GetHeaderMap and SetHeaderMap change](#v51-requestgetheadermap-and-setheadermap-change) * [v54+ GTK 3 example doesn't work anymore on Linux](#v54-gtk-3-example-doesnt-work-anymore-on-linux) @@ -157,7 +157,7 @@ http://opensource.spotify.com/cefbuilds/index.html ## v50+ Build instructions and build tools -Many changes in regards to building CEF and CEF Python has changed. +There were many changes in regards to building CEF and CEF Python. There are now new tools in the tools/ root directory that fully automate building CEF and CEF Python. CEF Python now provides upstream CEF prebuilt binaries and libraries on GitHub Releases @@ -180,7 +180,7 @@ API ref: ApplicationSettings.[windowless_rendering_enabled](../api/ApplicationSe LifespanHandler.RunModal callback is no more available. -## v51+: BrowserSettings options removed +## v51+ BrowserSettings options removed The following options were removed from BrowserSettings: - user_style_sheet_location diff --git a/examples/gtk2.py b/examples/gtk2.py index 2f882181..99d7cc14 100644 --- a/examples/gtk2.py +++ b/examples/gtk2.py @@ -49,10 +49,10 @@ def main(): def check_versions(): - print("[gkt2.py] CEF Python {ver}".format(ver=cef.__version__)) - print("[gkt2.py] Python {ver} {arch}".format( + print("[gtk2.py] CEF Python {ver}".format(ver=cef.__version__)) + print("[gtk2.py] Python {ver} {arch}".format( ver=platform.python_version(), arch=platform.architecture()[0])) - print("[gkt2.py] GTK {ver}".format(ver='.'.join( + print("[gtk2.py] GTK {ver}".format(ver='.'.join( map(str, list(gtk.gtk_version))))) assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" pygtk.require('2.0') @@ -64,11 +64,11 @@ def configure_message_loop(): print("[gtk2.py] Force --message-loop-cef flag on Mac") sys.argv.append("--message-loop-cef") if "--message-loop-cef" in sys.argv: - print("[gkt2.py] Message loop mode: CEF (best performance)") + print("[gtk2.py] Message loop mode: CEF (best performance)") g_message_loop = MESSAGE_LOOP_CEF sys.argv.remove("--message-loop-cef") else: - print("[gkt2.py] Message loop mode: TIMER") + print("[gtk2.py] Message loop mode: TIMER") g_message_loop = MESSAGE_LOOP_TIMER diff --git a/src/browser.pyx b/src/browser.pyx index 59c1aaf1..edb3071a 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -42,7 +42,6 @@ cdef PyBrowser GetPyBrowser(CefRefPtr[CefBrowser] cefBrowser, global g_pyBrowsers - # This probably ain't needed, but just to be sure. if cefBrowser == NULL or not cefBrowser.get(): raise Exception("{caller}: CefBrowser reference is NULL" .format(caller=callerIdStr)) diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 9a492764..7e5283cf 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -384,7 +384,7 @@ cdef extern from *: # cannot cimport *, that would cause name conflicts with constants # noinspection PyUnresolvedReferences from cef_types cimport ( - CefSettings, CefBrowserSettings, CefRect, CefPoint, + CefSettings, CefBrowserSettings, CefRect, CefSize, CefPoint, CefKeyEvent, CefMouseEvent, CefScreenInfo, PathKey, PK_DIR_EXE, PK_DIR_MODULE, int32, uint32, int64, uint64, @@ -433,6 +433,8 @@ from cef_path_util cimport * from cef_drag_data cimport * from cef_image cimport * from main_message_loop cimport * +# noinspection PyUnresolvedReferences +from cef_views cimport * # ----------------------------------------------------------------------------- # GLOBAL VARIABLES @@ -775,6 +777,46 @@ def CreateBrowserSync(windowInfo=None, assert IsThread(TID_UI), ( "cefpython.CreateBrowserSync() may only be called on the UI thread") + """ + # CEF views + # noinspection PyUnresolvedReferences + cdef CefRefPtr[CefWindow] cef_window + # noinspection PyUnresolvedReferences + cdef CefRefPtr[CefBoxLayout] cef_box_layout + cdef CefBoxLayoutSettings cef_box_layout_settings + cdef CefRefPtr[CefPanel] cef_panel + if not windowInfo and browserSettings \ + and "window_title" in browserSettings: + # noinspection PyUnresolvedReferences + cef_window = CefWindow.CreateTopLevelWindow( + NULL) + Debug("CefWindow.GetChildViewCount = " + +str(cef_window.get().GetChildViewCount())) + + cef_window.get().CenterWindow(CefSize(800, 600)) + cef_window.get().SetBounds(CefRect(0, 0, 800, 600)) + # noinspection PyUnresolvedReferences + #cef_box_layout = cef_window.get().SetToBoxLayout( + # cef_box_layout_settings) + #cef_box_layout.get().SetFlexForView(cef_window, 1) + cef_window.get().SetToFillLayout() + # noinspection PyUnresolvedReferences + cef_panel = CefPanel.CreatePanel(NULL) + cef_window.get().AddChildView(cef_panel) + cef_window.get().Layout() + cef_window.get().SetVisible(True) + cef_window.get().Show() + cef_window.get().RequestFocus() + windowInfo = WindowInfo() + windowInfo.SetAsChild(cef_window.get().GetWindowHandle()) + Debug("CefWindow handle = "+str(cef_window.get().GetWindowHandle())) + """ + + # Only title was set in hello_world.py example + if windowInfo and not windowInfo.windowType: + windowInfo.SetAsChild(0) + + # No window info provided if not windowInfo: windowInfo = WindowInfo() windowInfo.SetAsChild(0) @@ -831,6 +873,9 @@ def CreateBrowserSync(windowInfo=None, else: Debug("CefBrowser::CreateBrowserSync() succeeded") + Debug("CefBrowser window handle = " + +str(cefBrowser.get().GetHost().get().GetWindowHandle())) + # Request context - part 2/2. if g_applicationSettings["unique_request_context_per_browser"]: requestContextHandler.get().SetBrowser(cefBrowser) @@ -842,6 +887,21 @@ def CreateBrowserSync(windowInfo=None, pyBrowser.SetUserData("__outerWindowHandle", int(windowInfo.parentWindowHandle)) + """ + if cef_window.get(): + cef_window.get().ReorderChildView(cef_panel, -1) + cef_window.get().Layout() + cef_window.get().Show() + cef_window.get().RequestFocus() + """ + + if windowInfo.parentWindowHandle == 0 and windowInfo.windowType == "child": + # Set window title in hello_world.py example + IF UNAME_SYSNAME == "Linux": + pass + ELIF UNAME_SYSNAME == "Darwin": + pass + return pyBrowser def MessageLoop(): diff --git a/src/client_handler/dialog_handler_gtk.cpp b/src/client_handler/dialog_handler_gtk.cpp index 1db13290..76bf1851 100644 --- a/src/client_handler/dialog_handler_gtk.cpp +++ b/src/client_handler/dialog_handler_gtk.cpp @@ -11,12 +11,15 @@ #include #include #include +#include #include #include "include/cef_browser.h" #include "include/cef_parser.h" #include "include/wrapper/cef_helpers.h" +#include "LOG_DEBUG.h" + #include "dialog_handler_gtk.h" #include "x11.h" @@ -156,6 +159,7 @@ GtkWindow* GetWindow(CefRefPtr browser) { // internally, so GTK wasn't yet initialized and must do it // now, so that display is available. Also must install X11 // error handlers to avoid 'BadWindow' errors. + LOG_DEBUG << "Initialize GTK"; gtk_init(0, NULL); InstallX11ErrorHandlers(); // Now the display is available diff --git a/src/common/cefpython_public_api.h b/src/common/cefpython_public_api.h index 7de51952..0fa115d4 100644 --- a/src/common/cefpython_public_api.h +++ b/src/common/cefpython_public_api.h @@ -9,6 +9,12 @@ #ifndef CEFPYTHON_PUBLIC_API_H #define CEFPYTHON_PUBLIC_API_H +// Includes required by "cefpython_fixed.h". +#include "include/cef_client.h" +#include "include/cef_urlrequest.h" +#include "include/cef_command_line.h" +#include "util.h" + #if defined(OS_WIN) #pragma warning(disable:4190) // cefpython API extern C-linkage warnings #endif @@ -24,12 +30,6 @@ #define DL_EXPORT(RTYPE) RTYPE #endif -// Includes required by "cefpython_fixed.h". -#include "include/cef_client.h" -#include "include/cef_urlrequest.h" -#include "include/cef_command_line.h" -#include "util.h" - #if PY_MAJOR_VERSION == 2 #if PY_MINOR_VERSION == 7 #include "../../build/build_cefpython/cefpython_py27_fixed.h" diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 457e4a58..35f85002 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,3 @@ # This file was generated by setup.py -DEF UNAME_SYSNAME = "Darwin" -DEF PY_MAJOR_VERSION = 3 +DEF UNAME_SYSNAME = "Linux" +DEF PY_MAJOR_VERSION = 2 diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 78484f6e..60ace4f3 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -15,9 +15,13 @@ from libc.limits cimport UINT_MAX cdef extern from "include/internal/cef_types.h": + # noinspection PyUnresolvedReferences ctypedef int32_t int32 + # noinspection PyUnresolvedReferences ctypedef uint32_t uint32 + # noinspection PyUnresolvedReferences ctypedef int64_t int64 + # noinspection PyUnresolvedReferences ctypedef uint64_t uint64 IF UNAME_SYSNAME == "Windows": @@ -97,6 +101,11 @@ cdef extern from "include/internal/cef_types.h": CefRect() CefRect(int x, int y, int width, int height) + cdef cppclass CefSize: + int width, height + CefSize() + CefSize(int width, int height) + cdef cppclass CefPoint: pass diff --git a/src/extern/cef/cef_views.pxd b/src/extern/cef/cef_views.pxd new file mode 100644 index 00000000..09763a07 --- /dev/null +++ b/src/extern/cef/cef_views.pxd @@ -0,0 +1,84 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +from cef_ptr cimport CefRefPtr +from cef_browser cimport CefWindowHandle +from cef_types cimport CefRect, CefSize +from libcpp cimport bool as cpp_bool + + +cdef extern from "include/internal/cef_types.h": + ctypedef struct cef_insets_t: + int top; + int left; + int bottom; + int right; + + ctypedef enum cef_main_axis_alignment_t: + CEF_MAIN_AXIS_ALIGNMENT_START, + CEF_MAIN_AXIS_ALIGNMENT_CENTER, + CEF_MAIN_AXIS_ALIGNMENT_END, + + ctypedef enum cef_cross_axis_alignment_t: + CEF_CROSS_AXIS_ALIGNMENT_STRETCH, + CEF_CROSS_AXIS_ALIGNMENT_START, + CEF_CROSS_AXIS_ALIGNMENT_CENTER, + CEF_CROSS_AXIS_ALIGNMENT_END, + + ctypedef struct CefBoxLayoutSettings: + int horizontal; + int inside_border_horizontal_spacing; + int inside_border_vertical_spacing; + cef_insets_t inside_border_insets; + int between_child_spacing; + cef_main_axis_alignment_t main_axis_alignment; + cef_cross_axis_alignment_t cross_axis_alignment; + int minimum_cross_axis_size; + int default_flex; + +cdef extern from "include/views/cef_box_layout.h": + cdef cppclass CefBoxLayout: + void SetFlexForView(CefRefPtr[CefWindow] view, int flex) + +cdef extern from "include/views/cef_window_delegate.h": + cdef cppclass CefWindowDelegate: + pass + +cdef extern from "include/views/cef_view.h": + cdef cppclass CefView: + pass + +cdef extern from "include/views/cef_panel_delegate.h": + cdef cppclass CefPanelDelegate: + pass + +cdef extern from "include/views/cef_panel.h": + cdef cppclass CefPanel: + @staticmethod + CefRefPtr[CefPanel] CreatePanel(CefRefPtr[CefPanelDelegate] delegate) + + +cdef extern from "include/views/cef_fill_layout.h": + cdef cppclass CefFillLayout: + pass + +cdef extern from "include/views/cef_window.h": + cdef cppclass CefWindow: + @staticmethod + CefRefPtr[CefWindow] CreateTopLevelWindow( + CefRefPtr[CefWindowDelegate] delegate) + void Show() + CefWindowHandle GetWindowHandle() + void SetBounds(const CefRect& bounds) + void CenterWindow(const CefSize& size) + void SetVisible(cpp_bool visible) + void Layout() + CefRefPtr[CefFillLayout] SetToFillLayout() + CefRefPtr[CefBoxLayout] SetToBoxLayout( + const CefBoxLayoutSettings& settings) + void RequestFocus() + void RemoveAllChildViews() + size_t GetChildViewCount() + void AddChildView(CefRefPtr[CefPanel] view) + void ReorderChildView(CefRefPtr[CefPanel] view, int index) diff --git a/src/frame.pyx b/src/frame.pyx index 9342719c..6535791d 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -19,7 +19,6 @@ cdef PyFrame GetPyFrameById(int browserId, object frameId): cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): global g_pyFrames - # This code probably ain't needed, but just to be sure. if cefFrame == NULL or not cefFrame.get(): raise Exception("GetPyFrame(): CefFrame reference is NULL") diff --git a/src/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index b2cadbe7..0df1d867 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -358,6 +358,15 @@ cdef public cpp_bool RequestHandler_OnBeforePluginLoad( cdef py_bool returnValue cdef object clientCallback try: + # OnBeforePluginLoad is called from RequestContexthandler. + # The Browser object might not be available, because it is + # being set synchronously during CreateBrowserSync, after + # Browser is created. From testing it always works, however + # better be safe. + if not browser.get(): + Debug("WARNING: RequestHandler_OnBeforePluginLoad() failed," + " Browser object is not available") + return False py_browser = GetPyBrowser(browser, "OnBeforePluginLoad") py_plugin_info = CreatePyWebPluginInfo(plugin_info) clientCallback = GetGlobalClientCallback("OnBeforePluginLoad") diff --git a/src/linux/binaries_64bit/kivy_.py b/src/linux/binaries_64bit/kivy_.py index 477c4975..7d7ef4cc 100644 --- a/src/linux/binaries_64bit/kivy_.py +++ b/src/linux/binaries_64bit/kivy_.py @@ -187,9 +187,9 @@ def start_cef(self): # Start idle - CEF message loop work. Clock.schedule_once(self._message_loop_work, 0) - # CEF needs a valid window handle passed to SetAsOffscreen() - # for Printing to work. There is no API to get Kivy's window - # handle so creating a dummy hidden Window using GTK. + # TODO: For printing to work in off-screen-rendering mode + # it is enough to call gtk_init(). It is not required + # to provide window handle when calling SetAsOffscreen(). gtkwin = gtk.Window() gtkwin.realize() diff --git a/src/settings.pyx b/src/settings.pyx index 79efe679..aeb66353 100644 --- a/src/settings.pyx +++ b/src/settings.pyx @@ -133,7 +133,10 @@ cdef void SetBrowserSettings( cdef CefString* cefString for key in browserSettings: - if key == "accept_language_list": + if key == "window_title": + # CEF Python only options. These are not to be found in CEF. + continue + elif key == "accept_language_list": cefString = new CefString(&cefBrowserSettings.accept_language_list) PyToCefStringPointer(browserSettings[key], cefString) del cefString diff --git a/src/subprocess/cefpython_app.cpp b/src/subprocess/cefpython_app.cpp index 9dd456a0..63f9ad3e 100644 --- a/src/subprocess/cefpython_app.cpp +++ b/src/subprocess/cefpython_app.cpp @@ -9,6 +9,12 @@ #include "common/cefpython_public_api.h" #endif +#if defined(OS_LINUX) +#include +#include +#include "print_handler_gtk.h" +#endif + #include "cefpython_app.h" #include "util.h" #include "include/wrapper/cef_closure_task.h" @@ -95,6 +101,13 @@ void CefPythonApp::OnContextInitialized() { #ifdef BROWSER_PROCESS REQUIRE_UI_THREAD(); #if defined(OS_LINUX) + // For print handler to work GTK must be initialized. This is + // required for hello_world.py example to work. + GdkDisplay* gdk_display = gdk_display_get_default(); + if (!gdk_display) { + LOG_DEBUG << "Initialize GTK"; + gtk_init(0, NULL); + } print_handler_ = new ClientPrintHandlerGtk(); #endif // OS_LINUX #endif // BROWSER_PROCESS diff --git a/src/subprocess/cefpython_app.h b/src/subprocess/cefpython_app.h index 63e30225..e76b2691 100644 --- a/src/subprocess/cefpython_app.h +++ b/src/subprocess/cefpython_app.h @@ -6,10 +6,6 @@ #include "include/cef_app.h" #include "include/cef_print_handler.h" -#if defined(OS_LINUX) -#include "print_handler_gtk.h" -#endif - #include // CefPythonApp class is instantiated in subprocess and in diff --git a/src/window_info.pyx b/src/window_info.pyx index 9cfe17fb..2ac24df7 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -76,8 +76,10 @@ cdef class WindowInfo: cdef public py_string windowName cdef public py_bool transparentPainting - def __init__(self): + def __init__(self, title=""): self.transparentPainting = False + if title: + self.windowName = title cpdef py_void SetAsChild(self, WindowHandle parentWindowHandle, list windowRect=None): @@ -117,7 +119,8 @@ cdef class WindowInfo: % parentWindowHandle) self.parentWindowHandle = parentWindowHandle self.windowType = "popup" - self.windowName = str(windowName) + if windowName: + self.windowName = str(windowName) cpdef py_void SetAsOffscreen(self, WindowHandle parentWindowHandle):