Home | History | Annotate | Download | only in x11
      1 // Copyright 2014 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/platform_window/x11/x11_window.h"
      6 
      7 #include <X11/extensions/XInput2.h>
      8 #include <X11/Xatom.h>
      9 #include <X11/Xlib.h>
     10 #include <X11/Xutil.h>
     11 
     12 #include "ui/events/event.h"
     13 #include "ui/events/event_utils.h"
     14 #include "ui/events/platform/platform_event_dispatcher.h"
     15 #include "ui/events/platform/platform_event_source.h"
     16 #include "ui/events/platform/x11/x11_event_source.h"
     17 #include "ui/events/x/touch_factory_x11.h"
     18 #include "ui/gfx/geometry/rect.h"
     19 #include "ui/gfx/x/x11_atom_cache.h"
     20 #include "ui/gfx/x/x11_types.h"
     21 #include "ui/platform_window/platform_window_delegate.h"
     22 
     23 namespace ui {
     24 
     25 namespace {
     26 
     27 const char* kAtomsToCache[] = {
     28   "WM_DELETE_WINDOW",
     29   "_NET_WM_PING",
     30   "_NET_WM_PID",
     31   NULL
     32 };
     33 
     34 XID FindXEventTarget(XEvent* xevent) {
     35   XID target = xevent->xany.window;
     36   if (xevent->type == GenericEvent)
     37     target = static_cast<XIDeviceEvent*>(xevent->xcookie.data)->event;
     38   return target;
     39 }
     40 
     41 }  // namespace
     42 
     43 X11Window::X11Window(PlatformWindowDelegate* delegate)
     44     : delegate_(delegate),
     45       xdisplay_(gfx::GetXDisplay()),
     46       xwindow_(None),
     47       xroot_window_(DefaultRootWindow(xdisplay_)),
     48       atom_cache_(xdisplay_, kAtomsToCache),
     49       window_mapped_(false) {
     50   CHECK(delegate_);
     51   TouchFactory::SetTouchDeviceListFromCommandLine();
     52 }
     53 
     54 X11Window::~X11Window() {
     55   Destroy();
     56 }
     57 
     58 void X11Window::Destroy() {
     59   if (xwindow_ == None)
     60     return;
     61 
     62   // Stop processing events.
     63   PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
     64   XDestroyWindow(xdisplay_, xwindow_);
     65   xwindow_ = None;
     66 }
     67 
     68 void X11Window::ProcessXInput2Event(XEvent* xev) {
     69   if (!TouchFactory::GetInstance()->ShouldProcessXI2Event(xev))
     70     return;
     71   EventType event_type = EventTypeFromNative(xev);
     72   switch (event_type) {
     73     case ET_KEY_PRESSED:
     74     case ET_KEY_RELEASED: {
     75       KeyEvent key_event(xev);
     76       delegate_->DispatchEvent(&key_event);
     77       break;
     78     }
     79     case ET_MOUSE_PRESSED:
     80     case ET_MOUSE_MOVED:
     81     case ET_MOUSE_DRAGGED:
     82     case ET_MOUSE_RELEASED: {
     83       MouseEvent mouse_event(xev);
     84       delegate_->DispatchEvent(&mouse_event);
     85       break;
     86     }
     87     case ET_MOUSEWHEEL: {
     88       MouseWheelEvent wheel_event(xev);
     89       delegate_->DispatchEvent(&wheel_event);
     90       break;
     91     }
     92     case ET_SCROLL_FLING_START:
     93     case ET_SCROLL_FLING_CANCEL:
     94     case ET_SCROLL: {
     95       ScrollEvent scroll_event(xev);
     96       delegate_->DispatchEvent(&scroll_event);
     97       break;
     98     }
     99     case ET_TOUCH_MOVED:
    100     case ET_TOUCH_PRESSED:
    101     case ET_TOUCH_CANCELLED:
    102     case ET_TOUCH_RELEASED: {
    103       TouchEvent touch_event(xev);
    104       delegate_->DispatchEvent(&touch_event);
    105       break;
    106     }
    107     default:
    108       break;
    109   }
    110 }
    111 
    112 void X11Window::Show() {
    113   if (window_mapped_)
    114     return;
    115 
    116   CHECK(PlatformEventSource::GetInstance());
    117   PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
    118 
    119   XSetWindowAttributes swa;
    120   memset(&swa, 0, sizeof(swa));
    121   swa.background_pixmap = None;
    122   swa.override_redirect = False;
    123   xwindow_ = XCreateWindow(xdisplay_,
    124                            xroot_window_,
    125                            requested_bounds_.x(),
    126                            requested_bounds_.y(),
    127                            requested_bounds_.width(),
    128                            requested_bounds_.height(),
    129                            0,               // border width
    130                            CopyFromParent,  // depth
    131                            InputOutput,
    132                            CopyFromParent,  // visual
    133                            CWBackPixmap | CWOverrideRedirect,
    134                            &swa);
    135 
    136   long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
    137                     KeyPressMask | KeyReleaseMask | EnterWindowMask |
    138                     LeaveWindowMask | ExposureMask | VisibilityChangeMask |
    139                     StructureNotifyMask | PropertyChangeMask |
    140                     PointerMotionMask;
    141   XSelectInput(xdisplay_, xwindow_, event_mask);
    142 
    143   unsigned char mask[XIMaskLen(XI_LASTEVENT)];
    144   memset(mask, 0, sizeof(mask));
    145 
    146   XISetMask(mask, XI_TouchBegin);
    147   XISetMask(mask, XI_TouchUpdate);
    148   XISetMask(mask, XI_TouchEnd);
    149   XISetMask(mask, XI_ButtonPress);
    150   XISetMask(mask, XI_ButtonRelease);
    151   XISetMask(mask, XI_Motion);
    152   XISetMask(mask, XI_KeyPress);
    153   XISetMask(mask, XI_KeyRelease);
    154 
    155   XIEventMask evmask;
    156   evmask.deviceid = XIAllDevices;
    157   evmask.mask_len = sizeof(mask);
    158   evmask.mask = mask;
    159   XISelectEvents(xdisplay_, xwindow_, &evmask, 1);
    160   XFlush(xdisplay_);
    161 
    162   ::Atom protocols[2];
    163   protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
    164   protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
    165   XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
    166 
    167   // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
    168   // the desktop environment.
    169   XSetWMProperties(
    170       xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
    171 
    172   // Likewise, the X server needs to know this window's pid so it knows which
    173   // program to kill if the window hangs.
    174   // XChangeProperty() expects "pid" to be long.
    175   COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long);
    176   long pid = getpid();
    177   XChangeProperty(xdisplay_,
    178                   xwindow_,
    179                   atom_cache_.GetAtom("_NET_WM_PID"),
    180                   XA_CARDINAL,
    181                   32,
    182                   PropModeReplace,
    183                   reinterpret_cast<unsigned char*>(&pid),
    184                   1);
    185   // Before we map the window, set size hints. Otherwise, some window managers
    186   // will ignore toplevel XMoveWindow commands.
    187   XSizeHints size_hints;
    188   size_hints.flags = PPosition | PWinGravity;
    189   size_hints.x = requested_bounds_.x();
    190   size_hints.y = requested_bounds_.y();
    191   // Set StaticGravity so that the window position is not affected by the
    192   // frame width when running with window manager.
    193   size_hints.win_gravity = StaticGravity;
    194   XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
    195 
    196   delegate_->OnAcceleratedWidgetAvailable(xwindow_);
    197 
    198   XMapWindow(xdisplay_, xwindow_);
    199 
    200   // We now block until our window is mapped. Some X11 APIs will crash and
    201   // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
    202   // asynchronous.
    203   if (X11EventSource::GetInstance())
    204     X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
    205   window_mapped_ = true;
    206 }
    207 
    208 void X11Window::Hide() {
    209   if (!window_mapped_)
    210     return;
    211   XWithdrawWindow(xdisplay_, xwindow_, 0);
    212   window_mapped_ = false;
    213 }
    214 
    215 void X11Window::Close() {
    216   Destroy();
    217 }
    218 
    219 void X11Window::SetBounds(const gfx::Rect& bounds) {
    220   requested_bounds_ = bounds;
    221   if (!window_mapped_)
    222     return;
    223   XWindowChanges changes = {0};
    224   unsigned value_mask = CWX | CWY | CWWidth | CWHeight;
    225   changes.x = bounds.x();
    226   changes.y = bounds.y();
    227   changes.width = bounds.width();
    228   changes.height = bounds.height();
    229   XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
    230 }
    231 
    232 gfx::Rect X11Window::GetBounds() {
    233   return confirmed_bounds_;
    234 }
    235 
    236 void X11Window::SetCapture() {}
    237 
    238 void X11Window::ReleaseCapture() {}
    239 
    240 void X11Window::ToggleFullscreen() {}
    241 
    242 void X11Window::Maximize() {}
    243 
    244 void X11Window::Minimize() {}
    245 
    246 void X11Window::Restore() {}
    247 
    248 void X11Window::SetCursor(PlatformCursor cursor) {}
    249 
    250 void X11Window::MoveCursorTo(const gfx::Point& location) {}
    251 
    252 bool X11Window::CanDispatchEvent(const PlatformEvent& event) {
    253   return FindXEventTarget(event) == xwindow_;
    254 }
    255 
    256 uint32_t X11Window::DispatchEvent(const PlatformEvent& event) {
    257   XEvent* xev = event;
    258   switch (xev->type) {
    259     case EnterNotify: {
    260       // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is
    261       // not real mouse move event.
    262       MouseEvent mouse_event(xev);
    263       CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type());
    264       mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED);
    265       delegate_->DispatchEvent(&mouse_event);
    266       break;
    267     }
    268     case LeaveNotify: {
    269       MouseEvent mouse_event(xev);
    270       delegate_->DispatchEvent(&mouse_event);
    271       break;
    272     }
    273 
    274     case Expose: {
    275       gfx::Rect damage_rect(xev->xexpose.x,
    276                             xev->xexpose.y,
    277                             xev->xexpose.width,
    278                             xev->xexpose.height);
    279       delegate_->OnDamageRect(damage_rect);
    280       break;
    281     }
    282 
    283     case KeyPress:
    284     case KeyRelease: {
    285       KeyEvent key_event(xev);
    286       delegate_->DispatchEvent(&key_event);
    287       break;
    288     }
    289 
    290     case ButtonPress:
    291     case ButtonRelease: {
    292       switch (EventTypeFromNative(xev)) {
    293         case ET_MOUSEWHEEL: {
    294           MouseWheelEvent mouseev(xev);
    295           delegate_->DispatchEvent(&mouseev);
    296           break;
    297         }
    298         case ET_MOUSE_PRESSED:
    299         case ET_MOUSE_RELEASED: {
    300           MouseEvent mouseev(xev);
    301           delegate_->DispatchEvent(&mouseev);
    302           break;
    303         }
    304         case ET_UNKNOWN:
    305           // No event is created for X11-release events for mouse-wheel
    306           // buttons.
    307           break;
    308         default:
    309           NOTREACHED();
    310       }
    311       break;
    312     }
    313 
    314     case FocusOut:
    315       if (xev->xfocus.mode != NotifyGrab)
    316         delegate_->OnLostCapture();
    317       break;
    318 
    319     case ConfigureNotify: {
    320       DCHECK_EQ(xwindow_, xev->xconfigure.event);
    321       DCHECK_EQ(xwindow_, xev->xconfigure.window);
    322       gfx::Rect bounds(xev->xconfigure.x,
    323                        xev->xconfigure.y,
    324                        xev->xconfigure.width,
    325                        xev->xconfigure.height);
    326       if (confirmed_bounds_ != bounds) {
    327         confirmed_bounds_ = bounds;
    328         delegate_->OnBoundsChanged(confirmed_bounds_);
    329       }
    330       break;
    331     }
    332 
    333     case ClientMessage: {
    334       Atom message = static_cast<Atom>(xev->xclient.data.l[0]);
    335       if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
    336         delegate_->OnCloseRequest();
    337       } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) {
    338         XEvent reply_event = *xev;
    339         reply_event.xclient.window = xroot_window_;
    340 
    341         XSendEvent(xdisplay_,
    342                    reply_event.xclient.window,
    343                    False,
    344                    SubstructureRedirectMask | SubstructureNotifyMask,
    345                    &reply_event);
    346         XFlush(xdisplay_);
    347       }
    348       break;
    349     }
    350 
    351     case GenericEvent: {
    352       ProcessXInput2Event(xev);
    353       break;
    354     }
    355   }
    356   return POST_DISPATCH_STOP_PROPAGATION;
    357 }
    358 
    359 }  // namespace ui
    360