/* * 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 #include "config-ver.h" #include #include #include #include #include #include #include #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 gboolean log_evdev = 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 }, { "evdev", 0, 0, G_OPTION_ARG_NONE, &log_evdev, "Log evdev events", NULL }, { 0 }, }; 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) { char buf[1024] = {0}; const char *prefix = ""; uint32_t mask = data->mask; for (uint32_t flag = 0x1; flag <= _WAXIS_LAST; flag <<= 1) { const char *name = "unknown axis"; if ((mask & flag) == 0) continue; switch (flag) { case WAXIS_X: name = "x"; break; case WAXIS_Y: name = "y"; break; case WAXIS_PRESSURE: name = "pressure"; break; case WAXIS_TILT_X: name = "tilt-x"; break; case WAXIS_TILT_Y: name = "tilt-y"; break; case WAXIS_ROTATION: name = "rotation"; break; case WAXIS_THROTTLE: name = "throttle"; break; case WAXIS_WHEEL: name = "wheel"; break; case WAXIS_RING: name = "ring"; break; case WAXIS_RING2: name = "ring"; break; } g_assert_cmpint(strlen(buf) + strlen(prefix) + strlen(name), <, sizeof(buf)); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s\"%s\"", prefix, name); prefix = ", "; } printf(" mask: [ %s ]\n", buf); 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 & WAXIS_PRESSURE) ? data->pressure : 0, (data->mask & WAXIS_TILT_X) ? data->tilt_x : 0, (data->mask & WAXIS_TILT_Y) ? data->tilt_y : 0, (data->mask & WAXIS_ROTATION) ? data->rotation : 0, (data->mask & WAXIS_THROTTLE) ? data->throttle : 0, (data->mask & WAXIS_WHEEL) ? data->wheel : 0, (data->mask & WAXIS_RING) ? data->ring : 0, (data->mask & WAXIS_RING2) ? data->ring2 : 0); } static void proximity(WacomDevice *device, gboolean is_prox_in, WacomEventData *data) { printf(" - source: %u\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: %u\n" " mode: %s\n" " event: motion\n", wacom_device_get_id(device), is_absolute ? "absolute" : "relative"); print_axes(data); } static void button(WacomDevice *device, gboolean is_absolute, int button, gboolean is_press, WacomEventData *data) { printf(" - source: %u\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: %u\n" " event: key\n" " key: %d\n" " is-press: %s\n", wacom_device_get_id(device), keycode, strbool(is_press)); } static void evdev(WacomDevice *device, const struct input_event *evdev) { printf(" - { source: %u, event: evdev, data: [%6ld, %6ld, %3d, %3d, %10d] } # %s / %-20s %5d\n", wacom_device_get_id(device), evdev->input_event_sec, evdev->input_event_usec, evdev->type, evdev->code, evdev->value, libevdev_event_type_get_name(evdev->type), libevdev_event_code_get_name(evdev->type, evdev->code), evdev->value ); } static void device_added(WacomDriver *driver, WacomDevice *device) { WacomOptions *options = wacom_device_get_options(device); GSList *opts = wacom_options_list_keys(options); printf(" - source: %u\n" " event: new-device\n" " name: \"%s\"\n", wacom_device_get_id(device), wacom_device_get_name(device)); printf(" options:\n"); for (guint i = 0; i < g_slist_length(opts); i++) { gchar *key = g_slist_nth_data(opts, i); printf(" - %s: \"%s\"\n", key, wacom_options_get(options, key)); } g_slist_free_full(g_steal_pointer(&opts), g_free); 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 (log_evdev) g_signal_connect(device, "evdev-event", G_CALLBACK(evdev), 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 WTOOL_INVALID: typestr = "invalid"; break; case WTOOL_STYLUS: typestr = "stylus"; break; case WTOOL_ERASER: typestr = "eraser"; break; case WTOOL_CURSOR: typestr = "cursor"; break; case WTOOL_PAD: typestr = "pad"; break; case WTOOL_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 (WacomEventAxis which = WAXIS_X; which <= _WAXIS_LAST; which <<= 1) { const WacomAxis *axis = wacom_device_get_axis(device, which); const char *typestr = NULL; if (!axis) continue; switch (axis->type) { case WAXIS_X: typestr = "x"; break; case WAXIS_Y: typestr = "y"; break; case WAXIS_PRESSURE: typestr = "pressure"; break; case WAXIS_TILT_X: typestr = "tilt_x"; break; case WAXIS_TILT_Y: typestr = "tilt_y"; break; case WAXIS_STRIP_X: typestr = "strip_x"; break; case WAXIS_STRIP_Y: typestr = "strip_y"; break; case WAXIS_ROTATION: typestr = "rotation"; break; case WAXIS_THROTTLE: typestr = "throttle"; break; case WAXIS_WHEEL: typestr = "wheel"; break; case WAXIS_RING: typestr = "ring"; break; case WAXIS_RING2: typestr = "ring2"; break; case WAXIS_SCROLL_X: typestr = "scroll_x"; break; case WAXIS_SCROLL_Y: typestr = "scroll_y"; 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: %u\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 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 = NULL; 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 *autopath = 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) { autopath = find_device(); if (!autopath) { 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); driver = wacom_driver_new(); options = wacom_options_new(NULL, NULL); if (grab_device) wacom_options_set(options, "Grab", "true"); if (debug_level) { g_autofree char *lvl = g_strdup_printf("%u", 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; 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); 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) { g_autofree char *path = (autopath) ? autopath : strdup(*argv); g_autofree char *name = get_device_name(path); wacom_options_set(options, "device", path); name = get_device_name(path); if (!name) name = g_strdup_printf("Unamed device %s", path); device = wacom_device_new(driver, name, options); assert(device); if (--argc <= 0) break; argv++; } loop = g_main_loop_new(NULL, FALSE); g_unix_signal_add(SIGINT, cb_sigint, loop); g_main_loop_run(loop); return 0; }