Chromium Code Reviews| 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..a039fb121f05bc472fffa013fb9c121c44cda591 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) { |
| + int event, err; |
|
evanm
2010/11/11 20:45:44
these can be moved closer to where they're used
sadrul
2010/11/11 22:12:30
Done.
|
| + GdkDisplay* display = gdk_display_get_default(); |
| + Display* xdisplay = GDK_DISPLAY_XDISPLAY(display); |
| + |
| + if (!XQueryExtension(xdisplay, "XInputExtension", &xiopcode_, &event, &err)) { |
| + DLOG(INFO) << "X Input extension not available."; |
|
evanm
2010/11/11 20:45:44
Is this a problem? Should it be WARNING instead o
sadrul
2010/11/11 22:12:30
Done (Changed to WARNING).
Note: When XInputExten
|
| + xiopcode_ = -1; |
| + return; |
| + } |
| + |
| + int major = 2, minor = 0; |
| + if (XIQueryVersion(xdisplay, &major, &minor) == BadRequest) { |
| + DLOG(INFO) << "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); |