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_x11.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(MessagePumpX11::GetDefaultXDisplay()))
     21     *timeout_ms = 0;
     22   else
     23     *timeout_ms = -1;
     24   return FALSE;
     25 }
     26 
     27 gboolean XSourceCheck(GSource* source) {
     28   return XPending(MessagePumpX11::GetDefaultXDisplay());
     29 }
     30 
     31 gboolean XSourceDispatch(GSource* source,
     32                          GSourceFunc unused_func,
     33                          gpointer data) {
     34   MessagePumpX11* pump = static_cast<MessagePumpX11*>(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 ~MessagePumpX11() 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 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away.
     53 Display* g_xdisplay = NULL;
     54 int g_xinput_opcode = -1;
     55 
     56 bool InitializeXInput2() {
     57   Display* display = MessagePumpX11::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 InitializeXkb() {
    101   Display* display = MessagePumpX11::GetDefaultXDisplay();
    102   if (!display)
    103     return false;
    104 
    105   int opcode, event, error;
    106   int major = XkbMajorVersion;
    107   int minor = XkbMinorVersion;
    108   if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
    109     DVLOG(1) << "Xkb extension not available.";
    110     return false;
    111   }
    112 
    113   // Ask the server not to send KeyRelease event when the user holds down a key.
    114   // crbug.com/138092
    115   Bool supported_return;
    116   if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
    117     DVLOG(1) << "XKB not supported in the server.";
    118     return false;
    119   }
    120 
    121   return true;
    122 }
    123 
    124 }  // namespace
    125 
    126 MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
    127     x_source_(NULL) {
    128   InitializeXInput2();
    129   InitializeXkb();
    130   InitXSource();
    131 
    132   // Can't put this in the initializer list because g_xdisplay may not exist
    133   // until after InitXSource().
    134   x_root_window_ = DefaultRootWindow(g_xdisplay);
    135 }
    136 
    137 MessagePumpX11::~MessagePumpX11() {
    138   g_source_destroy(x_source_);
    139   g_source_unref(x_source_);
    140   XCloseDisplay(g_xdisplay);
    141   g_xdisplay = NULL;
    142 }
    143 
    144 // static
    145 Display* MessagePumpX11::GetDefaultXDisplay() {
    146   if (!g_xdisplay)
    147     g_xdisplay = XOpenDisplay(NULL);
    148   return g_xdisplay;
    149 }
    150 
    151 #if defined(TOOLKIT_GTK)
    152 // static
    153 MessagePumpX11* MessagePumpX11::Current() {
    154   MessageLoop* loop = MessageLoop::current();
    155   return static_cast<MessagePumpX11*>(loop->pump_gpu());
    156 }
    157 #else
    158 // static
    159 MessagePumpX11* MessagePumpX11::Current() {
    160   MessageLoopForUI* loop = MessageLoopForUI::current();
    161   return static_cast<MessagePumpX11*>(loop->pump_ui());
    162 }
    163 #endif
    164 
    165 void MessagePumpX11::AddDispatcherForWindow(
    166     MessagePumpDispatcher* dispatcher,
    167     unsigned long xid) {
    168   dispatchers_.insert(std::make_pair(xid, dispatcher));
    169 }
    170 
    171 void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) {
    172   dispatchers_.erase(xid);
    173 }
    174 
    175 void MessagePumpX11::AddDispatcherForRootWindow(
    176     MessagePumpDispatcher* dispatcher) {
    177   root_window_dispatchers_.AddObserver(dispatcher);
    178 }
    179 
    180 void MessagePumpX11::RemoveDispatcherForRootWindow(
    181     MessagePumpDispatcher* dispatcher) {
    182   root_window_dispatchers_.RemoveObserver(dispatcher);
    183 }
    184 
    185 void MessagePumpX11::AddObserver(MessagePumpObserver* observer) {
    186   observers_.AddObserver(observer);
    187 }
    188 
    189 void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) {
    190   observers_.RemoveObserver(observer);
    191 }
    192 
    193 bool MessagePumpX11::DispatchXEvents() {
    194   Display* display = GetDefaultXDisplay();
    195   DCHECK(display);
    196   MessagePumpDispatcher* dispatcher =
    197       GetDispatcher() ? GetDispatcher() : this;
    198 
    199   // In the general case, we want to handle all pending events before running
    200   // the tasks. This is what happens in the message_pump_glib case.
    201   while (XPending(display)) {
    202     XEvent xev;
    203     XNextEvent(display, &xev);
    204     if (dispatcher && ProcessXEvent(dispatcher, &xev))
    205       return TRUE;
    206   }
    207   return TRUE;
    208 }
    209 
    210 void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) {
    211   XEvent event;
    212 
    213   Display* display = GetDefaultXDisplay();
    214   DCHECK(display);
    215 
    216   MessagePumpDispatcher* dispatcher =
    217       GetDispatcher() ? GetDispatcher() : this;
    218 
    219   do {
    220     // Block until there's a message of |event_mask| type on |w|. Then remove
    221     // it from the queue and stuff it in |event|.
    222     XWindowEvent(display, xid, StructureNotifyMask, &event);
    223     ProcessXEvent(dispatcher, &event);
    224   } while (event.type != MapNotify);
    225 }
    226 
    227 void MessagePumpX11::InitXSource() {
    228   // CHECKs are to help track down crbug.com/113106.
    229   CHECK(!x_source_);
    230   Display* display = GetDefaultXDisplay();
    231   CHECK(display) << "Unable to get connection to X server";
    232   x_poll_.reset(new GPollFD());
    233   CHECK(x_poll_.get());
    234   x_poll_->fd = ConnectionNumber(display);
    235   x_poll_->events = G_IO_IN;
    236 
    237   x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource));
    238   g_source_add_poll(x_source_, x_poll_.get());
    239   g_source_set_can_recurse(x_source_, TRUE);
    240   g_source_set_callback(x_source_, NULL, this, NULL);
    241   g_source_attach(x_source_, g_main_context_default());
    242 }
    243 
    244 bool MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
    245                                        XEvent* xev) {
    246   bool should_quit = false;
    247 
    248   bool have_cookie = false;
    249   if (xev->type == GenericEvent &&
    250       XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
    251     have_cookie = true;
    252   }
    253 
    254   if (!WillProcessXEvent(xev)) {
    255     if (!dispatcher->Dispatch(xev)) {
    256       should_quit = true;
    257       Quit();
    258     }
    259     DidProcessXEvent(xev);
    260   }
    261 
    262   if (have_cookie) {
    263     XFreeEventData(xev->xgeneric.display, &xev->xcookie);
    264   }
    265 
    266   return should_quit;
    267 }
    268 
    269 bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) {
    270   if (!observers().might_have_observers())
    271     return false;
    272   ObserverListBase<MessagePumpObserver>::Iterator it(observers());
    273   MessagePumpObserver* obs;
    274   while ((obs = it.GetNext()) != NULL) {
    275     if (obs->WillProcessEvent(xevent))
    276       return true;
    277   }
    278   return false;
    279 }
    280 
    281 void MessagePumpX11::DidProcessXEvent(XEvent* xevent) {
    282   FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
    283 }
    284 
    285 MessagePumpDispatcher* MessagePumpX11::GetDispatcherForXEvent(
    286     const NativeEvent& xev) const {
    287   ::Window x_window = FindEventTarget(xev);
    288   DispatchersMap::const_iterator it = dispatchers_.find(x_window);
    289   return it != dispatchers_.end() ? it->second : NULL;
    290 }
    291 
    292 bool MessagePumpX11::Dispatch(const NativeEvent& xev) {
    293   // MappingNotify events (meaning that the keyboard or pointer buttons have
    294   // been remapped) aren't associated with a window; send them to all
    295   // dispatchers.
    296   if (xev->type == MappingNotify) {
    297     for (DispatchersMap::const_iterator it = dispatchers_.begin();
    298          it != dispatchers_.end(); ++it) {
    299       it->second->Dispatch(xev);
    300     }
    301     return true;
    302   }
    303 
    304   if (FindEventTarget(xev) == x_root_window_) {
    305     FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
    306                       Dispatch(xev));
    307     return true;
    308   }
    309   MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
    310   return dispatcher ? dispatcher->Dispatch(xev) : true;
    311 }
    312 
    313 }  // namespace base
    314