Home | History | Annotate | Download | only in desktop_aura
      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