| OLD | NEW |
| (Empty) |
| 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 void MessagePumpAuraX11::AddObserver(MessagePumpObserver* observer) { | |
| 188 observers_.AddObserver(observer); | |
| 189 } | |
| 190 | |
| 191 void MessagePumpAuraX11::RemoveObserver(MessagePumpObserver* observer) { | |
| 192 observers_.RemoveObserver(observer); | |
| 193 } | |
| 194 | |
| 195 bool MessagePumpAuraX11::DispatchXEvents() { | |
| 196 Display* display = GetDefaultXDisplay(); | |
| 197 DCHECK(display); | |
| 198 MessagePumpDispatcher* dispatcher = | |
| 199 GetDispatcher() ? GetDispatcher() : 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 if (dispatcher && ProcessXEvent(dispatcher, &xev)) | |
| 207 return TRUE; | |
| 208 } | |
| 209 return TRUE; | |
| 210 } | |
| 211 | |
| 212 void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) { | |
| 213 XEvent event; | |
| 214 | |
| 215 Display* display = GetDefaultXDisplay(); | |
| 216 DCHECK(display); | |
| 217 | |
| 218 MessagePumpDispatcher* dispatcher = | |
| 219 GetDispatcher() ? GetDispatcher() : this; | |
| 220 | |
| 221 do { | |
| 222 // Block until there's a message of |event_mask| type on |w|. Then remove | |
| 223 // it from the queue and stuff it in |event|. | |
| 224 XWindowEvent(display, xid, StructureNotifyMask, &event); | |
| 225 ProcessXEvent(dispatcher, &event); | |
| 226 } while (event.type != MapNotify); | |
| 227 } | |
| 228 | |
| 229 void MessagePumpAuraX11::InitXSource() { | |
| 230 // CHECKs are to help track down crbug.com/113106. | |
| 231 CHECK(!x_source_); | |
| 232 Display* display = GetDefaultXDisplay(); | |
| 233 CHECK(display) << "Unable to get connection to X server"; | |
| 234 x_poll_.reset(new GPollFD()); | |
| 235 CHECK(x_poll_.get()); | |
| 236 x_poll_->fd = ConnectionNumber(display); | |
| 237 x_poll_->events = G_IO_IN; | |
| 238 | |
| 239 x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource)); | |
| 240 g_source_add_poll(x_source_, x_poll_.get()); | |
| 241 g_source_set_can_recurse(x_source_, TRUE); | |
| 242 g_source_set_callback(x_source_, NULL, this, NULL); | |
| 243 g_source_attach(x_source_, g_main_context_default()); | |
| 244 } | |
| 245 | |
| 246 bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, | |
| 247 XEvent* xev) { | |
| 248 bool should_quit = false; | |
| 249 | |
| 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 if (!dispatcher->Dispatch(xev)) { | |
| 258 should_quit = true; | |
| 259 Quit(); | |
| 260 } | |
| 261 DidProcessXEvent(xev); | |
| 262 } | |
| 263 | |
| 264 if (have_cookie) { | |
| 265 XFreeEventData(xev->xgeneric.display, &xev->xcookie); | |
| 266 } | |
| 267 | |
| 268 return should_quit; | |
| 269 } | |
| 270 | |
| 271 bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) { | |
| 272 if (!observers().might_have_observers()) | |
| 273 return false; | |
| 274 ObserverListBase<MessagePumpObserver>::Iterator it(observers()); | |
| 275 MessagePumpObserver* obs; | |
| 276 while ((obs = it.GetNext()) != NULL) { | |
| 277 if (obs->WillProcessEvent(xevent)) | |
| 278 return true; | |
| 279 } | |
| 280 return false; | |
| 281 } | |
| 282 | |
| 283 void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) { | |
| 284 FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); | |
| 285 } | |
| 286 | |
| 287 MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent( | |
| 288 const NativeEvent& xev) const { | |
| 289 ::Window x_window = FindEventTarget(xev); | |
| 290 DispatchersMap::const_iterator it = dispatchers_.find(x_window); | |
| 291 return it != dispatchers_.end() ? it->second : NULL; | |
| 292 } | |
| 293 | |
| 294 bool MessagePumpAuraX11::Dispatch(const NativeEvent& xev) { | |
| 295 // MappingNotify events (meaning that the keyboard or pointer buttons have | |
| 296 // been remapped) aren't associated with a window; send them to all | |
| 297 // dispatchers. | |
| 298 if (xev->type == MappingNotify) { | |
| 299 for (DispatchersMap::const_iterator it = dispatchers_.begin(); | |
| 300 it != dispatchers_.end(); ++it) { | |
| 301 it->second->Dispatch(xev); | |
| 302 } | |
| 303 return true; | |
| 304 } | |
| 305 | |
| 306 if (FindEventTarget(xev) == x_root_window_) { | |
| 307 FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, | |
| 308 Dispatch(xev)); | |
| 309 return true; | |
| 310 } | |
| 311 MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); | |
| 312 return dispatcher ? dispatcher->Dispatch(xev) : true; | |
| 313 } | |
| 314 | |
| 315 } // namespace base | |
| OLD | NEW |