diff options
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/module-device-manager.c | 222 |
1 files changed, 140 insertions, 82 deletions
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 89fb4609..407d76dd 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -143,8 +143,7 @@ enum { SUBCOMMAND_RENAME, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, - SUBCOMMAND_PREFER_DEVICE, - SUBCOMMAND_DEFER_DEVICE, + SUBCOMMAND_REORDER, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -1121,115 +1120,174 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; } - case SUBCOMMAND_PREFER_DEVICE: - case SUBCOMMAND_DEFER_DEVICE: { + case SUBCOMMAND_REORDER: { - const char *role, *device; + const char *role; struct entry *e; - uint32_t role_index; + uint32_t role_index, n_devices; + pa_datum key, data; + pa_bool_t done, sink_mode = TRUE; + struct device_t { uint32_t prio; char *device; }; + struct device_t *device; + struct device_t **devices; + uint32_t i, idx, offset; + pa_hashmap *h; + pa_bool_t first; if (pa_tagstruct_gets(t, &role) < 0 || - pa_tagstruct_gets(t, &device) < 0) - goto fail; - - if (!role || !device || !*device) - goto fail; - - role_index = get_role_index(role); - if (PA_INVALID_INDEX == role_index) + pa_tagstruct_getu32(t, &n_devices) < 0 || + n_devices < 1) goto fail; - if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { - pa_datum key, data; - pa_bool_t done; - char* prefix = NULL; - uint32_t priority; - pa_bool_t haschanged = FALSE; - - if (strncmp(device, "sink:", 5) == 0) - prefix = pa_xstrdup("sink:"); - else if (strncmp(device, "source:", 7) == 0) - prefix = pa_xstrdup("source:"); + if (PA_INVALID_INDEX == (role_index = get_role_index(role))) + goto fail; + + /* Cycle through the devices given and make sure they exist */ + h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + first = TRUE; + idx = 0; + for (i = 0; i < n_devices; ++i) { + const char *s; + if (pa_tagstruct_gets(t, &s) < 0) { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - if (!prefix) + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Protocol error on reorder"); goto fail; + } - priority = e->priority[role_index]; + /* Ensure this is a valid entry */ + if (!(e = read_entry(u, s))) { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - /* Now we need to load up all the other entries of this type and shuffle the priroities around */ + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Client specified an unknown device in it's reorder list."); + goto fail; + } + pa_xfree(e); - done = !pa_database_first(u->database, &key, NULL); + if (first) { + first = FALSE; + sink_mode = (0 == strncmp("sink:", s, 5)); + } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) + || (!sink_mode && 0 != strncmp("source:", s, 7))) + { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - while (!done && !haschanged) { - pa_datum next_key; + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Attempted to reorder mixed devices (sinks and sources)"); + goto fail; + } - done = !pa_database_next(u->database, &key, &next_key, NULL); + /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */ + device = pa_xnew(struct device_t, 1); + device->device = pa_xstrdup(s); + if (pa_hashmap_put(h, device->device, device) == 0) { + device->prio = idx; + idx++; + } else { + pa_xfree(device->device); + pa_xfree(device); + } + } - /* Only read devices with the right prefix */ - if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { - char *name; - struct entry *e2; + /* Now cycle through our list and add all the devices. + This has the effect of addign in any in our DB, + not specified in the device list (and thus will be + tacked on at the end) */ + offset = idx; + done = !pa_database_first(u->database, &key, NULL); - name = pa_xstrndup(key.data, key.size); + while (!done && idx < 256) { + pa_datum next_key; - if ((e2 = read_entry(u, name))) { - if (SUBCOMMAND_PREFER_DEVICE == command) { - /* PREFER */ - if (e2->priority[role_index] == (priority - 1)) { - e2->priority[role_index]++; - haschanged = TRUE; - } - } else { - /* DEFER */ - if (e2->priority[role_index] == (priority + 1)) { - e2->priority[role_index]--; - haschanged = TRUE; - } - } + done = !pa_database_next(u->database, &key, &next_key, NULL); - if (haschanged) { - data.data = e2; - data.size = sizeof(*e2); + device = pa_xnew(struct device_t, 1); + device->device = pa_xstrndup(key.data, key.size); + if ((sink_mode && 0 == strncmp("sink:", device->device, 5)) + || (!sink_mode && 0 == strncmp("source:", device->device, 7))) { + + /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */ + if (pa_hashmap_put(h, device->device, device) == 0 + && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) { + /* We add offset on to the existing priorirty so that when we order, the + existing entries are always lower priority than the new ones. */ + device->prio = (offset + e->priority[role_index]); + pa_xfree(e); + } + else { + pa_xfree(device->device); + pa_xfree(device); + } + } else { + pa_xfree(device->device); + pa_xfree(device); + } - if (pa_database_set(u->database, &key, &data, TRUE)) - pa_log_warn("Could not save device"); - } + pa_datum_free(&key); - pa_xfree(e2); - } + key = next_key; + } - pa_xfree(name); + /* Now we put all the entries in a simple list for sorting it. */ + n_devices = pa_hashmap_size(h); + devices = pa_xnew(struct device_t *, n_devices); + idx = 0; + while ((device = pa_hashmap_steal_first(h))) { + devices[idx++] = device; + } + pa_hashmap_free(h, NULL, NULL); + + /* Simple bubble sort */ + for (i = 0; i < n_devices; ++i) { + for (uint32_t j = i; j < n_devices; ++j) { + if (devices[i]->prio > devices[j]->prio) { + struct device_t *tmp; + tmp = devices[i]; + devices[i] = devices[j]; + devices[j] = tmp; } - - pa_datum_free(&key); - key = next_key; } + } - /* Now write out our actual entry */ - if (haschanged) { - if (SUBCOMMAND_PREFER_DEVICE == command) - e->priority[role_index]--; - else - e->priority[role_index]++; + /* Go through in order and write the new entry and cleanup our own list */ + i = 0; idx = 1; + first = TRUE; + for (i = 0; i < n_devices; ++i) { + if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) { + if (e->priority[role_index] != idx) { + e->priority[role_index] = idx; - key.data = (char *) device; - key.size = strlen(device); + key.data = (char *) devices[i]->device; + key.size = strlen(devices[i]->device); - data.data = e; - data.size = sizeof(*e); + data.data = e; + data.size = sizeof(*e); - if (pa_database_set(u->database, &key, &data, TRUE)) - pa_log_warn("Could not save device"); + if (pa_database_set(u->database, &key, &data, TRUE) == 0) { + first = FALSE; + idx++; + } + } - trigger_save(u); + pa_xfree(e); } - - pa_xfree(e); - - pa_xfree(prefix); + pa_xfree(devices[i]->device); + pa_xfree(devices[i]); } - else - pa_log_warn("Could not reorder device %s, no entry in database", device); + + if (!first) + trigger_save(u); break; } |