Linux: cefclient: Port to GTK3 (see issue #2969)

GTK3 is required by the Chrome runtime. The cefclient off-screen rendering
example no longer works with Ubuntu 16.04. With end-of-life in April 2021
we are dropping support for 16.04 in the near future in any case.
This commit is contained in:
Marshall Greenblatt 2021-03-01 11:41:27 -08:00
parent 8424f166cc
commit 020ac1b509
14 changed files with 174 additions and 205 deletions

View File

@ -2035,18 +2035,12 @@ if (is_mac) {
pkg_config("gtk") { pkg_config("gtk") {
packages = [ packages = [
"gmodule-2.0", "gmodule-2.0",
"gtk+-2.0", "gtk+-3.0",
"gthread-2.0", "gthread-2.0",
"gtk+-unix-print-2.0", "gtk+-unix-print-3.0",
"xi", "xi",
] ]
} }
pkg_config("gtkglext") {
packages = [
"gtkglext-1.0",
]
}
} }
if (is_linux) { if (is_linux) {
@ -2127,13 +2121,13 @@ if (is_mac) {
] ]
libs = [ libs = [
"GL",
"X11", "X11",
] ]
if (cef_use_gtk) { if (cef_use_gtk) {
configs += [ configs += [
":gtk", ":gtk",
":gtkglext",
] ]
} }

View File

@ -44,8 +44,7 @@
# work but may not have been tested. # work but may not have been tested.
# Required packages include: # Required packages include:
# build-essential # build-essential
# libgtk2.0-dev (required by the cefclient target only) # libgtk3.0-dev (required by the cefclient target only)
# libgtkglext1-dev (required by the cefclient target only)
# #
# - MacOS requirements: # - MacOS requirements:
# Xcode 8 or newer building on MacOS 10.11 (El Capitan) or newer for x86_64. # Xcode 8 or newer building on MacOS 10.11 (El Capitan) or newer for x86_64.

View File

@ -95,6 +95,7 @@ if(OS_LINUX)
-Wno-unused-parameter # Don't warn about unused parameters -Wno-unused-parameter # Don't warn about unused parameters
-Wno-error=comment # Don't warn about code in comments -Wno-error=comment # Don't warn about code in comments
-Wno-comment # Don't warn about code in comments -Wno-comment # Don't warn about code in comments
-Wno-deprecated-declarations # Don't warn about using deprecated methods
) )
list(APPEND CEF_C_COMPILER_FLAGS list(APPEND CEF_C_COMPILER_FLAGS
-std=c99 # Use the C99 language standard -std=c99 # Use the C99 language standard

View File

@ -122,13 +122,13 @@ if(OS_LINUX)
) )
# Find required libraries and update compiler/linker variables. # Find required libraries and update compiler/linker variables.
FIND_LINUX_LIBRARIES("gmodule-2.0 gtk+-2.0 gthread-2.0 gtk+-unix-print-2.0 gtkglext-1.0 xi") FIND_LINUX_LIBRARIES("gmodule-2.0 gtk+-3.0 gthread-2.0 gtk+-unix-print-3.0 xi")
# Executable target. # Executable target.
add_executable(${CEF_TARGET} ${CEFCLIENT_SRCS}) add_executable(${CEF_TARGET} ${CEFCLIENT_SRCS})
SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET}) SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
add_dependencies(${CEF_TARGET} libcef_dll_wrapper) add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS}) target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper "GL" ${CEF_STANDARD_LIBS})
# Set rpath so that libraries can be placed next to the executable. # Set rpath so that libraries can be placed next to the executable.
set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN") set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN")

View File

