1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
7 #include <X11/extensions/XInput.h>
8 #include <X11/extensions/XInput2.h>
12 #include "ui/aura/client/aura_constants.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_delegate.h"
15 #include "ui/aura/window_tree_host.h"
16 #include "ui/base/hit_test.h"
17 #include "ui/events/event.h"
18 #include "ui/events/event_utils.h"
19 #include "ui/gfx/x/x11_types.h"
20 #include "ui/views/linux_ui/linux_ui.h"
21 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
22 #include "ui/views/widget/native_widget_aura.h"
26 // These constants are defined in the Extended Window Manager Hints
27 // standard...and aren't in any header that I can find.
28 const int k_NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
29 const int k_NET_WM_MOVERESIZE_SIZE_TOP = 1;
30 const int k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
31 const int k_NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
32 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
33 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
34 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
35 const int k_NET_WM_MOVERESIZE_SIZE_LEFT = 7;
36 const int k_NET_WM_MOVERESIZE_MOVE = 8;
38 // This data structure represents additional hints that we send to the window
39 // manager and has a direct lineage back to Motif, which defined this de facto
40 // standard. This struct doesn't seem 64-bit safe though, but it's what GDK
44 unsigned long functions;
45 unsigned long decorations;
50 // The bitflag in |flags| in MotifWmHints that signals that the reader should
51 // pay attention to the value in |decorations|.
52 const unsigned long kHintsDecorations = (1L << 1);
54 const char* kAtomsToCache[] = {
64 X11WindowEventFilter::X11WindowEventFilter(
65 DesktopWindowTreeHost* window_tree_host)
66 : xdisplay_(gfx::GetXDisplay()),
67 xwindow_(window_tree_host->AsWindowTreeHost()->GetAcceleratedWidget()),
68 x_root_window_(DefaultRootWindow(xdisplay_)),
69 atom_cache_(xdisplay_, kAtomsToCache),
70 window_tree_host_(window_tree_host),
72 click_component_(HTNOWHERE) {
75 X11WindowEventFilter::~X11WindowEventFilter() {
78 void X11WindowEventFilter::SetUseHostWindowBorders(bool use_os_border) {
79 MotifWmHints motif_hints;
80 memset(&motif_hints, 0, sizeof(motif_hints));
81 motif_hints.flags = kHintsDecorations;
82 motif_hints.decorations = use_os_border ? 1 : 0;
84 ::Atom hint_atom = atom_cache_.GetAtom("_MOTIF_WM_HINTS");
85 XChangeProperty(gfx::GetXDisplay(),
91 reinterpret_cast<unsigned char*>(&motif_hints),
92 sizeof(MotifWmHints)/sizeof(long));
95 void X11WindowEventFilter::OnMouseEvent(ui::MouseEvent* event) {
96 if (event->type() != ui::ET_MOUSE_PRESSED)
99 if (!(event->IsLeftMouseButton() || event->IsMiddleMouseButton()))
102 aura::Window* target = static_cast<aura::Window*>(event->target());
103 if (!target->delegate())
106 int previous_click_component = HTNOWHERE;
108 target->delegate()->GetNonClientComponent(event->location());
109 if (event->IsLeftMouseButton()) {
110 previous_click_component = click_component_;
111 click_component_ = component;
113 if (component == HTCLIENT)
116 if (event->IsMiddleMouseButton() && (component == HTCAPTION)) {
117 LinuxUI::NonClientMiddleClickAction action =
118 LinuxUI::MIDDLE_CLICK_ACTION_LOWER;
119 LinuxUI* linux_ui = LinuxUI::instance();
121 action = linux_ui->GetNonClientMiddleClickAction();
124 case LinuxUI::MIDDLE_CLICK_ACTION_NONE:
126 case LinuxUI::MIDDLE_CLICK_ACTION_LOWER:
127 XLowerWindow(xdisplay_, xwindow_);
129 case LinuxUI::MIDDLE_CLICK_ACTION_MINIMIZE:
130 window_tree_host_->Minimize();
132 case LinuxUI::MIDDLE_CLICK_ACTION_TOGGLE_MAXIMIZE:
133 if (target->GetProperty(aura::client::kCanMaximizeKey))
134 ToggleMaximizedState();
143 if (event->flags() & ui::EF_IS_DOUBLE_CLICK) {
144 click_component_ = HTNOWHERE;
145 if (component == HTCAPTION &&
146 target->GetProperty(aura::client::kCanMaximizeKey) &&
147 previous_click_component == component) {
148 // Our event is a double click in the caption area in a window that can be
149 // maximized. We are responsible for dispatching this as a minimize/
150 // maximize on X11 (Windows converts this to min/max events for us).
151 ToggleMaximizedState();
157 // Get the |x_root_window_| location out of the native event.
158 if (event->native_event()) {
159 const gfx::Point x_root_location =
160 ui::EventSystemLocationFromNative(event->native_event());
161 if ((component == HTCAPTION ||
162 target->GetProperty(aura::client::kCanResizeKey)) &&
163 DispatchHostWindowDragMovement(component, x_root_location)) {
164 event->StopPropagation();
169 void X11WindowEventFilter::ToggleMaximizedState() {
170 if (window_tree_host_->IsMaximized())
171 window_tree_host_->Restore();
173 window_tree_host_->Maximize();
176 bool X11WindowEventFilter::DispatchHostWindowDragMovement(
178 const gfx::Point& screen_location) {
182 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOM;
185 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
188 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
191 direction = k_NET_WM_MOVERESIZE_MOVE;
194 direction = k_NET_WM_MOVERESIZE_SIZE_LEFT;
197 direction = k_NET_WM_MOVERESIZE_SIZE_RIGHT;
200 direction = k_NET_WM_MOVERESIZE_SIZE_TOP;
203 direction = k_NET_WM_MOVERESIZE_SIZE_TOPLEFT;
206 direction = k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
212 // We most likely have an implicit grab right here. We need to dump it
213 // because what we're about to do is tell the window manager
214 // that it's now responsible for moving the window around; it immediately
215 // grabs when it receives the event below.
216 XUngrabPointer(xdisplay_, CurrentTime);
219 memset(&event, 0, sizeof(event));
220 event.xclient.type = ClientMessage;
221 event.xclient.display = xdisplay_;
222 event.xclient.window = xwindow_;
223 event.xclient.message_type = atom_cache_.GetAtom("_NET_WM_MOVERESIZE");
224 event.xclient.format = 32;
225 event.xclient.data.l[0] = screen_location.x();
226 event.xclient.data.l[1] = screen_location.y();
227 event.xclient.data.l[2] = direction;
228 event.xclient.data.l[3] = 0;
229 event.xclient.data.l[4] = 0;
231 XSendEvent(xdisplay_, x_root_window_, False,
232 SubstructureRedirectMask | SubstructureNotifyMask,