Home | History | Annotate | Download | only in message_loop
      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 "base/message_loop/message_pump_aurax11.h"
      6 
      7 #include <glib.h>
      8 #include <X11/X.h>
      9 #include <X11/extensions/XInput2.h>
     10 #include <X11/XKBlib.h>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/message_loop/message_loop.h"
     14 
     15 namespace base {
     16 
     17 namespace {
     18 
     19 gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
     20   if (XPending(MessagePumpAuraX11::GetDefaultXDisplay()))
     21     *timeout_ms = 0;
     22   else
     23     *timeout_ms = -1;
     24   return FALSE;
     25 }
     26 
     27 gboolean XSourceCheck(GSource* source) {
     28   return XPending(MessagePumpAuraX11::GetDefaultXDisplay());
     29 }
     30 
     31 gboolean XSourceDispatch(GSource* source,
     32                          GSourceFunc unused_func,
     33                          gpointer data) {
     34   MessagePumpAuraX11* pump = static_cast<MessagePumpAuraX11*>(data);
     35   return pump->DispatchXEvents();
     36 }
     37 
     38 GSourceFuncs XSourceFuncs = {
     39   XSourcePrepare,
     40   XSourceCheck,
     41   XSourceDispatch,
     42   NULL
     43 };
     44 
     45 // The connection is essentially a global that's accessed through a static
     46 // method and destroyed whenever ~MessagePumpAuraX11() is called. We do this
     47 // for historical reasons so user code can call
     48 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
     49 // to whatever type in the current build.
     50 //
     51 // TODO(erg): This can be changed to something more sane like
     52 // MessagePumpAuraX11::Current()->display() once MessagePumpGtk goes away.
     53 Display* g_xdisplay = NULL;
     54 int g_xinput_opcode = -1;
     55 
     56 bool InitializeXInput2Internal() {
     57   Display* display = MessagePumpAuraX11::GetDefaultXDisplay();
     58   if (!display)
     59     return false;
     60 
     61   int event, err;
     62 
     63   int xiopcode;
     64   if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) {
     65     DVLOG(1) << "X Input extension not available.";
     66     return false;
     67   }
     68   g_xinput_opcode = xiopcode;
     69 
     70 #if defined(USE_XI2_MT)
     71   // USE_XI2_MT also defines the required XI2 minor minimum version.
     72   int major = 2, minor = USE_XI2_MT;
     73 #else
     74   int major = 2, minor = 0;
     75 #endif
     76   if (XIQueryVersion(display, &major, &minor) == BadRequest) {
     77     DVLOG(1) << "XInput2 not supported in the server.";
     78     return false;
     79   }
     80 #if defined(USE_XI2_MT)
     81   if (major < 2 || (major == 2 && minor < USE_XI2_MT)) {
     82     DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
     83             << "But 2." << USE_XI2_MT << " is required.";
     84     return false;
     85   }
     86 #endif
     87 
     88   return true;
     89 }
     90 
     91 Window FindEventTarget(const NativeEvent& xev) {
     92   Window target = xev->xany.window;
     93   if (xev->type == GenericEvent &&
     94       static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) {
     95     target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
     96   }
     97   return target;
     98 }
     99 
    100 bool InitializeXInput2() {
    101   static bool xinput2_supported = InitializeXInput2Internal();
    102   return xinput2_supported;
    103 }
    104 
    105 bool InitializeXkb() {
    106   Display* display = MessagePumpAuraX11::GetDefaultXDisplay();
    107   if (!display)
    108     return false;
    109 
    110   int opcode, event, error;
    111   int major = XkbMajorVersion;
    112   int minor = XkbMinorVersion;
    113   if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
    114     DVLOG(1) << "Xkb extension not available.";
    115     return false;
    116   }
    117 
    118   // Ask the server not to send KeyRelease event when the user holds down a key.
    119   // crbug.com/138092
    120   Bool supported_return;
    121   if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
    122     DVLOG(1) << "XKB not supported in the server.";
    123     return false;
    124   }
    125 
    126   return true;
    127 }
    128 
    129 }  // namespace
    130 
    131 MessagePumpAuraX11::MessagePumpAuraX11() : MessagePumpGlib(),
    132     x_source_(NULL) {
    133   InitializeXInput2();
    134   InitializeXkb();
    135   InitXSource();
    136 
    137   // Can't put this in the initializer list because g_xdisplay may not exist
    138   // until after InitXSource().
    139   x_root_window_ = DefaultRootWindow(g_xdisplay);
    140 }
    141 
    142 MessagePumpAuraX11::~MessagePumpAuraX11() {
    143   g_source_destroy(x_source_);
    144   g_source_unref(x_source_);
    145   XCloseDisplay(g_xdisplay);
    146   g_xdisplay = NULL;
    147 }
    148 
    149 // static
    150 Display* MessagePumpAuraX11::GetDefaultXDisplay() {
    151   if (!g_xdisplay)
    152     g_xdisplay = XOpenDisplay(NULL);
    153   return g_xdisplay;
    154 }
    155 
    156 // static
    157 bool MessagePumpAuraX11::HasXInput2() {
    158   return InitializeXInput2();
    159 }
    160 
    161 // static
    162 MessagePumpAuraX11* MessagePumpAuraX11::Current() {
    163   MessageLoopForUI* loop = MessageLoopForUI::current();
    164   return static_cast<MessagePumpAuraX11*>(loop->pump_ui());
    165 }
    166 
    167 void MessagePumpAuraX11::AddDispatcherForWindow(
    168     MessagePumpDispatcher* dispatcher,
    169     unsigned long xid) {
    170   dispatchers_.insert(std::make_pair(xid, dispatcher));
    171 }
    172 
    173 void MessagePumpAuraX11::RemoveDispatcherForWindow(unsigned long xid) {
    174   dispatchers_.erase(xid);
    175 }
    176 
    177 void MessagePumpAuraX11::AddDispatcherForRootWindow(
    178     MessagePumpDispatcher* dispatcher) {
    179   root_window_dispatchers_.AddObserver(dispatcher);
    180 }
    181 
    182 void MessagePumpAuraX11::RemoveDispatcherForRootWindow(
    183     MessagePumpDispatcher* dispatcher) {
    184   root_window_dispatchers_.RemoveObserver(dispatcher);
    185 }
    186 
    187 bool MessagePumpAuraX11::DispatchXEvents() {
    188   Display* display = GetDefaultXDisplay();
    189   DCHECK(display);
    190   MessagePumpDispatcher* dispatcher =
    191       GetDispatcher() ? GetDispatcher() : this;
    192 
    193   // In the general case, we want to handle all pending events before running
    194   // the tasks. This is what happens in the message_pump_glib case.
    195   while (XPending(display)) {
    196     XEvent xev;
    197     XNextEvent(display, &xev);
    198     if (dispatcher && ProcessXEvent(dispatcher, &xev))
    199       return TRUE;
    200   }
    201   return TRUE;
    202 }
    203 
    204 void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) {
    205   XEvent event;
    206 
    207   Display* display = GetDefaultXDisplay();
    208   DCHECK(display);
    209 
    210   MessagePumpDispatcher* dispatcher =
    211       GetDispatcher() ? GetDispatcher() : this;
    212 
    213   do {
    214     // Block until there's a message of |event_mask| type on |w|. Then remove
    215     // it from the queue and stuff it in |event|.
    216     XWindowEvent(display, xid, StructureNotifyMask, &event);
    217     ProcessXEvent(dispatcher, &event);
    218   } while (event.type != MapNotify);
    219 }
    220 
    221 void MessagePumpAuraX11::InitXSource() {
    222   // CHECKs are to help track down crbug.com/113106.
    223   CHECK(!x_source_);
    224   Display* display = GetDefaultXDisplay();
    225   CHECK(display) << "Unable to get connection to X server";
    226   x_poll_.reset(new GPollFD());
    227   CHECK(x_poll_.get());
    228   x_poll_->fd = ConnectionNumber(display);
    229   x_poll_->events = G_IO_IN;
    230 
    231   x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource));
    232   g_source_add_poll(x_source_, x_poll_.get());
    233   g_source_set_can_recurse(x_source_, TRUE);
    234   g_source_set_callback(x_source_, NULL, this, NULL);
    235   g_source_attach(x_source_, g_main_context_default());
    236 }
    237 
    238 bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
    239                                        XEvent* xev) {
    240   bool should_quit = false;
    241 
    242   bool have_cookie = false;
    243   if (xev->type == GenericEvent &&
    244       XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
    245     have_cookie = true;
    246   }
    247 
    248   if (!WillProcessXEvent(xev)) {
    249     if (!dispatcher->Dispatch(xev)) {
    250       should_quit = true;
    251       Quit();
    252     }
    253     DidProcessXEvent(xev);
    254   }
    255 
    256   if (have_cookie) {
    257     XFreeEventData(xev->xgeneric.display, &xev->xcookie);
    258   }
    259 
    260   return should_quit;
    261 }
    262 
    263 bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) {
    264   if (!observers().might_have_observers())
    265     return false;
    266   ObserverListBase<MessagePumpObserver>::Iterator it(observers());
    267   MessagePumpObserver* obs;
    268   while ((obs = it.GetNext()) != NULL) {
    269     if (obs->WillProcessEvent(xevent))
    270       return true;
    271   }
    272   return false;
    273 }
    274 
    275 void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) {
    276   FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
    277 }
    278 
    279 MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent(
    280     const NativeEvent& xev) const {
    281   ::Window x_window = FindEventTarget(xev);
    282   DispatchersMap::const_iterator it = dispatchers_.find(x_window);
    283   return it != dispatchers_.end() ? it->second : NULL;
    284 }
    285 
    286 bool MessagePumpAuraX11::Dispatch(const NativeEvent& xev) {
    287   // MappingNotify events (meaning that the keyboard or pointer buttons have
    288   // been remapped) aren't associated with a window; send them to all
    289   // dispatchers.
    290   if (xev->type == MappingNotify) {
    291     for (DispatchersMap::const_iterator it = dispatchers_.begin();
    292          it != dispatchers_.end(); ++it) {
    293       it->second->Dispatch(xev);
    294     }
    295     return true;
    296   }
    297 
    298   if (FindEventTarget(xev) == x_root_window_) {
    299     FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
    300                       Dispatch(xev));
    301     return true;
    302   }
    303   MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
    304   return dispatcher ? dispatcher->Dispatch(xev) : true;
    305 }
    306 
    307 }  // namespace base
    308