diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-12 01:17:35 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-12 01:17:35 +0000 |
commit | f4f7dd072cb18b122edeca7992b1984759757962 (patch) | |
tree | 1e205be4073d2d19a08a6797718642e8c640a2d0 /base/message_pump_glib_x.cc | |
parent | 11dd68cd5c43ea76082eed94d7ffc2e887241005 (diff) | |
download | chromium_src-f4f7dd072cb18b122edeca7992b1984759757962.zip chromium_src-f4f7dd072cb18b122edeca7992b1984759757962.tar.gz chromium_src-f4f7dd072cb18b122edeca7992b1984759757962.tar.bz2 |
touchui: First pass at XInput2 message pump.
Capture X events using XInput2.
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/4186004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65888 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/message_pump_glib_x.cc')
-rw-r--r-- | base/message_pump_glib_x.cc | 143 |
1 files changed, 142 insertions, 1 deletions
diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc index 675774e..78c1799 100644 --- a/base/message_pump_glib_x.cc +++ b/base/message_pump_glib_x.cc @@ -5,7 +5,11 @@ #include "base/message_pump_glib_x.h" #include <gdk/gdkx.h> +#if defined(HAVE_XINPUT2) +#include <X11/extensions/XInput2.h> +#else #include <X11/Xlib.h> +#endif #include "base/message_pump_glib_x_dispatch.h" @@ -17,17 +21,61 @@ gboolean PlaceholderDispatch(GSource* source, return TRUE; } +#if defined(HAVE_XINPUT2) + +// Setup XInput2 select for the GtkWidget. +gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams, + const GValue* pvalues, gpointer data) { + GtkWidget* widget = GTK_WIDGET(g_value_get_object(pvalues)); + GdkWindow* window = widget->window; + base::MessagePumpGlibX* msgpump = static_cast<base::MessagePumpGlibX*>(data); + + DCHECK(window); // TODO(sad): Remove once determined if necessary. + + // TODO(sad): Do we need to set a flag on |window| to make sure we don't + // select for the same GdkWindow multiple times? Does it matter? + msgpump->SetupXInput2ForXWindow(GDK_WINDOW_XID(window)); + + return true; +} + +// We need to capture all the GDK windows that get created, and start +// listening for XInput2 events. So we setup a callback to the 'realize' +// signal for GTK+ widgets, so that whenever the signal triggers for any +// GtkWidget, which means the GtkWidget should now have a GdkWindow, we can +// setup XInput2 events for the GdkWindow. +void SetupGtkWidgetRealizeNotifier(base::MessagePumpGlibX* msgpump) { + guint signal_id; + gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET); + + g_signal_parse_name("realize", GTK_TYPE_WIDGET, &signal_id, NULL, FALSE); + g_signal_add_emission_hook(signal_id, 0, GtkWidgetRealizeCallback, + static_cast<gpointer>(msgpump), NULL); + + g_type_class_unref(klass); +} + +#endif // HAVE_XINPUT2 + } // namespace namespace base { MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(), +#if defined(HAVE_XINPUT2) + xiopcode_(-1), + masters_(), + slaves_(), +#endif gdksource_(NULL), dispatching_event_(false), capture_x_events_(0), capture_gdk_events_(0) { gdk_event_handler_set(&EventDispatcherX, this, NULL); +#if defined(HAVE_XINPUT2) + InitializeXInput2(); +#endif InitializeEventsToCapture(); } @@ -40,7 +88,11 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { if (XPending(display)) { XEvent xev; XPeekEvent(display, &xev); - if (capture_x_events_[xev.type]) { + if (capture_x_events_[xev.type] +#if defined(HAVE_XINPUT2) + && (xev.type != GenericEvent || xev.xcookie.extension == xiopcode_) +#endif + ) { XNextEvent(display, &xev); bool processed = static_cast<MessagePumpGlibXDispatcher*> @@ -48,6 +100,13 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { if (!processed) { DLOG(WARNING) << "Event (" << xev.type << ") not handled."; + + // TODO(sad): It is necessary to put back the event so that the default + // GDK events handler can take care of it. Without this, it is + // impossible to use the omnibox at the moment. However, this will + // eventually be removed once the omnibox code is updated for touchui. + XPutBackEvent(display, &xev); + g_main_context_iteration(context, FALSE); } } else { // TODO(sad): A couple of extra events can still sneak in during this. @@ -94,8 +153,90 @@ void MessagePumpGlibX::InitializeEventsToCapture(void) { capture_x_events_[MotionNotify] = true; capture_gdk_events_[GDK_MOTION_NOTIFY] = true; + +#if defined(HAVE_XINPUT2) + capture_x_events_[GenericEvent] = true; +#endif +} + +#if defined(HAVE_XINPUT2) +void MessagePumpGlibX::InitializeXInput2(void) { + GdkDisplay* display = gdk_display_get_default(); + Display* xdisplay = GDK_DISPLAY_XDISPLAY(display); + int event, err; + + if (!XQueryExtension(xdisplay, "XInputExtension", &xiopcode_, &event, &err)) { + DLOG(WARNING) << "X Input extension not available."; + xiopcode_ = -1; + return; + } + + int major = 2, minor = 0; + if (XIQueryVersion(xdisplay, &major, &minor) == BadRequest) { + DLOG(WARNING) << "XInput2 not supported in the server."; + xiopcode_ = -1; + return; + } + + SetupGtkWidgetRealizeNotifier(this); + + // Instead of asking X for the list of devices all the time, let's maintain a + // list of slave (physical) and master (virtual) pointer devices. + int count = 0; + XIDeviceInfo* devices = XIQueryDevice(xdisplay, XIAllDevices, &count); + for (int i = 0; i < count; i++) { + XIDeviceInfo* devinfo = devices + i; + if (devinfo->use == XISlavePointer) { + slaves_.insert(devinfo->deviceid); + } else if (devinfo->use == XIMasterPointer) { + masters_.insert(devinfo->deviceid); + } + // We do not need to care about XIFloatingSlave, because the callback for + // XI_HierarchyChanged event will take care of it. + } + XIFreeDeviceInfo(devices); + + // TODO(sad): Select on root for XI_HierarchyChanged so that slaves_ and + // masters_ can be kept up-to-date. This is a relatively rare event, so we can + // put it off for a later time. + // Note: It is not necessary to listen for XI_DeviceChanged events. } +void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) { + Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + + // Setup mask for mouse events. + unsigned char mask[(XI_LASTEVENT + 7)/8]; + memset(mask, 0, sizeof(mask)); + + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + XISetMask(mask, XI_Motion); + + // It is necessary to select only for the master devices. XInput2 provides + // enough information to the event callback to decide which slave device + // triggered the event, thus decide whether the 'pointer event' is a 'mouse + // event' or a 'touch event'. So it is not necessary to select for the slave + // devices here. + XIEventMask evmasks[masters_.size()]; + int count = 0; + for (std::set<int>::const_iterator iter = masters_.begin(); + iter != masters_.end(); + ++iter, ++count) { + evmasks[count].deviceid = *iter; + evmasks[count].mask_len = sizeof(mask); + evmasks[count].mask = mask; + } + + XISelectEvents(xdisplay, xwindow, evmasks, masters_.size()); + + // TODO(sad): Setup masks for keyboard events. + + XFlush(xdisplay); +} + +#endif // HAVE_XINPUT2 + void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) { MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data); |