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