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/display.h" 20 #include "ui/gfx/screen.h" 21 #include "ui/gfx/x/x11_types.h" 22 #include "ui/views/linux_ui/linux_ui.h" 23 #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" 24 #include "ui/views/widget/native_widget_aura.h" 25 #include "ui/views/widget/widget.h" 26 27 namespace { 28 29 // These constants are defined in the Extended Window Manager Hints 30 // standard...and aren't in any header that I can find. 31 const int k_NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; 32 const int k_NET_WM_MOVERESIZE_SIZE_TOP = 1; 33 const int k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; 34 const int k_NET_WM_MOVERESIZE_SIZE_RIGHT = 3; 35 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; 36 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; 37 const int k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; 38 const int k_NET_WM_MOVERESIZE_SIZE_LEFT = 7; 39 const int k_NET_WM_MOVERESIZE_MOVE = 8; 40 41 const char* kAtomsToCache[] = { 42 "_NET_WM_MOVERESIZE", 43 NULL 44 }; 45 46 } // namespace 47 48 namespace views { 49 50 X11WindowEventFilter::X11WindowEventFilter( 51 DesktopWindowTreeHost* window_tree_host) 52 : xdisplay_(gfx::GetXDisplay()), 53 xwindow_(window_tree_host->AsWindowTreeHost()->GetAcceleratedWidget()), 54 x_root_window_(DefaultRootWindow(xdisplay_)), 55 atom_cache_(xdisplay_, kAtomsToCache), 56 window_tree_host_(window_tree_host), 57 is_active_(false), 58 click_component_(HTNOWHERE) { 59 } 60 61 X11WindowEventFilter::~X11WindowEventFilter() { 62 } 63 64 void X11WindowEventFilter::OnMouseEvent(ui::MouseEvent* event) { 65 if (event->type() != ui::ET_MOUSE_PRESSED) 66 return; 67 68 aura::Window* target = static_cast<aura::Window*>(event->target()); 69 if (!target->delegate()) 70 return; 71 72 int previous_click_component = HTNOWHERE; 73 int component = 74 target->delegate()->GetNonClientComponent(event->location()); 75 if (event->IsLeftMouseButton()) { 76 previous_click_component = click_component_; 77 click_component_ = component; 78 } 79 80 if (component == HTCAPTION) { 81 OnClickedCaption(event, previous_click_component); 82 } else if (component == HTMAXBUTTON) { 83 OnClickedMaximizeButton(event); 84 } else { 85 // Get the |x_root_window_| location out of the native event. 86 if (event->IsLeftMouseButton() && event->native_event()) { 87 const gfx::Point x_root_location = 88 ui::EventSystemLocationFromNative(event->native_event()); 89 if (target->GetProperty(aura::client::kCanResizeKey) && 90 DispatchHostWindowDragMovement(component, x_root_location)) { 91 event->StopPropagation(); 92 } 93 } 94 } 95 } 96 97 void X11WindowEventFilter::OnClickedCaption(ui::MouseEvent* event, 98 int previous_click_component) { 99 aura::Window* target = static_cast<aura::Window*>(event->target()); 100 101 if (event->IsMiddleMouseButton()) { 102 LinuxUI::NonClientMiddleClickAction action = 103 LinuxUI::MIDDLE_CLICK_ACTION_LOWER; 104 LinuxUI* linux_ui = LinuxUI::instance(); 105 if (linux_ui) 106 action = linux_ui->GetNonClientMiddleClickAction(); 107 108 switch (action) { 109 case LinuxUI::MIDDLE_CLICK_ACTION_NONE: 110 break; 111 case LinuxUI::MIDDLE_CLICK_ACTION_LOWER: 112 XLowerWindow(xdisplay_, xwindow_); 113 break; 114 case LinuxUI::MIDDLE_CLICK_ACTION_MINIMIZE: 115 window_tree_host_->Minimize(); 116 break; 117 case LinuxUI::MIDDLE_CLICK_ACTION_TOGGLE_MAXIMIZE: 118 if (target->GetProperty(aura::client::kCanMaximizeKey)) 119 ToggleMaximizedState(); 120 break; 121 } 122 123 event->SetHandled(); 124 return; 125 } 126 127 if (event->IsLeftMouseButton() && event->flags() & ui::EF_IS_DOUBLE_CLICK) { 128 click_component_ = HTNOWHERE; 129 if (target->GetProperty(aura::client::kCanMaximizeKey) && 130 previous_click_component == HTCAPTION) { 131 // Our event is a double click in the caption area in a window that can be 132 // maximized. We are responsible for dispatching this as a minimize/ 133 // maximize on X11 (Windows converts this to min/max events for us). 134 ToggleMaximizedState(); 135 event->SetHandled(); 136 return; 137 } 138 } 139 140 // Get the |x_root_window_| location out of the native event. 141 if (event->IsLeftMouseButton() && event->native_event()) { 142 const gfx::Point x_root_location = 143 ui::EventSystemLocationFromNative(event->native_event()); 144 if (DispatchHostWindowDragMovement(HTCAPTION, x_root_location)) 145 event->StopPropagation(); 146 } 147 } 148 149 void X11WindowEventFilter::OnClickedMaximizeButton(ui::MouseEvent* event) { 150 aura::Window* target = static_cast<aura::Window*>(event->target()); 151 views::Widget* widget = views::Widget::GetWidgetForNativeView(target); 152 if (!widget) 153 return; 154 155 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); 156 gfx::Rect display_work_area = 157 screen->GetDisplayNearestWindow(target).work_area(); 158 gfx::Rect bounds = widget->GetWindowBoundsInScreen(); 159 if (event->IsMiddleMouseButton()) { 160 bounds.set_y(display_work_area.y()); 161 bounds.set_height(display_work_area.height()); 162 widget->SetBounds(bounds); 163 event->StopPropagation(); 164 } else if (event->IsRightMouseButton()) { 165 bounds.set_x(display_work_area.x()); 166 bounds.set_width(display_work_area.width()); 167 widget->SetBounds(bounds); 168 event->StopPropagation(); 169 } 170 } 171 172 void X11WindowEventFilter::ToggleMaximizedState() { 173 if (window_tree_host_->IsMaximized()) 174 window_tree_host_->Restore(); 175 else 176 window_tree_host_->Maximize(); 177 } 178 179 bool X11WindowEventFilter::DispatchHostWindowDragMovement( 180 int hittest, 181 const gfx::Point& screen_location) { 182 int direction = -1; 183 switch (hittest) { 184 case HTBOTTOM: 185 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOM; 186 break; 187 case HTBOTTOMLEFT: 188 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; 189 break; 190 case HTBOTTOMRIGHT: 191 direction = k_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; 192 break; 193 case HTCAPTION: 194 direction = k_NET_WM_MOVERESIZE_MOVE; 195 break; 196 case HTLEFT: 197 direction = k_NET_WM_MOVERESIZE_SIZE_LEFT; 198 break; 199 case HTRIGHT: 200 direction = k_NET_WM_MOVERESIZE_SIZE_RIGHT; 201 break; 202 case HTTOP: 203 direction = k_NET_WM_MOVERESIZE_SIZE_TOP; 204 break; 205 case HTTOPLEFT: 206 direction = k_NET_WM_MOVERESIZE_SIZE_TOPLEFT; 207 break; 208 case HTTOPRIGHT: 209 direction = k_NET_WM_MOVERESIZE_SIZE_TOPRIGHT; 210 break; 211 default: 212 return false; 213 } 214 215 // We most likely have an implicit grab right here. We need to dump it 216 // because what we're about to do is tell the window manager 217 // that it's now responsible for moving the window around; it immediately 218 // grabs when it receives the event below. 219 XUngrabPointer(xdisplay_, CurrentTime); 220 221 XEvent event; 222 memset(&event, 0, sizeof(event)); 223 event.xclient.type = ClientMessage; 224 event.xclient.display = xdisplay_; 225 event.xclient.window = xwindow_; 226 event.xclient.message_type = atom_cache_.GetAtom("_NET_WM_MOVERESIZE"); 227 event.xclient.format = 32; 228 event.xclient.data.l[0] = screen_location.x(); 229 event.xclient.data.l[1] = screen_location.y(); 230 event.xclient.data.l[2] = direction; 231 event.xclient.data.l[3] = 0; 232 event.xclient.data.l[4] = 0; 233 234 XSendEvent(xdisplay_, x_root_window_, False, 235 SubstructureRedirectMask | SubstructureNotifyMask, 236 &event); 237 238 return true; 239 } 240 241 } // namespace views 242