summaryrefslogtreecommitdiffstats
path: root/views/touchui/touch_factory.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/touchui/touch_factory.cc')
-rw-r--r--views/touchui/touch_factory.cc137
1 files changed, 121 insertions, 16 deletions
diff --git a/views/touchui/touch_factory.cc b/views/touchui/touch_factory.cc
index d9a65a1..4ac8ddc 100644
--- a/views/touchui/touch_factory.cc
+++ b/views/touchui/touch_factory.cc
@@ -4,6 +4,8 @@
#include "views/touchui/touch_factory.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
#include <X11/cursorfont.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
@@ -14,28 +16,26 @@
#include "base/logging.h"
#include "ui/base/x/x11_util.h"
-// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
-static int kCursorIdleSeconds = 5;
-
-namespace views {
-
namespace {
+// The X cursor is hidden if it is idle for kCursorIdleSeconds seconds.
+int kCursorIdleSeconds = 5;
+
// Given the TouchParam, return the correspoding valuator index using
// the X device information through Atom name matching.
char FindTPValuator(Display* display,
XIDeviceInfo* info,
- TouchFactory::TouchParam touch_param) {
+ views::TouchFactory::TouchParam touch_param) {
// Lookup table for mapping TouchParam to Atom string used in X.
// A full set of Atom strings can be found at xserver-properties.h.
static struct {
- TouchFactory::TouchParam tp;
+ views::TouchFactory::TouchParam tp;
const char* atom;
} kTouchParamAtom[] = {
- { TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
- { TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
- { TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
- { TouchFactory::TP_LAST_ENTRY, NULL },
+ { views::TouchFactory::TP_TOUCH_MAJOR, "Abs MT Touch Major" },
+ { views::TouchFactory::TP_TOUCH_MINOR, "Abs MT Touch Minor" },
+ { views::TouchFactory::TP_ORIENTATION, "Abs MT Orientation" },
+ { views::TouchFactory::TP_LAST_ENTRY, NULL },
};
const char* atom_tp = NULL;
@@ -65,8 +65,52 @@ char FindTPValuator(Display* display,
return -1;
}
+// 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;
+ views::TouchFactory* factory = static_cast<views::TouchFactory*>(data);
+
+ if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL &&
+ GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD &&
+ GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG)
+ return true;
+
+ factory->SetupXI2ForXWindow(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.
+guint realize_signal_id = 0;
+guint realize_hook_id = 0;
+
+void SetupGtkWidgetRealizeNotifier(views::TouchFactory* factory) {
+ gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET);
+
+ g_signal_parse_name("realize", GTK_TYPE_WIDGET,
+ &realize_signal_id, NULL, FALSE);
+ realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0,
+ GtkWidgetRealizeCallback, static_cast<gpointer>(factory), NULL);
+
+ g_type_class_unref(klass);
+}
+
+void RemoveGtkWidgetRealizeNotifier() {
+ if (realize_signal_id != 0)
+ g_signal_remove_emission_hook(realize_signal_id, realize_hook_id);
+ realize_signal_id = 0;
+ realize_hook_id = 0;
+}
+
} // namespace
+namespace views {
+
// static
TouchFactory* TouchFactory::GetInstance() {
return Singleton<TouchFactory>::get();
@@ -75,6 +119,7 @@ TouchFactory* TouchFactory::GetInstance() {
TouchFactory::TouchFactory()
: is_cursor_visible_(true),
cursor_timer_(),
+ pointer_devices_(),
touch_device_list_() {
char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
XColor black;
@@ -87,7 +132,29 @@ TouchFactory::TouchFactory()
arrow_cursor_ = XCreateFontCursor(display, XC_arrow);
SetCursorVisible(false, false);
+ UpdateDeviceList(display);
+
+ // TODO(sad): Here, we only setup so that the X windows created by GTK+ are
+ // setup for XInput2 events. We need a way to listen for XInput2 events for X
+ // windows created by other means (e.g. for context menus).
+ SetupGtkWidgetRealizeNotifier(this);
+
+ // TODO(sad): Select on root for XI_HierarchyChanged so that floats_ 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.
+}
+
+TouchFactory::~TouchFactory() {
+ SetCursorVisible(true, false);
+ Display* display = ui::GetXDisplay();
+ XFreeCursor(display, invisible_cursor_);
+ XFreeCursor(display, arrow_cursor_);
+ RemoveGtkWidgetRealizeNotifier();
+}
+
+void TouchFactory::UpdateDeviceList(Display* display) {
// Detect touch devices.
// NOTE: The new API for retrieving the list of devices (XIQueryDevice) does
// not provide enough information to detect a touch device. As a result, the
@@ -95,6 +162,8 @@ TouchFactory::TouchFactory()
// If XInput2 is not supported, this will return null (with count of -1) so
// we assume there cannot be any touch devices.
int count = 0;
+ touch_device_lookup_.reset();
+ touch_device_list_.clear();
XDeviceInfo* devlist = XListInputDevices(display, &count);
for (int i = 0; i < count; i++) {
const char* devtype = XGetAtomName(display, devlist[i].type);
@@ -106,14 +175,50 @@ TouchFactory::TouchFactory()
if (devlist)
XFreeDeviceList(devlist);
+ // Instead of asking X for the list of devices all the time, let's maintain a
+ // list of pointer devices we care about.
+ // It is not necessary to select for slave 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'.
+ // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which
+ // is possible), then the device is detected as a floating device, and a
+ // floating device is not connected to a master device. So it is necessary to
+ // also select on the floating devices.
+ pointer_devices_.clear();
+ XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &count);
+ for (int i = 0; i < count; i++) {
+ XIDeviceInfo* devinfo = devices + i;
+ if (devinfo->use == XIFloatingSlave || devinfo->use == XIMasterPointer) {
+ pointer_devices_.insert(devinfo->deviceid);
+ }
+ }
+ XIFreeDeviceInfo(devices);
+
SetupValuator();
}
-TouchFactory::~TouchFactory() {
- SetCursorVisible(true, false);
+void TouchFactory::SetupXI2ForXWindow(Window window) {
Display* display = ui::GetXDisplay();
- XFreeCursor(display, invisible_cursor_);
- XFreeCursor(display, arrow_cursor_);
+
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+ memset(mask, 0, sizeof(mask));
+
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+ XISetMask(mask, XI_Motion);
+
+ XIEventMask evmask[pointer_devices_.size()];
+ int count = 0;
+ for (std::set<int>::const_iterator iter = pointer_devices_.begin();
+ iter != pointer_devices_.end();
+ ++iter, ++count) {
+ evmask[count].deviceid = *iter;
+ evmask[count].mask_len = sizeof(mask);
+ evmask[count].mask = mask;
+ }
+ XISelectEvents(display, window, evmask, pointer_devices_.size());
+ XFlush(display);
}
void TouchFactory::SetTouchDeviceList(
@@ -139,7 +244,7 @@ bool TouchFactory::GrabTouchDevices(Display* display, ::Window window) {
if (touch_device_list_.empty())
return true;
- unsigned char mask[(XI_LASTEVENT + 7) / 8];
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
bool success = true;
memset(mask, 0, sizeof(mask));