Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / x11_window_event_filter.cc
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.
4
5 #include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
6
7 #include <X11/extensions/XInput.h>
8 #include <X11/extensions/XInput2.h>
9 #include <X11/Xatom.h>
10 #include <X11/Xlib.h>
11
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"
23
24 namespace {
25
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;
37
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
41 // does.
42 typedef struct {
43   unsigned long flags;
44   unsigned long functions;
45   unsigned long decorations;
46   long input_mode;
47   unsigned long status;
48 } MotifWmHints;
49
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);
53
54 const char* kAtomsToCache[] = {
55   "_MOTIF_WM_HINTS",
56   "_NET_WM_MOVERESIZE",
57   NULL
58 };
59
60 }  // namespace
61
62 namespace views {
63
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),
71       is_active_(false),
72       click_component_(HTNOWHERE) {
73 }
74
75 X11WindowEventFilter::~X11WindowEventFilter() {
76 }
77
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;
83
84   ::Atom hint_atom = atom_cache_.GetAtom("_MOTIF_WM_HINTS");
85   XChangeProperty(gfx::GetXDisplay(),
86                   xwindow_,
87                   hint_atom,
88                   hint_atom,
89                   32,
90                   PropModeReplace,
91                   reinterpret_cast<unsigned char*>(&motif_hints),
92                   sizeof(MotifWmHints)/sizeof(long));
93 }
94
95 void X11WindowEventFilter::OnMouseEvent(ui::MouseEvent* event) {
96   if (event->type() != ui::ET_MOUSE_PRESSED)
97     return;
98
99   if (!(event->IsLeftMouseButton() || event->IsMiddleMouseButton()))
100     return;
101
102   aura::Window* target = static_cast<aura::Window*>(event->target());
103   if (!target->delegate())
104     return;
105
106   int previous_click_component = HTNOWHERE;
107   int component =
108       target->delegate()->GetNonClientComponent(event->location());
109   if (event->IsLeftMouseButton()) {
110     previous_click_component = click_component_;
111     click_component_ = component;
112   }
113   if (component == HTCLIENT)
114     return;
115
116   if (event->IsMiddleMouseButton() && (component == HTCAPTION)) {
117     LinuxUI::NonClientMiddleClickAction action =
118         LinuxUI::MIDDLE_CLICK_ACTION_LOWER;
119     LinuxUI* linux_ui = LinuxUI::instance();
120     if (linux_ui)
121       action = linux_ui->GetNonClientMiddleClickAction();
122
123     switch (action) {
124       case LinuxUI::MIDDLE_CLICK_ACTION_NONE:
125         break;
126       case LinuxUI::MIDDLE_CLICK_ACTION_LOWER:
127         XLowerWindow(xdisplay_, xwindow_);
128         break;
129       case LinuxUI::MIDDLE_CLICK_ACTION_MINIMIZE:
130         window_tree_host_->Minimize();
131         break;
132       case LinuxUI::MIDDLE_CLICK_ACTION_TOGGLE_MAXIMIZE:
133         if (target->GetProperty(aura::client::kCanMaximizeKey))
134           ToggleMaximizedState();
135         break;
136     }
137
138     event->SetHandled();
139     return;
140   }
141
142   // Left button case.
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();
152       event->SetHandled();
153       return;
154     }
155   }
156
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();
165     }
166   }
167 }
168
169 void X11WindowEventFilter::ToggleMaximizedState() {
170   if (window_tree_host_->IsMaximized())
171     window_tree_host_->Restore();
172   else
173     window_tree_host_->Maximize();
174 }
175
176 bool X11WindowEventFilter::DispatchHostWindowDragMovement(
177     int hittest,
178     const gfx::Point& screen_location) {
179   int direction = -1;
180   switch (hittest) {
181     case HTBOTTOM:
182       direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOM;
183       break;
184     case HTBOTTOMLEFT:
185       direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
186       break;
187     case HTBOTTOMRIGHT:
188       direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
189       break;
190     case HTCAPTION:
191       direction = k_NET_WM_MOVERESIZE_MOVE;
192       break;
193     case HTLEFT:
194       direction = k_NET_WM_MOVERESIZE_SIZE_LEFT;
195       break;
196     case HTRIGHT:
197       direction = k_NET_WM_MOVERESIZE_SIZE_RIGHT;
198       break;
199     case HTTOP:
200       direction = k_NET_WM_MOVERESIZE_SIZE_TOP;
201       break;
202     case HTTOPLEFT:
203       direction = k_NET_WM_MOVERESIZE_SIZE_TOPLEFT;
204       break;
205     case HTTOPRIGHT:
206       direction = k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
207       break;
208     default:
209       return false;
210   }
211
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);
217
218   XEvent event;
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;
230
231   XSendEvent(xdisplay_, x_root_window_, False,
232              SubstructureRedirectMask | SubstructureNotifyMask,
233              &event);
234
235   return true;
236 }
237
238 }  // namespace views