Skip to content

Commit

Permalink
tools: add a debug-device utility (#666)
Browse files Browse the repository at this point in the history
This one simply prints all return values for a device from our API,
making it easier to figure out the exact behavior of a device.
This is a debug utility so it is not installed.

Example output:
  libwacom_get_name()                      -> "Wacom Cintiq 24HD touch"
  libwacom_get_model_name()                -> "DTH-2400"
  libwacom_get_layout_filename()           -> "data/layouts/cintiq-24hd.svg"
  libwacom_get_vendor_id()                 -> 0x56a
  libwacom_get_product_id()                -> 0xf8
  libwacom_get_bustype()                   -> USB
  libwacom_get_width()                     -> 21
  libwacom_get_height()                    -> 13
  libwacom_is_reversible()                 -> 0
  • Loading branch information
whot authored Apr 28, 2024
1 parent b923391 commit c7402c7
Show file tree
Hide file tree
Showing 2 changed files with 373 additions and 0 deletions.
8 changes: 8 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ configure_file(input: 'tools/65-libwacom.rules.in',
install: true,
install_dir: dir_udev / 'rules.d')

# The non-installed version of list-devices uses the git tree's data files
executable('debug-device',
'tools/debug-device.c',
dependencies: [dep_libwacom, dep_glib],
include_directories: [includes_src],
c_args: tools_cflags,
install: false)

# The non-installed version of list-devices uses the git tree's data files
executable('list-devices',
'tools/list-devices.c',
Expand Down
365 changes: 365 additions & 0 deletions tools/debug-device.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
/*
* Copyright © 2024 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Red Hat
* not be used in advertising or publicity pertaining to distribution
* of the software without specific, written prior permission. Red
* Hat makes no representations about the suitability of this software
* for any purpose. It is provided "as is" without express or implied
* warranty.
*
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors:
* Peter Hutterer <[email protected]>
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <glib/gi18n.h>
#include <glib.h>
#include "libwacom.h"

static char *database_path;
static gboolean with_styli;

static GOptionEntry opts[] = {
{"database", 0, 0, G_OPTION_ARG_FILENAME, &database_path, N_("Path to device database"), NULL },
{"with-styli", 0, 0, G_OPTION_ARG_NONE, &with_styli, N_("Select to also list styli for this device"), NULL },
{ .long_name = NULL}
};

static int indent = 0;

#define push() indent += 2
#define pop() indent -= 2

#define ip(fmt_, ...) \
printf("%-*s" fmt_, indent, "", __VA_ARGS__);
/* Usage: p("function_name", "return value is %d", a) */
#define p(f_, fmt_, ...) \
printf("%-*s%-*s -> " fmt_ "\n", indent, "", (46 - indent), f_, __VA_ARGS__)

/* Usage: func(myfunc, "return value is %d", a) */
#define func(f_, fmt_, ...) \
p(#f_"()", fmt_, __VA_ARGS__)

/* Usage: func(myfunc, "%d", argval, "return value is %d", a) */
#define func_arg(f_, arg_fmt_, arg_val_, fmt_, ...) \
{ \
char buf_[256]; \
snprintf(buf_, sizeof(buf_), #f_"(" arg_fmt_ ")", arg_val_); \
p(buf_, fmt_, __VA_ARGS__); \
}

#define strfunc(f_, dev_) \
func(f_, "\"%s\"", f_(dev_))

#define hexfunc(f_, dev_) \
func(f_, "0x%04x", f_(dev_))

#define intfunc(f_, dev_) \
func(f_, "%d", f_(dev_))

static void
handle_match(const WacomMatch *m)
{
if (m == NULL) {
printf(" <none>\n");
return;
}
push();
ip("%s\n", "{");
push();
strfunc(libwacom_match_get_match_string, m);
strfunc(libwacom_match_get_name, m);
strfunc(libwacom_match_get_uniq, m);
hexfunc(libwacom_match_get_bustype, m);
hexfunc(libwacom_match_get_vendor_id, m);
hexfunc(libwacom_match_get_product_id, m);
pop();
ip("%s\n", "}");
pop();
}

static int
handle_device(WacomDeviceDatabase *db, const char *path)
{
WacomDevice *device;

device = libwacom_new_from_path(db, path, WFALLBACK_NONE, NULL);
if (!device) {
fprintf(stderr, "Device not known to libwacom\n");
return EXIT_FAILURE;
}

strfunc(libwacom_get_name, device);
strfunc(libwacom_get_model_name, device);
strfunc(libwacom_get_layout_filename, device);

hexfunc(libwacom_get_vendor_id, device);
hexfunc(libwacom_get_product_id, device);
{
char *busstr = NULL;
switch (libwacom_get_bustype(device)) {
case WBUSTYPE_UNKNOWN: busstr = "UNKNOWN"; break;
case WBUSTYPE_USB: busstr = "USB"; break;
case WBUSTYPE_SERIAL: busstr = "SERIAL"; break;
case WBUSTYPE_BLUETOOTH: busstr = "BLUETOOTH"; break;
case WBUSTYPE_I2C: busstr = "I2C"; break;
}
func(libwacom_get_bustype, "%s", busstr);
}

intfunc(libwacom_get_width, device);
intfunc(libwacom_get_height, device);

intfunc(libwacom_is_reversible, device);

{
const WacomMatch **matches = libwacom_get_matches(device);
const WacomMatch **m;

printf("libwacom_get_matches() -> {\n");
for (m = matches; *m; m++) {
handle_match(*m);
}
printf("}\n");
}

strfunc(libwacom_get_match, device);

printf("libwacom_get_paired_device() -> {");
handle_match(libwacom_get_paired_device(device));
printf("}\n");

intfunc(libwacom_has_stylus, device);
intfunc(libwacom_has_touch, device);
intfunc(libwacom_get_num_buttons, device);
intfunc(libwacom_get_num_keys, device);
intfunc(libwacom_has_ring, device);
intfunc(libwacom_has_ring2, device);
intfunc(libwacom_has_touchswitch, device);
intfunc(libwacom_get_ring_num_modes, device);
intfunc(libwacom_get_ring2_num_modes, device);
intfunc(libwacom_get_num_strips, device);
intfunc(libwacom_get_strips_num_modes, device);

{
WacomIntegrationFlags flags = libwacom_get_integration_flags(device);
func(libwacom_get_integration_flags, "%s%s %s",
flags == WACOM_DEVICE_INTEGRATED_NONE ? "NONE" : "",
flags == WACOM_DEVICE_INTEGRATED_DISPLAY ? "DISPLAY" : "",
flags == WACOM_DEVICE_INTEGRATED_SYSTEM ? "SYSTEM" : ""
);
}

{
for (int i = 0; i < libwacom_get_num_buttons(device); i++) {
char b = 'A' + i;
func_arg(libwacom_get_button_led_group, "%c", b, "%d",
libwacom_get_button_led_group(device, b));
}

for (int i = 0; i < libwacom_get_num_buttons(device); i++) {
char b = 'A' + i;
func_arg(libwacom_get_button_evdev_code, "%c", b, "0x%x",
libwacom_get_button_evdev_code(device, b));
}

for (int i = 0; i < libwacom_get_num_buttons(device); i++) {
char b = 'A' + i;
WacomButtonFlags flags;

flags = libwacom_get_button_flag(device, b);
func_arg(libwacom_get_button_flag, "%c", b, "%s%s%s%s%s%s%s%s%s%s",
flags == WACOM_BUTTON_NONE ? "NONE" : "",
flags & WACOM_BUTTON_POSITION_LEFT ? "POSITION_LEFT|" : "",
flags & WACOM_BUTTON_POSITION_RIGHT ? "POSITION_RIGHT|" : "",
flags & WACOM_BUTTON_POSITION_TOP ? "POSITION_TOP|" : "",
flags & WACOM_BUTTON_POSITION_BOTTOM ? "POSITION_BOTTOM|" : "",
flags & WACOM_BUTTON_RING_MODESWITCH ? "RING_MODESWITCH|" : "",
flags & WACOM_BUTTON_RING2_MODESWITCH ? "RING2_MODESWITCH|" : "",
flags & WACOM_BUTTON_TOUCHSTRIP_MODESWITCH ? "TOUCHSTRIP_MODESWITCH|" : "",
flags & WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH ? "TOUCHSTRIP2_MODESWITCH|" : "",
flags & WACOM_BUTTON_OLED ? "OLED " : "");

}
}

{
char buf[1024] = {0};
int nleds;
const WacomStatusLEDs *leds = libwacom_get_status_leds(device, &nleds);

for (int i = 0; i < nleds; i++) {
char *ledstr = NULL;
switch (leds[i]) {
case WACOM_STATUS_LED_UNAVAILABLE: ledstr = "UNAVAILABLE"; break;
case WACOM_STATUS_LED_RING: ledstr = "RING"; break;
case WACOM_STATUS_LED_RING2: ledstr = "RING2"; break;
case WACOM_STATUS_LED_TOUCHSTRIP: ledstr = "TOUCHSTRIP"; break;
case WACOM_STATUS_LED_TOUCHSTRIP2: ledstr = "TOUCHSTRIP2"; break;
}
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s%s", i > 0 ? ", " : "", ledstr);
}
func(libwacom_get_status_leds, "[%s]", buf);
}

{
int nstyli;
const int *styli = libwacom_get_supported_styli(device, &nstyli);

{
char buf[1024] = {0};
for (int i = 0; i < nstyli; i++)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s0x%06x", i > 0 ? ", " : "", styli[i]);

func(libwacom_get_supported_styli, "[%s]", buf);
}


if (with_styli) {
printf("\n---------- Listing styli for this device ----------\n");

for (int i = 0; i < nstyli; i++) {
int id = styli[i];
const WacomStylus *stylus = libwacom_stylus_get_for_id (db, id);

ip("%s\n", "{");
push();
func_arg(libwacom_stylus_get_id, "0x%04x", id, "0x%04x", libwacom_stylus_get_id(stylus));
func_arg(libwacom_stylus_get_name, "0x%04x", id, "%s", libwacom_stylus_get_name(stylus));
func_arg(libwacom_stylus_get_num_buttons, "0x%04x", id, "%d", libwacom_stylus_get_num_buttons(stylus));
func_arg(libwacom_stylus_has_eraser, "0x%04x", id, "%d", libwacom_stylus_has_eraser(stylus));
func_arg(libwacom_stylus_is_eraser, "0x%04x", id, "%d", libwacom_stylus_is_eraser(stylus));
func_arg(libwacom_stylus_has_lens, "0x%04x", id, "%d", libwacom_stylus_has_lens(stylus));
func_arg(libwacom_stylus_has_wheel, "0x%04x", id, "%d", libwacom_stylus_has_wheel(stylus));

{
char buf[1024] = {0};
int npaired;
const int *paired = libwacom_stylus_get_paired_ids(stylus, &npaired);

for (int i = 0; i < npaired; i++)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s0x%06x", i > 0 ? ", " : "", paired[i]);
func_arg(libwacom_stylus_get_paired_ids, "0x%04x", id, "[%s]", buf);
}

{
WacomAxisTypeFlags flags = libwacom_stylus_get_axes(stylus);
func_arg(libwacom_stylus_has_wheel, "0x%04x", id, "%s%s%s%s%s%s",
flags == WACOM_AXIS_TYPE_NONE ? "NONE" : "",
flags & WACOM_AXIS_TYPE_TILT ? "TILT|" : "",
flags & WACOM_AXIS_TYPE_ROTATION_Z ? "ROTATION_Z|" : "",
flags & WACOM_AXIS_TYPE_DISTANCE ? "DISTANCE|" : "",
flags & WACOM_AXIS_TYPE_PRESSURE ? "PRESSURE|" : "",
flags & WACOM_AXIS_TYPE_SLIDER ? "SLIDER" : "");
}

{
const char *typestr = NULL;
switch (libwacom_stylus_get_type(stylus)) {
case WSTYLUS_UNKNOWN: typestr = "UNKNOWN"; break;
case WSTYLUS_GENERAL: typestr = "GENERAL"; break;
case WSTYLUS_INKING: typestr = "INKING"; break;
case WSTYLUS_AIRBRUSH: typestr = "AIRBRUSH"; break;
case WSTYLUS_CLASSIC: typestr = "CLASSIC"; break;
case WSTYLUS_MARKER: typestr = "MARKER"; break;
case WSTYLUS_STROKE: typestr = "STROKE"; break;
case WSTYLUS_PUCK: typestr = "PUCK"; break;
case WSTYLUS_3D: typestr = "3D"; break;
case WSTYLUS_MOBILE: typestr = "MOBILE"; break;
}

func_arg(libwacom_stylus_get_type, "0x%04x", id, "%s", typestr);
}

{
const char *eraserstr = NULL;
switch (libwacom_stylus_get_eraser_type(stylus)) {
case WACOM_ERASER_UNKNOWN: eraserstr = "UNKNOWN"; break;
case WACOM_ERASER_NONE: eraserstr = "NONE"; break;
case WACOM_ERASER_INVERT: eraserstr = "INVERT"; break;
case WACOM_ERASER_BUTTON: eraserstr = "BUTTON"; break;
}

func_arg(libwacom_stylus_get_type, "0x%04x", id, "%s", eraserstr);
}
pop();
ip("%s\n", "}");
}
}
}

libwacom_destroy(device);

return EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
WacomDeviceDatabase *db;
GOptionContext *context;
GError *error;
int rc;

context = g_option_context_new (NULL);

g_option_context_add_main_entries (context, opts, NULL);
error = NULL;

if (!g_option_context_parse (context, &argc, &argv, &error)) {
if (error != NULL) {
fprintf (stderr, "%s\n", error->message);
g_error_free (error);
}
return EXIT_FAILURE;
}

g_option_context_free (context);

if (database_path) {
db = libwacom_database_new_for_path(database_path);
g_free (database_path);
} else {
#ifdef DATABASEPATH
db = libwacom_database_new_for_path(DATABASEPATH);
#else
db = libwacom_database_new();
#endif
}

if (!db) {
fprintf(stderr, "Failed to initialize device database\n");
return EXIT_FAILURE;
}

if (argc <= 1) {
fprintf(stderr, "Missing device node\n");
libwacom_database_destroy (db);
return EXIT_FAILURE;
}

rc = handle_device(db, argv[1]);

libwacom_database_destroy (db);
return rc;
}

/* vim: set noexpandtab tabstop=8 shiftwidth=8: */

0 comments on commit c7402c7

Please sign in to comment.