diff options
-rw-r--r-- | meson.build | 91 | ||||
-rw-r--r-- | meson_options.txt | 5 | ||||
-rw-r--r-- | src/gwacom/wacom-device.c | 936 | ||||
-rw-r--r-- | src/gwacom/wacom-device.h | 198 | ||||
-rw-r--r-- | src/gwacom/wacom-driver.c | 149 | ||||
-rw-r--r-- | src/gwacom/wacom-driver.h | 48 | ||||
-rw-r--r-- | src/gwacom/wacom-private.h | 29 | ||||
-rw-r--r-- | src/x11/xf86Wacom.c | 1 | ||||
-rw-r--r-- | tools/wacom-record.c | 367 |
9 files changed, 1822 insertions, 2 deletions
diff --git a/meson.build b/meson.build index 74b867e..ea0189c 100644 --- a/meson.build +++ b/meson.build @@ -98,10 +98,16 @@ config_ver_h = vcs_tag(command : ['git', 'describe'], # config.h config_h = configuration_data() config_h.set('_GNU_SOURCE', 1) +config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h.set('PACKAGE_VERSION_MAJOR', wacom_version[0]) config_h.set('PACKAGE_VERSION_MINOR', wacom_version[1]) config_h.set('PACKAGE_VERSION_PATCHLEVEL', wacom_version[2]) +# shortcut to avoid linking to _Xasprintf if xorg-server isn't included +if cc.has_function('asprintf', prefix: '#define _GNU_SOURCE') + config_h.set10('HAVE_ASPRINTF', true) +endif + if get_option('debug-messages') config_h.set10('DEBUG', true) endif @@ -112,7 +118,7 @@ endif # Driver -src_wacom = [ +src_wacom_core = [ config_ver_h, 'src/WacomInterface.h', 'src/wcmCommon.c', @@ -126,6 +132,9 @@ src_wacom = [ 'src/xf86WacomDefs.h', 'src/xf86Wacom.h', 'src/wcmUSB.c', +] + +src_wacom = src_wacom_core + [ 'src/x11/xf86WacomProperties.c', 'src/x11/xf86Wacom.c', ] @@ -162,6 +171,86 @@ configure_file( install_dir: dir_pkgconf, ) +# GWacom library +# This is not a stable library interface + +dep_glib = dependency('glib-2.0', required: get_option('wacom-gobject')) +dep_gobject = dependency('gobject-2.0', required: get_option('wacom-gobject')) +dep_gio = dependency('gio-2.0', required: get_option('wacom-gobject')) +dep_gio_unix = dependency('gio-unix-2.0', required: get_option('wacom-gobject')) +dep_object_ir = dependency('gobject-introspection-1.0', required: get_option('wacom-gobject')) + +build_gwacom = dep_glib.found() and dep_gobject.found() and dep_gio.found() and dep_gio_unix.found() and dep_object_ir.found() + +if build_gwacom + gnome = import('gnome') + + src_gwacom = [ + 'src/gwacom/wacom-driver.h', + 'src/gwacom/wacom-driver.c', + 'src/gwacom/wacom-device.h', + 'src/gwacom/wacom-device.c', + ] + + src_libgwacom = src_wacom_core + src_gwacom + [ + 'src/wcmISDV4-stub.c', + ] + + deps_gwacom = [ + dep_xserver, + dep_m, + dep_glib, + dep_gobject, + dep_gio, + dep_gio_unix, + ] + + lib_gwacom = both_libraries( + 'gwacom', + src_libgwacom + src_wacom_core, + include_directories: [dir_src, dir_include], + dependencies: deps_gwacom, + install: false, + c_args: ['-fvisibility=default'], + ) + + dep_gwacom = declare_dependency( + link_with: lib_gwacom, + dependencies: deps_gwacom, + include_directories: ['src/gwacom/'], + ) + + # GIR bindings are not installed, use + # export GI_TYPELIB_PATH="$PWD/builddir:$LD_LIBRARY_PATH" + # export LD_LIBRARY_PATH="$PWD/builddir:$LD_LIBRARY_PATH" + dep_gir = gnome.generate_gir(lib_gwacom, + sources: src_gwacom, + dependencies: [deps_gwacom], + namespace: 'wacom', + nsversion: '@0@.@1@'.format(wacom_version[0], wacom_version[1]), + identifier_prefix: 'Wacom', + symbol_prefix: 'wacom', + includes: 'GObject-2.0', + install: false, + ) + + # A custom target that forces the GIR bindings to be built. Usually + # those get built on install but we don't install ours. + custom_target('force GIR build', + build_by_default: true, + command: ['echo'], + output: 'force-gir-build.dummy', + capture: true, + depends: [dep_gir]) + + executable('wacom-record', + 'tools/wacom-record.c', + config_ver_h, + dependencies: [dep_libudev, dep_glib, dep_gwacom], + install: false, + ) +endif + # Tools if get_option('serial-device-support') src_shared = [ diff --git a/meson_options.txt b/meson_options.txt index 5a503cc..c552b45 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -43,3 +43,8 @@ option('serial-device-support', value: true, description: 'Build with ISDV4 serial support' ) +option('wacom-gobject', + type: 'feature', + value: 'auto', + description: 'Build the Wacom GObject library and associated tools [default: auto]' +) diff --git a/src/gwacom/wacom-device.c b/src/gwacom/wacom-device.c new file mode 100644 index 0000000..2c61b99 --- /dev/null +++ b/src/gwacom/wacom-device.c @@ -0,0 +1,936 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* THIS IS NOT STABLE API, USE WITHIN THIS REPO ONLY */ + +#include "config.h" +#include "wacom-device.h" +#include "wacom-private.h" +#include <stdarg.h> +#include <stdlib.h> +#include <fcntl.h> +#include <gio/gio.h> + + +#include "xf86Wacom.h" + +struct _WacomOptions { + GObject parent_instance; + GHashTable *ht; +}; + +struct _WacomDevice { + GObject parent_instance; + WacomDriver *driver; + + guint id; /* incrementing ID */ + + WacomOptions *options; + int fd; + char *name; + char *path; + + enum WacomToolType type; + + gboolean enabled; + + WacomDevicePtr priv; + + int nbuttons; + int naxes; + int ntouches; + gboolean has_keys; + gboolean is_absolute; + gboolean is_direct_touch; + + guint axis_mask; + WacomAxis axes[32]; /* ffs of the type is the axis index */ + + GIOChannel *channel; + guint watch; +}; + +G_DEFINE_TYPE (WacomOptions, wacom_options, G_TYPE_OBJECT) +G_DEFINE_TYPE (WacomDevice, wacom_device, G_TYPE_OBJECT) +G_DEFINE_BOXED_TYPE (WacomAxis, wacom_axis, wacom_axis_copy, wacom_axis_free) + +WacomOptions *wacom_options_new(const char *key, ...) +{ + g_autoptr(WacomOptions) opts = g_object_new(WACOM_TYPE_OPTIONS, NULL); + GHashTable *ht = opts->ht; + va_list args; + const char *k, *v; + + va_start(args, key); + k = key; + while (k) { + v = va_arg(args, const char *); + g_return_val_if_fail(v != NULL, NULL); + g_hash_table_replace(ht, g_strdup(k), g_strdup(v)); + k = va_arg(args, const char *); + } + va_end(args); + + return g_steal_pointer(&opts); +} + +static void copy_options(gpointer key, gpointer value, gpointer user_data) +{ + WacomOptions *dup = user_data; + wacom_options_set(dup, key, value); +} + +WacomOptions *wacom_options_duplicate(WacomOptions *opts) +{ + WacomOptions *dup = wacom_options_new(NULL, NULL); + g_hash_table_foreach(opts->ht, copy_options, dup); + return dup; +} + +const char *wacom_options_get(WacomOptions *opts, const char *key) +{ + g_return_val_if_fail(key != NULL, NULL); + + g_autofree char *lower = g_ascii_strdown(key, -1); + return g_hash_table_lookup(opts->ht, lower); +} + +void wacom_options_set(WacomOptions *opts, const char *key, const char *value) +{ + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + g_hash_table_replace(opts->ht, g_ascii_strdown(key, -1), g_strdup(value)); +} + +static void +wacom_options_finalize(GObject *gobject) +{ + WacomOptions *opts = WACOM_OPTIONS(gobject); + g_hash_table_destroy(opts->ht); + G_OBJECT_CLASS (wacom_options_parent_class)->finalize (gobject); +} + +static void +wacom_options_class_init(WacomOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wacom_options_finalize; +} + +static void +wacom_options_init(WacomOptions *self) +{ + g_autoptr(GHashTable) ht = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + /* Pretend this is a udev device */ + g_hash_table_replace(ht, g_strdup("_source"), g_strdup("server/udev")); + + self->ht = g_steal_pointer(&ht); +} + +enum { + PROP_0, + PROP_NAME, + PROP_ENABLED, + PROP_PATH, + + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +enum { + SIGNAL_KEY, + SIGNAL_BUTTON, + SIGNAL_MOTION, + SIGNAL_TOUCH, + SIGNAL_PROXIMITY, + + SIGNAL_LOGMSG, /* A log message from the driver */ + SIGNAL_DBGMSG, /* A debug message from the driver */ + + SIGNAL_READ_ERROR, + + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +WacomDevice* +wacom_device_new(WacomDriver *driver, + const char *name, + WacomOptions *options) +{ + static guint deviceid = 0; + WacomDevice *device = g_object_new(WACOM_TYPE_DEVICE, NULL); + WacomDevicePtr priv = wcmAllocate(device, name); + + device->id = deviceid++; + device->driver = g_object_ref(driver); + device->priv = priv; + device->name = g_strdup(name); + device->options = wacom_options_duplicate(options); + + wacom_driver_add_device(driver, device); + + return device; +} + +void +wacom_device_remove(WacomDevice *device) +{ + wcmUnInit(device->priv); + wacom_driver_remove_device(device->driver, device); +} + +gboolean +wacom_device_preinit(WacomDevice *device) +{ + gboolean rc = wcmPreInit(device->priv) == Success; + + device->type = (enum WacomToolType)device->priv->type; + + return rc; +} + +gboolean +wacom_device_setup(WacomDevice *device) +{ + return wcmDevInit(device->priv); +} + +static gboolean read_device(GIOChannel *source, GIOCondition condition, gpointer data) +{ + WacomDevice *device = data; + int rc; + + do { + rc = wcmReadPacket(device->priv); + } while (rc > 0); + + return rc == 0; +} + +gboolean +wacom_device_enable(WacomDevice *device) +{ + + g_return_val_if_fail(!device->enabled, true); + + if (!wcmDevOpen(device->priv) || ! wcmDevStart(device->priv)) + return false; + + device->channel = g_io_channel_unix_new(device->fd); + device->watch = g_io_add_watch(device->channel, G_IO_IN, + read_device, device); + + device->enabled = true; + g_object_notify_by_pspec(G_OBJECT(device), obj_properties[PROP_ENABLED]); + + return true; +} + +void +wacom_device_disable(WacomDevice *device) +{ + g_return_if_fail(device->enabled); + + wcmDevStop(device->priv); + wcmDevClose(device->priv); + + device->enabled = false; + g_object_notify_by_pspec(G_OBJECT(device), obj_properties[PROP_ENABLED]); +} + +WacomDriver *wacom_device_get_driver(WacomDevice *device) +{ + return device->driver; +} +void *wacom_device_get_impl(WacomDevice *device) +{ + return device->priv; +} + +/****************** Driver layer *****************/ + +int +wcmGetFd(WacomDevicePtr priv) +{ + WacomDevice *device = priv->frontend; + return device->fd; +} + +void +wcmSetFd(WacomDevicePtr priv, int fd) +{ + WacomDevice *device = priv->frontend; + device->fd = fd; +} + +void +wcmSetName(WacomDevicePtr priv, const char *name) +{ + WacomDevice *device = priv->frontend; + g_free(device->name); + device->name = g_strdup(name); +} + +__attribute__((__format__(__printf__ , 3, 0))) +static void wcmLogDevice(WacomDevicePtr priv, WacomLogType type, + const char *format, va_list args) +{ + WacomDevice *device = priv->frontend; + const char *prefix = "."; + g_autofree char *str = NULL; + + switch (type) { + case W_PROBED: prefix = "p"; break; + case W_CONFIG: prefix = "c"; break; + case W_DEFAULT: prefix = "d"; break; + case W_CMDLINE: prefix = "c"; break; + case W_NOTICE: prefix = "N"; break; + case W_ERROR: prefix = "E"; break; + case W_WARNING: prefix = "W"; break; + case W_INFO: prefix = "I"; break; + case W_NONE: prefix = "-"; break; + case W_NOT_IMPLEMENTED: prefix = "N/I"; break; + case W_DEBUG: prefix = "D"; break; + case W_UNKNOWN: prefix = "?"; break; + } + + str = g_strdup_vprintf(format, args); + g_signal_emit(device, signals[SIGNAL_LOGMSG], 0, prefix, str); +} + +void wcmLog(WacomDevicePtr priv, WacomLogType type, const char *format, ...) +{ + va_list args; + va_start(args, format); + wcmLogDevice(priv, type, format, args); + va_end(args); +} + +/* identical to wcmLog, we don't need sigsafe logging */ +void wcmLogSafe(WacomDevicePtr priv, WacomLogType type, const char *format, ...) +{ + va_list args; + va_start(args, format); + wcmLogDevice(priv, type, format, args); + va_end(args); +} + +void +wcmLogDebugDevice(WacomDevicePtr priv, int debug_level, const char *func, const char *format, ...) +{ + WacomDevice *device = priv->frontend; + g_autofree char *str = NULL; + va_list args; + + va_start(args, format); + str = g_strdup_vprintf(format, args); + va_end(args); + + g_signal_emit(device, signals[SIGNAL_DBGMSG], 0, debug_level, func, str); +} + +void wcmLogCommon(WacomCommonPtr common, WacomLogType type, const char *format, ...) +{ + va_list args; + va_start(args, format); + /* We just log the common ones through the first device in the list, + * probably good enough */ + wcmLogDevice(common->wcmDevices, type, format, args); + va_end(args); +} + +/* identical to wcmLogCommon, we don't need sigsafe logging */ +void wcmLogCommonSafe(WacomCommonPtr common, WacomLogType type, const char *format, ...) +{ + va_list args; + va_start(args, format); + /* We just log the common ones through the first device in the list, + * probably good enough */ + wcmLogDevice(common->wcmDevices, type, format, args); + va_end(args); +} + +void +wcmLogDebugCommon(WacomCommonPtr common, int debug_level, const char *func, const char *format, ...) +{ + /* We just log the common ones through the first device in the list, + * probably good enough */ + WacomDevice *device = common->wcmDevices->frontend; + g_autofree char *str = NULL; + va_list args; + + va_start(args, format); + str = g_strdup_vprintf(format, args); + va_end(args); + + g_signal_emit(device, signals[SIGNAL_DBGMSG], 0, debug_level, func, str); +} + +char *wcmOptGetStr(WacomDevicePtr priv, const char *key, const char *default_value) +{ + WacomDevice *dev = priv->frontend; + WacomOptions *opts = dev->options; + const char *value = wacom_options_get(opts, key); + + if (!value) + value = default_value; + + return g_strdup(value); +} + +int wcmOptGetInt(WacomDevicePtr priv, const char *key, int default_value) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%d", default_value); + char *str = wcmOptGetStr(priv, key, buf); + int value = default_value; + if (str) + value = atoi(str); + g_free(str); + return value; +} + +bool wcmOptGetBool(WacomDevicePtr priv, const char *key, bool default_value) +{ + char *str = wcmOptGetStr(priv, key, default_value ? "true" : "false"); + bool is_true = false; + + if (g_str_equal(str, "true") || + g_str_equal(str, "yes") || + g_str_equal(str, "on") || + g_str_equal(str, "1")) + is_true = true; + g_free(str); + return is_true; +} + +/* Get the option of the given type, quietly (without logging) */ +char *wcmOptCheckStr(WacomDevicePtr priv, const char *key, const char *default_value) +{ + return wcmOptGetStr(priv, key, default_value); +} + +int wcmOptCheckInt(WacomDevicePtr priv, const char *key, int default_value) +{ + return wcmOptGetInt(priv, key, default_value); +} + +bool wcmOptCheckBool(WacomDevicePtr priv, const char *key, bool default_value) +{ + return wcmOptGetBool(priv, key, default_value); +} + +/* Change the option to the new value */ +void wcmOptSetStr(WacomDevicePtr priv, const char *key, const char *value) +{ + WacomDevice *device = priv->frontend; + WacomOptions *opts = device->options; + + wacom_options_set(opts, key, value); +} + +void wcmOptSetInt(WacomDevicePtr priv, const char *key, int value) +{ + char buf[64]; + snprintf(buf, sizeof(buf), "%d", value); + wcmOptSetStr(priv, key, buf); +} + +void wcmOptSetBool(WacomDevicePtr priv, const char *key, bool value) +{ + wcmOptSetStr(priv, key, value ? "true" : "false"); +} + +void wcmEmitKeycode(WacomDevicePtr priv, int keycode, int state) +{ + WacomDevice *device = priv->frontend; + g_signal_emit(device, signals[SIGNAL_KEY], 0, keycode, state); +} + +void wcmEmitProximity(WacomDevicePtr priv, bool is_proximity_in, + const WacomAxisData *axes) +{ + WacomDevice *device = priv->frontend; + g_signal_emit(device, signals[SIGNAL_PROXIMITY], 0, is_proximity_in, axes); +} + +void wcmEmitMotion(WacomDevicePtr priv, bool is_absolute, const WacomAxisData *axes) +{ + WacomDevice *device = priv->frontend; + g_signal_emit(device, signals[SIGNAL_MOTION], 0, is_absolute, axes); +} + +void wcmEmitButton(WacomDevicePtr priv, bool is_absolute, int button, bool is_press, const WacomAxisData *axes) +{ + WacomDevice *device = priv->frontend; + g_signal_emit(device, signals[SIGNAL_BUTTON], 0, is_absolute, button, is_press, axes); +} + +void wcmEmitTouch(WacomDevicePtr priv, int type, unsigned int touchid, int x, int y) +{ + WacomDevice *device = priv->frontend; + enum WacomTouchState state; + + switch (type) { + case XI_TouchBegin: state = WACOM_TOUCH_BEGIN; break; + case XI_TouchUpdate: state = WACOM_TOUCH_UPDATE; break; + case XI_TouchEnd: state = WACOM_TOUCH_END; break; + default: + abort(); + } + g_signal_emit(device, signals[SIGNAL_TOUCH], 0, state, touchid, x, y); +} + +void wcmInitAxis(WacomDevicePtr priv, enum WacomAxisType type, + int min, int max, int res) +{ + WacomDevice *device = priv->frontend; + WacomAxis ax = { + .type = (enum WacomEventAxis)type, + .min = min, + .max = max, + .res = res, + }; + device->axes[ffs(type)] = ax; + device->axis_mask |= (enum WacomEventAxis)type; +} + +bool wcmInitButtons(WacomDevicePtr priv, unsigned int nbuttons) +{ + WacomDevice *device = priv->frontend; + device->nbuttons = nbuttons; + return true; +} + +bool wcmInitKeyboard(WacomDevicePtr priv) +{ + WacomDevice *device = priv->frontend; + device->has_keys = true; + return true; +} + +bool wcmInitPointer(WacomDevicePtr priv, int naxes, bool is_absolute) +{ + WacomDevice *device = priv->frontend; + device->naxes = naxes; + device->is_absolute = is_absolute; + return true; +} + +bool wcmInitTouch(WacomDevicePtr priv, int ntouches, bool is_direct_touch) +{ + WacomDevice *device = priv->frontend; + device->ntouches = ntouches; + device->is_direct_touch = is_direct_touch; + return true; +} + +guint wacom_device_get_id(WacomDevice *device) +{ + return device->id; +} + +const char *wacom_device_get_name(WacomDevice *device) +{ + return device->name; +} + +enum WacomToolType wacom_device_get_tool_type(WacomDevice *device) +{ + return device->type; +} + +const WacomAxis *wacom_device_get_axis(WacomDevice *device, + enum WacomEventAxis which) +{ + g_return_val_if_fail(which <= WACOM_RING2, NULL); + + return (device->axis_mask & which) ? &device->axes[ffs(which)] : NULL; +} + +int wacom_device_get_num_buttons(WacomDevice *device) +{ + return device->nbuttons; +} +gboolean wacom_device_has_keys(WacomDevice *device) +{ + return device->has_keys; +} +int wacom_device_get_num_touches(WacomDevice *device) +{ + return device->ntouches; +} +gboolean wacom_device_is_direct_touch(WacomDevice *device) +{ + return device->is_direct_touch; +} +gboolean wacom_device_is_absolute(WacomDevice *device) +{ + return device->is_absolute; +} +int wacom_device_get_num_axes(WacomDevice *device) +{ + return device->naxes; +} + +int wcmOpen(WacomDevicePtr priv) +{ + WacomDevice *device = priv->frontend; + const char *path = wacom_options_get(device->options, "device"); + int fd; + + if (!path) { + wcmLog(priv, W_ERROR, "Error opending device, no option 'device'\n"); + return -ENODEV; + } + + fd = open(path, O_RDONLY|O_NONBLOCK); + + g_free(device->path); + device->path = strdup(path); + g_object_notify_by_pspec(G_OBJECT(device), obj_properties[PROP_PATH]); + + return fd != -1 ? fd : -errno; +} + + +void wcmClose(WacomDevicePtr priv) +{ + WacomDevice *device = priv->frontend; + + close(device->fd); + device->fd = -1; +} + +/* FIXME: the bits we emulate in the driver don't depend on timers, so we just + * have a stub timer implementation that does nothing */ +WacomTimerPtr wcmTimerNew(void) +{ + return calloc(1, 1); +} + +void wcmTimerFree(WacomTimerPtr timer) +{ + g_free(timer); +} + +void wcmTimerCancel(WacomTimerPtr timer) +{ +} + +void wcmTimerSet(WacomTimerPtr timer, uint32_t millis, WacomTimerCallback func, void *userdata) +{ +} + +void wcmUpdateSerialProperty(WacomDevicePtr priv) {} +void wcmUpdateHWTouchProperty(WacomDevicePtr priv) {} +void wcmUpdateRotationProperty(WacomDevicePtr priv) {} + +struct hotplug { + WacomDriver *driver; + char *name; + WacomOptions *opts; +}; + +static gboolean hotplugDevice(gpointer data) +{ + struct hotplug *hotplug = data; + g_autoptr(WacomDevice) new_device = wacom_device_new(hotplug->driver, hotplug->name, hotplug->opts); + + g_object_unref(hotplug->driver); + g_object_unref(hotplug->opts); + g_free(hotplug->name); + g_free(hotplug); + + g_return_val_if_fail(new_device != NULL, FALSE); /* silence 'unused variable device' compiler warning */ + + /* WacomDriver holds the device ref */ + return FALSE; +} + +void wcmQueueHotplug(WacomDevicePtr priv, const char* name, const char *type, int serial) +{ + WacomDevice *device = priv->frontend; + struct hotplug *hotplug = g_new0(struct hotplug, 1); + WacomOptions *new_opts = wacom_options_duplicate(device->options); + char buf[64] = {0}; + + wacom_options_set(new_opts, "Type", type); + if (serial > -1) { + snprintf(buf, sizeof(buf), "0x%x", serial); + wacom_options_set(new_opts, "Serial", buf); + } + + hotplug->name = g_strdup(name); + hotplug->opts = g_steal_pointer(&new_opts); + hotplug->driver = g_object_ref(device->driver); + + g_idle_add(hotplugDevice, hotplug); +} + +uint32_t wcmTimeInMillis(void) +{ + return (uint32_t)g_get_monotonic_time(); +} + +/****************** GObject boilerplate *****************/ + +static void +wacom_device_dispose(GObject *gobject) +{ + G_OBJECT_CLASS (wacom_device_parent_class)->dispose (gobject); +} + +static void +wacom_device_finalize(GObject *gobject) +{ + WacomDevice *device = WACOM_DEVICE(gobject); + g_free(device->path); + g_object_unref(device->driver); + G_OBJECT_CLASS (wacom_device_parent_class)->finalize (gobject); +} + +static void +wacom_device_get_property(GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + WacomDevice *self = WACOM_DEVICE(object); + + switch (property_id) { + case PROP_NAME: + g_value_set_string(value, self->name); + break; + case PROP_ENABLED: + g_value_set_boolean(value, self->enabled); + break; + case PROP_PATH: + g_value_set_string(value, self->path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void +wacom_device_class_init(WacomDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wacom_device_dispose; + object_class->finalize = wacom_device_finalize; + object_class->get_property = wacom_device_get_property; + + /** + * WacomDevice:name + */ + obj_properties[PROP_NAME] = + g_param_spec_string("name", + "Device name", + "The device's name", + NULL, /* default value */ + G_PARAM_READABLE); + g_object_class_install_property(object_class, PROP_NAME, + obj_properties[PROP_NAME]); + + /** + * WacomDevice:enabled + */ + obj_properties[PROP_ENABLED] = + g_param_spec_boolean("enabled", + "Enabled indicator", + "Indicates if the device is enabled", + FALSE, + G_PARAM_READABLE); + g_object_class_install_property(object_class, PROP_ENABLED, + obj_properties[PROP_ENABLED]); + + /** + * WacomDevice:path + */ + obj_properties[PROP_PATH] = + g_param_spec_string("path", + "Device path", + "The device's event node", + NULL /* default value */, + G_PARAM_READABLE); + g_object_class_install_property(object_class, PROP_PATH, + obj_properties[PROP_PATH]); + + /** + * WacomDevice::keycode: + * @device: the device that sent the event + * @keycode: the keycode + * @is_press: true for down, false for up + * + * The keycode signal is emitted whenever the device sends a key event + */ + signals[SIGNAL_KEY] = + g_signal_new("keycode", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* key, is_press */ + 2, G_TYPE_UINT, G_TYPE_BOOLEAN); + + /** + * WacomDevice::button: + * @device: the device that sent the event + * @is_absolute: true if the axis values are absolute + * @button: the 1-indexed button number + * @is_press: true for down, false for up + * @axes: a WacomEventData pointer + * + * The button signal is emitted whenever the device sends a button + * event + */ + signals[SIGNAL_BUTTON] = + g_signal_new("button", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* is_absolute, button, is_press, axes */ + 4, G_TYPE_BOOLEAN, G_TYPE_UINT, + G_TYPE_BOOLEAN, G_TYPE_POINTER); + + /** + * WacomDevice::motion: + * @device: the device that sent the event + * @is_absolute: true if the axis values are absolute + * @axes: a WacomEventData pointer + * + * The motion signal is emitted whenever the device changes position + */ + signals[SIGNAL_MOTION] = + g_signal_new("motion", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* is_absolute, axes */ + 2, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + /** + * WacomDevice::touch: + * @device: the device that sent the event + * @type: one of WacomTouchState + * @touchid: an unsigned integer with the touch ID of this touch + * @x: x position + * @y: y position + * + * The touch signal is emitted whenever a touch starts/moves/stops + */ + signals[SIGNAL_TOUCH] = + g_signal_new("touch", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* type, touchid, x, y */ + 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_INT, G_TYPE_INT); + + /** + * WacomDevice::proximity: + * @device: the device that sent the event + * @is_prox_in: true for proximity in, false for proxiity out + * @axes: a WacomEventData pointer + * + * The proximity signal is emitted whenever the device enters of + * leaves proximity + */ + signals[SIGNAL_PROXIMITY] = + g_signal_new("proximity", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* is_prox_in, axes */ + 2, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + /** + * WacomDevice::log-message: + * @device: the device that sent the event + * @type: a string to use as prefix + * @msg: the log message + * + * The log-message signal is emitted whenever the device is trying to + * write a message to the regular log + */ + signals[SIGNAL_LOGMSG] = + g_signal_new("log-message", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* type, string */ + 2, G_TYPE_STRING, G_TYPE_STRING); + + + /** + * WacomDevice::debug-message: + * @device: the device that sent the event + * @level: the numerical debug level for this message + * @func: function where the debug message was triggered + * @msg: the debug message + * + * The debug-message signal is emitted whenever the device is trying to + * write a debug messag + */ + signals[SIGNAL_DBGMSG] = + g_signal_new("debug-message", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* level, func, string */ + 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING); + + /** + * WacomDevice::read-error: + * @device: the device that sent the event + * @errno: the errno that was detected + * + * The read-error signal is emitted whenever that was an error reading + * the device. + */ + signals[SIGNAL_READ_ERROR] = + g_signal_new("read-error", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + /* errno */ + 1, G_TYPE_INT); +} + +static void +wacom_device_init(WacomDevice *self) +{ +} + +WacomAxis* wacom_axis_copy(const WacomAxis *axis) +{ + WacomAxis *new_axis = malloc(sizeof(*axis)); + memcpy(new_axis, axis, sizeof(*axis)); + return new_axis; +} + +void wacom_axis_free(WacomAxis *axis) +{ + free(axis); +} diff --git a/src/gwacom/wacom-device.h b/src/gwacom/wacom-device.h new file mode 100644 index 0000000..2108661 --- /dev/null +++ b/src/gwacom/wacom-device.h @@ -0,0 +1,198 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* THIS IS NOT STABLE API, USE WITHIN THIS REPO ONLY */ + +#pragma once + +#include <stdint.h> +#include <glib.h> +#include <glib-object.h> + +#include "wacom-driver.h" + +G_BEGIN_DECLS + +#define WACOM_TYPE_OPTIONS (wacom_options_get_type()) +G_DECLARE_FINAL_TYPE (WacomOptions, wacom_options, WACOM, OPTIONS, GObject); + +/** + * wacom_options_new: (skip) + * @key: (nullable): The first key, followed by a const char * value, followed + * by other key/valuy pairs, terminated with a NULL key. + * + * Create a new set of configuration options in key-value format, matching the + * xorg.conf options the driver supports. Parsing of the varargs stops when + * the next key is NULL. + * + * Use g_object_unref() to free the returned object. + * + * Returns: (transfer full): a new WacomOptions object + */ +__attribute__((sentinel)) +WacomOptions *wacom_options_new(const char *key, ...); + +/** + * wacom_options_duplicate: + * + * Returns: (transfer full): a deep copy of the options given as argument. + */ +WacomOptions *wacom_options_duplicate(WacomOptions *opts); + +/** + * wacom_options_get: + * + * Return the value for the given key or NULL if the key does not exist. + */ +const char *wacom_options_get(WacomOptions *opts, const char *key); + +/** + * wacom_options_set: + * + * Add or replace the option with the given string value. + */ +void wacom_options_set(WacomOptions *opts, const char *key, const char *value); + + +#define WACOM_TYPE_DEVICE (wacom_device_get_type()) +G_DECLARE_FINAL_TYPE (WacomDevice, wacom_device, WACOM, DEVICE, GObject) + +enum WacomToolType { + WACOM_TOOL_INVALID = 0, + WACOM_TOOL_STYLUS, + WACOM_TOOL_ERASER, + WACOM_TOOL_CURSOR, + WACOM_TOOL_PAD, + WACOM_TOOL_TOUCH, +}; + +enum WacomTouchState { + WACOM_TOUCH_BEGIN, + WACOM_TOUCH_UPDATE, + WACOM_TOUCH_END, +}; + +enum WacomEventAxis { + WACOM_X = (1 << 0), + WACOM_Y = (1 << 1), + WACOM_PRESSURE = (1 << 2), + WACOM_TILT_X = (1 << 3), + WACOM_TILT_Y = (1 << 4), + WACOM_STRIP_X = (1 << 5), + WACOM_STRIP_Y = (1 << 6), + WACOM_ROTATION = (1 << 7), + WACOM_THROTTLE = (1 << 8), + WACOM_WHEEL = (1 << 9), + WACOM_RING = (1 << 10), + WACOM_RING2 = (1 << 11), +}; + +/* The pointer argument to all the event signals. If the mask is set for + * a given axis, that value contains the current state of the axis */ +typedef struct { + uint32_t mask; /* bitmask of WacomEventAxis */ + int x, y; + int pressure; + int tilt_x, tilt_y; + int strip_x, strip_y; + int rotation; + int throttle; + int wheel; + int ring, ring2; +} WacomEventData; + +#define WACOM_TYPE_AXIS (wacom_axis_get_type()) + +typedef struct { + enum WacomEventAxis type; + int min, max; + int res; +} WacomAxis; + +GType wacom_axis_get_type(void); +WacomAxis* wacom_axis_copy(const WacomAxis *axis); +void wacom_axis_free(WacomAxis *axis); + +/** + * wacom_device_new: + * + * Note that the driver may rename the device, the name is not guaranteed to + * be the device's name. + * + * If options does not contain a key "Device" with the path to the event node, + * the driver will auto-detect the event node during PreInit. + * + * Use wacom_device_preinit() to pre-init the device and verify it can be + * used. Then call wacom_device_setup() to initialize the device with the + * right axes, followed by wacom_device_enable() to have it process events. + * + * You should connect to the log-message and debug-message signals before + * calling wacom_device_preinit(). + * + * Use g_object_unref() to free the returned object. + * + * @driver The driver to assign to + * @param name (trqnsfer none) The device name + * @param options (transfer none) The device name + * + * Returns: (transfer full): a new allocated device witha refcount of at + * least 1 + */ +WacomDevice *wacom_device_new(WacomDriver *driver, + const char *name, + WacomOptions *options); + +void wacom_device_remove(WacomDevice *device); + +gboolean wacom_device_preinit(WacomDevice *device); +gboolean wacom_device_setup(WacomDevice *device); +gboolean wacom_device_enable(WacomDevice *device); +void wacom_device_disable(WacomDevice *device); + +/** + * wacom_device_get_id: + * + * A numeric value assigned to the device by this wrapper library during + * wacom_device_new(). This value serves the same purpose as the X11 device + * ID. + */ +guint wacom_device_get_id(WacomDevice *device); +const char *wacom_device_get_name(WacomDevice *device); +enum WacomToolType wacom_device_get_tool_type(WacomDevice *device); + +/* The following getters are only available after wacom_device_setup() */ + +int wacom_device_get_num_buttons(WacomDevice *device); +gboolean wacom_device_has_keys(WacomDevice *device); +int wacom_device_get_num_touches(WacomDevice *device); +gboolean wacom_device_is_direct_touch(WacomDevice *device); +gboolean wacom_device_is_absolute(WacomDevice *device); +int wacom_device_get_num_axes(WacomDevice *device); + +/** + * wacom_device_get_axis: + * + * This function is only available after wacom_device_setup() + * + * Returns: (transfer none): the axis of this device or NULL for an invalid + * index. + */ +const WacomAxis* wacom_device_get_axis(WacomDevice *device, + enum WacomEventAxis which); + +G_END_DECLS diff --git a/src/gwacom/wacom-driver.c b/src/gwacom/wacom-driver.c new file mode 100644 index 0000000..bc9d6bf --- /dev/null +++ b/src/gwacom/wacom-driver.c @@ -0,0 +1,149 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "wacom-driver.h" +#include "wacom-private.h" + +#include "xf86Wacom.h" + +struct _WacomDriver { + GObject parent_instance; + + GList *devices; +}; + +enum { + SIGNAL_DEVICE_ADDED, + SIGNAL_DEVICE_REMOVED, + LAST_SIGNAL, +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (WacomDriver, wacom_driver, G_TYPE_OBJECT) + +int wcmForeachDevice(WacomDevicePtr priv, WacomDeviceCallback func, void *data) +{ + WacomDevice *device = priv->frontend; + WacomDriver *driver = wacom_device_get_driver(device); + GList *elem = driver->devices; + int nmatch = 0; + + while (elem) + { + WacomDevice *d = elem->data; + int rc = func(wacom_device_get_impl(d), data); + elem = elem->next; + if (rc == -ENODEV) + continue; + + if (rc < 0) + return -rc; + nmatch += 1; + if (rc == 0) + break; + } + + return nmatch; +} + +WacomDriver* +wacom_driver_new(void) +{ + return g_object_new(WACOM_TYPE_DRIVER, NULL); +} + +GList * +wacom_driver_get_devices(WacomDriver *driver) +{ + return g_list_copy(driver->devices); +} + +void +wacom_driver_add_device(WacomDriver *driver, WacomDevice *device) +{ + driver->devices = g_list_append(driver->devices, g_object_ref(device)); + g_signal_emit(driver, signals[SIGNAL_DEVICE_ADDED], 0, device); +} + +void +wacom_driver_remove_device(WacomDriver *driver, WacomDevice *device) +{ + if (g_list_find(driver->devices, device)) { + driver->devices = g_list_remove(driver->devices, device); + g_signal_emit(driver, signals[SIGNAL_DEVICE_REMOVED], 0, device); + g_object_unref(device); + } +} + +static void +wacom_driver_dispose(GObject *gobject) +{ + G_OBJECT_CLASS (wacom_driver_parent_class)->dispose (gobject); +} + +static void +wacom_driver_finalize(GObject *gobject) +{ + G_OBJECT_CLASS (wacom_driver_parent_class)->finalize (gobject); +} + + +static void +wacom_driver_class_init(WacomDriverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wacom_driver_dispose; + object_class->finalize = wacom_driver_finalize; + + /** + * WacomDriver::device-added: + * @driver: the driver instance + * @device: the device that was added + * + * The device-added signal is emitted whenever a device is added. A + * caller should connect to the device's signals as required and + * initialize and enable the device. + */ + signals[SIGNAL_DEVICE_ADDED] = + g_signal_new("device-added", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + 1, WACOM_TYPE_DEVICE); + + /** + * WacomDriver::device-removed: + * @driver: the driver instance + * @device: the device that was removed + */ + signals[SIGNAL_DEVICE_REMOVED] = + g_signal_new("device-removed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, + 1, WACOM_TYPE_DEVICE); +} + +static void +wacom_driver_init(WacomDriver *self) +{ +} + diff --git a/src/gwacom/wacom-driver.h b/src/gwacom/wacom-driver.h new file mode 100644 index 0000000..a6a52f8 --- /dev/null +++ b/src/gwacom/wacom-driver.h @@ -0,0 +1,48 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define WACOM_TYPE_DRIVER (wacom_driver_get_type()) +G_DECLARE_FINAL_TYPE (WacomDriver, wacom_driver, WACOM, DRIVER, GObject) + + +/** + * wacom_driver_new: + * + * Use g_object_unref() to release the driver object + * + * Returns: (transfer full): a new WacomDriver object + */ +WacomDriver *wacom_driver_new(void); + +/** + * wacom_driver_get_devices: + * + * Returns: (element-type WacomDevice) (transfer container): + */ +GList *wacom_driver_get_devices(WacomDriver *driver); + +G_END_DECLS + + diff --git a/src/gwacom/wacom-private.h b/src/gwacom/wacom-private.h new file mode 100644 index 0000000..f8c7fa1 --- /dev/null +++ b/src/gwacom/wacom-private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include "wacom-driver.h" +#include "wacom-device.h" + + +WacomDriver *wacom_device_get_driver(WacomDevice *device); +void *wacom_device_get_impl(WacomDevice *device); + +void wacom_driver_add_device(WacomDriver *driver, WacomDevice *device); +void wacom_driver_remove_device(WacomDriver *driver, WacomDevice *device); diff --git a/src/x11/xf86Wacom.c b/src/x11/xf86Wacom.c index 5babf82..df74d5c 100644 --- a/src/x11/xf86Wacom.c +++ b/src/x11/xf86Wacom.c @@ -246,7 +246,6 @@ static InputOption *wcmOptionDupConvert(WacomDevicePtr priv, const char* name, c WacomToolPtr ser = common->serials; InputOption *iopts = NULL; pointer options, o; - int rc; options = xf86OptionListDuplicate(original); options = xf86ReplaceStrOption(options, "Type", type); diff --git a/tools/wacom-record.c b/tools/wacom-record.c new file mode 100644 index 0000000..63e86fa --- /dev/null +++ b/tools/wacom-record.c @@ -0,0 +1,367 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "config-ver.h" + +#include <stdbool.h> +#include <stdio.h> +#include <glib.h> +#include <glib-unix.h> +#include <libudev.h> +#include "wacom-driver.h" +#include "wacom-device.h" + +#define strbool(x_) (x_) ? "true" : "false" + +static guint debug_level = 0; +static gboolean print_version = false; +static gboolean grab_device = false; +static const char *driver_options = NULL; + +static GOptionEntry opts[] = +{ + { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Print version and exit", NULL }, + { "debug-level", 'v', 0, G_OPTION_ARG_INT, &debug_level, "Set the debug level", NULL }, + { "options", 0, 0, G_OPTION_ARG_STRING, &driver_options, "Driver options in the form \"Foo=bar,Baz=bat\"", NULL }, + { "grab", 0, 0, G_OPTION_ARG_NONE, &grab_device, "Grab the device while recording", NULL }, + { NULL }, +}; + +static void log_message(WacomDevice *device, const char *type, const char *message) +{ + printf("# [%s] %s: %s", type, wacom_device_get_name(device), message); +} + +static void debug_message(WacomDevice *device, int debug_level, const char *func, const char *message) +{ + printf("# DBG%02d %-35s| %s: %s", debug_level, func, wacom_device_get_name(device), message); +} + +static inline void print_axes(const WacomEventData *data) +{ + printf(" axes: { x: %5d, y: %5d, pressure: %4d, tilt: [%3d,%3d], rotation: %3d, throttle: %3d, wheel: %3d, rings: [%3d, %3d] }\n", + data->x, data->y, + (data->mask & WACOM_PRESSURE) ? data->pressure : 0, + (data->mask & WACOM_TILT_X) ? data->tilt_x : 0, + (data->mask & WACOM_TILT_Y) ? data->tilt_y : 0, + (data->mask & WACOM_ROTATION) ? data->rotation : 0, + (data->mask & WACOM_THROTTLE) ? data->throttle : 0, + (data->mask & WACOM_WHEEL) ? data->wheel : 0, + (data->mask & WACOM_RING) ? data->ring : 0, + (data->mask & WACOM_RING2) ? data->ring2 : 0); +} + +static void proximity(WacomDevice *device, gboolean is_prox_in, WacomEventData *data) +{ + printf(" - source: %d\n" + " event: proximity\n" + " proximity-in: %s\n", + wacom_device_get_id(device), strbool(is_prox_in)); + print_axes(data); +} + +static void motion(WacomDevice *device, gboolean is_absolute, WacomEventData *data) +{ + printf(" - source: %d\n" + " event: motion\n", wacom_device_get_id(device)); + print_axes(data); +} + +static void button(WacomDevice *device, gboolean is_absolute, int button, + gboolean is_press, WacomEventData *data) +{ + printf(" - source: %d\n" + " event: button\n" + " button: %d\n" + " is-press: %s\n", + wacom_device_get_id(device), button, strbool(is_press)); + print_axes(data); +} + +static void key(WacomDevice *device, gboolean keycode, gboolean is_press) +{ + printf(" - source: %d\n" + " event: key\n" + " key: %d\n" + " is-press: %s\n", wacom_device_get_id(device), + keycode, strbool(is_press)); +} + +static void device_added(WacomDriver *driver, WacomDevice *device) +{ + printf(" - source: %d\n" + " event: new-device\n" + " name: \"%s\"\n", + wacom_device_get_id(device), wacom_device_get_name(device)); + + g_signal_connect(device, "log-message", G_CALLBACK(log_message), NULL); + g_signal_connect(device, "debug-message", G_CALLBACK(debug_message), NULL); + g_signal_connect(device, "motion", G_CALLBACK(motion), NULL); + g_signal_connect(device, "proximity", G_CALLBACK(proximity), NULL); + g_signal_connect(device, "button", G_CALLBACK(button), NULL); + g_signal_connect(device, "keycode", G_CALLBACK(key), NULL); + + if (!wacom_device_preinit(device)) + fprintf(stderr, "Failed to preinit device %s\n", wacom_device_get_name(device)); + else if (!wacom_device_setup(device)) + fprintf(stderr, "Failed to setup device %s\n", wacom_device_get_name(device)); + else if (!wacom_device_enable(device)) + fprintf(stderr, "Failed to enable device %s\n", wacom_device_get_name(device)); + else { + const char *typestr = NULL; + + switch(wacom_device_get_tool_type(device)) { + case WACOM_TOOL_INVALID: typestr = "invalid"; break; + case WACOM_TOOL_STYLUS: typestr = "stylus"; break; + case WACOM_TOOL_ERASER: typestr = "eraser"; break; + case WACOM_TOOL_CURSOR: typestr = "cursor"; break; + case WACOM_TOOL_PAD: typestr = "pad"; break; + case WACOM_TOOL_TOUCH: typestr = "touch"; break; + + } + + printf(" type: %s\n", typestr); + printf(" capabilities:\n" + " keys: %s\n" + " is-absolute: %s\n" + " is-direct-touch: %s\n" + " ntouches: %d\n" + " naxes: %d\n", + strbool(wacom_device_has_keys(device)), + strbool(wacom_device_is_absolute(device)), + strbool(wacom_device_is_direct_touch(device)), + wacom_device_get_num_touches(device), + wacom_device_get_num_axes(device)); + printf(" axes:\n"); + for (enum WacomEventAxis which = WACOM_X; which <= WACOM_RING2; which <<= 1) { + const WacomAxis *axis = wacom_device_get_axis(device, which); + const char *typestr = NULL; + + if (!axis) + continue; + + switch (axis->type) { + case WACOM_X: typestr = "x"; break; + case WACOM_Y: typestr = "y"; break; + case WACOM_PRESSURE: typestr = "pressure"; break; + case WACOM_TILT_X: typestr = "tilt_x"; break; + case WACOM_TILT_Y: typestr = "tilt_y"; break; + case WACOM_STRIP_X: typestr = "strip_x"; break; + case WACOM_STRIP_Y: typestr = "strip_y"; break; + case WACOM_ROTATION: typestr = "rotation"; break; + case WACOM_THROTTLE: typestr = "throttle"; break; + case WACOM_WHEEL: typestr = "wheel"; break; + case WACOM_RING: typestr = "ring"; break; + case WACOM_RING2: typestr = "ring2"; break; + } + + printf(" - {type: %-12s, range: [%5d, %5d], resolution: %5d}\n", + typestr, axis->min, axis->max, axis->res); + + } + return; + } + + wacom_device_remove(device); +} + + +static void device_removed(WacomDriver *driver, WacomDevice *device) +{ + printf(" - source: %d\n" + " event: removed-device\n" + " name: \"%s\"\n", + wacom_device_get_id(device), wacom_device_get_name(device)); +} + +static char *find_device(void) +{ + const char *entry; + GDir *dir = g_dir_open("/dev/input/", 0, NULL); + struct udev *udev; + const char *prefix = isatty(STDERR_FILENO) ? "" : "#"; + int selected_device; + + if (!dir) + return NULL; + + udev = udev_new(); + fprintf(stderr, "%sAvailable devices:\n", prefix); + + while ((entry = g_dir_read_name(dir))) { + struct udev_device *dev, *parent; + const char *name; + g_autofree char *syspath = NULL; + + if (!g_str_has_prefix(entry, "event")) + continue; + + syspath = g_strdup_printf("/sys/class/input/%s", entry); + dev = udev_device_new_from_syspath(udev, syspath); + if (!dev) + continue; + + parent = udev_device_get_parent(dev); + name = udev_device_get_sysattr_value(parent, "name"); + fprintf(stderr, "%s/dev/input/%s: %s\n", prefix, entry, name); + + udev_device_unref(dev); + } + fprintf(stderr, "%sSelect the device event number: ", prefix); + if (scanf("%d", &selected_device) != 1 || selected_device < 0) + return NULL; + + udev_unref(udev); + g_dir_close(dir); + + return g_strdup_printf("/dev/input/event%d", selected_device); +} + +static void print_device_info(const char *path, char *name) +{ + printf(" device:\n"); + printf(" path: %s\n", path); + printf(" name: \"%s\"\n", name); +} + +static char *get_device_name(const char *path) +{ + struct udev *udev; + struct udev_device *dev, *parent; + g_autofree char *basename = g_path_get_basename(path); + g_autofree char *syspath; + char *name = NULL; + + udev = udev_new(); + + /* This won't work for symlinks. Oh well */ + syspath = g_strdup_printf("/sys/class/input/%s", basename); + dev = udev_device_new_from_syspath(udev, syspath); + if (dev) { + const char *udev_name; + parent = udev_device_get_parent(dev); + udev_name = udev_device_get_sysattr_value(parent, "name"); + name = g_strdup(udev_name); + } + + udev_device_unref(dev); + udev_unref(udev); + return name; +} + +static gboolean cb_sigint(gpointer loop) +{ + fprintf(stderr, "Exiting\n"); + g_main_loop_quit(loop); + return FALSE; +} + + +int main(int argc, char **argv) +{ + g_autoptr(WacomDriver) driver = NULL; + g_autoptr(WacomDevice) device = NULL; + g_autoptr(WacomOptions) options = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GOptionContext) context = g_option_context_new("- record events from a Wacom device"); + GError *error = NULL; + g_autofree char *path = NULL; + g_autofree char *name = NULL; + + g_option_context_add_main_entries(context, opts, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + fprintf(stderr, "option parsing failed: %s\n", error->message); + return 2; + } + + if (print_version) { + printf("%s\n", PACKAGE_VERSION); + return 0; + } + + if (argc <= 1) + path = find_device(); + else + path = strdup(argv[1]); + + if (!path) { + fprintf(stderr, "Invalid device path or unable to find device"); + return 1; + } + + printf("wacom-record:\n"); + printf(" version: %s\n", PACKAGE_VERSION); + printf(" git: %s\n", BUILD_VERSION); + + name = get_device_name(path); + print_device_info(path, name); + + driver = wacom_driver_new(); + options = wacom_options_new("device", path, NULL); + if (grab_device) + wacom_options_set(options, "Grab", "true"); + if (debug_level) { + g_autofree char *lvl = g_strdup_printf("%d", debug_level); + wacom_options_set(options, "DebugLevel", lvl); + wacom_options_set(options, "CommonDBG", lvl); + } + if (driver_options) { + g_auto(GStrv) strv = g_strsplit(driver_options, ",", -1); + char **opt = strv; + + printf(" options:\n"); + + while (*opt) { + g_auto(GStrv) kv = g_strsplit(*opt, "=", 2); + g_return_val_if_fail(kv[0] != NULL, 1); + g_return_val_if_fail(kv[1] != NULL, 1); + printf(" - %s: \"%s\"\n", kv[0], kv[1]); + wacom_options_set(options, kv[0], kv[1]); + opt++; + } + } + + printf(" events:\n"); + + g_signal_connect(driver, "device-added", G_CALLBACK(device_added), NULL); + g_signal_connect(driver, "device-removed", G_CALLBACK(device_removed), NULL); + + ++argv; --argc; /* first arg is already in path */ + while (true) { + device = wacom_device_new(driver, name, options); + if (!device) { + fprintf(stderr, "Unable to record device %s - is this a Wacom tablet?\n", path); + return 1; + } + + if (--argc == 0) + break; + + g_free(path); + path = strdup(*(++argv)); + wacom_options_set(options, "device", path); + } + + + loop = g_main_loop_new(NULL, FALSE); + g_unix_signal_add(SIGINT, cb_sigint, loop); + g_main_loop_run(loop); + + return 0; +} |