introspect: Expose port info per card to clients

For volume control UIs to be able to show ports in inactive profiles,
expose all ports together with the card info. This includes updating
the protocol and the client API to show the connection between ports
and for which profiles the ports are relevant.

Update protocol to 26.

Signed-off-by: David Henningsson <david.henningsson@canonical.com>
This commit is contained in:
David Henningsson 2012-02-23 07:17:06 +01:00 committed by Arun Raghavan
parent 752ae7285e
commit 793f46320e
7 changed files with 207 additions and 7 deletions

View file

@ -299,6 +299,29 @@ The field is added once for every port.
When port availability changes, send a subscription event for the When port availability changes, send a subscription event for the
owning card. owning card.
## v26, implemented by >= 2.0
In reply from PA_COMMAND_GET_CARD_INFO (and thus
PA_COMMAND_GET_CARD_INFO_LIST), the following is added:
uint32_t n_ports
...followed by n_ports extended port entries, which look like this:
string name
string description
uint32_t priority
uint32_t available
uint8_t direction
proplist
uint32_t n_profiles
string profile_name_1
...
string profile_name_n
Profile names must match earlier sent profile names for the same card.
#### If you just changed the protocol, read this #### If you just changed the protocol, read this
## module-tunnel depends on the sink/source/sink-input/source-input protocol ## module-tunnel depends on the sink/source/sink-input/source-input protocol
## internals, so if you changed these, you might have broken module-tunnel. ## internals, so if you changed these, you might have broken module-tunnel.

View file

@ -36,7 +36,7 @@ AC_SUBST(PA_MINOR, pa_minor)
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor) AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PA_API_VERSION, 12) AC_SUBST(PA_API_VERSION, 12)
AC_SUBST(PA_PROTOCOL_VERSION, 25) AC_SUBST(PA_PROTOCOL_VERSION, 26)
# The stable ABI for client applications, for the version info x:y:z # The stable ABI for client applications, for the version info x:y:z
# always will hold y=z # always will hold y=z

View file

@ -124,6 +124,20 @@ typedef enum pa_context_flags {
#define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL #define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
/** \endcond */ /** \endcond */
/** Direction bitfield - while we currently do not expose anything bidirectional,
one should test against the bit instead of the value (e g if (d & PA_DIRECTION_OUTPUT)),
because we might add bidirectional stuff in the future. \since 2.0
*/
typedef enum pa_direction {
PA_DIRECTION_OUTPUT = 0x0001U, /**< Output direction */
PA_DIRECTION_INPUT = 0x0002U /**< Input direction */
} pa_direction_t;
/** \cond fulldocs */
#define PA_DIRECTION_OUTPUT PA_DIRECTION_OUTPUT
#define PA_DIRECTION_INPUT PA_DIRECTION_INPUT
/** \endcond */
/** The type of device we are dealing with */ /** The type of device we are dealing with */
typedef enum pa_device_type { typedef enum pa_device_type {
PA_DEVICE_TYPE_SINK, /**< Playback device */ PA_DEVICE_TYPE_SINK, /**< Playback device */

View file

@ -763,9 +763,101 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t
/*** Card info ***/ /*** Card info ***/
static void card_info_free(pa_card_info* i)
{
if (i->proplist)
pa_proplist_free(i->proplist);
pa_xfree(i->profiles);
if (i->ports) {
uint32_t j;
for (j = 0; j < i->n_ports; j++) {
if (i->ports[j]) {
if (i->ports[j]->profiles)
pa_xfree(i->ports[j]->profiles);
if (i->ports[j]->proplist)
pa_proplist_free(i->ports[j]->proplist);
}
}
pa_xfree(i->ports[0]);
pa_xfree(i->ports);
}
}
static int fill_card_port_info(pa_tagstruct* t, pa_card_info* i)
{
uint32_t j, k, l;
if (pa_tagstruct_getu32(t, &i->n_ports) < 0)
return -PA_ERR_PROTOCOL;
if (i->n_ports == 0) {
i->ports = NULL;
return 0;
}
i->ports = pa_xnew0(pa_card_port_info*, i->n_ports+1);
i->ports[0] = pa_xnew0(pa_card_port_info, i->n_ports);
for (j = 0; j < i->n_ports; j++) {
uint8_t direction;
uint32_t available;
pa_card_port_info* port = i->ports[j] = &i->ports[0][j];
port->proplist = pa_proplist_new();
if (pa_tagstruct_gets(t, &port->name) < 0 ||
pa_tagstruct_gets(t, &port->description) < 0 ||
pa_tagstruct_getu32(t, &port->priority) < 0 ||
pa_tagstruct_getu32(t, &available) < 0 ||
pa_tagstruct_getu8(t, &direction) < 0 ||
pa_tagstruct_get_proplist(t, port->proplist) < 0 ||
pa_tagstruct_getu32(t, &port->n_profiles) < 0) {
return -PA_ERR_PROTOCOL;
}
if (available > PA_PORT_AVAILABLE_YES ||
direction > PA_DIRECTION_OUTPUT + PA_DIRECTION_INPUT) {
return -PA_ERR_PROTOCOL;
}
port->direction = direction;
port->available = available;
if (port->n_profiles > 0) {
port->profiles = pa_xnew0(pa_card_profile_info*, i->n_profiles+1);
for (k = 0; k < port->n_profiles; k++) {
const char* profilename;
if (pa_tagstruct_gets(t, &profilename) < 0)
return -PA_ERR_PROTOCOL;
for (l = 0; l < i->n_profiles; l++) {
if (pa_streq(i->profiles[l].name, profilename)) {
port->profiles[k] = &i->profiles[l];
break;
}
}
if (l >= i->n_profiles)
return -PA_ERR_PROTOCOL;
}
}
}
return 0;
}
static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata; pa_operation *o = userdata;
int eol = 1; int eol = 1;
pa_card_info i;
pa_assert(pd); pa_assert(pd);
pa_assert(o); pa_assert(o);
@ -782,7 +874,6 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
} else { } else {
while (!pa_tagstruct_eof(t)) { while (!pa_tagstruct_eof(t)) {
pa_card_info i;
uint32_t j; uint32_t j;
const char*ap; const char*ap;
@ -795,6 +886,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_getu32(t, &i.n_profiles) < 0) { pa_tagstruct_getu32(t, &i.n_profiles) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
card_info_free(&i);
goto finish; goto finish;
} }
@ -810,7 +902,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_getu32(t, &i.profiles[j].priority) < 0) { pa_tagstruct_getu32(t, &i.profiles[j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.profiles); card_info_free(&i);
goto finish; goto finish;
} }
} }
@ -826,8 +918,7 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_get_proplist(t, i.proplist) < 0) { pa_tagstruct_get_proplist(t, i.proplist) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.profiles); card_info_free(&i);
pa_proplist_free(i.proplist);
goto finish; goto finish;
} }
@ -839,13 +930,20 @@ static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, u
} }
} }
if (o->context->version >= 26) {
if (fill_card_port_info(t, &i) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
card_info_free(&i);
goto finish;
}
}
if (o->callback) { if (o->callback) {
pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback; pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata); cb(o->context, &i, 0, o->userdata);
} }
pa_proplist_free(i.proplist); card_info_free(&i);
pa_xfree(i.profiles);
} }
} }

