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