From 79bd4dd93cbe4f1caa5e86e2f96b405f6542508f Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 23 May 2014 17:47:07 +0000 Subject: [PATCH] Linux: Use Aura to display context menus (issue #1258) git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1710 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef.gyp | 2 + libcef/browser/browser_host_impl.h | 8 + libcef/browser/browser_host_impl_linux.cc | 296 +------------------- libcef/browser/menu_creator.cc | 4 + libcef/browser/menu_creator.h | 2 + libcef/browser/menu_creator_runner_linux.cc | 45 ++- libcef/browser/menu_creator_runner_linux.h | 7 + libcef/browser/menu_model_impl.cc | 8 +- libcef/browser/menu_model_impl.h | 4 + libcef/browser/window_x11.cc | 286 +++++++++++++++++++ libcef/browser/window_x11.h | 62 ++++ 11 files changed, 423 insertions(+), 301 deletions(-) create mode 100644 libcef/browser/window_x11.cc create mode 100644 libcef/browser/window_x11.h diff --git a/cef.gyp b/cef.gyp index 85ccdf09d..34a6bbc18 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1093,6 +1093,8 @@ 'libcef/browser/javascript_dialog_linux.cc', 'libcef/browser/menu_creator_runner_linux.cc', 'libcef/browser/menu_creator_runner_linux.h', + 'libcef/browser/window_x11.cc', + 'libcef/browser/window_x11.h', #Include sources for printing. '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_linux.cc', ], diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 14c851d66..04eb1bc68 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -245,6 +245,14 @@ class CefBrowserHostImpl : public CefBrowserHost, CefRefPtr client() const { return client_; } int browser_id() const; +#if defined(USE_AURA) + views::Widget* window_widget() const { return window_widget_; } +#endif + +#if defined(USE_X11) + CefWindowX11* window_x11() const { return window_x11_; } +#endif + // Returns the URL that is currently loading (or loaded) in the main frame. GURL GetLoadingURL(); diff --git a/libcef/browser/browser_host_impl_linux.cc b/libcef/browser/browser_host_impl_linux.cc index 2b0e8c46d..952f543be 100644 --- a/libcef/browser/browser_host_impl_linux.cc +++ b/libcef/browser/browser_host_impl_linux.cc @@ -6,13 +6,10 @@ #include "libcef/browser/browser_host_impl.h" #include -#include -#include -#include -#include #include "libcef/browser/context.h" #include "libcef/browser/window_delegate_view.h" +#include "libcef/browser/window_x11.h" #include "libcef/browser/thread_util.h" #include "base/bind.h" @@ -22,9 +19,6 @@ #include "content/public/common/renderer_preferences.h" #include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" #include "third_party/WebKit/public/web/WebInputEvent.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/events/platform/x11/x11_event_source.h" -#include "ui/gfx/x/x11_atom_cache.h" #include "ui/views/widget/widget.h" namespace { @@ -37,296 +31,8 @@ long GetSystemUptime() { return 0; } -const char* kAtomsToCache[] = { - "WM_DELETE_WINDOW", - "WM_PROTOCOLS", - "_NET_WM_PING", - "_NET_WM_PID", - NULL -}; - -::Window FindEventTarget(const base::NativeEvent& xev) { - ::Window target = xev->xany.window; - if (xev->type == GenericEvent) - target = static_cast(xev->xcookie.data)->event; - return target; -} - -::Window FindChild(::Display* display, ::Window window) { - ::Window root; - ::Window parent; - ::Window* children; - unsigned int nchildren; - if (XQueryTree(display, window, &root, &parent, &children, &nchildren)) { - DCHECK_EQ(1U, nchildren); - return children[0]; - } - return None; -} - } // namespace -#if defined(USE_X11) -CEF_EXPORT XDisplay* cef_get_xdisplay() { - if (!CEF_CURRENTLY_ON(CEF_UIT)) - return NULL; - return gfx::GetXDisplay(); -} -#endif - -// Object wrapper for an X11 Window. -// Based on WindowTreeHostX11 and DesktopWindowTreeHostX11. -class CefWindowX11 : public ui::PlatformEventDispatcher { - public: - CefWindowX11(CefRefPtr browser, - ::Window parent_xwindow, - const gfx::Rect& bounds) - : browser_(browser), - xdisplay_(gfx::GetXDisplay()), - parent_xwindow_(parent_xwindow), - xwindow_(0), - window_mapped_(false), - bounds_(bounds), - atom_cache_(xdisplay_, kAtomsToCache) { - if (parent_xwindow_ == None) - parent_xwindow_ = DefaultRootWindow(xdisplay_); - - XSetWindowAttributes swa; - memset(&swa, 0, sizeof(swa)); - swa.background_pixmap = None; - swa.override_redirect = false; - xwindow_ = XCreateWindow( - xdisplay_, parent_xwindow_, - bounds.x(), bounds.y(), bounds.width(), bounds.height(), - 0, // border width - CopyFromParent, // depth - InputOutput, - CopyFromParent, // visual - CWBackPixmap | CWOverrideRedirect, - &swa); - - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - - long event_mask = FocusChangeMask | StructureNotifyMask | PropertyChangeMask; - XSelectInput(xdisplay_, xwindow_, event_mask); - XFlush(xdisplay_); - - // TODO(erg): We currently only request window deletion events. We also - // should listen for activation events and anything else that GTK+ listens - // for, and do something useful. - ::Atom protocols[2]; - protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); - protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); - XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); - - // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with - // the desktop environment. - XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); - - // Likewise, the X server needs to know this window's pid so it knows which - // program to kill if the window hangs. - // XChangeProperty() expects "pid" to be long. - COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long); - long pid = getpid(); - XChangeProperty(xdisplay_, - xwindow_, - atom_cache_.GetAtom("_NET_WM_PID"), - XA_CARDINAL, - 32, - PropModeReplace, - reinterpret_cast(&pid), 1); - - // Allow subclasses to create and cache additional atoms. - atom_cache_.allow_uncached_atoms(); - } - - virtual ~CefWindowX11() { - DCHECK(!xwindow_); - if (ui::PlatformEventSource::GetInstance()) - ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - } - - void Close() { - XEvent ev = {0}; - ev.xclient.type = ClientMessage; - ev.xclient.window = xwindow_; - ev.xclient.message_type = atom_cache_.GetAtom("WM_PROTOCOLS"); - ev.xclient.format = 32; - ev.xclient.data.l[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(xdisplay_, xwindow_, False, NoEventMask, &ev); - } - - void Show() { - if (xwindow_ == None) - return; - - if (!window_mapped_) { - // Before we map the window, set size hints. Otherwise, some window managers - // will ignore toplevel XMoveWindow commands. - XSizeHints size_hints; - size_hints.flags = PPosition | PWinGravity; - size_hints.x = bounds_.x(); - size_hints.y = bounds_.y(); - // Set StaticGravity so that the window position is not affected by the - // frame width when running with window manager. - size_hints.win_gravity = StaticGravity; - XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); - - XMapWindow(xdisplay_, xwindow_); - - // We now block until our window is mapped. Some X11 APIs will crash and - // burn if passed |xwindow_| before the window is mapped, and XMapWindow is - // asynchronous. - if (ui::X11EventSource::GetInstance()) - ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); - window_mapped_ = true; - } - } - - void Hide() { - if (xwindow_ == None) - return; - - if (window_mapped_) { - XWithdrawWindow(xdisplay_, xwindow_, 0); - window_mapped_ = false; - } - } - - void SetBounds(const gfx::Rect& bounds) { - if (xwindow_ == None) - return; - - bool origin_changed = bounds_.origin() != bounds.origin(); - bool size_changed = bounds_.size() != bounds.size(); - XWindowChanges changes = {0}; - unsigned value_mask = 0; - - if (size_changed) { - changes.width = bounds.width(); - changes.height = bounds.height(); - value_mask = CWHeight | CWWidth; - } - - if (origin_changed) { - changes.x = bounds.x(); - changes.y = bounds.y(); - value_mask |= CWX | CWY; - } - - if (value_mask) - XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); - } - - // ui::PlatformEventDispatcher methods: - virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE { - ::Window target = FindEventTarget(event); - return target == xwindow_; - } - - virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE { - XEvent* xev = event; - switch (xev->type) { - case ConfigureNotify: { - DCHECK_EQ(xwindow_, xev->xconfigure.event); - DCHECK_EQ(xwindow_, xev->xconfigure.window); - // It's possible that the X window may be resized by some other means - // than from within Aura (e.g. the X window manager can change the - // size). Make sure the root window size is maintained properly. - gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, - xev->xconfigure.width, xev->xconfigure.height); - bounds_ = bounds; - - ::Window child = FindChild(xdisplay_, xwindow_); - if (child) { - // Resize the child DesktopWindowTreeHostX11 to match this window. - XWindowChanges changes = {0}; - changes.width = bounds.width(); - changes.height = bounds.height(); - XConfigureWindow(xdisplay_, child, CWHeight | CWWidth, &changes); - } - break; - } - case ClientMessage: { - Atom message_type = static_cast(xev->xclient.data.l[0]); - if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { - // We have received a close message from the window manager. - if (browser_->destruction_state() <= - CefBrowserHostImpl::DESTRUCTION_STATE_PENDING) { - if (browser_->destruction_state() == - CefBrowserHostImpl::DESTRUCTION_STATE_NONE) { - // Request that the browser close. - browser_->CloseBrowser(false); - } - - // Cancel the close. - } else { - // Allow the close. - XDestroyWindow(xdisplay_, xwindow_); - } - } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { - XEvent reply_event = *xev; - reply_event.xclient.window = parent_xwindow_; - - XSendEvent(xdisplay_, - reply_event.xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &reply_event); - XFlush(xdisplay_); - } - break; - } - case DestroyNotify: - xwindow_ = None; - - // Force the browser to be destroyed and release the reference added - // in PlatformCreateWindow(). - browser_->WindowDestroyed(); - - delete this; - break; - case FocusIn: - // This message is recieved first followed by a "_NET_ACTIVE_WINDOW" - // message sent to the root window. When X11DesktopHandler handles the - // "_NET_ACTIVE_WINDOW" message it will erroneously mark the WebView - // (hosted in a DesktopWindowTreeHostX11) as unfocused. Use a delayed - // task here to restore the WebView's focus state. - CEF_POST_DELAYED_TASK(CEF_UIT, - base::Bind(&CefBrowserHostImpl::OnSetFocus, browser_, - FOCUS_SOURCE_SYSTEM), - 100); - break; - } - - return ui::POST_DISPATCH_STOP_PROPAGATION; - } - - ::Window xwindow() const { return xwindow_; } - gfx::Rect bounds() const { return bounds_; } - - private: - CefRefPtr browser_; - - // The display and the native X window hosting the root window. - XDisplay* xdisplay_; - ::Window parent_xwindow_; - ::Window xwindow_; - - // Is the window mapped to the screen? - bool window_mapped_; - - // The bounds of |xwindow_|. - gfx::Rect bounds_; - - ui::X11AtomCache atom_cache_; - - DISALLOW_COPY_AND_ASSIGN(CefWindowX11); -}; - - bool CefBrowserHostImpl::PlatformCreateWindow() { DCHECK(!window_x11_); DCHECK(!window_widget_); diff --git a/libcef/browser/menu_creator.cc b/libcef/browser/menu_creator.cc index c7b47f078..a0b21c68a 100644 --- a/libcef/browser/menu_creator.cc +++ b/libcef/browser/menu_creator.cc @@ -193,6 +193,10 @@ void CefMenuCreator::MenuClosed(CefRefPtr source) { } } +bool CefMenuCreator::FormatLabel(base::string16& label) { + return runner_->FormatLabel(label); +} + void CefMenuCreator::CreateDefaultModel() { if (params_.is_editable) { // Editable node. diff --git a/libcef/browser/menu_creator.h b/libcef/browser/menu_creator.h index 2dd7cce5c..3ca124102 100644 --- a/libcef/browser/menu_creator.h +++ b/libcef/browser/menu_creator.h @@ -24,6 +24,7 @@ class CefMenuCreator : public CefMenuModelImpl::Delegate { public: virtual ~Runner() {} virtual bool RunContextMenu(CefMenuCreator* manager) =0; + virtual bool FormatLabel(base::string16& label) { return false; } }; explicit CefMenuCreator(CefBrowserHostImpl* browser); @@ -49,6 +50,7 @@ class CefMenuCreator : public CefMenuModelImpl::Delegate { cef_event_flags_t event_flags) OVERRIDE; virtual void MenuWillShow(CefRefPtr source) OVERRIDE; virtual void MenuClosed(CefRefPtr source) OVERRIDE; + virtual bool FormatLabel(base::string16& label) OVERRIDE; // Create the default menu model. void CreateDefaultModel(); diff --git a/libcef/browser/menu_creator_runner_linux.cc b/libcef/browser/menu_creator_runner_linux.cc index 1b87f40c4..a705524f9 100644 --- a/libcef/browser/menu_creator_runner_linux.cc +++ b/libcef/browser/menu_creator_runner_linux.cc @@ -1,8 +1,19 @@ -// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that can -// be found in the LICENSE file. +// Copyright 2014 The Chromium Embedded Framework Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file. #include "libcef/browser/menu_creator_runner_linux.h" +#include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/window_x11.h" + +#include "base/strings/string_util.h" +#include "ui/aura/window.h" +#include "ui/gfx/point.h" + +// Used to silence warnings about unused variables. +#if !defined(UNUSED) +#define UNUSED(x) ((void)(x)) +#endif CefMenuCreatorRunnerLinux::CefMenuCreatorRunnerLinux() { } @@ -11,6 +22,30 @@ CefMenuCreatorRunnerLinux::~CefMenuCreatorRunnerLinux() { } bool CefMenuCreatorRunnerLinux::RunContextMenu(CefMenuCreator* manager) { - NOTIMPLEMENTED(); - return false; + menu_.reset(new views::MenuRunner(manager->model())); + + // We can't use aura::Window::GetBoundsInScreen on Linux because it will + // return bounds from DesktopWindowTreeHostX11 which in our case is relative + // to the parent window instead of the root window (screen). + const gfx::Rect& bounds_in_screen = + manager->browser()->window_x11()->GetBoundsInScreen(); + gfx::Point screen_point = + gfx::Point(bounds_in_screen.x() + manager->params().x, + bounds_in_screen.y() + manager->params().y); + + views::MenuRunner::RunResult result = + menu_->RunMenuAt(manager->browser()->window_widget(), + NULL, gfx::Rect(screen_point, gfx::Size()), + views::MenuItemView::TOPRIGHT, ui::MENU_SOURCE_NONE, + views::MenuRunner::CONTEXT_MENU); + UNUSED(result); + + return true; } + +bool CefMenuCreatorRunnerLinux::FormatLabel(base::string16& label) { + // Remove the accelerator indicator (&) from label strings. + const char16 replace[] = {L'&', 0}; + return base::ReplaceChars(label, replace, base::string16(), &label); +} + diff --git a/libcef/browser/menu_creator_runner_linux.h b/libcef/browser/menu_creator_runner_linux.h index bac6fa44c..0af6e9112 100644 --- a/libcef/browser/menu_creator_runner_linux.h +++ b/libcef/browser/menu_creator_runner_linux.h @@ -8,6 +8,9 @@ #include "libcef/browser/menu_creator.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/controls/menu/menu_runner.h" + class CefMenuCreatorRunnerLinux: public CefMenuCreator::Runner { public: CefMenuCreatorRunnerLinux(); @@ -15,6 +18,10 @@ class CefMenuCreatorRunnerLinux: public CefMenuCreator::Runner { // CefMemoryManager::Runner methods. virtual bool RunContextMenu(CefMenuCreator* manager) OVERRIDE; + virtual bool FormatLabel(base::string16& label) OVERRIDE; + + private: + scoped_ptr menu_; }; #endif // CEF_LIBCEF_BROWSER_MENU_MANAGER_RUNNER_LINUX_H_ diff --git a/libcef/browser/menu_model_impl.cc b/libcef/browser/menu_model_impl.cc index c8648d0e7..f8c4147a9 100644 --- a/libcef/browser/menu_model_impl.cc +++ b/libcef/browser/menu_model_impl.cc @@ -65,7 +65,7 @@ class CefSimpleMenuModel : public ui::MenuModel { } virtual base::string16 GetLabelAt(int index) const OVERRIDE { - return impl_->GetLabelAt(index).ToString16(); + return impl_->GetFormattedLabelAt(index); } virtual bool IsItemDynamicAt(int index) const OVERRIDE { @@ -641,6 +641,12 @@ void CefMenuModelImpl::MenuClosed() { base::Bind(&CefMenuModelImpl::OnMenuClosed, this)); } +base::string16 CefMenuModelImpl::GetFormattedLabelAt(int index) { + base::string16 label = GetLabelAt(index).ToString16(); + delegate_->FormatLabel(label); + return label; +} + bool CefMenuModelImpl::VerifyRefCount() { if (!VerifyContext()) return false; diff --git a/libcef/browser/menu_model_impl.h b/libcef/browser/menu_model_impl.h index 102b68d97..62aa7ebff 100644 --- a/libcef/browser/menu_model_impl.h +++ b/libcef/browser/menu_model_impl.h @@ -31,6 +31,9 @@ class CefMenuModelImpl : public CefMenuModel { // Notifies the delegate that the menu has closed. virtual void MenuClosed(CefRefPtr source) =0; + // Allows the delegate to modify a menu item label before it's displayed. + virtual bool FormatLabel(base::string16& label) =0; + protected: virtual ~Delegate() {} }; @@ -104,6 +107,7 @@ class CefMenuModelImpl : public CefMenuModel { void ActivatedAt(int index, cef_event_flags_t event_flags); void MenuWillShow(); void MenuClosed(); + base::string16 GetFormattedLabelAt(int index); // Verify that only a single reference exists to all CefMenuModelImpl objects. bool VerifyRefCount(); diff --git a/libcef/browser/window_x11.cc b/libcef/browser/window_x11.cc new file mode 100644 index 000000000..96b8bcc40 --- /dev/null +++ b/libcef/browser/window_x11.cc @@ -0,0 +1,286 @@ +// Copyright 2014 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libcef/browser/window_x11.h" +#include "libcef/browser/thread_util.h" + +#include +#include +#include + +#include "ui/events/platform/x11/x11_event_source.h" + +namespace { + +const char* kAtomsToCache[] = { + "WM_DELETE_WINDOW", + "WM_PROTOCOLS", + "_NET_WM_PING", + "_NET_WM_PID", + NULL +}; + +::Window FindEventTarget(const base::NativeEvent& xev) { + ::Window target = xev->xany.window; + if (xev->type == GenericEvent) + target = static_cast(xev->xcookie.data)->event; + return target; +} + +::Window FindChild(::Display* display, ::Window window) { + ::Window root; + ::Window parent; + ::Window* children; + unsigned int nchildren; + if (XQueryTree(display, window, &root, &parent, &children, &nchildren)) { + DCHECK_EQ(1U, nchildren); + return children[0]; + } + return None; +} + +} // namespace + +CEF_EXPORT XDisplay* cef_get_xdisplay() { + if (!CEF_CURRENTLY_ON(CEF_UIT)) + return NULL; + return gfx::GetXDisplay(); +} + +CefWindowX11::CefWindowX11(CefRefPtr browser, + ::Window parent_xwindow, + const gfx::Rect& bounds) + : browser_(browser), + xdisplay_(gfx::GetXDisplay()), + parent_xwindow_(parent_xwindow), + xwindow_(0), + window_mapped_(false), + bounds_(bounds), + atom_cache_(xdisplay_, kAtomsToCache) { + if (parent_xwindow_ == None) + parent_xwindow_ = DefaultRootWindow(xdisplay_); + + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.background_pixmap = None; + swa.override_redirect = false; + xwindow_ = XCreateWindow( + xdisplay_, parent_xwindow_, + bounds.x(), bounds.y(), bounds.width(), bounds.height(), + 0, // border width + CopyFromParent, // depth + InputOutput, + CopyFromParent, // visual + CWBackPixmap | CWOverrideRedirect, + &swa); + + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); + + long event_mask = FocusChangeMask | StructureNotifyMask | PropertyChangeMask; + XSelectInput(xdisplay_, xwindow_, event_mask); + XFlush(xdisplay_); + + // TODO(erg): We currently only request window deletion events. We also + // should listen for activation events and anything else that GTK+ listens + // for, and do something useful. + ::Atom protocols[2]; + protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); + protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); + XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); + + // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with + // the desktop environment. + XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + // Likewise, the X server needs to know this window's pid so it knows which + // program to kill if the window hangs. + // XChangeProperty() expects "pid" to be long. + COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long); + long pid = getpid(); + XChangeProperty(xdisplay_, + xwindow_, + atom_cache_.GetAtom("_NET_WM_PID"), + XA_CARDINAL, + 32, + PropModeReplace, + reinterpret_cast(&pid), 1); + + // Allow subclasses to create and cache additional atoms. + atom_cache_.allow_uncached_atoms(); +} + +CefWindowX11::~CefWindowX11() { + DCHECK(!xwindow_); + if (ui::PlatformEventSource::GetInstance()) + ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); +} + +void CefWindowX11::Close() { + XEvent ev = {0}; + ev.xclient.type = ClientMessage; + ev.xclient.window = xwindow_; + ev.xclient.message_type = atom_cache_.GetAtom("WM_PROTOCOLS"); + ev.xclient.format = 32; + ev.xclient.data.l[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(xdisplay_, xwindow_, False, NoEventMask, &ev); +} + +void CefWindowX11::Show() { + if (xwindow_ == None) + return; + + if (!window_mapped_) { + // Before we map the window, set size hints. Otherwise, some window managers + // will ignore toplevel XMoveWindow commands. + XSizeHints size_hints; + size_hints.flags = PPosition | PWinGravity; + size_hints.x = bounds_.x(); + size_hints.y = bounds_.y(); + // Set StaticGravity so that the window position is not affected by the + // frame width when running with window manager. + size_hints.win_gravity = StaticGravity; + XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); + + XMapWindow(xdisplay_, xwindow_); + + // We now block until our window is mapped. Some X11 APIs will crash and + // burn if passed |xwindow_| before the window is mapped, and XMapWindow is + // asynchronous. + if (ui::X11EventSource::GetInstance()) + ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); + window_mapped_ = true; + } +} + +void CefWindowX11::Hide() { + if (xwindow_ == None) + return; + + if (window_mapped_) { + XWithdrawWindow(xdisplay_, xwindow_, 0); + window_mapped_ = false; + } +} + +void CefWindowX11::SetBounds(const gfx::Rect& bounds) { + if (xwindow_ == None) + return; + + bool origin_changed = bounds_.origin() != bounds.origin(); + bool size_changed = bounds_.size() != bounds.size(); + XWindowChanges changes = {0}; + unsigned value_mask = 0; + + if (size_changed) { + changes.width = bounds.width(); + changes.height = bounds.height(); + value_mask = CWHeight | CWWidth; + } + + if (origin_changed) { + changes.x = bounds.x(); + changes.y = bounds.y(); + value_mask |= CWX | CWY; + } + + if (value_mask) + XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); +} + +gfx::Rect CefWindowX11::GetBoundsInScreen() { + int x, y; + Window child; + if (XTranslateCoordinates(xdisplay_, xwindow_, DefaultRootWindow(xdisplay_), + 0, 0, &x, &y, &child)) { + return gfx::Rect(gfx::Point(x, y), bounds_.size()); + } + return gfx::Rect(); +} + +bool CefWindowX11::CanDispatchEvent(const ui::PlatformEvent& event) { + ::Window target = FindEventTarget(event); + return target == xwindow_; +} + +uint32_t CefWindowX11::DispatchEvent(const ui::PlatformEvent& event) { + XEvent* xev = event; + switch (xev->type) { + case ConfigureNotify: { + DCHECK_EQ(xwindow_, xev->xconfigure.event); + DCHECK_EQ(xwindow_, xev->xconfigure.window); + // It's possible that the X window may be resized by some other means + // than from within Aura (e.g. the X window manager can change the + // size). Make sure the root window size is maintained properly. + gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, + xev->xconfigure.width, xev->xconfigure.height); + bounds_ = bounds; + + ::Window child = FindChild(xdisplay_, xwindow_); + if (child) { + // Resize the child DesktopWindowTreeHostX11 to match this window. + XWindowChanges changes = {0}; + changes.width = bounds.width(); + changes.height = bounds.height(); + XConfigureWindow(xdisplay_, child, CWHeight | CWWidth, &changes); + } + break; + } + case ClientMessage: { + Atom message_type = static_cast(xev->xclient.data.l[0]); + if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { + // We have received a close message from the window manager. + if (browser_->destruction_state() <= + CefBrowserHostImpl::DESTRUCTION_STATE_PENDING) { + if (browser_->destruction_state() == + CefBrowserHostImpl::DESTRUCTION_STATE_NONE) { + // Request that the browser close. + browser_->CloseBrowser(false); + } + + // Cancel the close. + } else { + // Allow the close. + XDestroyWindow(xdisplay_, xwindow_); + } + } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { + XEvent reply_event = *xev; + reply_event.xclient.window = parent_xwindow_; + + XSendEvent(xdisplay_, + reply_event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &reply_event); + XFlush(xdisplay_); + } + break; + } + case DestroyNotify: + xwindow_ = None; + + // Force the browser to be destroyed and release the reference added + // in PlatformCreateWindow(). + browser_->WindowDestroyed(); + + delete this; + break; + case FocusIn: + // This message is recieved first followed by a "_NET_ACTIVE_WINDOW" + // message sent to the root window. When X11DesktopHandler handles the + // "_NET_ACTIVE_WINDOW" message it will erroneously mark the WebView + // (hosted in a DesktopWindowTreeHostX11) as unfocused. Use a delayed + // task here to restore the WebView's focus state. + CEF_POST_DELAYED_TASK(CEF_UIT, + base::Bind(&CefBrowserHostImpl::OnSetFocus, browser_, + FOCUS_SOURCE_SYSTEM), + 100); + break; + } + + return ui::POST_DISPATCH_STOP_PROPAGATION; +} + diff --git a/libcef/browser/window_x11.h b/libcef/browser/window_x11.h new file mode 100644 index 000000000..042ec8092 --- /dev/null +++ b/libcef/browser/window_x11.h @@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Embedded Framework Authors. +// Portions copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_WINDOW_X11_H_ +#define CEF_LIBCEF_BROWSER_WINDOW_X11_H_ +#pragma once + +#include + +#include "libcef/browser/browser_host_impl.h" + +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/x/x11_atom_cache.h" + +// Object wrapper for an X11 Window. +// Based on WindowTreeHostX11 and DesktopWindowTreeHostX11. +class CefWindowX11 : public ui::PlatformEventDispatcher { + public: + CefWindowX11(CefRefPtr browser, + ::Window parent_xwindow, + const gfx::Rect& bounds); + virtual ~CefWindowX11(); + + void Close(); + + void Show(); + void Hide(); + + void SetBounds(const gfx::Rect& bounds); + + gfx::Rect GetBoundsInScreen(); + + // ui::PlatformEventDispatcher methods: + virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE; + + ::Window xwindow() const { return xwindow_; } + gfx::Rect bounds() const { return bounds_; } + + private: + CefRefPtr browser_; + + // The display and the native X window hosting the root window. + ::Display* xdisplay_; + ::Window parent_xwindow_; + ::Window xwindow_; + + // Is the window mapped to the screen? + bool window_mapped_; + + // The bounds of |xwindow_|. + gfx::Rect bounds_; + + ui::X11AtomCache atom_cache_; + + DISALLOW_COPY_AND_ASSIGN(CefWindowX11); +}; + +#endif // CEF_LIBCEF_BROWSER_WINDOW_X11_H_