device-manager: Provide a method for prefering/defering a device.
authorColin Guthrie <cguthrie@mandriva.org>
Sat, 19 Sep 2009 16:48:10 +0000 (17:48 +0100)
committerColin Guthrie <cguthrie@mandriva.org>
Thu, 1 Oct 2009 08:08:29 +0000 (09:08 +0100)
This allows clients to edit the priroity order. What is not yet in place is the initialisation of that priority list
when new devices are detected or the cleaning (remove holes) when devices are removed.

In order to keep the storage transparent I will likely remove the write functionality and replace it with a
simple rename method.

I also still need to expose the priority itself when reading the data.

src/modules/module-device-manager.c
src/pulse/ext-device-manager.c
src/pulse/ext-device-manager.h

index b3c407c..740b98f 100644 (file)
@@ -87,9 +87,23 @@ struct userdata {
 
 #define ENTRY_VERSION 1
 
+#define NUM_ROLES 9
+enum {
+    ROLE_NONE,
+    ROLE_VIDEO,
+    ROLE_MUSIC,
+    ROLE_GAME,
+    ROLE_EVENT,
+    ROLE_PHONE,
+    ROLE_ANIMATION,
+    ROLE_PRODUCTION,
+    ROLE_A11Y,
+};
+
 struct entry {
     uint8_t version;
     char description[PA_NAME_MAX];
+    uint32_t priority[NUM_ROLES];
 } PA_GCC_PACKED;
 
 enum {
@@ -98,6 +112,8 @@ enum {
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_PREFER_DEVICE,
+    SUBCOMMAND_DEFER_DEVICE,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -346,6 +362,31 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
     }
 }
 
+
+static uint32_t get_role_index(const char* role) {
+    pa_assert(role);
+
+    if (strcmp(role, "") == 0)
+        return ROLE_NONE;
+    if (strcmp(role, "video") == 0)
+        return ROLE_VIDEO;
+    if (strcmp(role, "music") == 0)
+        return ROLE_MUSIC;
+    if (strcmp(role, "game") == 0)
+        return ROLE_GAME;
+    if (strcmp(role, "event") == 0)
+        return ROLE_EVENT;
+    if (strcmp(role, "phone") == 0)
+        return ROLE_PHONE;
+    if (strcmp(role, "animation") == 0)
+        return ROLE_ANIMATION;
+    if (strcmp(role, "production") == 0)
+        return ROLE_PRODUCTION;
+    if (strcmp(role, "a11y") == 0)
+        return ROLE_A11Y;
+    return PA_INVALID_INDEX;
+}
+
 #define EXT_VERSION 1
 
 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
@@ -526,6 +567,113 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         break;
     }
 
+    case SUBCOMMAND_PREFER_DEVICE:
+    case SUBCOMMAND_DEFER_DEVICE: {
+
+        const char *role, *device;
+        struct entry *e;
+        uint32_t role_index;
+
+        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)
+            goto fail;
+
+        if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
+            pa_datum key;
+            pa_datum data;
+            pa_bool_t done;
+            char* prefix;
+            uint32_t priority;
+            pa_bool_t haschanged = FALSE;
+
+            if (strncmp(device, "sink:", 5) == 0)
+                prefix = pa_xstrdup("sink:");
+            else
+                prefix = pa_xstrdup("source:");
+
+            priority = e->priority[role_index];
+
+            /* Now we need to load up all the other entries of this type and shuffle the priroities around */
+
+            done = !pa_database_first(u->database, &key, NULL);
+
+            while (!done && !haschanged) {
+                pa_datum next_key;
+
+                done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+                /* Only read devices with the right prefix */
+                if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+                    char *name;
+                    struct entry *e2;
+
+                    name = pa_xstrndup(key.data, key.size);
+                    pa_datum_free(&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;
+                            }
+                        }
+
+                        if (haschanged) {
+                            data.data = e2;
+                            data.size = sizeof(*e2);
+
+                            if (pa_database_set(u->database, &key, &data, FALSE))
+                                pa_log_warn("Could not save device");
+                        }
+                        pa_xfree(e2);
+                    }
+
+                    pa_xfree(name);
+                }
+
+                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]++;
+
+                key.data = (char *) device;
+                key.size = strlen(device);
+
+                data.data = e;
+                data.size = sizeof(*e);
+
+                if (pa_database_set(u->database, &key, &data, FALSE))
+                    pa_log_warn("Could not save device");
+
+                trigger_save(u);
+            }
+
+            pa_xfree(e);
+
+            pa_xfree(prefix);
+        }
+        break;
+    }
+
     case SUBCOMMAND_SUBSCRIBE: {
 
       pa_bool_t enabled;
index 0603a89..a634e21 100644 (file)
@@ -42,6 +42,8 @@ enum {
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_PREFER_DEVICE,
+    SUBCOMMAND_DEFER_DEVICE,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -321,6 +323,78 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
     return o;
 }
 
+pa_operation *pa_ext_device_manager_prefer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    pa_assert(role);
+    pa_assert(device);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_PREFER_DEVICE);
+    pa_tagstruct_puts(t, role);
+    pa_tagstruct_puts(t, device);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_defer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    pa_assert(role);
+    pa_assert(device);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DEFER_DEVICE);
+    pa_tagstruct_puts(t, role);
+    pa_tagstruct_puts(t, device);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,
index 29d56ba..d6de132 100644 (file)
@@ -90,6 +90,22 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
         void *userdata);
 
 /** Subscribe to changes in the device database. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_prefer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_defer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,