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 |