device-manager: Provide a method for prefering/defering a device.

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.
This commit is contained in:
Colin Guthrie 2009-09-19 17:48:10 +01:00
parent a9bd1ab69c
commit 4981268738
3 changed files with 238 additions and 0 deletions

View file

@ -87,9 +87,23 @@ struct userdata {
#define ENTRY_VERSION 1 #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 { struct entry {
uint8_t version; uint8_t version;
char description[PA_NAME_MAX]; char description[PA_NAME_MAX];
uint32_t priority[NUM_ROLES];
} PA_GCC_PACKED; } PA_GCC_PACKED;
enum { enum {
@ -98,6 +112,8 @@ enum {
SUBCOMMAND_WRITE, SUBCOMMAND_WRITE,
SUBCOMMAND_DELETE, SUBCOMMAND_DELETE,
SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
SUBCOMMAND_PREFER_DEVICE,
SUBCOMMAND_DEFER_DEVICE,
SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_SUBSCRIBE,
SUBCOMMAND_EVENT 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 #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) { 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; 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: { case SUBCOMMAND_SUBSCRIBE: {
pa_bool_t enabled; pa_bool_t enabled;

View file

@ -42,6 +42,8 @@ enum {
SUBCOMMAND_WRITE, SUBCOMMAND_WRITE,
SUBCOMMAND_DELETE, SUBCOMMAND_DELETE,
SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
SUBCOMMAND_PREFER_DEVICE,
SUBCOMMAND_DEFER_DEVICE,
SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_SUBSCRIBE,
SUBCOMMAND_EVENT SUBCOMMAND_EVENT
}; };
@ -321,6 +323,78 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
return o; 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_operation *pa_ext_device_manager_subscribe(
pa_context *c, pa_context *c,
int enable, int enable,

View file

@ -89,6 +89,22 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
pa_context_success_cb_t cb, pa_context_success_cb_t cb,
void *userdata); 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 */ /** Subscribe to changes in the device database. \since 0.9.19 */
pa_operation *pa_ext_device_manager_subscribe( pa_operation *pa_ext_device_manager_subscribe(
pa_context *c, pa_context *c,