@ -6,16 +6,14 @@
#include <GL/gl.h> #include <GL/gl.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms-compat.h>
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#include <glib-object.h> #include <glib-object.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#define XK_3270 // for XK_3270_BackTab #define XK_3270 // for XK_3270_BackTab
#include <X11/XF86keysym.h> #include <X11/XF86keysym.h>
#include <X11/Xcursor/Xcursor.h> #include <X11/Xcursor/Xcursor.h>
#include <X11/extensions/XInput2.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include "include/base/cef_logging.h" #include "include/base/cef_logging.h"
@ -29,17 +27,10 @@ namespace client {
namespace { namespace {
// Major opcode of XInputExtension, or -1 if XInput 2.2 is not available.
int g_xinput_extension = -1;
// Static BrowserWindowOsrGtk::EventFilter needs to forward touch events // Static BrowserWindowOsrGtk::EventFilter needs to forward touch events
// to correct browser, so we maintain a vector of all windows. // to correct browser, so we maintain a vector of all windows.
std::vector<BrowserWindowOsrGtk*> g_browser_windows; std::vector<BrowserWindowOsrGtk*> g_browser_windows;
bool IsTouchAvailable() {
return g_xinput_extension != -1;
}
int GetCefStateModifiers(guint state) { int GetCefStateModifiers(guint state) {
int modifiers = 0; int modifiers = 0;
if (state & GDK_SHIFT_MASK) if (state & GDK_SHIFT_MASK)
@ -59,20 +50,6 @@ int GetCefStateModifiers(guint state) {
return modifiers; return modifiers;
} }
int GetCefStateModifiers(XIModifierState mods, XIButtonState buttons) {
guint state = mods.effective;
if (buttons.mask_len >= 1) {
if (XIMaskIsSet(buttons.mask, 1))
state |= GDK_BUTTON1_MASK;
if (XIMaskIsSet(buttons.mask, 2))
state |= GDK_BUTTON2_MASK;
if (XIMaskIsSet(buttons.mask, 3))
state |= GDK_BUTTON3_MASK;
}
return GetCefStateModifiers(state);
}
// From ui/events/keycodes/keyboard_codes_posix.h. // From ui/events/keycodes/keyboard_codes_posix.h.
enum KeyboardCode { enum KeyboardCode {
VKEY_BACK = 0x08, VKEY_BACK = 0x08,
@ -894,17 +871,20 @@ void GetWidgetRectInScreen(GtkWidget* widget, GdkRectangle* r) {
// Get parent's left-top screen coordinates. // Get parent's left-top screen coordinates.
gdk_window_get_root_origin(window, &x, &y); gdk_window_get_root_origin(window, &x, &y);
// Get parent's width and height. // Get parent's width and height.
gdk_drawable_get_size(window, &w, &h); w = gdk_window_get_width(window);
h = gdk_window_get_height(window);
// Get parent's extents including decorations. // Get parent's extents including decorations.
gdk_window_get_frame_extents(window, &extents); gdk_window_get_frame_extents(window, &extents);
// X and Y calculations assume that left, right and bottom border sizes are // X and Y calculations assume that left, right and bottom border sizes are
// all the same. // all the same.
const gint border = (extents.width - w) / 2; const gint border = (extents.width - w) / 2;
r->x = x + border + widget->allocation.x; GtkAllocation allocation;
r->y = y + (extents.height - h) - border + widget->allocation.y; gtk_widget_get_allocation(widget, &allocation);
r->width = widget->allocation.width; r->x = x + border + allocation.x;
r->height = widget->allocation.height; r->y = y + (extents.height - h) - border + allocation.y;
r->width = allocation.width;
r->height = allocation.height;
} }
CefBrowserHost::DragOperationsMask GetDragOperationsMask( CefBrowserHost::DragOperationsMask GetDragOperationsMask(
@ -925,30 +905,25 @@ CefBrowserHost::DragOperationsMask GetDragOperationsMask(
class ScopedGLContext { class ScopedGLContext {
public: public:
ScopedGLContext(GtkWidget* widget, bool swap_buffers) ScopedGLContext(GtkWidget* widget, bool swap_buffers)
: swap_buffers_(swap_buffers) { : swap_buffers_(swap_buffers), widget_(widget) {
GdkGLContext* glcontext = gtk_widget_get_gl_context(widget); gtk_gl_area_make_current(GTK_GL_AREA(widget));
gldrawable_ = gtk_widget_get_gl_drawable(widget); is_valid_ = gtk_gl_area_get_error(GTK_GL_AREA(widget)) == NULL;
is_valid_ = gdk_gl_drawable_gl_begin(gldrawable_, glcontext); if (swap_buffers_ && is_valid_) {
gtk_gl_area_queue_render(GTK_GL_AREA(widget_));
gtk_gl_area_attach_buffers(GTK_GL_AREA(widget));
}
} }
virtual ~ScopedGLContext() { virtual ~ScopedGLContext() {
if (is_valid_) { if (swap_buffers_ && is_valid_)
gdk_gl_drawable_gl_end(gldrawable_); glFlush();
if (swap_buffers_) {
if (gdk_gl_drawable_is_double_buffered(gldrawable_))
gdk_gl_drawable_swap_buffers(gldrawable_);
else
glFlush();
}
}
} }
bool IsValid() const { return is_valid_; } bool IsValid() const { return is_valid_; }
private: private:
bool swap_buffers_; bool swap_buffers_;
GdkGLDrawable* gldrawable_; GtkWidget* const widget_;
bool is_valid_; bool is_valid_;
ScopedGdkThreadsEnter scoped_gdk_threads_; ScopedGdkThreadsEnter scoped_gdk_threads_;
}; };
@ -1013,11 +988,11 @@ void BrowserWindowOsrGtk::CreateBrowser(
// Retrieve the X11 Window ID for the GTK parent window. // Retrieve the X11 Window ID for the GTK parent window.
GtkWidget* window = GtkWidget* window =
gtk_widget_get_ancestor(GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW); gtk_widget_get_ancestor(GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW);
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(window)); CefWindowHandle handle = GDK_WINDOW_XID(gtk_widget_get_window(window));
DCHECK(xwindow); DCHECK(handle);
CefWindowInfo window_info; CefWindowInfo window_info;
window_info.SetAsWindowless(xwindow); window_info.SetAsWindowless(handle);
// Create the browser asynchronously. // Create the browser asynchronously.
CefBrowserHost::CreateBrowser(window_info, client_handler_, CefBrowserHost::CreateBrowser(window_info, client_handler_,
@ -1172,11 +1147,12 @@ void BrowserWindowOsrGtk::GetViewRect(CefRefPtr<CefBrowser> browser,
// The simulated screen and view rectangle are the same. This is necessary // The simulated screen and view rectangle are the same. This is necessary
// for popup menus to be located and sized inside the view. // for popup menus to be located and sized inside the view.
rect.width = DeviceToLogical(glarea_->allocation.width, device_scale_factor); GtkAllocation allocation;
gtk_widget_get_allocation(glarea_, &allocation);
rect.width = DeviceToLogical(allocation.width, device_scale_factor);
if (rect.width == 0) if (rect.width == 0)
rect.width = 1; rect.width = 1;
rect.height = rect.height = DeviceToLogical(allocation.height, device_scale_factor);
DeviceToLogical(glarea_->allocation.height, device_scale_factor);
if (rect.height == 0) if (rect.height == 0)
rect.height = 1; rect.height = 1;
} }
@ -1373,18 +1349,13 @@ void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
ScopedGdkThreadsEnter scoped_gdk_threads; ScopedGdkThreadsEnter scoped_gdk_threads;
glarea_ = gtk_drawing_area_new(); glarea_ = gtk_gl_area_new();
DCHECK(glarea_); DCHECK(glarea_);
GdkGLConfig* glconfig =
gdk_gl_config_new_by_mode(static_cast<GdkGLConfigMode>(
GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
DCHECK(glconfig);
gtk_widget_set_gl_capability(glarea_, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
gtk_widget_set_can_focus(glarea_, TRUE); gtk_widget_set_can_focus(glarea_, TRUE);
gtk_gl_area_set_auto_render(GTK_GL_AREA(glarea_), FALSE);
g_signal_connect(G_OBJECT(glarea_), "size_allocate", g_signal_connect(G_OBJECT(glarea_), "size_allocate",
G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this); G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this);
@ -1414,18 +1385,16 @@ void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this); G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
g_signal_connect(G_OBJECT(glarea_), "focus_out_event", g_signal_connect(G_OBJECT(glarea_), "focus_out_event",
G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this); G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
g_signal_connect(G_OBJECT(glarea_), "touch-event",
G_CALLBACK(&BrowserWindowOsrGtk::TouchEvent), this);
RegisterDragDrop(); RegisterDragDrop();
gtk_container_add(GTK_CONTAINER(parent_handle), glarea_); gtk_widget_set_vexpand(glarea_, TRUE);
gtk_grid_attach(GTK_GRID(parent_handle), glarea_, 0, 3, 1, 1);
// Make the GlArea visible in the parent container. // Make the GlArea visible in the parent container.
gtk_widget_show_all(parent_handle); gtk_widget_show_all(parent_handle);
InitializeXinput(xdisplay_);
if (IsTouchAvailable())
RegisterTouch();
} }
// static // static
@ -1661,6 +1630,9 @@ gint BrowserWindowOsrGtk::ScrollEvent(GtkWidget* widget,
case GDK_SCROLL_RIGHT: case GDK_SCROLL_RIGHT:
deltaX = -scrollbarPixelsPerGtkTick; deltaX = -scrollbarPixelsPerGtkTick;
break; break;
case GDK_SCROLL_SMOOTH:
NOTIMPLEMENTED();
break;
} }
host->SendMouseWheelEvent(mouse_event, deltaX, deltaY); host->SendMouseWheelEvent(mouse_event, deltaX, deltaY);
@ -1677,106 +1649,49 @@ gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
return TRUE; return TRUE;
} }
void BrowserWindowOsrGtk::TouchEvent(CefXIDeviceEvent event) { // static
if (!browser_.get()) gboolean BrowserWindowOsrGtk::TouchEvent(GtkWidget* widget,
return; GdkEventTouch* event,
BrowserWindowOsrGtk* self) {
REQUIRE_MAIN_THREAD();
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(event); if (!self->browser_.get())
CefTouchEvent cef_event; return TRUE;
switch (ev->evtype) {
case XI_TouchBegin: CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
cef_event.type = CEF_TET_PRESSED;
float device_scale_factor;
{
base::AutoLock lock_scope(self->lock_);
device_scale_factor = self->device_scale_factor_;
}
CefTouchEvent touch_event;
switch (event->type) {
case GDK_TOUCH_BEGIN:
touch_event.type = CEF_TET_PRESSED;
break; break;
case XI_TouchUpdate: case GDK_TOUCH_UPDATE:
cef_event.type = CEF_TET_MOVED; touch_event.type = CEF_TET_MOVED;
break; break;
case XI_TouchEnd: case GDK_TOUCH_END:
cef_event.type = CEF_TET_RELEASED; touch_event.type = CEF_TET_RELEASED;
break; break;
default: default:
return; return TRUE;
} }
cef_event.id = ev->detail; touch_event.x = event->x;
cef_event.x = ev->event_x; touch_event.y = event->y;
cef_event.y = ev->event_y; touch_event.radius_x = 0;
cef_event.radius_x = 0; touch_event.radius_y = 0;
cef_event.radius_y = 0; touch_event.rotation_angle = 0;
cef_event.rotation_angle = 0; touch_event.pressure = 0;
cef_event.pressure = 0; DeviceToLogical(touch_event, device_scale_factor);
cef_event.modifiers = GetCefStateModifiers(ev->mods, ev->buttons); touch_event.modifiers = GetCefStateModifiers(event->state);
browser_->GetHost()->SendTouchEvent(cef_event); host->SendTouchEvent(touch_event);
} return TRUE;
void BrowserWindowOsrGtk::RegisterTouch() {
GdkWindow* glwindow = gtk_widget_get_window(glarea_);
::Window xwindow = GDK_WINDOW_XID(glwindow);
uint32_t bitMask = XI_TouchBeginMask | XI_TouchUpdateMask | XI_TouchEndMask;
XIEventMask mask;
mask.deviceid = XIAllMasterDevices;
mask.mask = reinterpret_cast<unsigned char*>(&bitMask);
mask.mask_len = sizeof(bitMask);
XISelectEvents(xdisplay_, xwindow, &mask, 1);
}
// static
GdkFilterReturn BrowserWindowOsrGtk::EventFilter(GdkXEvent* gdk_xevent,
GdkEvent* event,
gpointer data) {
XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
if (xevent->type == GenericEvent &&
xevent->xgeneric.extension == g_xinput_extension) {
XGetEventData(xevent->xcookie.display, &xevent->xcookie);
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
if (!ev)
return GDK_FILTER_REMOVE;
for (BrowserWindowOsrGtk* browser_window : g_browser_windows) {
GtkWidget* widget = browser_window->GetWindowHandle();
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
if (xwindow == ev->event) {
browser_window->TouchEvent(ev);
break;
}
}
XFreeEventData(xevent->xcookie.display, &xevent->xcookie);
// Even if we didn't find a consumer for this event, we will make sure Gdk
// doesn't attempt to process the event, since it can't parse GenericEvents
return GDK_FILTER_REMOVE;
}
return GDK_FILTER_CONTINUE;
}
// static
void BrowserWindowOsrGtk::InitializeXinput(XDisplay* xdisplay) {
static bool initialized = false;
if (initialized)
return;
initialized = true;
int firstEvent, firstError;
if (XQueryExtension(xdisplay, "XInputExtension", &g_xinput_extension,
&firstEvent, &firstError)) {
int major = 2, minor = 2;
// X Input Extension 2.2 is needed for multitouch events.
if (XIQueryVersion(xdisplay, &major, &minor) == Success) {
// Ideally we would add an event filter for each glarea_ window
// separately, but unfortunately GDK can't parse X GenericEvents
// which have the target window stored in different way compared
// to other X events. That is why we add this global event filter
// just once, and dispatch the event to correct BrowserWindowOsrGtk
// manually.
gdk_window_add_filter(nullptr, &BrowserWindowOsrGtk::EventFilter,
nullptr);
} else {
g_xinput_extension = -1;
}
}
} }
bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const { bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {

View File

@ -117,8 +117,10 @@ class BrowserWindowOsrGtk : public BrowserWindow,
static gint FocusEvent(GtkWidget* widget, static gint FocusEvent(GtkWidget* widget,
GdkEventFocus* event, GdkEventFocus* event,
BrowserWindowOsrGtk* self); BrowserWindowOsrGtk* self);
static gboolean TouchEvent(GtkWidget* widget,
GdkEventTouch* event,
BrowserWindowOsrGtk* self);
void TouchEvent(CefXIDeviceEvent event);
void RegisterTouch(); void RegisterTouch();
bool IsOverPopupWidget(int x, int y) const; bool IsOverPopupWidget(int x, int y) const;
@ -176,7 +178,6 @@ class BrowserWindowOsrGtk : public BrowserWindow,
static GdkFilterReturn EventFilter(GdkXEvent* gdk_xevent, static GdkFilterReturn EventFilter(GdkXEvent* gdk_xevent,
GdkEvent* event, GdkEvent* event,
gpointer data); gpointer data);
static void InitializeXinput(XDisplay* xdisplay);
XDisplay* xdisplay_; XDisplay* xdisplay_;

View File

@ -135,10 +135,10 @@ GtkWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
scoped_refptr<RootWindow> root_window = scoped_refptr<RootWindow> root_window =
RootWindow::GetForBrowser(browser->GetIdentifier()); RootWindow::GetForBrowser(browser->GetIdentifier());
if (root_window) { if (root_window) {
GtkWindow* window = GTK_WINDOW(root_window->GetWindowHandle()); GtkWidget* window = root_window->GetWindowHandle();
if (!window) if (!window)
LOG(ERROR) << "No GtkWindow for browser"; LOG(ERROR) << "No GtkWindow for browser";
return window; return GTK_WINDOW(window);
} }
return nullptr; return nullptr;
} }
@ -238,13 +238,13 @@ void ClientDialogHandlerGtk::OnFileDialogContinue(OnFileDialogParams params,
if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) { if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
action = GTK_FILE_CHOOSER_ACTION_OPEN; action = GTK_FILE_CHOOSER_ACTION_OPEN;
accept_button = GTK_STOCK_OPEN; accept_button = "_Open";
} else if (mode_type == FILE_DIALOG_OPEN_FOLDER) { } else if (mode_type == FILE_DIALOG_OPEN_FOLDER) {
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
accept_button = GTK_STOCK_OPEN; accept_button = "_Open";
} else if (mode_type == FILE_DIALOG_SAVE) { } else if (mode_type == FILE_DIALOG_SAVE) {
action = GTK_FILE_CHOOSER_ACTION_SAVE; action = GTK_FILE_CHOOSER_ACTION_SAVE;
accept_button = GTK_STOCK_SAVE; accept_button = "_Save";
} else { } else {
NOTREACHED(); NOTREACHED();
params.callback->Cancel(); params.callback->Cancel();
@ -274,7 +274,7 @@ void ClientDialogHandlerGtk::OnFileDialogContinue(OnFileDialogParams params,
} }
GtkWidget* dialog = gtk_file_chooser_dialog_new( GtkWidget* dialog = gtk_file_chooser_dialog_new(
title_str.c_str(), GTK_WINDOW(window), action, GTK_STOCK_CANCEL, title_str.c_str(), GTK_WINDOW(window), action, "_Cancel",
GTK_RESPONSE_CANCEL, accept_button, GTK_RESPONSE_ACCEPT, NULL); GTK_RESPONSE_CANCEL, accept_button, GTK_RESPONSE_ACCEPT, NULL);
if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) if (mode_type == FILE_DIALOG_OPEN_MULTIPLE)
@ -406,8 +406,8 @@ void ClientDialogHandlerGtk::OnJSDialogContinue(OnJSDialogParams params,
gtk_window_set_title(GTK_WINDOW(gtk_dialog_), title.c_str()); gtk_window_set_title(GTK_WINDOW(gtk_dialog_), title.c_str());
GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), GtkWidget* ok_button =
GTK_STOCK_OK, GTK_RESPONSE_OK); gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), "_OK", GTK_RESPONSE_OK);
if (params.dialog_type != JSDIALOGTYPE_PROMPT) if (params.dialog_type != JSDIALOGTYPE_PROMPT)
gtk_widget_grab_focus(ok_button); gtk_widget_grab_focus(ok_button);

View File

@ -5,7 +5,7 @@
#include "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h" #include "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h"
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <gtk/gtkmain.h> #include <gtk/gtk.h>
#include "include/base/cef_bind.h" #include "include/base/cef_bind.h"
#include "include/base/cef_logging.h" #include "include/base/cef_logging.h"

View File

@ -539,7 +539,7 @@ struct ClientPrintHandlerGtk::PrintHandler {
} }
} }
void OnJobCompleted(GtkPrintJob* print_job, GError* error) { void OnJobCompleted(GtkPrintJob* print_job, const GError* error) {
// Continue() will result in a call to ClientPrintHandlerGtk::OnPrintReset // Continue() will result in a call to ClientPrintHandlerGtk::OnPrintReset
// which deletes |this|. Execute it asnychronously so the call stack has a // which deletes |this|. Execute it asnychronously so the call stack has a
// chance to unwind. // chance to unwind.
@ -556,7 +556,7 @@ struct ClientPrintHandlerGtk::PrintHandler {
static void OnJobCompletedThunk(GtkPrintJob* print_job, static void OnJobCompletedThunk(GtkPrintJob* print_job,
void* handler, void* handler,
GError* error) { const GError* error) {
static_cast<PrintHandler*>(handler)->OnJobCompleted(print_job, error); static_cast<PrintHandler*>(handler)->OnJobCompleted(print_job, error);
} }

View File

@ -29,6 +29,36 @@ namespace {
const char kMenuIdKey[] = "menu_id"; const char kMenuIdKey[] = "menu_id";
void UseDefaultX11VisualForGtk(GtkWidget* widget) {
#if GTK_CHECK_VERSION(3, 15, 1)
// GTK+ > 3.15.1 uses an X11 visual optimized for GTK+'s OpenGL stuff
// since revid dae447728d: https://github.com/GNOME/gtk/commit/dae447728d
// However, it breaks CEF: https://github.com/cztomczak/cefcapi/issues/9
// Let's use the default X11 visual instead of the GTK's blessed one.
// Copied from: https://github.com/cztomczak/cefcapi.
GdkScreen* screen = gdk_screen_get_default();
GList* visuals = gdk_screen_list_visuals(screen);
GdkX11Screen* x11_screen = GDK_X11_SCREEN(screen);
if (x11_screen == NULL)
return;
Visual* default_xvisual = DefaultVisual(GDK_SCREEN_XDISPLAY(x11_screen),
GDK_SCREEN_XNUMBER(x11_screen));
GList* cursor = visuals;
while (cursor != NULL) {
GdkVisual* visual = GDK_X11_VISUAL(cursor->data);
if (default_xvisual->visualid ==
gdk_x11_visual_get_xvisual(visual)->visualid) {
gtk_widget_set_visual(widget, visual);
break;
}
cursor = cursor->next;
}
g_list_free(visuals);
#endif
}
bool IsWindowMaximized(GtkWindow* window) { bool IsWindowMaximized(GtkWindow* window) {
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
gint state = gdk_window_get_state(gdk_window); gint state = gdk_window_get_state(gdk_window);
@ -143,6 +173,7 @@ void RootWindowGtk::Show(ShowMode mode) {
ScopedGdkThreadsEnter scoped_gdk_threads; ScopedGdkThreadsEnter scoped_gdk_threads;
// Show the GTK window. // Show the GTK window.
UseDefaultX11VisualForGtk(GTK_WIDGET(window_));
gtk_widget_show_all(window_); gtk_widget_show_all(window_);
if (mode == ShowMinimized) if (mode == ShowMinimized)
@ -290,23 +321,34 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
G_CALLBACK(&RootWindowGtk::WindowDelete), this); G_CALLBACK(&RootWindowGtk::WindowDelete), this);
const cef_color_t background_color = MainContext::Get()->GetBackgroundColor(); const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
GdkColor color = {0}; GdkRGBA rgba = {0};
color.red = CefColorGetR(background_color) * 65535 / 255; rgba.red = CefColorGetR(background_color) * 65535 / 255;
color.green = CefColorGetG(background_color) * 65535 / 255; rgba.green = CefColorGetG(background_color) * 65535 / 255;
color.blue = CefColorGetB(background_color) * 65535 / 255; rgba.blue = CefColorGetB(background_color) * 65535 / 255;
gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &color); rgba.alpha = 1;
GtkWidget* vbox = gtk_vbox_new(FALSE, 0); gchar* css = g_strdup_printf("#* { background-color: %s; }",
g_signal_connect(vbox, "size-allocate", gdk_rgba_to_string(&rgba));
G_CALLBACK(&RootWindowGtk::VboxSizeAllocated), this); GtkCssProvider* provider = gtk_css_provider_new();
gtk_container_add(GTK_CONTAINER(window_), vbox); gtk_css_provider_load_from_data(provider, css, -1, nullptr);
g_free(css);
gtk_style_context_add_provider(gtk_widget_get_style_context(window_),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref(provider);
GtkWidget* grid = gtk_grid_new();
gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
g_signal_connect(grid, "size-allocate",
G_CALLBACK(&RootWindowGtk::GridSizeAllocated), this);
gtk_container_add(GTK_CONTAINER(window_), grid);
if (with_controls_) { if (with_controls_) {
GtkWidget* menu_bar = CreateMenuBar(); GtkWidget* menu_bar = CreateMenuBar();
g_signal_connect(menu_bar, "size-allocate", g_signal_connect(menu_bar, "size-allocate",
G_CALLBACK(&RootWindowGtk::MenubarSizeAllocated), this); G_CALLBACK(&RootWindowGtk::MenubarSizeAllocated), this);
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); gtk_grid_attach(GTK_GRID(grid), menu_bar, 0, 0, 1, 1);
GtkWidget* toolbar = gtk_toolbar_new(); GtkWidget* toolbar = gtk_toolbar_new();
// Turn off the labels on the toolbar buttons. // Turn off the labels on the toolbar buttons.
@ -314,22 +356,26 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
g_signal_connect(toolbar, "size-allocate", g_signal_connect(toolbar, "size-allocate",
G_CALLBACK(&RootWindowGtk::ToolbarSizeAllocated), this); G_CALLBACK(&RootWindowGtk::ToolbarSizeAllocated), this);
back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); back_button_ = gtk_tool_button_new(
gtk_image_new_from_icon_name("go-previous", GTK_ICON_SIZE_MENU), NULL);
g_signal_connect(back_button_, "clicked", g_signal_connect(back_button_, "clicked",
G_CALLBACK(&RootWindowGtk::BackButtonClicked), this); G_CALLBACK(&RootWindowGtk::BackButtonClicked), this);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */);
forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); forward_button_ = gtk_tool_button_new(
gtk_image_new_from_icon_name("go-next", GTK_ICON_SIZE_MENU), NULL);
g_signal_connect(forward_button_, "clicked", g_signal_connect(forward_button_, "clicked",
G_CALLBACK(&RootWindowGtk::ForwardButtonClicked), this); G_CALLBACK(&RootWindowGtk::ForwardButtonClicked), this);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */);
reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); reload_button_ = gtk_tool_button_new(
gtk_image_new_from_icon_name("view-refresh", GTK_ICON_SIZE_MENU), NULL);
g_signal_connect(reload_button_, "clicked", g_signal_connect(reload_button_, "clicked",
G_CALLBACK(&RootWindowGtk::ReloadButtonClicked), this); G_CALLBACK(&RootWindowGtk::ReloadButtonClicked), this);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */);
stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); stop_button_ = gtk_tool_button_new(
gtk_image_new_from_icon_name("process-stop", GTK_ICON_SIZE_MENU), NULL);
g_signal_connect(stop_button_, "clicked", g_signal_connect(stop_button_, "clicked",
G_CALLBACK(&RootWindowGtk::StopButtonClicked), this); G_CALLBACK(&RootWindowGtk::StopButtonClicked), this);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */);
@ -345,7 +391,8 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
gtk_tool_item_set_expand(tool_item, TRUE); gtk_tool_item_set_expand(tool_item, TRUE);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1); // append gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1); // append
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); gtk_grid_attach_next_to(GTK_GRID(grid), toolbar, menu_bar, GTK_POS_BOTTOM,
1, 1);
} }
// Realize (show) the GTK widget. This must be done before the browser is // Realize (show) the GTK widget. This must be done before the browser is
@ -360,8 +407,8 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
// Windowed browsers are parented to the X11 Window underlying the GtkWindow* // Windowed browsers are parented to the X11 Window underlying the GtkWindow*
// and must be sized manually. The OSR GTK widget, on the other hand, can be // and must be sized manually. The OSR GTK widget, on the other hand, can be
// added to the Vbox container for automatic layout-based sizing. // added to the grid container for automatic layout-based sizing.
GtkWidget* parent = with_osr_ ? vbox : window_; GtkWidget* parent = with_osr_ ? grid : window_;
// Set the Display associated with the browser. // Set the Display associated with the browser.
::Display* xdisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(window_)); ::Display* xdisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(window_));
@ -746,7 +793,7 @@ gboolean RootWindowGtk::WindowDelete(GtkWidget* widget,
} }
// static // static
void RootWindowGtk::VboxSizeAllocated(GtkWidget* widget, void RootWindowGtk::GridSizeAllocated(GtkWidget* widget,
GtkAllocation* allocation, GtkAllocation* allocation,
RootWindowGtk* self) { RootWindowGtk* self) {
// May be called on the main thread and the UI thread. // May be called on the main thread and the UI thread.

View File

@ -97,7 +97,7 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
RootWindowGtk* self); RootWindowGtk* self);
// Signal handlers for the GTK Vbox containing all UX elements. // Signal handlers for the GTK Vbox containing all UX elements.
static void VboxSizeAllocated(GtkWidget* widget, static void GridSizeAllocated(GtkWidget* widget,
GtkAllocation* allocation, GtkAllocation* allocation,
RootWindowGtk* self); RootWindowGtk* self);

