Index: base/message_pump_glib_x.cc |
diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc |
index 675774ea5b5a8a8413b1b43265f855f66713bd1a..78c179933d00a7e5a70d0d94c3675fe7b9dd1379 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); |