View file

@ -454,6 +454,20 @@ typedef struct pa_card_profile_info {
uint32_t priority; /**< The higher this value is the more useful this profile is as a default */ uint32_t priority; /**< The higher this value is the more useful this profile is as a default */
} pa_card_profile_info; } pa_card_profile_info;
/** Stores information about a specific port of a card. Please
* note that this structure can be extended as part of evolutionary
* API updates at any time in any new release. \since 2.0 */
typedef struct pa_card_port_info {
const char *name; /**< Name of this port */
const char *description; /**< Description of this port */
uint32_t priority; /**< The higher this value is the more useful this port is as a default */
int available; /**< A \link pa_port_available_t, indicating availability status of this port. */
int direction; /**< This is a \link pa_direction_t enum, indicating the direction of this port. */
uint32_t n_profiles; /**< Number of entries in profile array */
pa_card_profile_info** profiles; /**< Array of pointers available profile, or NULL. Array is terminated by an entry set to NULL. */
pa_proplist *proplist; /**< Property list */
} pa_card_port_info;
/** Stores information about cards. Please note that this structure /** Stores information about cards. Please note that this structure
* can be extended as part of evolutionary API updates at any time in * can be extended as part of evolutionary API updates at any time in
* any new release. \since 0.9.15 */ * any new release. \since 0.9.15 */
@ -466,6 +480,8 @@ typedef struct pa_card_info {
pa_card_profile_info* profiles; /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */ pa_card_profile_info* profiles; /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */
pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */ pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */
pa_proplist *proplist; /**< Property list */ pa_proplist *proplist; /**< Property list */
uint32_t n_ports; /**< Number of entries in port array */
pa_card_port_info **ports; /**< Array of pointers to ports, or NULL. Array is terminated by an entry set to NULL. */
} pa_card_info; } pa_card_info;
/** Callback prototype for pa_context_get_card_info_...() \since 0.9.15 */ /** Callback prototype for pa_context_get_card_info_...() \since 0.9.15 */

View file

@ -3258,6 +3258,36 @@ static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_car
pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL); pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL);
pa_tagstruct_put_proplist(t, card->proplist); pa_tagstruct_put_proplist(t, card->proplist);
if (c->version < 26)
return;
if (card->ports) {
pa_device_port* port;
pa_proplist* proplist = pa_proplist_new(); /* For now - push an empty proplist */
pa_tagstruct_putu32(t, pa_hashmap_size(card->ports));
PA_HASHMAP_FOREACH(port, card->ports, state) {
pa_tagstruct_puts(t, port->name);
pa_tagstruct_puts(t, port->description);
pa_tagstruct_putu32(t, port->priority);
pa_tagstruct_putu32(t, port->available);
pa_tagstruct_putu8(t, /* FIXME: port->direction */ (port->is_input ? PA_DIRECTION_INPUT : 0) | (port->is_output ? PA_DIRECTION_OUTPUT : 0));
pa_tagstruct_put_proplist(t, proplist);
if (port->profiles) {
void* state2;
pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));
PA_HASHMAP_FOREACH(p, port->profiles, state2)
pa_tagstruct_puts(t, p->name);
} else
pa_tagstruct_putu32(t, 0);
}
pa_proplist_free(proplist);
} else
pa_tagstruct_putu32(t, 0);
} }
static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) { static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {

View file

@ -582,6 +582,25 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
printf(_("\tActive Profile: %s\n"), printf(_("\tActive Profile: %s\n"),
i->active_profile->name); i->active_profile->name);
if (i->ports) {
pa_card_port_info **p;
printf(_("\tPorts:\n"));
for (p = i->ports; *p; p++) {
pa_card_profile_info **pr = (*p)->profiles;
printf(_("\t\t%s: %s (priority %u)\n"), (*p)->name, (*p)->description, (*p)->priority);
if (pr) {
printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
pr++;
while (*pr) {
printf(", %s", pa_strnull((*pr)->name));
pr++;
}
printf("\n");
}
}
}
pa_xfree(pl); pa_xfree(pl);
} }