View File

@ -3,7 +3,6 @@
// can be found in the LICENSE file. // can be found in the LICENSE file.
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#undef Success // Definition conflicts with cef_message_router.h #undef Success // Definition conflicts with cef_message_router.h
@ -98,6 +97,13 @@ int RunMain(int argc, char* argv[]) {
// Populate the settings based on command line arguments. // Populate the settings based on command line arguments.
context->PopulateSettings(&settings); context->PopulateSettings(&settings);
if (settings.windowless_rendering_enabled) {
// Force the app to use OpenGL <= 3.1 when off-screen rendering is enabled.
// TODO(cefclient): Rewrite OSRRenderer to use shaders instead of the
// fixed-function pipeline which was removed in OpenGL 3.2 (back in 2009).
setenv("MESA_GL_VERSION_OVERRIDE", "3.1", /*overwrite=*/0);
}
// Create the main message loop object. // Create the main message loop object.
scoped_ptr<MainMessageLoop> message_loop; scoped_ptr<MainMessageLoop> message_loop;
if (settings.multi_threaded_message_loop) if (settings.multi_threaded_message_loop)
@ -110,13 +116,13 @@ int RunMain(int argc, char* argv[]) {
// Initialize CEF. // Initialize CEF.
context->Initialize(main_args, settings, app, nullptr); context->Initialize(main_args, settings, app, nullptr);
// Force Gtk to use Xwayland (in case a Wayland compositor is being used).
gdk_set_allowed_backends("x11");
// The Chromium sandbox requires that there only be a single thread during // The Chromium sandbox requires that there only be a single thread during
// initialization. Therefore initialize GTK after CEF. // initialization. Therefore initialize GTK after CEF.
gtk_init(&argc, &argv_copy); gtk_init(&argc, &argv_copy);
// Perform gtkglext initialization required by the OSR example.
gtk_gl_init(&argc, &argv_copy);
// Install xlib error handlers so that the application won't be terminated // Install xlib error handlers so that the application won't be terminated
// on non-fatal errors. Must be done after initializing GTK. // on non-fatal errors. Must be done after initializing GTK.
XSetErrorHandler(XErrorHandlerImpl); XSetErrorHandler(XErrorHandlerImpl);

View File

@ -30,4 +30,9 @@ void DeviceToLogical(CefMouseEvent& value, float device_scale_factor) {
value.y = DeviceToLogical(value.y, device_scale_factor); value.y = DeviceToLogical(value.y, device_scale_factor);
} }
void DeviceToLogical(CefTouchEvent& value, float device_scale_factor) {
value.x = DeviceToLogical(value.x, device_scale_factor);
value.y = DeviceToLogical(value.y, device_scale_factor);
}
} // namespace client } // namespace client

View File

@ -17,6 +17,7 @@ CefRect LogicalToDevice(const CefRect& value, float device_scale_factor);
// Convert |value| from device coordinates to logical coordinates. // Convert |value| from device coordinates to logical coordinates.
int DeviceToLogical(int value, float device_scale_factor); int DeviceToLogical(int value, float device_scale_factor);
void DeviceToLogical(CefMouseEvent& value, float device_scale_factor); void DeviceToLogical(CefMouseEvent& value, float device_scale_factor);
void DeviceToLogical(CefTouchEvent& value, float device_scale_factor);
} // namespace client } // namespace client