diff --git a/Makefile.am b/Makefile.am index a3d8270c..b99755c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,6 +80,7 @@ bin_PROGRAMS += \ ratbagd_SOURCES = \ src/ratbagd.h \ src/ratbagd.c \ + src/ratbagd-button.c \ src/ratbagd-device.c \ src/ratbagd-profile.c \ src/ratbagd-resolution.c diff --git a/src/ratbagd-button.c b/src/ratbagd-button.c new file mode 100644 index 00000000..3bb2117f --- /dev/null +++ b/src/ratbagd-button.c @@ -0,0 +1,104 @@ +/*** + 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. +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ratbagd.h" +#include "shared-macro.h" + +struct ratbagd_button { + struct ratbag_button *lib_button; + unsigned int index; + char *path; +}; + +const sd_bus_vtable ratbagd_button_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_button, index), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END, +}; + +int ratbagd_button_new(struct ratbagd_button **out, + struct ratbagd_device *device, + struct ratbagd_profile *profile, + struct ratbag_button *lib_button, + unsigned int index) +{ + _cleanup_(ratbagd_button_freep) struct ratbagd_button *button = NULL; + char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1], + button_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; + int r; + + assert(out); + assert(lib_button); + + button = calloc(1, sizeof(*button)); + if (!button) + return -ENOMEM; + + button->lib_button = lib_button; + button->index = index; + + sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile)); + sprintf(button_buffer, "b%u", index); + r = sd_bus_path_encode_many(&button->path, + "/org/freedesktop/ratbag1/button/%/%/%", + ratbagd_device_get_name(device), + profile_buffer, + button_buffer); + if (r < 0) + return r; + + *out = button; + button = NULL; + return 0; +} + +const char *ratbagd_button_get_path(struct ratbagd_button *button) +{ + assert(button); + return button->path; +} + +struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button) +{ + if (!button) + return NULL; + + button->path = mfree(button->path); + + /* we cannot assume the button is gone, so set NULL explicitly */ + ratbag_button_unref(button->lib_button); + button->lib_button = NULL; + + return mfree(button); +} + diff --git a/src/ratbagd-device.c b/src/ratbagd-device.c index 69a1fa88..45c17d99 100644 --- a/src/ratbagd-device.c +++ b/src/ratbagd-device.c @@ -384,6 +384,12 @@ const char *ratbagd_device_get_path(struct ratbagd_device *device) return device->path; } +unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device) +{ + assert(device); + return ratbag_device_get_num_buttons(device->lib_device); +} + bool ratbagd_device_linked(struct ratbagd_device *device) { return device && rbnode_linked(&device->node); @@ -457,6 +463,10 @@ void ratbagd_device_link(struct ratbagd_device *device) "Cannot register resolutions for '%s': %m\n", device->name); } + + r = ratbagd_profile_register_buttons(device->ctx->bus, + device, + device->profiles[i]); } } diff --git a/src/ratbagd-profile.c b/src/ratbagd-profile.c index 81c8ad5f..0aa43622 100644 --- a/src/ratbagd-profile.c +++ b/src/ratbagd-profile.c @@ -44,6 +44,11 @@ struct ratbagd_profile { sd_bus_slot *resolution_enum_slot; unsigned int n_resolutions; struct ratbagd_resolution **resolutions; + + sd_bus_slot *button_vtable_slot; + sd_bus_slot *button_enum_slot; + unsigned int n_buttons; + struct ratbagd_button **buttons; }; static int ratbagd_profile_find_resolution(sd_bus *bus, @@ -163,6 +168,37 @@ static int ratbagd_profile_get_default_resolution(sd_bus *bus, return sd_bus_message_append(reply, "u", 0); } +static int ratbagd_profile_find_button(sd_bus *bus, + const char *path, + const char *interface, + void *userdata, + void **found, + sd_bus_error *error) +{ + _cleanup_(freep) char *name = NULL; + struct ratbagd_profile *profile = userdata; + unsigned int index; + int r; + + r = sd_bus_path_decode_many(path, + "/org/freedesktop/ratbag1/button/%/p%/b%", + NULL, + NULL, + &name); + if (r <= 0) + return r; + + r = safe_atou(name, &index); + if (r < 0) + return 0; + + if (index >= profile->n_buttons || !profile->buttons[index]) + return 0; + + *found = profile->buttons[index]; + return 1; +} + static int ratbagd_profile_set_active(sd_bus_message *m, void *userdata, sd_bus_error *error) @@ -217,6 +253,7 @@ int ratbagd_profile_new(struct ratbagd_profile **out, { _cleanup_(ratbagd_profile_freep) struct ratbagd_profile *profile = NULL; struct ratbag_resolution *resolution; + struct ratbag_button *button; char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; unsigned int i; int r; @@ -245,6 +282,11 @@ int ratbagd_profile_new(struct ratbagd_profile **out, if (!profile->resolutions) return -ENOMEM; + profile->n_buttons = ratbagd_device_get_num_buttons(device); + profile->buttons = calloc(profile->n_buttons, sizeof(*profile->buttons)); + if (!profile->buttons) + return -ENOMEM; + for (i = 0; i < profile->n_resolutions; ++i) { resolution = ratbag_profile_get_resolution(profile->lib_profile, i); if (!resolution) @@ -263,6 +305,24 @@ int ratbagd_profile_new(struct ratbagd_profile **out, } } + for (i = 0; i < profile->n_buttons; ++i) { + button = ratbag_profile_get_button(profile->lib_profile, i); + if (!button) + continue; + + r = ratbagd_button_new(&profile->buttons[i], + device, + profile, + button, + i); + if (r < 0) { + errno = -r; + fprintf(stderr, + "Cannot allocate button for '%s': %m\n", + ratbagd_device_get_name(device)); + } + } + *out = profile; profile = NULL; return 0; @@ -378,3 +438,81 @@ int ratbagd_profile_register_resolutions(struct sd_bus *bus, return 0; } + +static int ratbagd_profile_list_buttons(sd_bus *bus, + const char *path, + void *userdata, + char ***paths, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbagd_button *button; + char **buttons; + unsigned int i; + + buttons = calloc(profile->n_buttons + 1, sizeof(char *)); + if (!buttons) + return -ENOMEM; + + for (i = 0; i < profile->n_buttons; ++i) { + button = profile->buttons[i]; + if (!button) + continue; + + buttons[i] = strdup(ratbagd_button_get_path(button)); + if (!buttons[i]) + goto error; + } + + buttons[i] = NULL; + *paths = buttons; + return 1; + +error: + for (i = 0; buttons[i]; ++i) + free(buttons[i]); + free(buttons); + return -ENOMEM; +} + +int ratbagd_profile_register_buttons(struct sd_bus *bus, + struct ratbagd_device *device, + struct ratbagd_profile *profile) +{ + _cleanup_(freep) char *prefix = NULL; + char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; + int r; + + sprintf(index_buffer, "p%u", profile->index); + + /* register resolution interfaces */ + r = sd_bus_path_encode_many(&prefix, + "/org/freedesktop/ratbag1/button/%/%", + ratbagd_device_get_name(device), + index_buffer); + + + if (r >= 0) { + r = sd_bus_add_fallback_vtable(bus, + &profile->button_vtable_slot, + prefix, + "org.freedesktop.ratbag1.Button", + ratbagd_button_vtable, + ratbagd_profile_find_button, + profile); + if (r >= 0) + r = sd_bus_add_node_enumerator(bus, + &profile->button_enum_slot, + prefix, + ratbagd_profile_list_buttons, + profile); + } + if (r < 0) { + errno = -r; + fprintf(stderr, + "Cannot register buttons for '%s': %m\n", + ratbagd_device_get_name(device)); + } + + return 0; +} diff --git a/src/ratbagd.h b/src/ratbagd.h index 296866c2..442f2e6f 100644 --- a/src/ratbagd.h +++ b/src/ratbagd.h @@ -40,7 +40,7 @@ struct ratbagd; struct ratbagd_device; struct ratbagd_profile; struct ratbagd_resolution; - +struct ratbagd_button; void log_verbose(const char *fmt, ...) _printf_(1, 2); @@ -62,6 +62,9 @@ unsigned int ratbagd_profile_get_index(struct ratbagd_profile *profile); int ratbagd_profile_register_resolutions(struct sd_bus *bus, struct ratbagd_device *device, struct ratbagd_profile *profile); +int ratbagd_profile_register_buttons(struct sd_bus *bus, + struct ratbagd_device *device, + struct ratbagd_profile *profile); DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free); @@ -82,6 +85,21 @@ bool ratbagd_resolution_is_default(struct ratbagd_resolution *resolution); DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_resolution *, ratbagd_resolution_free); +/* + * Buttons + */ +extern const sd_bus_vtable ratbagd_button_vtable[]; + +int ratbagd_button_new(struct ratbagd_button **out, + struct ratbagd_device *device, + struct ratbagd_profile *profile, + struct ratbag_button *lib_button, + unsigned int index); +struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button); +const char *ratbagd_button_get_path(struct ratbagd_button *button); + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_button *, ratbagd_button_free); + /* * Devices */ @@ -95,6 +113,7 @@ int ratbagd_device_new(struct ratbagd_device **out, struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device); const char *ratbagd_device_get_name(struct ratbagd_device *device); const char *ratbagd_device_get_path(struct ratbagd_device *device); +unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device); bool ratbagd_device_linked(struct ratbagd_device *device); void ratbagd_device_link(struct ratbagd_device *device);