diff options
Diffstat (limited to 'views/touchui/touch_factory.cc')
-rw-r--r-- | views/touchui/touch_factory.cc | 137 |
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)); |