diff --git a/libwacom/libwacom.c b/libwacom/libwacom.c index 5e18353b..16e2d697 100644 --- a/libwacom/libwacom.c +++ b/libwacom/libwacom.c @@ -26,6 +26,7 @@ #include "config.h" +#include "libwacom.h" #include "libwacomint.h" #include #include @@ -603,18 +604,87 @@ libwacom_new (const WacomDeviceDatabase *db, const char *name, const char *uniq, return device; } +static bool +builder_is_name_only(const WacomBuilder *builder) +{ + return builder->device_name != NULL && builder->match_name == NULL && + builder->uniq == NULL && + builder->vendor_id == 0 && builder->product_id == 0 && + builder->bus == WBUSTYPE_UNKNOWN; +} + +static bool +builder_is_uniq_only(const WacomBuilder *builder) +{ + return builder->device_name == NULL && builder->match_name == NULL && + builder->uniq != NULL && + builder->vendor_id == 0 && builder->product_id == 0 && + builder->bus == WBUSTYPE_UNKNOWN; +} + +/** + * Return a copy of the given device or, if NULL, the fallback device with + * the name changed to the override name. + */ +static WacomDevice * +fallback_or_device(const WacomDeviceDatabase *db, const WacomDevice *device, + const char *name_override, WacomFallbackFlags fallback_flags) +{ + WacomDevice *copy = NULL; + const WacomDevice *fallback; + const char *fallback_name = NULL; + + if (device != NULL) { + return libwacom_copy(device); + } + + switch (fallback_flags) { + case WFALLBACK_NONE: + return NULL; + case WFALLBACK_GENERIC: + fallback_name = "generic"; + break; + default: + g_assert_not_reached(); + break; + } + + fallback = libwacom_get_device(db, fallback_name); + if (fallback == NULL) + return NULL; + + copy = libwacom_copy(fallback); + if (name_override != NULL) { + g_free(copy->name); + copy->name = g_strdup(name_override); + } + return copy; +} + +static gint +find_named_device(const WacomDevice *device, const char *name) +{ + return g_strcmp0(device->name, name); +} + +static gint +find_uniq_device(const WacomDevice *device, const char *uniq) +{ + const WacomMatch **matches = libwacom_get_matches(device); + + for (const WacomMatch **match = matches; *match; match++) { + if (uniq && (*match)->uniq && g_str_equal((*match)->uniq, uniq)) + return 0; + } + return -1; +} + LIBWACOM_EXPORT WacomDevice* -libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFallbackFlags fallback, WacomError *error) +libwacom_new_from_builder(const WacomDeviceDatabase *db, const WacomBuilder *builder, + WacomFallbackFlags fallback, WacomError *error) { - int vendor_id, product_id; - WacomBusType bus; - const WacomDevice *device; + const WacomDevice *device = NULL; WacomDevice *ret = NULL; - WacomIntegrationFlags integration_flags; - char *name, *match_name; - char *uniq, *match_uniq; - WacomMatch *match; - switch (fallback) { case WFALLBACK_NONE: case WFALLBACK_GENERIC: @@ -629,115 +699,137 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal return NULL; } - if (!path) { - libwacom_error_set(error, WERROR_INVALID_PATH, "path is NULL"); - return NULL; - } - - if (!get_device_info (path, &vendor_id, &product_id, &name, &uniq, &bus, &integration_flags, error)) - return NULL; - - match_name = name; - match_uniq = uniq; - device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); - if (device == NULL) { - match_uniq = NULL; - device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); - if (device == NULL) { - match_name = NULL; - device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); + /* Name-only matches behave like new_from_name */ + if (builder_is_name_only(builder)) { + GList *keys = g_hash_table_get_values(db->device_ht); + GList *entry = g_list_find_custom(keys, builder->device_name, (GCompareFunc)find_named_device); + if (entry) + device = entry->data; + ret = fallback_or_device(db, device, builder->device_name, fallback); + g_list_free (keys); + /* Uniq-only behaves like new_from_name but matches on uniq in the match strings */ + } else if (builder_is_uniq_only(builder)) { + GList *keys = g_hash_table_get_values(db->device_ht); + GList *entry = g_list_find_custom(keys, builder->uniq, (GCompareFunc)find_uniq_device); + if (entry) + device = entry->data; + ret = fallback_or_device(db, device, builder->device_name, fallback); + g_list_free (keys); + } else { + WacomBusType all_busses[] = { + WBUSTYPE_USB, + WBUSTYPE_I2C, + WBUSTYPE_BLUETOOTH, + WBUSTYPE_UNKNOWN, + }; + WacomBusType fixed_bus[] = { + builder->bus, + WBUSTYPE_UNKNOWN, + }; + WacomBusType *bus; + + int vendor_id, product_id; + char *name, *uniq; + const char *match_name, *match_uniq; + WacomMatch *used_match; + + vendor_id = builder->vendor_id; + product_id = builder->product_id; + name = builder->match_name; + uniq = builder->uniq; + if (builder->bus) + bus = fixed_bus; + else + bus = all_busses; + + while (*bus != WBUSTYPE_UNKNOWN) { + match_name = name; + match_uniq = uniq; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, *bus, error); + if (device == NULL) { + match_uniq = NULL; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, *bus, error); + if (device == NULL) { + match_name = NULL; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, *bus, error); + } + } + if (device) + break; + bus++; } - } - - if (device == NULL) { - if (fallback == WFALLBACK_NONE) - goto out; - - /* WFALLBACK_GENERIC */ - device = libwacom_get_device(db, "generic"); - if (device == NULL) - goto out; - - ret = libwacom_copy(device); - - if (name != NULL) { - g_free (ret->name); - ret->name = g_strdup(name); + ret = fallback_or_device(db, device, builder->device_name, fallback); + if (ret) { + /* for multiple-match devices, set to the one we requested */ + used_match = libwacom_match_new(match_name, match_uniq, *bus, vendor_id, product_id); + libwacom_set_default_match(ret, used_match); + libwacom_match_unref(used_match); } - } else { - ret = libwacom_copy(device); } - /* for multiple-match devices, set to the one we requested */ - match = libwacom_match_new(match_name, match_uniq, bus, vendor_id, product_id); - libwacom_set_default_match(ret, match); - libwacom_match_unref(match); - - /* if unset, use the kernel flags. Could be unset as well. */ - if (device && ret->integration_flags == WACOM_DEVICE_INTEGRATED_UNSET) - ret->integration_flags = integration_flags; - -out: - g_free (name); - g_free (uniq); if (ret == NULL) libwacom_error_set(error, WERROR_UNKNOWN_MODEL, "unknown model"); return ret; } LIBWACOM_EXPORT WacomDevice* -libwacom_new_from_usbid(const WacomDeviceDatabase *db, int vendor_id, int product_id, WacomError *error) +libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFallbackFlags fallback, WacomError *error) { - const WacomDevice *device; + int vendor_id, product_id; + WacomBusType bus; + WacomDevice *device; + WacomIntegrationFlags integration_flags; + char *name, *uniq; + WacomBuilder *builder; - if (!db) { - libwacom_error_set(error, WERROR_INVALID_DB, "db is NULL"); + if (!path) { + libwacom_error_set(error, WERROR_INVALID_PATH, "path is NULL"); return NULL; } - device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_USB, error); - if (!device) - device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_I2C, error); - if (!device) - device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_BLUETOOTH, error); + if (!get_device_info (path, &vendor_id, &product_id, &name, &uniq, &bus, &integration_flags, error)) + return NULL; - if (device) - return libwacom_copy(device); + builder = libwacom_builder_new(); + libwacom_builder_set_match_name(builder, name); + libwacom_builder_set_uniq(builder, uniq); + libwacom_builder_set_usbid(builder, vendor_id, product_id); + device = libwacom_new_from_builder(db, builder, fallback, error); + /* if unset, use the kernel flags. Could be unset as well. */ + if (device && device->integration_flags == WACOM_DEVICE_INTEGRATED_UNSET) + device->integration_flags = integration_flags; - libwacom_error_set(error, WERROR_UNKNOWN_MODEL, NULL); - return NULL; + libwacom_builder_destroy(builder); + g_free (name); + g_free (uniq); + + return device; } LIBWACOM_EXPORT WacomDevice* -libwacom_new_from_name(const WacomDeviceDatabase *db, const char *name, WacomError *error) +libwacom_new_from_usbid(const WacomDeviceDatabase *db, int vendor_id, int product_id, WacomError *error) { - const WacomDevice *device; - GList *keys, *l; - - if (!db) { - libwacom_error_set(error, WERROR_INVALID_DB, "db is NULL"); - return NULL; - } + WacomDevice *device; + WacomBuilder *builder = libwacom_builder_new(); - g_return_val_if_fail(name != NULL, NULL); + libwacom_builder_set_usbid(builder, vendor_id, product_id); + device = libwacom_new_from_builder(db, builder, WFALLBACK_NONE, error); + libwacom_builder_destroy(builder); - device = NULL; - keys = g_hash_table_get_values (db->device_ht); - for (l = keys; l; l = l->next) { - WacomDevice *d = l->data; + return device; +} - if (g_str_equal(d->name, name)) { - device = d; - break; - } - } - g_list_free (keys); +LIBWACOM_EXPORT WacomDevice* +libwacom_new_from_name(const WacomDeviceDatabase *db, const char *name, WacomError *error) +{ + WacomBuilder *builder = libwacom_builder_new(); + WacomDevice *device; + libwacom_builder_set_device_name(builder, name); - if (device) - return libwacom_copy(device); + device = libwacom_new_from_builder(db, builder, WFALLBACK_NONE, error); + libwacom_builder_destroy(builder); - libwacom_error_set(error, WERROR_UNKNOWN_MODEL, NULL); - return NULL; + return device; } static void print_styli_for_device (int fd, const WacomDevice *device) @@ -1067,6 +1159,56 @@ libwacom_match_new(const char *name, const char *uniq, return match; } +LIBWACOM_EXPORT WacomBuilder* +libwacom_builder_new(void) +{ + WacomBuilder *builder = g_malloc0(sizeof(*builder)); + return builder; +} + +LIBWACOM_EXPORT void +libwacom_builder_destroy(WacomBuilder *builder) +{ + g_free (builder->device_name); + g_free (builder->match_name); + g_free (builder->uniq); + g_free (builder); +} + +LIBWACOM_EXPORT void +libwacom_builder_set_bustype(WacomBuilder *builder, WacomBusType bustype) +{ + builder->bus = bustype; +} + +LIBWACOM_EXPORT void +libwacom_builder_set_usbid(WacomBuilder *builder, int vendor_id, int product_id) +{ + builder->vendor_id = vendor_id; + builder->product_id = product_id; +} + +LIBWACOM_EXPORT void +libwacom_builder_set_device_name(WacomBuilder *builder, const char *name) +{ + g_free(builder->device_name); + builder->device_name = g_strdup(name); +} + +LIBWACOM_EXPORT void +libwacom_builder_set_match_name(WacomBuilder *builder, const char *name) +{ + g_free(builder->match_name); + builder->match_name = g_strdup(name); +} + +LIBWACOM_EXPORT void +libwacom_builder_set_uniq(WacomBuilder *builder, const char *uniq) +{ + g_free(builder->uniq); + builder->uniq = g_strdup(uniq); +} + void libwacom_add_match(WacomDevice *device, WacomMatch *newmatch) { diff --git a/libwacom/libwacom.h b/libwacom/libwacom.h index 5c0d1f9b..c0d380c9 100644 --- a/libwacom/libwacom.h +++ b/libwacom/libwacom.h @@ -94,6 +94,11 @@ */ typedef struct _WacomDevice WacomDevice; +/** + * @ingroup devices + */ +typedef struct _WacomBuilder WacomBuilder; + /** * @ingroup devices */ @@ -352,6 +357,36 @@ WacomDeviceDatabase* libwacom_database_new_for_path(const char *datadir); */ void libwacom_database_destroy(WacomDeviceDatabase *db); +/** + * Create a new device reference for the given builder. + * In case of error, NULL is returned and the error is set to the + * appropriate value. + * + * The behavior for unset values in the builder is as follows: + * - if the bus is WBUSTYPE_UNKNOWN but product and vendor ID are set + * (and optionally device name, match name, and/or uniq), all known bus types + * are tested until a match is found (if any) + * - if the device name is set but all other values are empty the behavior + * matches libwacom_new_from_name(), i.e. the name alone will be + * used as lookup + * - if the uniq is set but all other values are empty the only the uniq + * string will be used for lookup + * device is the first with a corresponding uniq match. + * - if one of vendor or product id is zero and the other one is nonzero, + * no match will be found and the fallback device (if requested) is + * returned + * + * @param db A device database + * @param builder A builder specifying the known fields for this device. + * @param fallback Whether we should create a generic if model is unknown + * @param error If not NULL, set to the error if any occurs + * + * @return A new reference to this device or NULL on error. + * + * @ingroup devices + */ +WacomDevice* libwacom_new_from_builder(const WacomDeviceDatabase *db, const WacomBuilder *builder, WacomFallbackFlags fallback, WacomError *error); + /** * Create a new device reference from the given device path. * In case of error, NULL is returned and the error is set to the @@ -923,6 +958,47 @@ uint32_t libwacom_match_get_vendor_id(const WacomMatch *match); const char* libwacom_match_get_match_string(const WacomMatch *match); /** @} */ + +/** + * Create a new builder to be used into libwacom_new_from_builder(). + * The returned builder must be freed with libwacom_builder_destroy(). + */ +WacomBuilder *libwacom_builder_new(void); +void libwacom_builder_destroy(WacomBuilder *builder); + +/** + * Change the bustype to the given bustype, overriding the currently set one (if any). + */ +void libwacom_builder_set_bustype(WacomBuilder *builder, WacomBusType bustype); + +/** + * Change the vendor and product id to the given ids, overriding the currently set ones (if any). + */ +void libwacom_builder_set_usbid(WacomBuilder *builder, int vendor_id, int product_id); + +/** + * Change the device name to the given name, overriding the currently set one (if any). + * + * The device name is the name set in the libwacom database and may not match the + * kernel name for this device. See libwacom_builder_set_match_name() to set the kernel + * name. + */ +void libwacom_builder_set_device_name(WacomBuilder *builder, const char *name); + +/** + * Change the match name to the given name, overriding the currently set one (if any). + * + * The match name is the device name advertised by the kernel and may be different + * to the device name, the human-readable name as set in the libwacom database. + */ +void libwacom_builder_set_match_name(WacomBuilder *builder, const char *name); + +/** + * Change the uniq to the given uniq, overriding the currently set one (if any). + */ +void libwacom_builder_set_uniq(WacomBuilder *builder, const char *uniq); + + /** @cond hide_from_doxygen */ #endif /* _LIBWACOM_H_ */ /** @endcond */ diff --git a/libwacom/libwacom.sym b/libwacom/libwacom.sym index 7238d63d..be0dd1e0 100644 --- a/libwacom/libwacom.sym +++ b/libwacom/libwacom.sym @@ -79,4 +79,12 @@ LIBWACOM_2.12 { libwacom_get_num_dials; libwacom_get_num_rings; libwacom_match_get_uniq; + libwacom_builder_new; + libwacom_builder_destroy; + libwacom_builder_set_bustype; + libwacom_builder_set_device_name; + libwacom_builder_set_match_name; + libwacom_builder_set_uniq; + libwacom_builder_set_usbid; + libwacom_new_from_builder; } LIBWACOM_2.9; diff --git a/libwacom/libwacomint.h b/libwacom/libwacomint.h index 265998ee..569683c8 100644 --- a/libwacom/libwacomint.h +++ b/libwacom/libwacomint.h @@ -48,6 +48,15 @@ enum WacomFeature { FEATURE_TOUCHSWITCH = (1 << 5) }; +struct _WacomBuilder { + char *device_name; + char *match_name; + char *uniq; + WacomBusType bus; + uint32_t vendor_id; + uint32_t product_id; +}; + /* WARNING: When adding new members to this struct * make sure to update libwacom_copy_match() ! */ struct _WacomMatch {