summaryrefslogtreecommitdiffstats
path: root/base/message_pump_glib_x.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/message_pump_glib_x.cc')
-rw-r--r--base/message_pump_glib_x.cc143
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);