Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(709)

Unified Diff: ui/aura/hostwm/host_window_manager_x11.cc

Issue 10789018: aura: Add X11 host window management. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Keep ash switches alphabetized. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/aura/hostwm/host_window_manager_x11.cc
diff --git a/ui/aura/hostwm/host_window_manager_x11.cc b/ui/aura/hostwm/host_window_manager_x11.cc
new file mode 100644
index 0000000000000000000000000000000000000000..03e6bfc797ac887a437c2ca9aa682e2ba3b3bccc
--- /dev/null
+++ b/ui/aura/hostwm/host_window_manager_x11.cc
@@ -0,0 +1,1042 @@
+// Copyright (c) 2012 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 "ui/aura/hostwm/host_window_manager_x11.h"
+
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+
+#include "base/bind.h"
+#include "base/message_pump_aurax11.h"
+#include "ui/aura/client/window_types.h"
+#include "ui/aura/dispatcher_linux.h"
+#include "ui/aura/env.h"
+#include "ui/aura/event.h"
+#include "ui/aura/focus_manager.h"
+#include "ui/aura/hostwm/host_window_manager_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_host_linux.h"
+#include "ui/aura/window.h"
+#include "ui/base/touch/touch_factory.h"
+#include "ui/base/x/x11_util.h"
+
+namespace {
+
+class ScopedPtrXFree {
+ public:
+ void operator()(void* x) const {
+ ::XFree(x);
+ }
+};
+
+int ExistingWMX11ErrorHandler(Display* d, XErrorEvent* e) {
+ LOG(FATAL)
+ << "X Error detected: "
+ << "serial " << e->serial << ", "
+ << "error_code " << static_cast<int>(e->error_code) << ", "
+ << "request_code " << static_cast<int>(e->request_code) << ", "
+ << "minor_code " << static_cast<int>(e->minor_code);
+ return 0;
+}
+
+std::queue<unsigned long> xerror_ignore_queue;
+
+int FilteredX11ErrorHandler(Display* d, XErrorEvent* e) {
+ while (!xerror_ignore_queue.empty()) {
+ if ((e->serial - xerror_ignore_queue.front()) > 0) {
+ xerror_ignore_queue.pop();
+ continue;
+ }
+
+ break;
+ }
+
+ if (!xerror_ignore_queue.empty() && xerror_ignore_queue.front() == e->serial)
+ return 0;
+
+ return aura::CallBaseX11ErrorHandler(d, e);
+}
+
+unsigned InitWindowChanges(const gfx::Rect& bounds,
+ ::Window siblingToStackAbove,
+ XWindowChanges& wc) {
+ wc.x = bounds.x();
+ wc.y = bounds.y();
+ wc.width = bounds.width();
+ wc.height = bounds.height();
+ if (!siblingToStackAbove) {
+ wc.stack_mode = Below;
+ return CWX | CWY | CWWidth | CWHeight | CWStackMode;
+ }
+
+ wc.sibling = siblingToStackAbove;
+ wc.stack_mode = Above;
+ return CWX | CWY | CWWidth | CWHeight | CWStackMode | CWSibling;
+}
+
+aura::Window* FindLowestCommonAncestor(
+ aura::Window* root, aura::Window* p, aura::Window* q) {
+ // Root is the LCA.
+ if (root == p || root == q)
+ return root;
+
+ aura::Window* prev = NULL;
+ const aura::Window::Windows& children = root->children();
+ for (size_t i = 0; i < children.size(); ++i) {
+
+ // Try to find LCA of p and q in subtree.
+ aura::Window* next = FindLowestCommonAncestor(children[i], p, q);
+ if (next) {
+
+ // If a LCA was previously found, p and q must be in different subtrees.
+ if (prev)
+ return root;
+
+ prev = next;
+ }
+ }
+
+ return prev;
+}
+
+gfx::Point GetTargetOriginInRootWindow(aura::Window* window) {
+ gfx::Point origin;
+
+ const aura::Window* p = window;
+ for (; p != window->GetRootWindow(); p = p->parent())
+ origin = origin.Add(p->GetTargetBounds().origin());
+
+ return origin;
+}
+
+gfx::Rect GetTargetBoundsInRootWindow(aura::Window* window) {
+ return gfx::Rect(
+ GetTargetOriginInRootWindow(window),
+ window->GetTargetBounds().size());
+}
+
+const char* kAtomsToCache[] = {
+ "WM_DELETE_WINDOW",
+ "WM_PROTOCOLS",
+ NULL
+};
+
+}
+
+namespace aura {
+
+XErrorHandler base_xerror_handler_ = 0;
+
+void SetBaseX11ErrorHandler(XErrorHandler error_handler) {
+ DCHECK(!base_xerror_handler_);
+ base_xerror_handler_ = error_handler;
+}
+
+int CallBaseX11ErrorHandler(Display* d, XErrorEvent* e) {
+ DCHECK(base_xerror_handler_);
+ return base_xerror_handler_(d, e);
+}
+
+HostWindowManagerX11WindowObserver::HostWindowManagerX11WindowObserver(
+ HostWindowManagerX11* manager)
+ : host_window_manager_(manager) {
+}
+
+void HostWindowManagerX11WindowObserver::OnWindowInitialized(Window* window) {
+ window->AddObserver(this);
+}
+
+void HostWindowManagerX11WindowObserver::OnWillRemoveWindow(Window* window) {
+ HostWindowManagerX11* wm = host_window_manager_;
+
+ if (wm->configure_window_ == window)
+ wm->configure_window_ = window->parent();
+}
+
+void HostWindowManagerX11WindowObserver::OnWindowDestroyed(Window* window) {
+ HostWindowManagerX11* wm = host_window_manager_;
+
+ if (wm->input_host_windows_.find(window) != wm->input_host_windows_.end()) {
+ scoped_refptr<HostWindowX11> top_level = wm->input_host_windows_[window];
+ wm->native_windows_.erase(top_level->xid());
+ wm->input_host_windows_.erase(window);
+ top_level->CloseWindow();
+ }
+
+ // This should only happend at close down.
+ if (wm->host_windows_.find(window) != wm->host_windows_.end()) {
+ wm->native_windows_.erase(wm->host_windows_[window]);
+ wm->host_windows_.erase(window);
+ }
+}
+
+void HostWindowManagerX11WindowObserver::OnWindowStackingChanged(
+ Window* window) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ wm->HostWindowNeedsConfigure(window->parent());
+}
+
+void HostWindowManagerX11WindowObserver::OnWindowVisibilityChanged(
+ Window* window, bool visible) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ // Window can be parent-less when being destroyed.
+ if (window->parent())
+ wm->HostWindowNeedsConfigure(window);
+}
+
+void HostWindowManagerX11WindowObserver::OnWindowBoundsChanged(
+ Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ wm->HostWindowNeedsConfigure(window);
+}
+
+HostWindowX11::HostWindowX11(::XID xwindow, unsigned state)
+ : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
+ xwindow_(xwindow),
+ state_(state) {
+}
+
+HostWindowX11::~HostWindowX11() {
+}
+
+bool HostWindowX11::CanResize() {
+ return false;
+}
+
+bool HostWindowX11::CanConfigure() {
+ return false;
+}
+
+bool HostWindowX11::CanActivate() {
+ return false;
+}
+
+InputHostWindowX11::InputHostWindowX11(::XID xwindow)
+ : HostWindowX11(xwindow, WithdrawnState) {
+}
+
+InputHostWindowX11::~InputHostWindowX11() {
+}
+
+void InputHostWindowX11::CloseWindow() {
+ XDestroyWindow(xdisplay_, xwindow_);
+}
+
+bool InputHostWindowX11::CanResize() {
+ return true;
+}
+
+bool InputHostWindowX11::CanConfigure() {
+ return true;
+}
+
+bool InputHostWindowX11::CanActivate() {
+ return false;
+}
+
+RedirectedHostWindowX11::RedirectedHostWindowX11(
+ HostWindowManagerX11* manager,
+ ::Window xwindow,
+ const gfx::Size& size)
+ : HostWindowX11(xwindow, WithdrawnState),
+ host_window_manager_(manager),
+ size_(size),
+ visible_(false) {
+ // Ignore possible X error as we can't guarantee that host window exists
+ // and is of InputOutput type.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ // Damage resource is automatically freed when the window is destroyed or
+ // we close our connection to the X server.
+ XDamageCreate(xdisplay_, xwindow_, XDamageReportRawRectangles);
+ static_cast<DispatcherLinux*>(
+ Env::GetInstance()->GetDispatcher())->
+ AddDispatcherForWindow(this, xwindow_);
+}
+
+RedirectedHostWindowX11::~RedirectedHostWindowX11() {
+ static_cast<DispatcherLinux*>(
+ Env::GetInstance()->GetDispatcher())->
+ RemoveDispatcherForWindow(xwindow_);
+}
+
+void RedirectedHostWindowX11::OnMapNotify() {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ if (wm->native_windows_.find(xwindow_) != wm->native_windows_.end())
+ client->OnHostWindowVisibilityChanged(wm->native_windows_[xwindow_], true);
+
+ SetVisible(true);
+}
+
+void RedirectedHostWindowX11::OnUnmapNotify() {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ if (wm->native_windows_.find(xwindow_) != wm->native_windows_.end())
+ client->OnHostWindowVisibilityChanged(
+ wm->native_windows_[xwindow_], false);
+
+ SetVisible(false);
+}
+
+void RedirectedHostWindowX11::OnConfigureNotify(
+ gfx::Rect bounds, ::Window above) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ if (wm->native_windows_.find(xwindow_) != wm->native_windows_.end())
+ client->OnHostWindowMovedOrResized(wm->native_windows_[xwindow_], bounds);
+
+ SetSize(bounds.size());
+
+ // TODO(reveman): Respect stacking properties.
+}
+
+bool RedirectedHostWindowX11::Dispatch(const base::NativeEvent& event) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ if (event->type == wm->damage_event_base() + XDamageNotify) {
+ UpdateExternalTexture();
+ return true;
+ }
+
+ switch (event->type) {
+ case ButtonPress: {
+ XEvent x_button_event = *event;
+ x_button_event.xbutton.x = event->xbutton.x_root;
+ x_button_event.xbutton.y = event->xbutton.y_root;
+ client->GetRootWindow()->DispatchNativeEvent(&x_button_event);
+ // We generate a ButtonRelease event here as we won't get a real release
+ // event when we replay event on the host window.
+ x_button_event.type = ButtonRelease;
+ client->GetRootWindow()->DispatchNativeEvent(&x_button_event);
+ XAllowEvents(xdisplay_, ReplayPointer, event->xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ NOTREACHED();
+ break;
+ case KeyPress:
+ case KeyRelease:
+ // TODO(reveman): Implement key grabs.
+ NOTREACHED();
+ break;
+ }
+ return true;
+}
+
+void RedirectedHostWindowX11::UpdateExternalTexture() {
+ // TODO(reveman): Get backing pixmap for window and bind to ui::Texture.
+}
+
+void RedirectedHostWindowX11::SetSize(const gfx::Size& size) {
+ if (size == size_)
+ return;
+
+ size_ = size;
+
+ // External texture will be updated as a result of receiving damage events.
+}
+
+void RedirectedHostWindowX11::SetVisible(bool visible) {
+ if (visible == visible_)
+ return;
+
+ visible_ = visible;
+}
+
+bool RedirectedHostWindowX11::CanResize() {
+ return false;
+}
+
+bool RedirectedHostWindowX11::CanConfigure() {
+ return false;
+}
+
+bool RedirectedHostWindowX11::CanActivate() {
+ return false;
+}
+
+ManagedRedirectedHostWindowX11::ManagedRedirectedHostWindowX11(
+ HostWindowManagerX11* manager, ::Window xwindow, const gfx::Size& size)
+ : RedirectedHostWindowX11(manager, xwindow, size) {
+}
+
+ManagedRedirectedHostWindowX11::~ManagedRedirectedHostWindowX11() {
+}
+
+void ManagedRedirectedHostWindowX11::CloseWindow() {
+ HostWindowManagerX11* wm = host_window_manager_;
+ XEvent xevent;
+ xevent.type = ClientMessage;
+ xevent.xclient.window = xwindow_;
+ xevent.xclient.message_type = wm->atom_cache_.GetAtom("WM_PROTOCOLS");
+ xevent.xclient.format = 32;
+ xevent.xclient.data.l[0] = wm->atom_cache_.GetAtom("WM_DELETE_WINDOW");
+ xevent.xclient.data.l[1] = CurrentTime;
+ xevent.xclient.data.l[2] = 0;
+ xevent.xclient.data.l[3] = 0;
+ xevent.xclient.data.l[4] = 0;
+ // Ignore possible X error as we can't guarantee that host window exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XSendEvent(xdisplay_, xwindow_, false, NoEventMask, &xevent);
+}
+
+bool ManagedRedirectedHostWindowX11::CanResize() {
+ // TODO(reveman): Use hints to determine if host window can be resized.
+ return true;
+}
+
+void ManagedRedirectedHostWindowX11::OnMapNotify() {
+ SetVisible(true);
+}
+
+void ManagedRedirectedHostWindowX11::OnUnmapNotify() {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ // The client unmapped the window, we should transition to withdrawn state.
+ if (state_ == NormalState) {
+ if (wm->native_windows_.find(xwindow_) != wm->native_windows_.end())
+ client->OnHostWindowVisibilityChanged(
+ wm->native_windows_[xwindow_], false);
+
+ state_ = WithdrawnState;
+ }
+
+ SetVisible(false);
+}
+
+void ManagedRedirectedHostWindowX11::OnConfigureNotify(
+ gfx::Rect bounds, ::Window above) {
+ SetSize(bounds.size());
+}
+
+bool ManagedRedirectedHostWindowX11::CanConfigure() {
+ return true;
+}
+
+bool ManagedRedirectedHostWindowX11::CanActivate() {
+ return true;
+}
+
+HostWindowManagerX11RootEventObserver::HostWindowManagerX11RootEventObserver(
+ HostWindowManagerX11* manager)
+ : host_window_manager_(manager),
+ xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()) {
+}
+
+bool HostWindowManagerX11RootEventObserver::ProcessHostWindowUpdate(
+ const base::NativeEvent& event) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ ::Window xwindow = 0;
+ switch (event->type) {
+ case CreateNotify: {
+ int border_size = event->xcreatewindow.border_width * 2;
+ gfx::Rect bounds(event->xcreatewindow.x,
+ event->xcreatewindow.y,
+ event->xcreatewindow.width + border_size,
+ event->xcreatewindow.height + border_size);
+ wm->RegisterNewTopLevel(event->xcreatewindow.window, bounds,
+ !event->xcreatewindow.override_redirect);
+ xwindow = event->xcreatewindow.window;
+ } break;
+ case ReparentNotify:
+ if (event->xreparent.parent == wm->x_root_window_)
+ wm->RegisterNewTopLevel(event->xreparent.window);
+ xwindow = event->xreparent.window;
+ break;
+ case MapNotify:
+ xwindow = event->xmap.window;
+ break;
+ case UnmapNotify:
+ xwindow = event->xunmap.window;
+ break;
+ case DestroyNotify:
+ xwindow = event->xdestroywindow.window;
+ break;
+ case ConfigureNotify:
+ xwindow = event->xconfigure.window;
+ break;
+ default:
+ return false;
+ }
+
+ scoped_refptr<HostWindowX11> top_level = wm->top_level_windows_[xwindow];
+
+ DCHECK(top_level);
+
+ switch (event->type) {
+ case CreateNotify:
+ wm->HostWindowNeedsConfigure(client->GetRootWindow());
+ break;
+ case MapNotify:
+ top_level->OnMapNotify();
+ break;
+ case UnmapNotify:
+ top_level->OnUnmapNotify();
+ break;
+ case ReparentNotify:
+ if (event->xreparent.parent == wm->x_root_window_) {
+ wm->HostWindowNeedsConfigure(client->GetRootWindow());
+ break;
+ }
+
+ XUngrabButton(xdisplay_, AnyButton, AnyModifier, xwindow);
+
+ // TODO(danakj): grab keys on the window so we can use shortcuts
+ //XUngrabKey(xdisplay_, AnyKey, AnyModifier, xwindow);
+
+ // Fallthrough.
+ case DestroyNotify: {
+ if (wm->native_windows_.find(xwindow) != wm->native_windows_.end()) {
+ Window* window = wm->native_windows_[xwindow];
+
+ DCHECK(wm->input_host_windows_.find(window) ==
+ wm->input_host_windows_.end());
+
+ if (wm->host_windows_.find(window) != wm->host_windows_.end()) {
+ // Reset delegate before calling OnHostWindowDestroyed to make sure
+ // we're not preventing widget from being closed.
+ SetHostWindowDelegate(window, 0);
+ client->OnHostWindowDestroyed(window);
+ wm->host_windows_.erase(window);
+ }
+
+ wm->native_windows_.erase(xwindow);
+ }
+ wm->top_level_windows_.erase(xwindow);
+
+ // Window destruction could have caused a previous configure request to
+ // fail.
+ wm->HostWindowNeedsConfigure(client->GetRootWindow());
+ } break;
+ case ConfigureNotify: {
+ int bw = event->xconfigure.border_width * 2;
+ gfx::Rect bounds(event->xconfigure.x,
+ event->xconfigure.y,
+ event->xconfigure.width + bw,
+ event->xconfigure.height + bw);
+ top_level->OnConfigureNotify(bounds, event->xconfigure.above);
+ } break;
+ }
+
+ return true;
+}
+
+bool HostWindowManagerX11RootEventObserver::ProcessHostWindowRequest(
+ const base::NativeEvent& event) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+
+ ::Window xwindow = 0;
+ switch (event->type) {
+ case MapRequest:
+ xwindow = event->xmaprequest.window;
+ break;
+ case ConfigureRequest:
+ xwindow = event->xconfigurerequest.window;
+ break;
+ case CirculateRequest:
+ xwindow = event->xcirculaterequest.window;
+ break;
+ default:
+ return false;
+ }
+
+ scoped_refptr<HostWindowX11> top_level = wm->top_level_windows_[xwindow];
+
+ DCHECK(top_level);
+ DCHECK(top_level->CanConfigure());
+
+ if (wm->native_windows_.find(xwindow) == wm->native_windows_.end())
+ return true;
+
+ Window* window = wm->native_windows_[xwindow];
+
+ switch (event->type) {
+ case MapRequest:
+ client->OnHostWindowMovedOrResizedConstrained(
+ window, GetTargetBoundsInRootWindow(window));
+ client->OnHostWindowVisibilityChanged(window, true);
+ break;
+ case ConfigureRequest: {
+ gfx::Rect bounds(GetTargetBoundsInRootWindow(window));
+ if (event->xconfigurerequest.value_mask & CWX)
+ bounds.set_x(event->xconfigurerequest.x);
+ if (event->xconfigurerequest.value_mask & CWY)
+ bounds.set_y(event->xconfigurerequest.y);
+ if (event->xconfigurerequest.value_mask & CWWidth)
+ bounds.set_width(event->xconfigurerequest.width);
+ if (event->xconfigurerequest.value_mask & CWHeight)
+ bounds.set_height(event->xconfigurerequest.height);
+ client->OnHostWindowMovedOrResizedConstrained(window, bounds);
+ // TODO(reveman): Respect stacking properties.
+ } break;
+ case CirculateRequest:
+ // TODO(reveman): Respect stacking properties.
+ break;
+ }
+
+ return true;
+}
+
+bool HostWindowManagerX11RootEventObserver::Dispatch(
+ const base::NativeEvent& event) {
+ if (ProcessHostWindowRequest(event))
+ return true;
+ if (ProcessHostWindowUpdate(event))
+ return true;
+
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+ // Let root window host handle all input events.
+ return client->GetRootWindow()->DispatchNativeEvent(event);
+}
+
+void HostWindowManagerX11RootEventObserver::OnWindowFocused(Window* window) {
+ HostWindowManagerX11* wm = host_window_manager_;
+ client::HostWindowManagerClient* client = wm->client_;
+ // We need to configure all windows when changing focus.
+ wm->HostWindowNeedsConfigure(client->GetRootWindow());
+}
+
+void HostWindowManagerX11RootEventObserver::OnCursorChanged(
+ const RootWindow* root, ui::PlatformCursor cursor) {
+ HostWindowManagerX11* wm = host_window_manager_;
+
+ // Set cursor for all input windows.
+ std::map<gfx::NativeWindow, scoped_refptr<InputHostWindowX11> >::iterator it;
+ for (it = wm->input_host_windows_.begin();
+ it != wm->input_host_windows_.end(); it++) {
+ scoped_refptr<HostWindowX11> input_window = (*it).second;
+ XDefineCursor(xdisplay_, input_window->xid(), cursor);
+ }
+}
+
+HostWindowManagerX11::HostWindowManagerX11(
+ client::HostWindowManagerClient* client)
+ : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
+ x_root_window_(DefaultRootWindow(xdisplay_)),
+ configure_window_(0),
+ damage_event_base_(0),
+ window_observer_(new HostWindowManagerX11WindowObserver(this)),
+ event_observer_(new HostWindowManagerX11RootEventObserver(this)),
+ atom_cache_(xdisplay_, kAtomsToCache),
+ client_(client) {
+ // TODO(reveman): Check that damage extension is present.
+ int error_base_ignored;
+ XDamageQueryExtension(xdisplay_, &damage_event_base_, &error_base_ignored);
+}
+
+HostWindowManagerX11::~HostWindowManagerX11() {
+ XCompositeUnredirectSubwindows(xdisplay_, x_root_window_,
+ CompositeRedirectManual);
+
+ std::map< ::Window, gfx::NativeWindow>::iterator it;
+ for (it = native_windows_.begin(); it != native_windows_.end(); it++) {
+ if ((*it).second) {
+ Window* window = (*it).second;
+ SetHostWindowDelegate(window, 0);
+ client_->OnHostWindowDestroyed(window);
+ window->RemoveObserver(window_observer_.get());
+ }
+ }
+
+ top_level_windows_.clear();
+ native_windows_.clear();
+ host_windows_.clear();
+ input_host_windows_.clear();
+
+ Env::GetInstance()->RemoveObserver(window_observer_.get());
+ static_cast<DispatcherLinux*>(
+ Env::GetInstance()->GetDispatcher())->
+ RemoveDispatcherForRootWindow(event_observer_.get());
+ if (client_->GetRootWindow()) {
+ client_->GetRootWindow()->RemoveRootWindowObserver(event_observer_.get());
+ client_->GetRootWindow()->GetFocusManager()->RemoveObserver(
+ event_observer_.get());
+ }
+}
+
+::Window HostWindowManagerX11::GetTopHostWindow(Window* window) {
+ // children is ordered back to front, so walk through it in reverse.
+ const Window::Windows& children = window->children();
+ for (size_t i = children.size(); i; --i) {
+ ::Window top = GetTopHostWindow(children[i - 1]);
+ if (top)
+ return top;
+ }
+
+ if (host_windows_.find(window) != host_windows_.end()) {
+ ::Window host_window = host_windows_[window];
+ scoped_refptr<HostWindowX11> top_level = top_level_windows_[host_window];
+
+ DCHECK(top_level);
+ // Ignore windows that we're not allowed to configure.
+ if (top_level->CanConfigure())
+ return host_window;
+ }
+
+ if (input_host_windows_.find(window) != input_host_windows_.end())
+ return input_host_windows_[window]->xid();
+
+ return 0;
+}
+
+::Window HostWindowManagerX11::FindHostWindowToStackAbove(Window* window) {
+ Window* parent = window->parent();
+ if (!parent)
+ return 0;
+
+ ::Window above = 0;
+
+ const Window::Windows& children = parent->children();
+ for (size_t i = 0; i < children.size(); ++i) {
+ if (children[i] == window)
+ break;
+
+ ::Window top = GetTopHostWindow(children[i]);
+ if (top)
+ above = top;
+ }
+
+ if (!above)
+ above = FindHostWindowToStackAbove(parent);
+
+ return above;
+}
+
+void HostWindowManagerX11::CreateHostInputWindowIfNeeded(Window* window) {
+ // Ignore root window.
+ if (window == window->GetRootWindow())
+ return;
+
+ // Don't create host input window when there's already host window.
+ if (host_windows_.find(window) != host_windows_.end())
+ return;
+
+ // Avoid windows without delegates.
+ if (!window->delegate())
+ return;
+
+ // Delay creation until window is visible.
+ if (!window->IsVisible())
+ return;
+
+ // Host input window already exists.
+ if (input_host_windows_.find(window) != input_host_windows_.end())
+ return;
+
+ scoped_refptr<InputHostWindowX11> delegate = new InputHostWindowX11(
+ XCreateWindow(xdisplay_, x_root_window_, -100, -100, 1, 1,
+ 0, CopyFromParent, InputOnly, CopyFromParent, 0, 0));
+ SetHostWindowDelegate(window, delegate);
+ native_windows_[delegate->xid()] = window;
+ input_host_windows_[window] = delegate;
+}
+
+void HostWindowManagerX11::RecursiveConfigureHostWindow(
+ Window* window,
+ gfx::Point origin,
+ ::Window& sibling_to_stack_above,
+ ::Window& focus_window,
+ bool has_focus) {
+ RootWindow* root_window = window->GetRootWindow();
+
+ // Lazy creation of input only windows.
+ CreateHostInputWindowIfNeeded(window);
+
+ // Special case to ensure that root window is stacked properly.
+ if (window == root_window) {
+ XWindowChanges wc;
+ unsigned mask = InitWindowChanges(gfx::Rect(), sibling_to_stack_above, wc);
+ ::Window root_host_window =
+ root_window->GetAcceleratedWidgetUsedForEvents();
+ DCHECK(window->IsVisible());
+ XConfigureWindow(xdisplay_, root_host_window,
+ mask & (CWSibling | CWStackMode),
+ &wc);
+
+ sibling_to_stack_above = root_host_window;
+ }
+
+ // We never move focus to our internal input host windows.
+ if (input_host_windows_.find(window) != input_host_windows_.end()) {
+ scoped_refptr<HostWindowX11> input_window = input_host_windows_[window];
+ gfx::Rect host_bounds(origin, window->GetTargetBounds().size());
+ host_bounds.Inset(window->hit_test_bounds_override_outer());
+ gfx::Rect non_empty_bounds(
+ host_bounds.Union(gfx::Rect(host_bounds.origin(), gfx::Size(1, 1))));
+
+ if (!window->IsVisible()) {
+ if (input_window->state() == NormalState) {
+ XUnmapWindow(xdisplay_, input_window->xid());
+ input_window->set_state(IconicState);
+ }
+ }
+
+ XWindowChanges wc;
+ unsigned mask = InitWindowChanges(
+ non_empty_bounds, sibling_to_stack_above, wc);
+ // Ignore possible X error as we can't guarantee that host window exists.
+ if (mask & CWSibling)
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XConfigureWindow(xdisplay_, input_window->xid(), mask, &wc);
+
+ sibling_to_stack_above = input_window->xid();
+
+ if (window->IsVisible()) {
+ if (input_window->state() != NormalState) {
+ XMapWindow(xdisplay_, input_window->xid());
+ input_window->set_state(NormalState);
+ }
+ }
+ }
+
+ if (host_windows_.find(window) != host_windows_.end()) {
+ ::Window host_window = host_windows_[window];
+ scoped_refptr<HostWindowX11> top_level = top_level_windows_[host_window];
+
+ DCHECK(top_level);
+ if (top_level->CanConfigure()) {
+ gfx::Rect host_bounds(origin, window->GetTargetBounds().size());
+ gfx::Rect non_empty_bounds(
+ host_bounds.Union(gfx::Rect(host_bounds.origin(), gfx::Size(1, 1))));
+
+ if (!window->IsVisible()) {
+ if (top_level->state() == NormalState) {
+ // Ignore possible X error as we can't guarantee that host window
+ // exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XUnmapWindow(xdisplay_, host_window);
+
+ top_level->set_state(IconicState);
+ }
+ }
+
+ XWindowChanges wc;
+ unsigned mask = InitWindowChanges(
+ non_empty_bounds, sibling_to_stack_above, wc);
+ // Get rid of any borders.
+ wc.border_width = 0;
+ mask |= CWBorderWidth;
+ // Ignore possible X error as we can't guarantee that host window exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XConfigureWindow(xdisplay_, host_window, mask, &wc);
+
+ sibling_to_stack_above = host_window;
+
+ if (window->IsVisible()) {
+ if (top_level->state() != NormalState) {
+ // Ignore possible X error as we can't guarantee that host window
+ // exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XMapWindow(xdisplay_, host_window);
+
+ // Grab buttons so that window can be activated when a click is
+ // received in the client area.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XGrabButton(xdisplay_, AnyButton, AnyModifier, host_window, false,
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+ GrabModeSync, GrabModeAsync, 0, 0);
+
+ // TODO(danakj): grab keys on the window so we can use shortcuts
+ //XGrabKey(xdisplay_, AnyKey, AnyModifier, event->xmaprequest.window,
+ // true, GrabModeAsync, GrabModeSync);
+
+ top_level->set_state(NormalState);
+ }
+
+ // Set |focus_window| to host window if aura sub-tree has focus.
+ if (has_focus)
+ focus_window = host_window;
+ }
+ }
+ }
+
+ const Window::Windows& children = window->children();
+ for (size_t i = 0; i < children.size(); ++i)
+ RecursiveConfigureHostWindow(
+ children[i],
+ origin.Add(children[i]->GetTargetBounds().origin()),
+ sibling_to_stack_above,
+ focus_window,
+ has_focus || window->HasFocus());
+}
+
+void HostWindowManagerX11::ConfigureHostWindows() {
+ if (!configure_window_)
+ return;
+
+ ::Window focus_window = 0;
+ ::Window sibling_to_stack_above =
+ FindHostWindowToStackAbove(configure_window_);
+ RecursiveConfigureHostWindow(
+ configure_window_,
+ GetTargetOriginInRootWindow(configure_window_),
+ sibling_to_stack_above,
+ focus_window,
+ configure_window_->HasFocus());
+
+ // Only set focus when we configure the root window.
+ if (configure_window_ == client_->GetRootWindow()) {
+ if (focus_window) {
+ // Ignore possible X error as we can't guarantee that host window exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ XSetInputFocus(xdisplay_, focus_window, 0, CurrentTime);
+ } else
+ XSetInputFocus(
+ xdisplay_,
+ client_->GetRootWindow()->GetAcceleratedWidgetUsedForEvents(),
+ 0, CurrentTime);
+ }
+
+ configure_window_ = 0;
+}
+
+void HostWindowManagerX11::HostWindowNeedsConfigure(Window* window) {
+ DCHECK(window->GetRootWindow() == client_->GetRootWindow());
+ if (!configure_window_) {
+ configure_window_ = window;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&HostWindowManagerX11::ConfigureHostWindows, AsWeakPtr()));
+ } else {
+ configure_window_ = FindLowestCommonAncestor(
+ window->GetRootWindow(), configure_window_, window);
+ }
+}
+
+void HostWindowManagerX11::Init() {
+ Env::GetInstance()->AddObserver(window_observer_.get());
+ static_cast<DispatcherLinux*>(
+ Env::GetInstance()->GetDispatcher())->
+ AddDispatcherForRootWindow(event_observer_.get());
+
+ // TODO(danakj): When env manages root windows, get it from there, and watch
+ // for monitor changes.
+ client_->GetRootWindow()->AddRootWindowObserver(event_observer_.get());
+ client_->GetRootWindow()->GetFocusManager()->AddObserver(
+ event_observer_.get());
+
+ if (client_->GetRootWindow()->GetAcceleratedWidget() ==
+ client_->GetRootWindow()->GetAcceleratedWidgetUsedForEvents())
+ LOG(FATAL) << "Overlay window must be used as output. Try using "
+ "--aura-host-window-use-fullscreen.";
+
+ int root_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
+ EnterWindowMask | LeaveWindowMask | SubstructureRedirectMask |
+ SubstructureNotifyMask;
+
+ XGrabServer(xdisplay_);
+
+ ::Window root, parent, *children = 0;
+ unsigned int nchildren;
+ XQueryTree(xdisplay_, x_root_window_, &root, &parent, &children, &nchildren);
+ scoped_ptr_malloc< ::Window, ScopedPtrXFree> xwindows(children);
+
+ XErrorHandler error_handler = XSetErrorHandler(ExistingWMX11ErrorHandler);
+ XWindowAttributes attr;
+ XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
+ XSelectInput(xdisplay_, x_root_window_, attr.your_event_mask | root_mask);
+ // Redirect top-level windows to offscreen buffers.
+ XCompositeRedirectSubwindows(xdisplay_, x_root_window_,
+ CompositeRedirectManual);
+ XSync(xdisplay_, 0);
+ XSetErrorHandler(error_handler);
+
+ // Install filtered error handler.
+ SetBaseX11ErrorHandler(XSetErrorHandler(FilteredX11ErrorHandler));
+
+ XUngrabServer(xdisplay_);
+
+ // Set root window cursor. Host windows that don't define their own cursor
+ // will inherit the default cursor defined here.
+ XDefineCursor(xdisplay_, x_root_window_, ui::GetXCursor(XC_left_ptr));
+
+ for (size_t i = 0; i < nchildren; ++i)
+ RegisterNewTopLevel(xwindows.get()[i]);
+
+ HostWindowNeedsConfigure(client_->GetRootWindow());
+}
+
+void HostWindowManagerX11::RegisterNewTopLevel(::Window xwindow) {
+ gfx::Rect bounds(gfx::Point(-100, -100), gfx::Size(1, 1));
+ bool visible = false;
+ bool managed = false;
+
+ XWindowAttributes attributes;
+ // Ignore possible X error as we can't guarantee that host window exists.
+ xerror_ignore_queue.push(NextRequest(xdisplay_));
+ if (XGetWindowAttributes(xdisplay_, xwindow, &attributes)) {
+ int border_size = attributes.border_width * 2;
+ bounds = gfx::Rect(attributes.x,
+ attributes.y,
+ attributes.width + border_size,
+ attributes.height + border_size);
+ visible = attributes.map_state == IsViewable;
+ managed = !attributes.override_redirect;
+ }
+
+ RegisterNewTopLevel(xwindow, bounds, managed);
+
+ if (visible) {
+ Window* window = native_windows_[xwindow];
+ DCHECK(window);
+ client_->OnHostWindowMovedOrResizedConstrained(
+ window, GetTargetBoundsInRootWindow(window));
+ client_->OnHostWindowVisibilityChanged(window, true);
+ }
+}
+
+void HostWindowManagerX11::RegisterNewTopLevel(
+ ::Window xwindow, const gfx::Rect bounds, bool managed) {
+ client::HostWindowManagerClient* client = client_;
+
+ // Add a HostWindowX11 for the root input window.
+ RootWindow* root_window = client->GetRootWindow();
+ if (xwindow == root_window->GetAcceleratedWidgetUsedForEvents()) {
+ top_level_windows_[xwindow] = new HostWindowX11(xwindow, NormalState);
+ return;
+ }
+
+ // Container input windows will already have a native mapping.
+ if (native_windows_.find(xwindow) != native_windows_.end()) {
+ Window* window = native_windows_[xwindow];
+
+ if (input_host_windows_.find(window) != input_host_windows_.end()) {
+ top_level_windows_[xwindow] = input_host_windows_[window];
+ return;
+ }
+ }
+
+ // This is foreign host window.
+ scoped_refptr<HostWindowX11> top_level;
+ bool decorated = false;
+
+ if (managed) {
+ top_level = new ManagedRedirectedHostWindowX11(
+ this, xwindow, bounds.size());
+ decorated = true;
+ } else {
+ top_level = new RedirectedHostWindowX11(this, xwindow, bounds.size());
+ }
+
+ // Create a native window.
+ Window* window = client->OnHostWindowCreated(bounds, decorated);
+ SetHostWindowDelegate(window, top_level);
+ top_level_windows_[xwindow] = top_level;
+
+ native_windows_[xwindow] = window;
+ host_windows_[window] = xwindow;
+}
+
+} // namespace aura

Powered by Google App Engine
This is Rietveld 408576698