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 b3c407cad64d0c31cee66dcbab571ebe97d5262f..740b98fa4e2f6eb2116fd2b789b8f0c19ee5c920 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 0603a89a54e311046d4f9e26771ff419eb68b2b5..a634e2129446325ba2083f4c236b1a967a5c9459 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 29d56ba481db3d4bd4ec984dd9cf7f9dd654459c..d6de1320495d4f3629d0bc00542a92cc96ba91de 100644 (file)
@@ -89,6 +89,22 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
         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_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,