From 0528981a0a193ed2d0990c6d856341b849cbf897 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 3 Mar 2016 20:13:00 +1000 Subject: [PATCH] Replace ratbagctl with a python version It's a lot easier to interact with ratbagd from a python script than it is with C code. Plus this means we test the bindings whenever we use ratbagctl. Signed-off-by: Peter Hutterer --- .gitignore | 1 + Makefile.am | 23 +- python/ratbagctl.py | 184 +++++++ src/ratbagctl.c | 1164 ------------------------------------------- 4 files changed, 191 insertions(+), 1181 deletions(-) create mode 100755 python/ratbagctl.py delete mode 100644 src/ratbagctl.c diff --git a/.gitignore b/.gitignore index 17d95834..74154a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ ratbagctl ./ratbagd stamp-* ratbagd.service +ratbagctl diff --git a/Makefile.am b/Makefile.am index af747773..542cf943 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,23 +54,6 @@ libshared_la_CFLAGS = \ libshared_la_LIBADD = \ -lm -# ------------------------------------------------------------------------------ -# ratbagctl - -bin_PROGRAMS += \ - ratbagctl - -ratbagctl_SOURCES = \ - src/ratbagctl.c - -ratbagctl_CFLAGS = \ - $(AM_CFLAGS) \ - $(LIBSYSTEMD_CFLAGS) - -ratbagctl_LDADD = \ - libshared.la \ - $(LIBSYSTEMD_LIBS) - # ------------------------------------------------------------------------------ # ratbagd @@ -110,3 +93,9 @@ BUILT_SOURCES += ratbagd.service # python module dist_pkgpython_DATA = \ python/ratbagd/__init__.py + +bin_SCRIPTS = \ + python/ratbagctl + +python/ratbagctl: python/ratbagctl.py + $(AM_V_GEN)$(SED) -e "s/__VERSION__/$(PACKAGE_VERSION)/" < $^ > $@ diff --git a/python/ratbagctl.py b/python/ratbagctl.py new file mode 100755 index 00000000..c86b70a9 --- /dev/null +++ b/python/ratbagctl.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python +# +# vim: set expandtab shiftwidth=4 tabstop=4: +# +# This file is part of ratbagd. +# +# Copyright 2016 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +from __future__ import print_function + +from ratbagd import * + +import sys +import argparse + +def list_devices(r, args): + for d in r.devices: + print("{:10s} {:32s}".format(d.id + ":", d.description)) + +def find_device(r, args): + dev = None + for d in r.devices: + if d.id == args.device: + dev = d + break; + if dev is None: + print("Unable to find device {}".format(args.device)) + sys.exit(1) + return dev + +def find_profile(r, args): + d = find_device(r, args) + try: + p = d.profiles[args.profile] + except IndexError: + print("Invalid profile index {}".format(args.profile)) + sys.exit(1) + return p, d + +def find_resolution(r, args): + p, d = find_profile(r, args) + try: + r = p.resolutions[args.resolution] + except IndexError: + print("Invalid resolution index {}".format(args.resolution)) + sys.exit(1) + return r, p, d + +def find_button(r, args): + p, d = find_profile(r, args) + try: + b = p.buttons[args.button] + except IndexError: + print("Invalid button index {}".format(args.button)) + sys.exit(1) + return b, p, d + +def show_device(r, args): + d = find_device(r, args) + + print("{} - {}".format(d.id, d.description)) + print(" SVG: {}".format(d.svg)) + print(" Capabilities: {}".format(", ".join(d.capabilities))) + print("Number of Profiles: {}".format(len(d.profiles))) + active = -1 + for i, p in enumerate(d.profiles): + if p == d.active_profile: + active = i + break + print(" Active Profile: {}".format(active)) + +def show_profile(r, args): + p, d = find_profile(r, args) + + print("Profile {} on {} ({})".format(args.profile, d.id, d.description)) + print(" Number of Buttons: {}".format(len(p.buttons))) + print("Number of Resolutions: {}".format(len(p.resolutions))) + active, default = -1, -1 + for i, r in enumerate(p.resolutions): + if p.active_resolution == r: + active = i + if p.default_resolution == r: + default = i + + print(" Active Resolution: {}".format(active)) + print(" Default Resolution: {}".format(default)) + +def show_resolution(r, args): + r, p, d = find_resolution(r, args) + + print("Resolution {} on Profile {} on {} ({})".format(args.resolution, + args.profile, + d.id, + d.description)) + print(" Report Rate: {}Hz".format(r.report_rate)) + if "separate-xy-resolution" in r.capabilities: + print(" Resolution: {}x{}dpi".format(r.resolution)) + else: + print(" Resolution: {}dpi".format(r.resolution[0])) + + print(" Capabilities: {}".format(", ".join(r.capabilities))) + +def show_button(r, args): + b, p, d = find_button(r, args) + + print("Button {} on Profile {} on {} ({})".format(args.button, + args.profile, + d.id, + d.description)) + + print(" Type: {}".format(b.button_type)) + print(" Action Type: {}".format(b.action_type)) + + if b.action_type == "button": + print(" Button Mapping: {}".format(b.button)) + elif b.action_type == "key": + print(" Key Mapping: {}".format(b.key)) + elif b.action_type == "special": + print("Special Mapping: {}".format(b.special)) + +def make_parser(): + parser = argparse.ArgumentParser(description="Inspect and modify configurable mice") + parser.add_argument("-V", "--version", action="version", version="__VERSION__") + subs = parser.add_subparsers(title="COMMANDS", help=None) + + list = subs.add_parser("list-devices", help="List available configurable mice") + list.set_defaults(func=list_devices) + + show = subs.add_parser("show-device", help="Show device information") + show.add_argument("device", metavar="event0", help="The device node") + show.set_defaults(func=show_device) + + profile = subs.add_parser("show-profile", help="Show profile information") + profile.add_argument("device", metavar="event0", help="The device node") + profile.add_argument("profile", metavar="0", help="The profile index", type=int) + profile.set_defaults(func=show_profile) + + resolution = subs.add_parser("show-resolution", help="Show resolution information") + resolution.add_argument("device", metavar="event0", help="The device node") + resolution.add_argument("profile", metavar="0", help="The profile index", type=int) + resolution.add_argument("resolution", metavar="0", help="The resolution index", type=int) + resolution.set_defaults(func=show_resolution) + + button = subs.add_parser("show-button", help="Show button information") + button.add_argument("device", metavar="event0", help="The device node") + button.add_argument("profile", metavar="0", help="The profile index", type=int) + button.add_argument("button", metavar="0", help="The button index", type=int) + button.set_defaults(func=show_button) + + return parser + +def main(argv): + cmd = make_parser().parse_args(argv) + + try: + r = Ratbagd() + if not r.devices: + print("No devices available.") + except RatbagdDBusUnavailable: + print("Unable to connect to ratbagd over dbus") + + cmd.func(r, cmd) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/src/ratbagctl.c b/src/ratbagctl.c deleted file mode 100644 index b06be8a8..00000000 --- a/src/ratbagctl.c +++ /dev/null @@ -1,1164 +0,0 @@ -/*** - This file is part of ratbagd. - - Copyright 2015 David Herrmann - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -***/ - -#include -#include -#include -#include -#include -#include -#include "shared-macro.h" - -struct ratbagctl { - sd_bus *bus; -}; - -static int verb_help(struct ratbagctl *ctl, int argc, char **argv); - -static struct ratbagctl *ratbagctl_free(struct ratbagctl *ctl) -{ - if (!ctl) - return NULL; - - ctl->bus = sd_bus_unref(ctl->bus); - - return mfree(ctl); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagctl *, ratbagctl_free); - -static int ratbagctl_new(struct ratbagctl **out) -{ - _cleanup_(ratbagctl_freep) struct ratbagctl *ctl = NULL; - int r; - - ctl = calloc(1, sizeof(*ctl)); - if (!ctl) - return -ENOMEM; - - r = sd_bus_open_system(&ctl->bus); - if (r < 0) - return r; - - *out = ctl; - ctl = NULL; - return 0; -} - -static int get_device_path(struct ratbagctl *ctl, - const char *device_name, - char **out_path, - sd_bus_error *error) -{ - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *device_path = NULL; - char *path; - int r; - - if (device_name) { - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - "/org/freedesktop/ratbag1", - "org.freedesktop.ratbag1.Manager", - "GetDeviceByName", - error, - &reply, - "s", - device_name); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "o", &device_path); - if (r < 0) - return r; - } else { - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - "/org/freedesktop/ratbag1", - "org.freedesktop.DBus.Properties", - "Get", - error, - &reply, - "ss", - "org.freedesktop.ratbag1.Manager", - "Devices"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'v', "ao"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'a', "o"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(reply, 'o', &path)) > 0) { - if (!device_path) - device_path = path; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - if (!device_path) - return -ENXIO; - } - - assert(device_path); - - path = strdup(device_path); - if (!path) - return -ENOMEM; - - *out_path = path; - return 0; -} - -static int get_profile_path(struct ratbagctl *ctl, - const char *device_name, - const char *profile_name, - char **out_path, - sd_bus_error *error) -{ - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply2 = NULL; - _cleanup_(freep) char *device_path = NULL; - const char *profile_path = NULL; - unsigned int profile_index; - char *path; - int r; - - r = get_device_path(ctl, device_name, &device_path, error); - if (r < 0) - return r; - - if (profile_name) { - r = safe_atou(profile_name, &profile_index); - if (r < 0) - return -ENXIO; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - device_path, - "org.freedesktop.ratbag1.Device", - "GetProfileByIndex", - error, - &reply2, - "u", - profile_index); - if (r < 0) - return r; - - r = sd_bus_message_read(reply2, "o", &profile_path); - if (r < 0) - return r; - } else { - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - device_path, - "org.freedesktop.DBus.Properties", - "Get", - error, - &reply2, - "ss", - "org.freedesktop.ratbag1.Device", - "ActiveProfile"); - if (r < 0) - return r; - - r = sd_bus_message_read(reply2, "v", "o", &profile_path); - if (r < 0) - return r; - - if (streq(profile_path, "/")) { - reply2 = sd_bus_message_unref(reply2); - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - device_path, - "org.freedesktop.DBus.Properties", - "Get", - error, - &reply2, - "ss", - "org.freedesktop.ratbag1.Device", - "DefaultProfile"); - if (r < 0) - return r; - - r = sd_bus_message_read(reply2, "v", "o", &profile_path); - if (r < 0) - return r; - - if (streq(profile_path, "/")) - return -ENXIO; - } - } - - assert(profile_path); - - path = strdup(profile_path); - if (!path) - return -ENOMEM; - - *out_path = path; - return 0; -} - -static int list_devices_show(struct ratbagctl *ctl, const char *path) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *prop_id = NULL, *prop_description = NULL; - int r; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", - "org.freedesktop.ratbag1.Device"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - return r; - - if (streq(property, "Id")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_id); - } else if (streq(property, "Description")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_description); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - printf("%10s %-32s\n", prop_id, prop_description); - return 0; -} - -static int list_devices_all(struct ratbagctl *ctl) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned int k = 0; - const char *path; - int r; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - "/org/freedesktop/ratbag1", - "org.freedesktop.DBus.Properties", - "Get", - &error, - &reply, - "ss", - "org.freedesktop.ratbag1.Manager", - "Devices"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'v', "ao"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "o"); - if (r < 0) - goto exit; - - printf("%10s %-32s\n", "DEVICE", "DESCRIPTION"); - - while ((r = sd_bus_message_read_basic(reply, 'o', &path)) > 0) { - r = list_devices_show(ctl, path); - if (r < 0) - goto exit; - - ++k; - } - if (r < 0) - goto exit; - - printf("\n%u device%s listed.\n", k, k > 1 ? "s" : ""); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); -exit: - if (r < 0) - fprintf(stderr, "Cannot list devices: %s\n", - error.message ? : "Parser error"); - return r; -} - -static int verb_list_devices(struct ratbagctl *ctl, int argc, char **argv) -{ - static const struct option options[] = { - {}, - }; - int c; - - while ((c = getopt_long(argc, argv, "+", options, NULL)) >= 0) { - switch (c) { - default: - return -EINVAL; - } - } - - if (argv[optind]) { - fprintf(stderr, "Command does not take arguments\n"); - return -EINVAL; - } - - return list_devices_all(ctl); -} - -static int show_device_get_profile_index(struct ratbagctl *ctl, - const char *path, - unsigned int *out_index) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - if (streq(path, "/")) { - *out_index = -1; - return 0; - } - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "Get", - &error, - &reply, - "ss", - "org.freedesktop.ratbag1.Profile", - "Index"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'v', "u"); - if (r < 0) - return r; - - r = sd_bus_message_read_basic(reply, 'u', out_index); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - return 0; -} - -static int show_device_print(struct ratbagctl *ctl, const char *device) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned int prop_min_index = 0, prop_max_index = 0; - unsigned int prop_active_profile = -1; - const char *prop_id = NULL, *prop_description = NULL, *prop_svg = NULL; - _cleanup_(freep) char *path = NULL; - int r; - - r = get_device_path(ctl, device, &path, &error); - if (r < 0) - goto exit; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", - "org.freedesktop.ratbag1.Device"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - goto exit; - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property, *profile; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (streq(property, "Id")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_id); - } else if (streq(property, "Description")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_description); - } else if (streq(property, "Svg")) { - r = sd_bus_message_read(reply, "v", "s", &prop_svg); - } else if (streq(property, "Profiles")) { - r = sd_bus_message_enter_container(reply, 'v', "ao"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "o"); - if (r < 0) - goto exit; - - while ((r = sd_bus_message_read_basic(reply, - 'o', - &profile)) > 0) { - unsigned int index; - - r = show_device_get_profile_index(ctl, - profile, - &index); - if (r < 0) - goto exit; - - if (index < prop_min_index) - prop_min_index = index; - if (index >= prop_max_index) - prop_max_index = index + 1; - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - } else if (streq(property, "ActiveProfile")) { - r = sd_bus_message_read(reply, "v", "u", &prop_active_profile); - if (r < 0) - goto exit; - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_rewind(reply, 0); - if (r < 0) - goto exit; - - printf("%s - %s\n", prop_id, prop_description); - printf("\t Svg: %s\n", strlen(prop_svg) > 0 ? prop_svg : ""); - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (startswith(property, "Cap")) { - bool has_cap; - r = sd_bus_message_read(reply, "v", "b", &has_cap); - - printf("%23s: %s\n", property, has_cap ? "yes" : "no"); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - if (prop_min_index == prop_max_index) - printf("\t Profiles:\n"); - else if (prop_min_index + 1 == prop_max_index) - printf("\t Profiles: %u\n", prop_min_index); - else - printf("\t Profiles: %u - %u\n", - prop_min_index, prop_max_index - 1); - - if (prop_active_profile == (unsigned int)-1) - printf("\t Active Profile: (unknown)\n"); - else - printf("\t Active Profile: %u\n", prop_active_profile); - -exit: - if (r < 0) - fprintf(stderr, "Cannot show device: %s\n", - error.message ? : "Parser error"); - return r; -} - -static int verb_show_device(struct ratbagctl *ctl, int argc, char **argv) -{ - static const struct option options[] = { - {}, - }; - const char *device; - int c; - - while ((c = getopt_long(argc, argv, "+", options, NULL)) >= 0) { - switch (c) { - default: - return -EINVAL; - } - } - - device = argv[optind]; - - return show_device_print(ctl, device); -} - -static int show_profile_print_resolution(struct ratbagctl *ctl, const char *path) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned int prop_index = -1, - prop_report_rate = -1, - prop_xres = -1, - prop_yres = -1; - int r; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", - "org.freedesktop.ratbag1.Resolution"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - goto exit; - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (streq(property, "Index")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_index); - } else if (streq(property, "ReportRate")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_report_rate); - } else if (streq(property, "XResolution")) { - r = sd_bus_message_read(reply, "v", "u", &prop_xres); - } else if (streq(property, "YResolution")) { - r = sd_bus_message_read(reply, "v", "u", &prop_yres); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_rewind(reply, 0); - if (r < 0) - goto exit; - - printf("resolution-%u\n", prop_index); - - printf("\t Index: %u\n", prop_index); - printf("\t Report Rate: %uHz\n", prop_report_rate); - printf("\t Resolution: %ux%udpi\n", prop_xres, prop_yres); - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (startswith(property, "Cap")) { - bool has_cap; - r = sd_bus_message_read(reply, "v", "b", &has_cap); - - printf("%24s: %s\n", property, has_cap ? "yes" : "no"); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - printf("\n"); - -exit: - if (r < 0) - fprintf(stderr, "Cannot show resolution: %s\n", - error.message ? : "Parser error"); - return r; -} - -static int show_profile_print_resolutions(struct ratbagctl *ctl, - sd_bus_message *m, - unsigned int active_resolution, - unsigned int default_resolution) -{ - const char *resolution; - int r; - - r = sd_bus_message_enter_container(m, 'v', "ao"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, 'a', "o"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(m, 'o', &resolution)) > 0) - show_profile_print_resolution(ctl, resolution); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int show_profile_print_button(struct ratbagctl *ctl, const char *path) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned int prop_index = -1, - prop_btnmap = -1; - unsigned int prop_keymap[20] = {0}; - const char *prop_type, - *prop_action_type, - *prop_special; - const char *prop_action_types[20] = {0}; - unsigned int idx = 0; - unsigned int modmap = 0; - int r; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", - "org.freedesktop.ratbag1.Button"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - goto exit; - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (streq(property, "Index")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_index); - } else if (streq(property, "Type")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_type); - } else if (streq(property, "ButtonMapping")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_btnmap); - } else if (streq(property, "SpecialMapping")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_special); - } else if (streq(property, "KeyMapping")) { - r = sd_bus_message_enter_container(reply, 'v', "au"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "u"); - if (r < 0) - goto exit; - - idx = 0; - while ((r = sd_bus_message_read_basic(reply, 'u', - &prop_keymap[idx++])) > 0) - ; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } else if (streq(property, "ActionType")) { - r = sd_bus_message_read(reply, "v", "s", - &prop_action_type); - } else if (streq(property, "ActionTypes")) { - r = sd_bus_message_enter_container(reply, 'v', "as"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "s"); - if (r < 0) - goto exit; - - idx = 0; - while ((r = sd_bus_message_read_basic(reply, 's', - &prop_action_types[idx++])) > 0) - ; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_rewind(reply, 0); - if (r < 0) - goto exit; - - printf("button-%u\n", prop_index); - - printf("\t Index: %u\n", prop_index); - printf("\t Type: %s\n", prop_type); - printf("\t Action type: %s\n", prop_action_type); - idx = 0; - printf("\t Action types: "); - while ((prop_action_type = prop_action_types[idx++]) != NULL) - printf("%s ", prop_action_type); - printf("\n"); - printf("\t Special mapping: %s\n", prop_special); - printf("\t Button mapping: %d\n", prop_btnmap); - - printf("\t Key mapping: %d (", prop_keymap[0]); - idx = 1; - if (prop_keymap[1] == 0) { - printf("no modifiers"); - } else { - printf("modifiers "); - while ((modmap = prop_keymap[idx++]) != 0) - printf("%d ", modmap); - } - printf(")\n"); -exit: - if (r < 0) - fprintf(stderr, "Cannot show button: %s\n", - error.message ? : "Parser error"); - return r; -} - -static int show_profile_print_buttons(struct ratbagctl *ctl, - sd_bus_message *m) -{ - const char *button; - int r; - - r = sd_bus_message_enter_container(m, 'v', "ao"); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, 'a', "o"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(m, 'o', &button)) > 0) - show_profile_print_button(ctl, button); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int show_profile_print(struct ratbagctl *ctl, - const char *device, - const char *profile) -{ - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(freep) char *path = NULL; - unsigned int prop_index = -1, prop_active_resolution = -1; - unsigned int prop_default_resolution = -1; - int r; - - r = get_profile_path(ctl, device, profile, &path, &error); - if (r < 0) - goto exit; - - r = sd_bus_call_method(ctl->bus, - "org.freedesktop.ratbag1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", - "org.freedesktop.ratbag1.Profile"); - if (r < 0) - goto exit; - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - goto exit; - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (streq(property, "Index")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_index); - } else if (streq(property, "ActiveResolution")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_active_resolution); - } else if (streq(property, "DefaultResolution")) { - r = sd_bus_message_read(reply, "v", "u", - &prop_default_resolution); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_rewind(reply, 0); - if (r < 0) - goto exit; - - printf("profile-%u\n", prop_index); - - printf("\t Index: %u\n", prop_index); - - while ((r = sd_bus_message_enter_container(reply, 'e', "sv")) > 0) { - const char *property; - - r = sd_bus_message_read_basic(reply, 's', &property); - if (r < 0) - goto exit; - - if (streq(property, "Resolutions")) { - r = show_profile_print_resolutions(ctl, - reply, - prop_active_resolution, - prop_default_resolution); - } else if (streq(property, "Buttons")) { - r = show_profile_print_buttons(ctl, reply); - } else { - r = sd_bus_message_skip(reply, "v"); - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto exit; - } - if (r < 0) - goto exit; - - r = sd_bus_message_exit_container(reply); -exit: - if (r < 0) - fprintf(stderr, "Cannot show device: %s\n", - error.message ? : "Parser error"); - return r; -} - -static int verb_show_profile(struct ratbagctl *ctl, int argc, char **argv) -{ - static const struct option options[] = { - { "device", required_argument, NULL, 'd' }, - {}, - }; - const char *device = NULL, *profile = NULL; - int c; - - while ((c = getopt_long(argc, argv, "+d:", options, NULL)) >= 0) { - switch (c) { - case 'd': - device = optarg; - break; - default: - return -EINVAL; - } - } - - profile = argv[optind]; - - return show_profile_print(ctl, device, profile); -} - -static const struct { - const char *verb; - const char *help; - int (*dispatch) (struct ratbagctl *ctl, int argc, char **argv); - void (*long_help) (void); -} verbs[] = { - { "list-devices", "List available configurable mice", verb_list_devices, NULL }, - { "show-device", "Show device information", verb_show_device, NULL }, - { "show-profile", "Show profile information", verb_show_profile, NULL }, - { "help", "Show help for a command", verb_help, NULL }, - {}, -}; - -static void help(void) -{ - unsigned int i; - - printf("%s [OPTIONS..] COMMAND [OPTIONS..]\n\n" - "Query or modify configurable mice.\n\n" - "Commands:\n", - program_invocation_short_name); - - for (i = 0; i < ELEMENTSOF(verbs); ++i) { - if (verbs[i].help != NULL) - printf(" %-12s %s\n", verbs[i].verb, verbs[i].help); - } -} - -static int verb_help(struct ratbagctl *ctl, int argc, char **argv) -{ - static const struct option options[] = { - {}, - }; - const char *verb; - unsigned int i; - int c; - - while ((c = getopt_long(argc, argv, "+", options, NULL)) >= 0) { - switch (c) { - default: - return -EINVAL; - } - } - - verb = argv[optind]; - if (!verb) { - help(); - return 1; - } else { - for (i = 0; i < ELEMENTSOF(verbs); ++i) { - if (!streq_ptr(verb, verbs[i].verb)) - continue; - - if (verbs[i].long_help) - verbs[i].long_help(); - else - printf("%s: No help available for '%s'\n", - program_invocation_short_name, verb); - - return 1; - } - } - - fprintf(stderr, "%s: Unknown verb '%s'\n", program_invocation_short_name, verb); - return 1; -} - -static void version(void) -{ - printf("%s\n", VERSION); -} - -static int ratbagctl_dispatch(struct ratbagctl *ctl, int argc, char **argv) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {}, - }; - char *fake[] = { (char*)verbs[0].verb, NULL }; - const char *verb; - unsigned int i; - int c; - - /* '+' makes getopt_long() stop at the first non-argument */ - while ((c = getopt_long(argc, argv, "+hV", options, NULL)) >= 0) { - switch (c) { - case 'h': - help(); - return 1; - case 'V': - version(); - return 1; - default: - return -EINVAL; - } - } - - verb = argv[optind]; - if (!verb) { - assert(argc - optind == 0); - - argc = ELEMENTSOF(fake) - 1; - argv = fake; - verb = fake[0]; - optind = 0; - - assert(verb); - } - - for (i = 0; i < ELEMENTSOF(verbs); ++i) { - if (!streq_ptr(verb, verbs[i].verb)) - continue; - - argc -= optind; - argv += optind; - optind = 0; - - return verbs[i].dispatch(ctl, argc, argv); - } - - fprintf(stderr, "%s: Missing or unknown command\n", - program_invocation_short_name); - - return -EINVAL; -} - -int main(int argc, char **argv) -{ - struct ratbagctl *ctl = NULL; - int r; - - r = ratbagctl_new(&ctl); - if (r < 0) - goto exit; - - r = ratbagctl_dispatch(ctl, argc, argv); - -exit: - ratbagctl_free(ctl); - - if (r < 0) { - errno = -r; - fprintf(stderr, "%s: Failed: %m\n", - program_invocation_short_name); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -}