Index: ui/events/platform/x11/x11_event_source.cc |
diff --git a/ui/events/platform/x11/x11_event_source.cc b/ui/events/platform/x11/x11_event_source.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..63cba762c252d0fe61c40137db16ad494600341d |
--- /dev/null |
+++ b/ui/events/platform/x11/x11_event_source.cc |
@@ -0,0 +1,201 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/events/platform/x11/x11_event_source.h" |
+ |
+#include <glib.h> |
+#include <X11/extensions/XInput2.h> |
+#include <X11/X.h> |
+#include <X11/Xlib.h> |
+#include <X11/XKBlib.h> |
+ |
+#include "base/logging.h" |
+#include "base/message_loop/message_loop.h" |
+#include "ui/events/platform/platform_event_dispatcher.h" |
+ |
+namespace ui { |
+ |
+namespace { |
+ |
+struct GLibX11Source : public GSource { |
+ // Note: The GLibX11Source is created and destroyed by GLib. So its |
+ // constructor/destructor may or may not get called. |
+ XDisplay* display; |
+ GPollFD* poll_fd; |
+}; |
+ |
+gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { |
+ GLibX11Source* gxsource = static_cast<GLibX11Source*>(source); |
+ if (XPending(gxsource->display)) |
+ *timeout_ms = 0; |
+ else |
+ *timeout_ms = -1; |
+ return FALSE; |
+} |
+ |
+gboolean XSourceCheck(GSource* source) { |
+ GLibX11Source* gxsource = static_cast<GLibX11Source*>(source); |
+ return XPending(gxsource->display); |
+} |
+ |
+gboolean XSourceDispatch(GSource* source, |
+ GSourceFunc unused_func, |
+ gpointer data) { |
+ X11EventSource* x11_source = static_cast<X11EventSource*>(data); |
+ x11_source->DispatchXEvents(); |
+ return TRUE; |
+} |
+ |
+GSourceFuncs XSourceFuncs = { |
+ XSourcePrepare, |
+ XSourceCheck, |
+ XSourceDispatch, |
+ NULL |
+}; |
+ |
+int g_xinput_opcode = -1; |
+ |
+bool InitializeXInput2(XDisplay* display) { |
+ if (!display) |
+ return false; |
+ |
+ int event, err; |
+ |
+ int xiopcode; |
+ if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { |
+ DVLOG(1) << "X Input extension not available."; |
+ return false; |
+ } |
+ g_xinput_opcode = xiopcode; |
+ |
+#if defined(USE_XI2_MT) |
+ // USE_XI2_MT also defines the required XI2 minor minimum version. |
+ int major = 2, minor = USE_XI2_MT; |
+#else |
+ int major = 2, minor = 0; |
+#endif |
+ if (XIQueryVersion(display, &major, &minor) == BadRequest) { |
+ DVLOG(1) << "XInput2 not supported in the server."; |
+ return false; |
+ } |
+#if defined(USE_XI2_MT) |
+ if (major < 2 || (major == 2 && minor < USE_XI2_MT)) { |
+ DVLOG(1) << "XI version on server is " << major << "." << minor << ". " |
+ << "But 2." << USE_XI2_MT << " is required."; |
+ return false; |
+ } |
+#endif |
+ |
+ return true; |
+} |
+ |
+bool InitializeXkb(XDisplay* display) { |
+ if (!display) |
+ return false; |
+ |
+ int opcode, event, error; |
+ int major = XkbMajorVersion; |
+ int minor = XkbMinorVersion; |
+ if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { |
+ DVLOG(1) << "Xkb extension not available."; |
+ return false; |
+ } |
+ |
+ // Ask the server not to send KeyRelease event when the user holds down a key. |
+ // crbug.com/138092 |
+ Bool supported_return; |
+ if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { |
+ DVLOG(1) << "XKB not supported in the server."; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+X11EventSource::X11EventSource(XDisplay* display) |
+ : display_(display), |
+ x_source_(NULL) { |
+ CHECK(display_); |
+ InitializeXInput2(display_); |
+ InitializeXkb(display_); |
+ |
+ InitXSource(); |
+} |
+ |
+X11EventSource::~X11EventSource() { |
+ g_source_destroy(x_source_); |
+ g_source_unref(x_source_); |
+} |
+ |
+// static |
+X11EventSource* X11EventSource::GetInstance() { |
+ return static_cast<X11EventSource*>(PlatformEventSource::GetInstance()); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// X11EventSource, public |
+ |
+void X11EventSource::DispatchXEvents() { |
+ DCHECK(display_); |
+ // Handle all pending events. |
+ // It may be useful to eventually align this event dispatch with vsync, but |
+ // not yet. |
+ while (XPending(display_)) { |
+ XEvent xevent; |
+ XNextEvent(display_, &xevent); |
+ uint32_t action = DispatchEvent(&xevent); |
+ if (action & POST_DISPATCH_QUIT_LOOP) |
+ break; |
+ } |
+} |
+ |
+void X11EventSource::BlockUntilWindowMapped(XID window) { |
+ XEvent event; |
+ do { |
+ // Block until there's a message of |event_mask| type on |w|. Then remove |
+ // it from the queue and stuff it in |event|. |
+ XWindowEvent(display_, window, StructureNotifyMask, &event); |
+ DispatchEvent(&event); |
+ } while (event.type != MapNotify); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// X11EventSource, private |
+ |
+void X11EventSource::InitXSource() { |
+ CHECK(!x_source_); |
+ CHECK(display_) << "Unable to get connection to X server"; |
+ |
+ x_poll_.reset(new GPollFD()); |
+ x_poll_->fd = ConnectionNumber(display_); |
+ x_poll_->events = G_IO_IN; |
+ x_poll_->revents = 0; |
+ |
+ GLibX11Source* glib_x_source = static_cast<GLibX11Source*> |
+ (g_source_new(&XSourceFuncs, sizeof(GLibX11Source))); |
+ glib_x_source->display = display_; |
+ glib_x_source->poll_fd = x_poll_.get(); |
+ |
+ x_source_ = glib_x_source; |
+ g_source_add_poll(x_source_, x_poll_.get()); |
+ g_source_set_can_recurse(x_source_, TRUE); |
+ g_source_set_callback(x_source_, NULL, this, NULL); |
+ g_source_attach(x_source_, g_main_context_default()); |
+} |
+ |
+uint32_t X11EventSource::DispatchEvent(XEvent* xevent) { |
+ bool have_cookie = false; |
+ if (xevent->type == GenericEvent && |
+ XGetEventData(xevent->xgeneric.display, &xevent->xcookie)) { |
+ have_cookie = true; |
+ } |
+ int action = PlatformEventSource::DispatchEvent(xevent); |
+ if (have_cookie) |
+ XFreeEventData(xevent->xgeneric.display, &xevent->xcookie); |
+ return action; |
+} |
+ |
+} // namespace ui |