From 51edc2967bac3c68514e3dbe75315fdb3905513e Mon Sep 17 00:00:00 2001 From: Kyoken Date: Fri, 9 Dec 2022 14:18:44 +0300 Subject: [PATCH] add angle snapping and debounce --- ratbagd/ratbagd-profile.c | 134 +++++++++++++++++++++++++++++++++++++ src/asus.c | 14 +++- src/driver-asus.c | 16 +++++ src/libratbag-private.h | 25 ++++++- src/libratbag.c | 60 +++++++++++++++++ src/libratbag.h | 85 ++++++++++++++++++++++- src/libratbag.i | 7 ++ tools/ratbagc.py.in | 33 +++++++++ tools/ratbagctl.body.py.in | 114 +++++++++++++++++++++++++++++++ tools/ratbagd.py | 31 +++++++++ 10 files changed, 516 insertions(+), 3 deletions(-) diff --git a/ratbagd/ratbagd-profile.c b/ratbagd/ratbagd-profile.c index 4178f4ec..c468acf5 100644 --- a/ratbagd/ratbagd-profile.c +++ b/ratbagd/ratbagd-profile.c @@ -454,6 +454,40 @@ ratbagd_profile_get_report_rate(sd_bus *bus, return sd_bus_message_append(reply, "u", rate); } +static int +ratbagd_profile_get_angle_snapping(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbag_profile *lib_profile = profile->lib_profile; + int value; + + value = ratbag_profile_get_angle_snapping(lib_profile); + return sd_bus_message_append(reply, "i", value); +} + +static int +ratbagd_profile_get_debounce(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbag_profile *lib_profile = profile->lib_profile; + int value; + + value = ratbag_profile_get_debounce(lib_profile); + return sd_bus_message_append(reply, "i", value); +} + static int ratbagd_profile_get_report_rates(sd_bus *bus, const char *path, @@ -487,6 +521,37 @@ ratbagd_profile_get_report_rates(sd_bus *bus, return sd_bus_message_close_container(reply); } +static int +ratbagd_profile_get_debounces(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbag_profile *lib_profile = profile->lib_profile; + unsigned int debounces[8]; + unsigned int ndebounces = ARRAY_LENGTH(debounces); + int r; + + r = sd_bus_message_open_container(reply, 'a', "u"); + if (r < 0) + return r; + + ndebounces = ratbag_profile_get_debounce_list(lib_profile, debounces, ndebounces); + assert(ndebounces <= ARRAY_LENGTH(debounces)); + + for (unsigned int i = 0; i < ndebounces; i++) { + verify_unsigned_int(debounces[i]); + r = sd_bus_message_append(reply, "u", debounces[i]); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} static int ratbagd_profile_set_report_rate(sd_bus *bus, @@ -521,6 +586,66 @@ ratbagd_profile_set_report_rate(sd_bus *bus, return 0; } +static int +ratbagd_profile_set_angle_snapping(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + int value; + int r; + + r = sd_bus_message_read(m, "i", &value); + if (r < 0) + return r; + + r = ratbag_profile_set_angle_snapping(profile->lib_profile, value); + if (r == 0) { + sd_bus *bus = sd_bus_message_get_bus(m); + sd_bus_emit_properties_changed(bus, + profile->path, + RATBAGD_NAME_ROOT ".Profile", + "AngleSnapping", + NULL); + } + + return 0; +} + +static int +ratbagd_profile_set_debounce(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + int value; + int r; + + r = sd_bus_message_read(m, "i", &value); + if (r < 0) + return r; + + r = ratbag_profile_set_debounce(profile->lib_profile, value); + if (r == 0) { + sd_bus *bus = sd_bus_message_get_bus(m); + sd_bus_emit_properties_changed(bus, + profile->path, + RATBAGD_NAME_ROOT ".Profile", + "Debounce", + NULL); + } + + return 0; +} + const sd_bus_vtable ratbagd_profile_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_WRITABLE_PROPERTY("Name", "s", @@ -541,7 +666,16 @@ const sd_bus_vtable ratbagd_profile_vtable[] = { ratbagd_profile_get_report_rate, ratbagd_profile_set_report_rate, 0, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_WRITABLE_PROPERTY("AngleSnapping", "i", + ratbagd_profile_get_angle_snapping, + ratbagd_profile_set_angle_snapping, 0, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_WRITABLE_PROPERTY("Debounce", "i", + ratbagd_profile_get_debounce, + ratbagd_profile_set_debounce, 0, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("ReportRates", "au", ratbagd_profile_get_report_rates, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Debounces", "au", ratbagd_profile_get_debounces, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("SetActive", "", "u", ratbagd_profile_set_active, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/asus.c b/src/asus.c index ed0efa89..73579918 100644 --- a/src/asus.c +++ b/src/asus.c @@ -76,6 +76,7 @@ static const unsigned char ASUS_KEY_MAPPING[] = { }; static unsigned int ASUS_POLLING_RATES[] = { 125, 250, 500, 1000 }; +static unsigned int ASUS_DEBOUNCE_TIMES[] = { 4, 8, 12, 16, 20, 24, 28, 32 }; /* search for ASUS button by ratbag types */ const struct asus_button * @@ -147,6 +148,8 @@ asus_setup_profile(struct ratbag_device *device, struct ratbag_profile *profile) { ratbag_profile_set_report_rate_list( profile, ASUS_POLLING_RATES, ARRAY_LENGTH(ASUS_POLLING_RATES)); + ratbag_profile_set_debounce_list( + profile, ASUS_DEBOUNCE_TIMES, ARRAY_LENGTH(ASUS_DEBOUNCE_TIMES)); } void @@ -311,6 +314,7 @@ asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_dat data->data2.dpi[i] *= 2; } data->data2.rate = ASUS_POLLING_RATES[data->data2.rate]; + data->data2.response = ASUS_DEBOUNCE_TIMES[data->data2.response]; break; case 4: /* 4 DPI presets */ @@ -320,6 +324,7 @@ asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_dat data->data4.dpi[i] *= 2; } data->data4.rate = ASUS_POLLING_RATES[data->data4.rate]; + data->data4.response = ASUS_DEBOUNCE_TIMES[data->data4.response]; break; default: @@ -392,11 +397,18 @@ asus_set_button_response(struct ratbag_device *device, unsigned int ms) int rc; unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions; union asus_response response; + unsigned int index = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_DEBOUNCE_TIMES); i++) { + if (ASUS_DEBOUNCE_TIMES[i] == ms) { + index = i; + break; + } + } union asus_request request = { .data.cmd = ASUS_CMD_SET_SETTING, .data.params[0] = dpi_count + ASUS_FIELD_RESPONSE, /* field index to set */ - .data.params[2] = ms / 4 - 1, + .data.params[2] = index, }; rc = asus_query(device, &request, &response); diff --git a/src/driver-asus.c b/src/driver-asus.c index f9e445f3..4a062a1a 100644 --- a/src/driver-asus.c +++ b/src/driver-asus.c @@ -148,6 +148,8 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr switch (dpi_count) { case 2: /* 2 DPI presets */ ratbag_profile_set_report_rate(profile, resolution_data.data2.rate); + ratbag_profile_set_angle_snapping(profile, resolution_data.data2.snapping); + ratbag_profile_set_debounce(profile, resolution_data.data2.response); ratbag_profile_for_each_resolution(profile, resolution) ratbag_resolution_set_resolution( resolution, @@ -157,6 +159,8 @@ asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *pr case 4: /* 4 DPI presets */ ratbag_profile_set_report_rate(profile, resolution_data.data4.rate); + ratbag_profile_set_angle_snapping(profile, resolution_data.data4.snapping); + ratbag_profile_set_debounce(profile, resolution_data.data4.response); ratbag_profile_for_each_resolution(profile, resolution) ratbag_resolution_set_resolution( resolution, @@ -265,6 +269,18 @@ asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *pr if (rc) return rc; } + if (profile->angle_snapping_dirty) { + log_debug(device->ratbag, "Angle snapping changed to %d\n", profile->angle_snapping); + rc = asus_set_angle_snapping(device, profile->angle_snapping); + if (rc) + return rc; + } + if (profile->debounce_dirty) { + log_debug(device->ratbag, "Debounce time changed to %d\n", profile->debounce); + rc = asus_set_button_response(device, profile->debounce); + if (rc) + return rc; + } /* set DPIs */ ratbag_profile_for_each_resolution(profile, resolution) { diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 8a510714..1187adcb 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -267,6 +267,14 @@ struct ratbag_profile { size_t nrates; /**< number of entries in rates */ bool rate_dirty; + int angle_snapping; + bool angle_snapping_dirty; + + int debounce; /**< debounce time in ms */ + bool debounce_dirty; + unsigned int debounces[8]; /**< debounce times available */ + size_t ndebounces; /**< number of entries in debounces */ + unsigned int num_resolutions; bool is_active; /**< profile is the currently active one */ @@ -538,6 +546,22 @@ ratbag_profile_set_report_rate_list(struct ratbag_profile *profile, profile->nrates = nrates; } +static inline void +ratbag_profile_set_debounce_list(struct ratbag_profile *profile, + const unsigned int *values, + size_t nvalues) +{ + assert(nvalues <= ARRAY_LENGTH(profile->debounces)); + _Static_assert(sizeof(*values) == sizeof(*profile->debounces), "Mismatching size"); + + for (size_t i = 0; i < nvalues; i++) { + profile->debounces[i] = values[i]; + if (i > 0) + assert(values[i] > values[i - 1]); + } + profile->ndebounces = nvalues; +} + static inline void ratbag_resolution_set_cap(struct ratbag_resolution *res, enum ratbag_resolution_capability cap) @@ -594,4 +618,3 @@ ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver); void ratbag_button_copy_macro(struct ratbag_button *button, const struct ratbag_button_macro *macro); - diff --git a/src/libratbag.c b/src/libratbag.c index adfe4883..db7b1d02 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -697,6 +697,8 @@ ratbag_create_profile(struct ratbag_device *device, profile->num_resolutions = 0; profile->is_enabled = true; profile->name = NULL; + profile->angle_snapping = -1; + profile->debounce = -1; list_append(&device->profiles, &profile->link); list_init(&profile->buttons); @@ -863,6 +865,8 @@ ratbag_device_commit(struct ratbag_device *device) list_for_each(profile, &device->profiles, link) { profile->dirty = false; + profile->angle_snapping_dirty = false; + profile->debounce_dirty = false; profile->rate_dirty = false; list_for_each(button, &profile->buttons, link) @@ -1034,6 +1038,32 @@ ratbag_profile_set_report_rate(struct ratbag_profile *profile, return RATBAG_SUCCESS; } +LIBRATBAG_EXPORT enum ratbag_error_code +ratbag_profile_set_angle_snapping(struct ratbag_profile *profile, + int value) +{ + if (profile->angle_snapping != value) { + profile->angle_snapping = value; + profile->dirty = true; + profile->angle_snapping_dirty = true; + } + + return RATBAG_SUCCESS; +} + +LIBRATBAG_EXPORT enum ratbag_error_code +ratbag_profile_set_debounce(struct ratbag_profile *profile, + int value) +{ + if (profile->debounce != value) { + profile->debounce = value; + profile->dirty = true; + profile->debounce_dirty = true; + } + + return RATBAG_SUCCESS; +} + LIBRATBAG_EXPORT int ratbag_resolution_get_dpi(struct ratbag_resolution *resolution) { @@ -1076,6 +1106,36 @@ ratbag_profile_get_report_rate(struct ratbag_profile *profile) return profile->hz; } +LIBRATBAG_EXPORT int +ratbag_profile_get_angle_snapping(struct ratbag_profile *profile) +{ + return profile->angle_snapping; +} + +LIBRATBAG_EXPORT int +ratbag_profile_get_debounce(struct ratbag_profile *profile) +{ + return profile->debounce; +} + +LIBRATBAG_EXPORT size_t +ratbag_profile_get_debounce_list(struct ratbag_profile *profile, + unsigned int *debounces, + size_t ndebounces) +{ + _Static_assert(sizeof(*debounces) == sizeof(*profile->debounces), "type mismatch"); + + assert(ndebounces > 0); + + if (profile->ndebounces == 0) + return 0; + + memcpy(debounces, profile->debounces, + sizeof(unsigned int) * min(ndebounces, profile->ndebounces)); + + return profile->ndebounces; +} + LIBRATBAG_EXPORT size_t ratbag_profile_get_report_rate_list(struct ratbag_profile *profile, unsigned int *rates, diff --git a/src/libratbag.h b/src/libratbag.h index 7ae5d2eb..44e93f85 100644 --- a/src/libratbag.h +++ b/src/libratbag.h @@ -769,6 +769,90 @@ ratbag_profile_get_report_rate_list(struct ratbag_profile *profile, unsigned int *rates, size_t nrates); +/** + * @ingroup profile + * + * Set the angle snapping option for the profile. + * + * A value of 0 disables the mode. + * + * If the profile mode is the currently active profile, + * the change takes effect immediately. + * + * @param profile A previously initialized ratbag profile + * @param value Angle snapping value + * + * @return zero on success or an error code on failure + */ +enum ratbag_error_code +ratbag_profile_set_angle_snapping(struct ratbag_profile *profile, + int value); + +/** + * @ingroup profile + * + * Get the angle snapping option for the profile. + * + * @param profile A previously initialized ratbag profile + * + * @return Angle snapping value + */ +int +ratbag_profile_get_angle_snapping(struct ratbag_profile *profile); + +/** + * @ingroup profile + * + * Set the debounce time in ms for the profile. + * + * If the profile mode is the currently active profile, + * the change takes effect immediately. + * + * @param profile A previously initialized ratbag profile + * @param value Debounce time in ms + * + * @return zero on success or an error code on failure + */ +enum ratbag_error_code +ratbag_profile_set_debounce(struct ratbag_profile *profile, + int value); + +/** + * @ingroup profile + * + * Get the debounce time in ms for the profile. + * + * @param profile A previously initialized ratbag profile + * + * @return Debounce time for this profile in ms + */ +int +ratbag_profile_get_debounce(struct ratbag_profile *profile); + +/** + * @ingroup profile + * + * Get the number of debounce times in ms available for this profile. + * The list of debounce times is sorted in ascending order but may be filtered + * by libratbag and does not necessarily reflect all debounce times supported by + * the physical device. + * + * This function writes at most ndebounces values but returns the number of + * debounce times available on this resolution. In other words, if it returns a + * number larger than ndebounces, call it again with an array the size of the + * return value. + * + * @param[out] debounces Set to the supported debounce times in ascending order + * @param[in] ndebounces The number of elements + * + * @return The number of valid items in values. If the returned value + * is larger than ndebounces, the list was truncated. + */ +size_t +ratbag_profile_get_debounce_list(struct ratbag_profile *profile, + unsigned int *debounces, + size_t ndebounces); + /** * @ingroup profile * @@ -1684,4 +1768,3 @@ ratbag_led_ref(struct ratbag_led *led); */ struct ratbag_led * ratbag_led_unref(struct ratbag_led *led); - diff --git a/src/libratbag.i b/src/libratbag.i index 2d1113ea..0618707f 100644 --- a/src/libratbag.i +++ b/src/libratbag.i @@ -52,6 +52,13 @@ %typemap(freearg) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres); /* END OF custom typemap for handling ratbag_resolution_get_report_rate_list */ +/* custom typemap for handling lists */ +/* We re-use the ratbag_profile_get_debounce_list typemap */ +%typemap(in) (unsigned int *debounces, size_t ndebounces) = (unsigned int *resolutions, size_t nres); +%typemap(argout) (unsigned int *debounces, size_t ndebounces) = (unsigned int *resolutions, size_t nres); +%typemap(freearg) (unsigned int *debounces, size_t ndebounces) = (unsigned int *resolutions, size_t nres); +/* END OF custom typemap for handling ratbag_resolution_get_report_rate_list */ + /* uintXX_t mapping: Python -> C */ %typemap(in) uint8_t { $1 = (uint8_t) PyInt_AsLong($input); diff --git a/tools/ratbagc.py.in b/tools/ratbagc.py.in index ca69778e..f783db1d 100644 --- a/tools/ratbagc.py.in +++ b/tools/ratbagc.py.in @@ -437,6 +437,39 @@ class RatbagdProfile(metaclass=MetaRatbag): n = libratbag.ratbag_resolution_get_report_rate_list(self._profile, rates) return rates[:n] + @property + def angle_snapping(self): + """The angle snapping option.""" + return libratbag.ratbag_profile_get_angle_snapping(self._profile) + + @angle_snapping.setter + def angle_snapping(self, value): + """Set the angle snapping option. + + @param value The angle snapping option as bool + """ + libratbag.ratbag_profile_set_angle_snapping(self._profile, value) + + @property + def debounce(self): + """The debounce time in ms.""" + return libratbag.ratbag_profile_get_debounce(self._profile) + + @debounce.setter + def debounce(self, value): + """Set the debounce time in ms. + + @param value The new debounce time, as int + """ + libratbag.ratbag_profile_set_debounce(self._profile, value) + + @property + def debounces(self): + """The list of supported debounce times""" + values = [0 for i in range(300)] + n = libratbag.ratbag_profile_get_debounce_list(self._profile, values) + return values[:n] + @property def resolutions(self): """A list of RatbagdResolution objects with this profile's resolutions. diff --git a/tools/ratbagctl.body.py.in b/tools/ratbagctl.body.py.in index 4da7427b..3832c198 100755 --- a/tools/ratbagctl.body.py.in +++ b/tools/ratbagctl.body.py.in @@ -278,6 +278,10 @@ def print_profile(device: RatbagdDevice, profile: RatbagdProfile, level: int) -> print(" " * level + "Resolutions:") for resolution in profile.resolutions: print_resolution(device, profile, resolution, level + 2) + if profile.angle_snapping != -1: + print(" " * level + f"Angle Snapping: {bool(profile.angle_snapping)}") + if profile.debounce != -1: + print(" " * level + f"Debounce time: {profile.debounce}ms") if profile.buttons: for button in profile.buttons: print_button(device, profile, button, level) @@ -517,6 +521,38 @@ def func_report_rate_set(ratbagd: Ratbagd, args: argparse.Namespace) -> None: commit(device, args) +def func_angle_snapping_get(ratbagd: Ratbagd, args: argparse.Namespace) -> None: + profile, _device = find_profile(ratbagd, args) + if profile.angle_snapping == -1: + return None + print(bool(profile.angle_snapping)) + + +def func_angle_snapping_set(ratbagd: Ratbagd, args: argparse.Namespace) -> None: + profile, device = find_profile(ratbagd, args) + profile.angle_snapping = 1 if args.angle_snapping_n else 0 + commit(device, args) + + +def func_debounce_get(ratbagd: Ratbagd, args: argparse.Namespace) -> None: + profile, _device = find_profile(ratbagd, args) + if profile.debounce == -1: + return None + print(profile.debounce) + + +def func_debounce_get_all(ratbagd: Ratbagd, args: argparse.Namespace) -> None: + profile, _device = find_profile(ratbagd, args) + values = profile.debounces + print(" ".join([str(x) for x in values])) + + +def func_debounce_set(ratbagd: Ratbagd, args: argparse.Namespace) -> None: + profile, device = find_profile(ratbagd, args) + profile.debounce = args.debounce_n + commit(device, args) + + def func_resolution_get(ratbagd: Ratbagd, args: argparse.Namespace) -> None: resolution, profile, device = find_resolution(ratbagd, args) print_resolution(device, profile, resolution, 0) @@ -855,6 +891,14 @@ parser_def: List[RatbagParserSchemaElement] = [ of_type: link, dest: "rate", }, + { + of_type: link, + dest: "angle_snapping", + }, + { + of_type: link, + dest: "debounce", + }, { of_type: link, dest: "button", @@ -1067,6 +1111,76 @@ Rate commands work on the given profile, or on the active profile if none is giv }, ], }, + { + of_type: switch, + name: "angle_snapping", + help_str: """Angle snapping information + +Angle snapping commands work on the given profile, or on the active profile if none is given.""", + tag: "angle_snapping", + group: "AngleSnapping", + switch: [ + { + of_type: command, + name: "get", + help_str: "Show current angle snapping", + func: func_angle_snapping_get, + }, + { + of_type: command, + name: "set", + help_str: "Set the current angle snapping to N", + pos_args: [ + { + of_type: argument, + name: "angle_snapping_n", + metavar: "N", + help_str: "The angle snapping to set as current", + arg_type: int, + }, + ], + func: func_angle_snapping_set, + }, + ], + }, + { + of_type: switch, + name: "debounce", + help_str: """Access debounce time information + +Debounce commands work on the given profile, or on the active profile if none is given.""", + tag: "debounce", + group: "Debounce", + switch: [ + { + of_type: command, + name: "get", + help_str: "Show current debounce time", + func: func_debounce_get, + }, + { + of_type: command, + name: "get-all", + help_str: "Show all available debounce times", + func: func_debounce_get_all, + }, + { + of_type: command, + name: "set", + help_str: "Set the current debounce time to N", + pos_args: [ + { + of_type: argument, + name: "debounce_n", + metavar: "N", + help_str: "The debounce time to set as current", + arg_type: int, + }, + ], + func: func_debounce_set, + }, + ], + }, { of_type: switch, name: "button", diff --git a/tools/ratbagd.py b/tools/ratbagd.py index d1f90bfd..bfa39465 100644 --- a/tools/ratbagd.py +++ b/tools/ratbagd.py @@ -543,6 +543,37 @@ class RatbagdProfile(_RatbagdDBus): """The list of supported report rates""" return self._get_dbus_property("ReportRates") or [] + @GObject.Property + def angle_snapping(self): + """The angle snapping option.""" + return self._get_dbus_property("AngleSnapping") + + @angle_snapping.setter + def angle_snapping(self, value): + """Set the angle snapping option. + + @param value The angle snapping option as int + """ + self._set_dbus_property("AngleSnapping", "i", value) + + @GObject.Property + def debounce(self): + """The button debounce time in ms.""" + return self._get_dbus_property("Debounce") + + @debounce.setter + def debounce(self, value): + """Set the button debounce time in ms. + + @param value The button debounce time, as int + """ + self._set_dbus_property("Debounce", "i", value) + + @GObject.Property + def debounces(self): + """The list of supported debounce times""" + return self._get_dbus_property("Debounces") or [] + @GObject.Property def resolutions(self): """A list of RatbagdResolution objects with this profile's resolutions.