forked from enlightenment/enlightenment
full pulseaudio mixer support: no external dependencies
SVN revision: 63557
This commit is contained in:
parent
277cbfdcaf
commit
e4b65bce0b
|
@ -27,7 +27,13 @@ module_la_SOURCES = e_mod_main.h \
|
|||
e_mod_system.h \
|
||||
conf_gadget.c \
|
||||
conf_module.c \
|
||||
app_mixer.c
|
||||
sys_pulse.c \
|
||||
app_mixer.c \
|
||||
msg.c \
|
||||
pa.c \
|
||||
serial.c \
|
||||
sink.c \
|
||||
tag.c
|
||||
|
||||
if HAVE_ALSA
|
||||
module_la_SOURCES += sys_alsa.c
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef PULSE_H
|
||||
#define PULSE_H
|
||||
|
||||
#include <Eina.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PULSE_SUCCESS (void*)1
|
||||
|
||||
extern int PULSE_EVENT_CONNECTED;
|
||||
extern int PULSE_EVENT_DISCONNECTED;
|
||||
extern int PULSE_EVENT_CHANGE;
|
||||
typedef struct Pulse Pulse;
|
||||
typedef uint32_t Pulse_Tag_Id;
|
||||
typedef struct Pulse_Sink Pulse_Sink;
|
||||
typedef void (*Pulse_Cb)(Pulse *, Pulse_Tag_Id, void *);
|
||||
|
||||
int pulse_init(void);
|
||||
void pulse_shutdown(void);
|
||||
|
||||
Pulse *pulse_new(void);
|
||||
Eina_Bool pulse_connect(Pulse *conn);
|
||||
void pulse_free(Pulse *conn);
|
||||
void pulse_cb_set(Pulse *conn, uint32_t tagnum, Pulse_Cb cb);
|
||||
|
||||
uint32_t pulse_cards_get(Pulse *conn);
|
||||
uint32_t pulse_sinks_get(Pulse *conn);
|
||||
uint32_t pulse_sink_get(Pulse *conn, uint32_t idx);
|
||||
uint32_t pulse_sink_mute_set(Pulse *conn, uint32_t idx, Eina_Bool mute);
|
||||
|
||||
void pulse_sink_free(Pulse_Sink *sink);
|
||||
const char *pulse_sink_name_get(Pulse_Sink *sink);
|
||||
const char *pulse_sink_desc_get(Pulse_Sink *sink);
|
||||
uint32_t pulse_sink_idx_get(Pulse_Sink *sink);
|
||||
Eina_Bool pulse_sink_muted_get(Pulse_Sink *sink);
|
||||
double pulse_sink_avg_get_pct(Pulse_Sink *sink);
|
||||
float pulse_sink_balance_get(Pulse_Sink *sink);
|
||||
uint8_t pulse_sink_channels_count(Pulse_Sink *sink);
|
||||
Eina_List *pulse_sink_channel_names_get(Pulse_Sink *sink);
|
||||
Eina_Bool pulse_sinks_watch(Pulse *conn);
|
||||
|
||||
uint32_t pulse_sink_channel_volume_set(Pulse *conn, Pulse_Sink *sink, uint32_t id, double vol);
|
||||
double pulse_sink_channel_volume_get(Pulse_Sink *sink, unsigned int id);
|
||||
float pulse_sink_channel_balance_get(Pulse_Sink *sink, unsigned int id);
|
||||
float pulse_sink_channel_depth_get(Pulse_Sink *sink, unsigned int id);
|
||||
unsigned int pulse_sink_channel_name_get_id(Pulse_Sink *sink, const char *name);
|
||||
const char *pulse_sink_channel_id_get_name(Pulse_Sink *sink, unsigned int id);
|
||||
|
||||
#endif
|
|
@ -73,7 +73,7 @@ _cb_changed_left(void *data, Evas_Object *obj __UNUSED__)
|
|||
state->right);
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(app->sys, app->channel_info->id,
|
||||
e_mod_mixer_volume_set(app->sys, app->channel_info->id,
|
||||
state->left, state->right);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ _cb_changed_right(void *data, Evas_Object *obj __UNUSED__)
|
|||
state->left);
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(app->sys, app->channel_info->id,
|
||||
e_mod_mixer_volume_set(app->sys, app->channel_info->id,
|
||||
state->left, state->right);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ _cb_changed_mute(void *data, Evas_Object *obj __UNUSED__)
|
|||
{
|
||||
E_Mixer_App_Dialog_Data *app = data;
|
||||
|
||||
e_mixer_system_set_mute(app->sys, app->channel_info->id, app->state.mute);
|
||||
e_mod_mixer_mute_set(app->sys, app->channel_info->id, app->state.mute);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -120,7 +120,7 @@ _cb_changed_lock_sliders(void *data, Evas_Object *obj __UNUSED__)
|
|||
|
||||
e_widget_slider_value_int_set(app->ui.channel_editor.left, state->left);
|
||||
e_widget_slider_value_int_set(app->ui.channel_editor.right, state->right);
|
||||
e_mixer_system_set_volume(app->sys, app->channel_info->id,
|
||||
e_mod_mixer_volume_set(app->sys, app->channel_info->id,
|
||||
state->left, state->right);
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ _update_channel_editor_state(E_Mixer_App_Dialog_Data *app, const E_Mixer_Channel
|
|||
e_widget_slider_value_int_set(ui->left, state.left);
|
||||
e_widget_slider_value_int_set(ui->right, state.right);
|
||||
|
||||
if (e_mixer_system_can_mute(app->sys, app->channel_info->id))
|
||||
if (e_mod_mixer_mutable_get(app->sys, app->channel_info->id))
|
||||
{
|
||||
e_widget_disabled_set(ui->mute, 0);
|
||||
e_widget_check_checked_set(ui->mute, state.mute);
|
||||
|
@ -151,18 +151,18 @@ _populate_channel_editor(E_Mixer_App_Dialog_Data *app)
|
|||
E_Mixer_Channel_State state;
|
||||
const char *card_name;
|
||||
|
||||
card_name = e_mixer_system_get_card_name(app->card);
|
||||
card_name = e_mod_mixer_card_name_get(app->card);
|
||||
e_widget_entry_text_set(ui->card, card_name);
|
||||
eina_stringshare_del(card_name);
|
||||
|
||||
e_widget_entry_text_set(ui->channel, app->channel_name);
|
||||
|
||||
if (e_mixer_system_has_capture(app->sys, app->channel_info->id))
|
||||
if (e_mod_mixer_capture_get(app->sys, app->channel_info->id))
|
||||
e_widget_entry_text_set(ui->type, _("Capture"));
|
||||
else
|
||||
e_widget_entry_text_set(ui->type, _("Playback"));
|
||||
|
||||
e_mixer_system_get_state(app->sys, app->channel_info->id, &state);
|
||||
e_mod_mixer_state_get(app->sys, app->channel_info->id, &state);
|
||||
_update_channel_editor_state(app, state);
|
||||
|
||||
app->lock_sliders = (state.left == state.right);
|
||||
|
@ -198,7 +198,7 @@ _channels_info_new(E_Mixer_System *sys)
|
|||
{
|
||||
Eina_List *channels, *channels_infos, *l;
|
||||
|
||||
channels = e_mixer_system_get_channels(sys);
|
||||
channels = e_mod_mixer_channels_get(sys);
|
||||
channels_infos = NULL;
|
||||
for (l = channels; l; l = l->next)
|
||||
{
|
||||
|
@ -206,12 +206,12 @@ _channels_info_new(E_Mixer_System *sys)
|
|||
|
||||
info = malloc(sizeof(*info));
|
||||
info->id = l->data;
|
||||
info->name = e_mixer_system_get_channel_name(sys, info->id);
|
||||
info->has_capture = e_mixer_system_has_capture(sys, info->id);
|
||||
info->name = e_mod_mixer_channel_name_get(sys, info->id);
|
||||
info->has_capture = e_mod_mixer_capture_get(sys, info->id);
|
||||
|
||||
channels_infos = eina_list_append(channels_infos, info);
|
||||
}
|
||||
e_mixer_system_free_channels(channels);
|
||||
e_mod_mixer_channels_free(channels);
|
||||
|
||||
return eina_list_sort(channels_infos, -1, _channel_info_cmp);
|
||||
}
|
||||
|
@ -237,13 +237,12 @@ _cb_system_update(void *data, E_Mixer_System *sys __UNUSED__)
|
|||
if ((!app->sys) || (!app->channel_info))
|
||||
return 1;
|
||||
|
||||
e_mixer_system_get_state(app->sys, app->channel_info->id, &state);
|
||||
e_mod_mixer_state_get(app->sys, app->channel_info->id, &state);
|
||||
_update_channel_editor_state(app, state);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_populate_channels(E_Mixer_App_Dialog_Data *app)
|
||||
{
|
||||
|
@ -258,12 +257,13 @@ _populate_channels(E_Mixer_App_Dialog_Data *app)
|
|||
e_widget_ilist_clear(ilist);
|
||||
|
||||
if (app->sys)
|
||||
e_mixer_system_del(app->sys);
|
||||
app->sys = e_mixer_system_new(app->card);
|
||||
e_mixer_system_callback_set(app->sys, _cb_system_update, app);
|
||||
e_mod_mixer_del(app->sys);
|
||||
app->sys = e_mod_mixer_new(app->card);
|
||||
if (_mixer_using_default)
|
||||
e_mixer_system_callback_set(app->sys, _cb_system_update, app);
|
||||
|
||||
eina_stringshare_del(app->channel_name);
|
||||
app->channel_name = e_mixer_system_get_default_channel_name(app->sys);
|
||||
app->channel_name = e_mod_mixer_channel_default_name_get(app->sys);
|
||||
|
||||
if (app->channels_infos)
|
||||
_channels_info_free(app->channels_infos);
|
||||
|
@ -333,8 +333,8 @@ _create_cards(E_Dialog *dialog __UNUSED__, Evas *evas, E_Mixer_App_Dialog_Data *
|
|||
const char *card;
|
||||
Eina_List *l;
|
||||
|
||||
app->card = e_mixer_system_get_default_card();
|
||||
app->cards = e_mixer_system_get_cards();
|
||||
app->card = e_mod_mixer_card_default_get();
|
||||
app->cards = e_mod_mixer_cards_get();
|
||||
if (eina_list_count(app->cards) < 2)
|
||||
return;
|
||||
|
||||
|
@ -345,7 +345,7 @@ _create_cards(E_Dialog *dialog __UNUSED__, Evas *evas, E_Mixer_App_Dialog_Data *
|
|||
{
|
||||
const char *card_name;
|
||||
|
||||
card_name = e_mixer_system_get_card_name(card);
|
||||
card_name = e_mod_mixer_card_name_get(card);
|
||||
|
||||
e_widget_ilist_append(ui->list, NULL, card_name, _cb_card_selected,
|
||||
app, card);
|
||||
|
@ -459,10 +459,10 @@ _mixer_app_dialog_del(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app)
|
|||
eina_stringshare_del(app->card);
|
||||
eina_stringshare_del(app->channel_name);
|
||||
if (app->cards)
|
||||
e_mixer_system_free_cards(app->cards);
|
||||
e_mod_mixer_cards_free(app->cards);
|
||||
if (app->channels_infos)
|
||||
_channels_info_free(app->channels_infos);
|
||||
e_mixer_system_del(app->sys);
|
||||
e_mod_mixer_del(app->sys);
|
||||
|
||||
e_util_defer_object_del(E_OBJECT(dialog));
|
||||
dialog->data = NULL;
|
||||
|
|
|
@ -50,11 +50,11 @@ _mixer_fill_cards_info(E_Config_Dialog_Data *cfdata)
|
|||
int i = 0;
|
||||
|
||||
cfdata->card_num = -1;
|
||||
cfdata->cards = e_mixer_system_get_cards();
|
||||
cfdata->cards = e_mod_mixer_cards_get();
|
||||
cfdata->cards_names = NULL;
|
||||
EINA_LIST_FOREACH(cfdata->cards, l, card)
|
||||
{
|
||||
name = e_mixer_system_get_card_name(card);
|
||||
name = e_mod_mixer_card_name_get(card);
|
||||
if ((cfdata->card_num < 0) && card && cfdata->card &&
|
||||
(strcmp(card, cfdata->card) == 0))
|
||||
cfdata->card_num = i;
|
||||
|
@ -76,13 +76,13 @@ _mixer_fill_channels_info(E_Config_Dialog_Data *cfdata)
|
|||
Eina_List *l;
|
||||
int i = 0;
|
||||
|
||||
sys = e_mixer_system_new(cfdata->card);
|
||||
sys = e_mod_mixer_new(cfdata->card);
|
||||
if (!sys)
|
||||
return;
|
||||
|
||||
cfdata->channel = 0;
|
||||
cfdata->channel_name = eina_stringshare_add(cfdata->conf->channel_name);
|
||||
cfdata->channels_names = e_mixer_system_get_channels_names(sys);
|
||||
cfdata->channels_names = e_mod_mixer_channels_names_get(sys);
|
||||
EINA_LIST_FOREACH(cfdata->channels_names, l, channel)
|
||||
{
|
||||
if (channel && cfdata->channel_name &&
|
||||
|
@ -95,7 +95,7 @@ _mixer_fill_channels_info(E_Config_Dialog_Data *cfdata)
|
|||
|
||||
i++;
|
||||
}
|
||||
e_mixer_system_del(sys);
|
||||
e_mod_mixer_del(sys);
|
||||
}
|
||||
|
||||
static void *
|
||||
|
@ -136,9 +136,9 @@ _free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata)
|
|||
eina_stringshare_del(card);
|
||||
|
||||
if (cfdata->channels_names)
|
||||
e_mixer_system_free_channels_names(cfdata->channels_names);
|
||||
e_mod_mixer_channels_free(cfdata->channels_names);
|
||||
if (cfdata->cards)
|
||||
e_mixer_system_free_cards(cfdata->cards);
|
||||
e_mod_mixer_cards_free(cfdata->cards);
|
||||
|
||||
eina_stringshare_del(cfdata->card);
|
||||
eina_stringshare_del(cfdata->channel_name);
|
||||
|
@ -292,7 +292,7 @@ _card_change(void *data, Evas_Object *obj, void *event __UNUSED__)
|
|||
|
||||
eina_stringshare_del(cfdata->card);
|
||||
|
||||
e_mixer_system_free_channels_names(cfdata->channels_names);
|
||||
e_mod_mixer_channels_free(cfdata->channels_names);
|
||||
|
||||
eina_stringshare_del(cfdata->channel_name);
|
||||
|
||||
|
@ -350,6 +350,12 @@ _basic_create(E_Config_Dialog *cfd __UNUSED__, Evas *evas, E_Config_Dialog_Data
|
|||
return cfdata->ui.table;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_config_pulse_toggle(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
E_Config_Dialog *
|
||||
e_mixer_config_dialog_new(E_Container *con, E_Mixer_Gadget_Config *conf)
|
||||
{
|
||||
|
|
|
@ -13,9 +13,31 @@ static char tmpbuf[PATH_MAX]; /* general purpose buffer, just use immediately */
|
|||
static const char _conf_domain[] = "module.mixer";
|
||||
static const char _name[] = "mixer";
|
||||
const char _e_mixer_Name[] = "Mixer";
|
||||
Eina_Bool _mixer_using_default = EINA_FALSE;
|
||||
E_Mixer_Volume_Get_Cb e_mod_mixer_volume_get;
|
||||
E_Mixer_Volume_Set_Cb e_mod_mixer_volume_set;
|
||||
E_Mixer_Mute_Get_Cb e_mod_mixer_mute_get;
|
||||
E_Mixer_Mute_Set_Cb e_mod_mixer_mute_set;
|
||||
E_Mixer_Capture_Cb e_mod_mixer_mutable_get;
|
||||
E_Mixer_State_Get_Cb e_mod_mixer_state_get;
|
||||
E_Mixer_Capture_Cb e_mod_mixer_capture_get;
|
||||
E_Mixer_Cb e_mod_mixer_new;
|
||||
E_Mixer_Cb e_mod_mixer_del;
|
||||
E_Mixer_Cb e_mod_mixer_channel_default_name_get;
|
||||
E_Mixer_Cb e_mod_mixer_channel_get_by_name;
|
||||
E_Mixer_Cb e_mod_mixer_channel_name_get;
|
||||
E_Mixer_Cb e_mod_mixer_channel_del;
|
||||
E_Mixer_Cb e_mod_mixer_channel_free;
|
||||
E_Mixer_Cb e_mod_mixer_channels_free;
|
||||
E_Mixer_Cb e_mod_mixer_channels_get;
|
||||
E_Mixer_Cb e_mod_mixer_channels_names_get;
|
||||
E_Mixer_Cb e_mod_mixer_card_name_get;
|
||||
E_Mixer_Cb e_mod_mixer_cards_get;
|
||||
E_Mixer_Cb e_mod_mixer_cards_free;
|
||||
E_Mixer_Cb e_mod_mixer_card_default_get;
|
||||
|
||||
static void
|
||||
_mixer_notify(const float val, E_Mixer_Instance *inst)
|
||||
_mixer_notify(const float val, E_Mixer_Instance *inst __UNUSED__)
|
||||
{
|
||||
#ifdef HAVE_ENOTIFY
|
||||
E_Notification *n;
|
||||
|
@ -30,7 +52,7 @@ _mixer_notify(const float val, E_Mixer_Instance *inst)
|
|||
return;
|
||||
|
||||
ret = snprintf(buf, (sizeof(buf) - 1), "%s: %d%%", _("New volume"), (int)(val + 0.5));
|
||||
if ((ret < 0) || (ret > sizeof(buf)))
|
||||
if ((ret < 0) || ((unsigned int)ret > sizeof(buf)))
|
||||
return;
|
||||
//Names are taken from FDO icon naming scheme
|
||||
if (val == 0.0)
|
||||
|
@ -72,19 +94,19 @@ _mixer_gadget_configuration_defaults(E_Mixer_Gadget_Config *conf)
|
|||
E_Mixer_System *sys;
|
||||
const char *card, *channel;
|
||||
|
||||
card = e_mixer_system_get_default_card();
|
||||
card = e_mod_mixer_card_default_get();
|
||||
if (!card)
|
||||
return 0;
|
||||
|
||||
sys = e_mixer_system_new(card);
|
||||
sys = e_mod_mixer_new(card);
|
||||
if (!sys)
|
||||
{
|
||||
eina_stringshare_del(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
channel = e_mixer_system_get_default_channel_name(sys);
|
||||
e_mixer_system_del(sys);
|
||||
channel = e_mod_mixer_channel_default_name_get(sys);
|
||||
e_mod_mixer_del(sys);
|
||||
|
||||
if (!channel)
|
||||
{
|
||||
|
@ -214,7 +236,7 @@ _mixer_gadget_update(E_Mixer_Instance *inst)
|
|||
if (!inst)
|
||||
return;
|
||||
|
||||
e_mixer_system_get_state(inst->sys, inst->channel, &inst->mixer_state);
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, &inst->mixer_state);
|
||||
|
||||
msg = alloca(sizeof(Edje_Message_Int_Set) + (2 * sizeof(int)));
|
||||
msg->count = 3;
|
||||
|
@ -235,7 +257,7 @@ _mixer_balance_left(E_Mixer_Instance *inst)
|
|||
E_Mixer_Channel_State *state;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
if (state->left >= 0)
|
||||
{
|
||||
|
@ -252,7 +274,7 @@ _mixer_balance_left(E_Mixer_Instance *inst)
|
|||
state->right = 100;
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
@ -263,7 +285,7 @@ _mixer_balance_right(E_Mixer_Instance *inst)
|
|||
E_Mixer_Channel_State *state;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
if (state->left >= 0)
|
||||
{
|
||||
|
@ -279,7 +301,7 @@ _mixer_balance_right(E_Mixer_Instance *inst)
|
|||
else
|
||||
state->right = 0;
|
||||
}
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
@ -290,7 +312,7 @@ _mixer_volume_increase(E_Mixer_Instance *inst, Eina_Bool non_ui)
|
|||
E_Mixer_Channel_State *state;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
if (state->left >= 0)
|
||||
{
|
||||
|
@ -308,7 +330,7 @@ _mixer_volume_increase(E_Mixer_Instance *inst, Eina_Bool non_ui)
|
|||
state->right = 100;
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
if (non_ui)
|
||||
|
@ -321,7 +343,7 @@ _mixer_volume_decrease(E_Mixer_Instance *inst, Eina_Bool non_ui)
|
|||
E_Mixer_Channel_State *state;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
if (state->left >= 0)
|
||||
{
|
||||
|
@ -338,7 +360,7 @@ _mixer_volume_decrease(E_Mixer_Instance *inst, Eina_Bool non_ui)
|
|||
state->right = 0;
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
if (non_ui)
|
||||
|
@ -350,13 +372,14 @@ _mixer_toggle_mute(E_Mixer_Instance *inst, Eina_Bool non_ui)
|
|||
{
|
||||
E_Mixer_Channel_State *state;
|
||||
|
||||
if (!e_mixer_system_can_mute(inst->sys, inst->channel))
|
||||
if (!e_mod_mixer_mutable_get(inst->sys, inst->channel))
|
||||
return;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_mute(inst->sys, inst->channel, &state->mute);
|
||||
e_mod_mixer_mute_get(inst->sys, inst->channel, &state->mute);
|
||||
state->mute = !state->mute;
|
||||
e_mixer_system_set_mute(inst->sys, inst->channel, state->mute);
|
||||
e_mod_mixer_mute_set(inst->sys, inst->channel, state->mute);
|
||||
if (!state->mute) e_mod_mixer_volume_set(inst->sys, inst->channel, state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
if (non_ui)
|
||||
_mixer_notify(0.0, inst);
|
||||
|
@ -368,7 +391,7 @@ _mixer_popup_cb_volume_left_change(void *data, Evas_Object *obj, void *event __U
|
|||
E_Mixer_Instance *inst = data;
|
||||
E_Mixer_Channel_State *state = &inst->mixer_state;
|
||||
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
|
||||
state->left = (int)e_slider_value_get(obj);
|
||||
|
@ -378,7 +401,7 @@ _mixer_popup_cb_volume_left_change(void *data, Evas_Object *obj, void *event __U
|
|||
e_slider_value_set(inst->ui.right, state->right);
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
@ -389,7 +412,7 @@ _mixer_popup_cb_volume_right_change(void *data, Evas_Object *obj, void *event __
|
|||
E_Mixer_Instance *inst = data;
|
||||
E_Mixer_Channel_State *state = &inst->mixer_state;
|
||||
|
||||
e_mixer_system_get_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_get(inst->sys, inst->channel,
|
||||
&state->left, &state->right);
|
||||
|
||||
state->right = (int)e_slider_value_get(obj);
|
||||
|
@ -399,7 +422,7 @@ _mixer_popup_cb_volume_right_change(void *data, Evas_Object *obj, void *event __
|
|||
e_slider_value_set(inst->ui.left, state->left);
|
||||
}
|
||||
|
||||
e_mixer_system_set_volume(inst->sys, inst->channel,
|
||||
e_mod_mixer_volume_set(inst->sys, inst->channel,
|
||||
state->left, state->right);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
@ -411,7 +434,8 @@ _mixer_popup_cb_mute_change(void *data, Evas_Object *obj, void *event __UNUSED__
|
|||
E_Mixer_Channel_State *state = &inst->mixer_state;
|
||||
|
||||
state->mute = e_widget_check_checked_get(obj);
|
||||
e_mixer_system_set_mute(inst->sys, inst->channel, state->mute);
|
||||
e_mod_mixer_mute_set(inst->sys, inst->channel, state->mute);
|
||||
if (!state->mute) e_mod_mixer_volume_set(inst->sys, inst->channel, state->left, state->right);
|
||||
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
@ -503,7 +527,7 @@ _mixer_popup_input_window_key_down_cb(void *data, int type __UNUSED__, void *eve
|
|||
mod |= E_BINDING_MODIFIER_WIN;
|
||||
|
||||
if (bind->key && (!strcmp(bind->key, ev->keyname)) &&
|
||||
((bind->modifiers == (int)mod) || (bind->any_mod)))
|
||||
((bind->modifiers == mod) || (bind->any_mod)))
|
||||
{
|
||||
if (!(act = e_action_find(bind->action))) continue;
|
||||
if (act->func.go_key)
|
||||
|
@ -624,7 +648,7 @@ _mixer_popup_new(E_Mixer_Instance *inst)
|
|||
return;
|
||||
|
||||
state = &inst->mixer_state;
|
||||
e_mixer_system_get_state(inst->sys, inst->channel, state);
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, state);
|
||||
|
||||
if ((state->right >= 0) &&
|
||||
(inst->conf->show_locked || (!inst->conf->lock_sliders)))
|
||||
|
@ -662,7 +686,7 @@ _mixer_popup_new(E_Mixer_Instance *inst)
|
|||
else
|
||||
inst->ui.right = NULL;
|
||||
|
||||
if (e_mixer_system_can_mute(inst->sys, inst->channel))
|
||||
if (e_mod_mixer_mutable_get(inst->sys, inst->channel))
|
||||
{
|
||||
inst->ui.mute = e_widget_check_add(evas, _("Mute"), &state->mute);
|
||||
evas_object_show(inst->ui.mute);
|
||||
|
@ -804,7 +828,7 @@ _mixer_cb_mouse_wheel(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUS
|
|||
else if (ev->z < 0)
|
||||
_mixer_volume_increase(inst, EINA_FALSE);
|
||||
}
|
||||
else if (ev->direction == 1)
|
||||
else if (_mixer_using_default && (ev->direction == 1)) /* invalid with pulse */
|
||||
{
|
||||
if (ev->z > 0)
|
||||
_mixer_balance_left(inst);
|
||||
|
@ -819,19 +843,19 @@ _mixer_sys_setup(E_Mixer_Instance *inst)
|
|||
E_Mixer_Gadget_Config *conf;
|
||||
|
||||
conf = inst->conf;
|
||||
if ((!_mixer_using_default) && (!e_mixer_pulse_ready())) return 1;
|
||||
|
||||
if (inst->sys)
|
||||
e_mixer_system_del(inst->sys);
|
||||
e_mod_mixer_del(inst->sys);
|
||||
|
||||
inst->sys = e_mixer_system_new(conf->card);
|
||||
inst->sys = e_mod_mixer_new(conf->card);
|
||||
if (!inst->sys)
|
||||
{
|
||||
inst->channel = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inst->channel = e_mixer_system_get_channel_by_name(inst->sys,
|
||||
conf->channel_name);
|
||||
inst->channel = e_mod_mixer_channel_get_by_name(inst->sys, conf->channel_name);
|
||||
return !!inst->channel;
|
||||
}
|
||||
|
||||
|
@ -839,7 +863,7 @@ static int
|
|||
_mixer_system_cb_update(void *data, E_Mixer_System *sys __UNUSED__)
|
||||
{
|
||||
E_Mixer_Instance *inst = data;
|
||||
e_mixer_system_get_state(inst->sys, inst->channel, &inst->mixer_state);
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, &inst->mixer_state);
|
||||
_mixer_gadget_update(inst);
|
||||
|
||||
return 1;
|
||||
|
@ -855,7 +879,7 @@ e_mixer_update(E_Mixer_Instance *inst)
|
|||
return 0;
|
||||
|
||||
r = _mixer_sys_setup(inst);
|
||||
if (r)
|
||||
if (r && _mixer_using_default)
|
||||
e_mixer_system_callback_set(inst->sys, _mixer_system_cb_update, inst);
|
||||
|
||||
return r;
|
||||
|
@ -871,11 +895,11 @@ _mixer_sys_setup_default_card(E_Mixer_Instance *inst)
|
|||
if (conf->card)
|
||||
eina_stringshare_del(conf->card);
|
||||
|
||||
card = e_mixer_system_get_default_card();
|
||||
card = e_mod_mixer_card_default_get();
|
||||
if (!card)
|
||||
goto error;
|
||||
|
||||
inst->sys = e_mixer_system_new(card);
|
||||
inst->sys = e_mod_mixer_new(card);
|
||||
if (!inst->sys)
|
||||
goto system_error;
|
||||
|
||||
|
@ -900,11 +924,11 @@ _mixer_sys_setup_default_channel(E_Mixer_Instance *inst)
|
|||
if (conf->channel_name)
|
||||
eina_stringshare_del(conf->channel_name);
|
||||
|
||||
channel_name = e_mixer_system_get_default_channel_name(inst->sys);
|
||||
channel_name = e_mod_mixer_channel_default_name_get(inst->sys);
|
||||
if (!channel_name)
|
||||
goto error;
|
||||
|
||||
inst->channel = e_mixer_system_get_channel_by_name(inst->sys, channel_name);
|
||||
inst->channel = e_mod_mixer_channel_get_by_name(inst->sys, channel_name);
|
||||
if (!inst->channel)
|
||||
goto system_error;
|
||||
|
||||
|
@ -921,12 +945,58 @@ error:
|
|||
static int
|
||||
_mixer_sys_setup_defaults(E_Mixer_Instance *inst)
|
||||
{
|
||||
if ((!_mixer_using_default) && (!e_mixer_pulse_ready())) return 1;
|
||||
if ((!inst->sys) && (!_mixer_sys_setup_default_card(inst)))
|
||||
return 0;
|
||||
|
||||
return _mixer_sys_setup_default_channel(inst);
|
||||
}
|
||||
|
||||
void
|
||||
e_mod_mixer_pulse_ready(Eina_Bool ready)
|
||||
{
|
||||
E_Mixer_Instance *inst;
|
||||
E_Mixer_Module_Context *ctxt;
|
||||
Eina_List *l;
|
||||
|
||||
if (!mixer_mod) return;
|
||||
|
||||
if (ready) e_mixer_pulse_setup();
|
||||
else e_mixer_default_setup();
|
||||
|
||||
ctxt = mixer_mod->data;
|
||||
EINA_LIST_FOREACH(ctxt->instances, l, inst)
|
||||
{
|
||||
if ((!_mixer_sys_setup(inst)) && (!_mixer_sys_setup_defaults(inst)))
|
||||
{
|
||||
if (inst->sys)
|
||||
e_mod_mixer_del(inst->sys);
|
||||
_mixer_gadget_configuration_free(ctxt->conf, inst->conf);
|
||||
E_FREE(inst);
|
||||
return;
|
||||
}
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, &inst->mixer_state);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
e_mod_mixer_pulse_update(void)
|
||||
{
|
||||
E_Mixer_Instance *inst;
|
||||
E_Mixer_Module_Context *ctxt;
|
||||
Eina_List *l;
|
||||
|
||||
if (!mixer_mod) return;
|
||||
|
||||
ctxt = mixer_mod->data;
|
||||
EINA_LIST_FOREACH(ctxt->instances, l, inst)
|
||||
{
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, &inst->mixer_state);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
}
|
||||
|
||||
/* Gadcon Api Functions */
|
||||
static void _mixer_module_configuration_setup(E_Mixer_Module_Context *ctxt);
|
||||
|
||||
|
@ -962,12 +1032,13 @@ _gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
|
|||
if ((!_mixer_sys_setup(inst)) && (!_mixer_sys_setup_defaults(inst)))
|
||||
{
|
||||
if (inst->sys)
|
||||
e_mixer_system_del(inst->sys);
|
||||
e_mod_mixer_del(inst->sys);
|
||||
_mixer_gadget_configuration_free(ctxt->conf, conf);
|
||||
E_FREE(inst);
|
||||
return NULL;
|
||||
}
|
||||
e_mixer_system_callback_set(inst->sys, _mixer_system_cb_update, inst);
|
||||
|
||||
if (_mixer_using_default) e_mixer_system_callback_set(inst->sys, _mixer_system_cb_update, inst);
|
||||
|
||||
inst->ui.gadget = edje_object_add(gc->evas);
|
||||
e_theme_edje_object_set(inst->ui.gadget, "base/theme/modules/mixer",
|
||||
|
@ -981,8 +1052,11 @@ _gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
|
|||
evas_object_event_callback_add(inst->ui.gadget, EVAS_CALLBACK_MOUSE_WHEEL,
|
||||
_mixer_cb_mouse_wheel, inst);
|
||||
|
||||
e_mixer_system_get_state(inst->sys, inst->channel, &inst->mixer_state);
|
||||
_mixer_gadget_update(inst);
|
||||
if (inst->sys)
|
||||
{
|
||||
e_mod_mixer_state_get(inst->sys, inst->channel, &inst->mixer_state);
|
||||
_mixer_gadget_update(inst);
|
||||
}
|
||||
|
||||
if (!ctxt->conf->default_gc_id)
|
||||
{
|
||||
|
@ -1021,8 +1095,8 @@ _gc_shutdown(E_Gadcon_Client *gcc)
|
|||
e_object_del(E_OBJECT(inst->menu));
|
||||
}
|
||||
evas_object_del(inst->ui.gadget);
|
||||
e_mixer_system_channel_del(inst->channel);
|
||||
e_mixer_system_del(inst->sys);
|
||||
e_mod_mixer_channel_del(inst->channel);
|
||||
e_mod_mixer_del(inst->sys);
|
||||
|
||||
inst->conf->instance = NULL;
|
||||
ctxt->instances = eina_list_remove(ctxt->instances, inst);
|
||||
|
@ -1340,6 +1414,58 @@ _mixer_actions_unregister(E_Mixer_Module_Context *ctxt)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_default_setup(void)
|
||||
{
|
||||
e_mod_mixer_volume_get = (void*)e_mixer_system_get_volume;
|
||||
e_mod_mixer_volume_set = (void*)e_mixer_system_set_volume;
|
||||
e_mod_mixer_mute_get = (void*)e_mixer_system_get_mute;
|
||||
e_mod_mixer_mute_set = (void*)e_mixer_system_set_mute;
|
||||
e_mod_mixer_mutable_get = (void*)e_mixer_system_can_mute;
|
||||
e_mod_mixer_state_get = (void*)e_mixer_system_get_state;
|
||||
e_mod_mixer_capture_get = (void*)e_mixer_system_has_capture;
|
||||
e_mod_mixer_new = (void*)e_mixer_system_new;
|
||||
e_mod_mixer_del = (void*)e_mixer_system_del;
|
||||
e_mod_mixer_channel_default_name_get = (void*)e_mixer_system_get_default_channel_name;
|
||||
e_mod_mixer_channel_get_by_name = (void*)e_mixer_system_get_channel_by_name;
|
||||
e_mod_mixer_channel_name_get = (void*)e_mixer_system_get_channel_name;
|
||||
e_mod_mixer_channel_del = (void*)e_mixer_system_channel_del;
|
||||
e_mod_mixer_channels_free = (void*)e_mixer_system_free_channels;
|
||||
e_mod_mixer_channels_get = (void*)e_mixer_system_get_channels;
|
||||
e_mod_mixer_channels_names_get = (void*)e_mixer_system_get_channels_names;
|
||||
e_mod_mixer_card_name_get = (void*)e_mixer_system_get_card_name;
|
||||
e_mod_mixer_cards_get = (void*)e_mixer_system_get_cards;
|
||||
e_mod_mixer_cards_free = (void*)e_mixer_system_free_cards;
|
||||
e_mod_mixer_card_default_get = (void*)e_mixer_system_get_default_card;
|
||||
_mixer_using_default = EINA_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_setup(void)
|
||||
{
|
||||
e_mod_mixer_volume_get = (void*)e_mixer_pulse_get_volume;
|
||||
e_mod_mixer_volume_set = (void*)e_mixer_pulse_set_volume;
|
||||
e_mod_mixer_mute_get = (void*)e_mixer_pulse_get_mute;
|
||||
e_mod_mixer_mute_set = (void*)e_mixer_pulse_set_mute;
|
||||
e_mod_mixer_mutable_get = (void*)e_mixer_pulse_can_mute;
|
||||
e_mod_mixer_state_get = (void*)e_mixer_pulse_get_state;
|
||||
e_mod_mixer_capture_get = (void*)e_mixer_pulse_has_capture;
|
||||
e_mod_mixer_new = (void*)e_mixer_pulse_new;
|
||||
e_mod_mixer_del = (void*)e_mixer_pulse_del;
|
||||
e_mod_mixer_channel_default_name_get = (void*)e_mixer_pulse_get_default_channel_name;
|
||||
e_mod_mixer_channel_get_by_name = (void*)e_mixer_pulse_get_channel_by_name;
|
||||
e_mod_mixer_channel_name_get = (void*)e_mixer_pulse_get_channel_name;
|
||||
e_mod_mixer_channel_del = (void*)e_mixer_pulse_channel_del;
|
||||
e_mod_mixer_channels_free = (void*)e_mixer_pulse_free_channels;
|
||||
e_mod_mixer_channels_get = (void*)e_mixer_pulse_get_channels;
|
||||
e_mod_mixer_channels_names_get = (void*)e_mixer_pulse_get_channels_names;
|
||||
e_mod_mixer_card_name_get = (void*)e_mixer_pulse_get_card_name;
|
||||
e_mod_mixer_cards_get = (void*)e_mixer_pulse_get_cards;
|
||||
e_mod_mixer_cards_free = (void*)e_mixer_pulse_free_cards;
|
||||
e_mod_mixer_card_default_get = (void*)e_mixer_pulse_get_default_card;
|
||||
_mixer_using_default = EINA_FALSE;
|
||||
}
|
||||
|
||||
EAPI void *
|
||||
e_modapi_init(E_Module *m)
|
||||
{
|
||||
|
@ -1356,6 +1482,9 @@ e_modapi_init(E_Module *m)
|
|||
_mixer_configure_registry_register();
|
||||
_mixer_actions_register(ctxt);
|
||||
e_gadcon_provider_register(&_gc_class);
|
||||
if (!e_mixer_pulse_init()) e_mixer_default_setup();
|
||||
else e_mixer_pulse_setup();
|
||||
|
||||
mixer_mod = m;
|
||||
return ctxt;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,14 @@ typedef struct E_Mixer_Module_Context
|
|||
int desktop_notification;
|
||||
} E_Mixer_Module_Context;
|
||||
|
||||
typedef int (*E_Mixer_Volume_Set_Cb)(E_Mixer_System *, E_Mixer_Channel *, int, int);
|
||||
typedef int (*E_Mixer_Volume_Get_Cb)(E_Mixer_System *, E_Mixer_Channel *, int *, int *);
|
||||
typedef int (*E_Mixer_Mute_Get_Cb)(E_Mixer_System *, E_Mixer_Channel *, int *);
|
||||
typedef int (*E_Mixer_Mute_Set_Cb)(E_Mixer_System *, E_Mixer_Channel *, int);
|
||||
typedef int (*E_Mixer_State_Get_Cb)(E_Mixer_System *, E_Mixer_Channel *, E_Mixer_Channel_State *);
|
||||
typedef int (*E_Mixer_Capture_Cb)(E_Mixer_System *, E_Mixer_Channel *);
|
||||
typedef void *(*E_Mixer_Cb)();
|
||||
|
||||
EAPI extern E_Module_Api e_modapi;
|
||||
EAPI void *e_modapi_init(E_Module *m);
|
||||
EAPI int e_modapi_shutdown(E_Module *m);
|
||||
|
@ -89,6 +97,33 @@ E_Dialog *e_mixer_app_dialog_new(E_Container *con, void (*func)(E_Dialog *dialog
|
|||
int e_mixer_app_dialog_select(E_Dialog *dialog, const char *card_name, const char *channel_name);
|
||||
|
||||
int e_mixer_update(E_Mixer_Instance *inst);
|
||||
void e_mixer_default_setup(void);
|
||||
void e_mixer_pulse_setup(void);
|
||||
const char *e_mixer_theme_path(void);
|
||||
|
||||
void e_mod_mixer_pulse_ready(Eina_Bool);
|
||||
void e_mod_mixer_pulse_update(void);
|
||||
|
||||
extern Eina_Bool _mixer_using_default;
|
||||
extern E_Mixer_Volume_Get_Cb e_mod_mixer_volume_get;
|
||||
extern E_Mixer_Volume_Set_Cb e_mod_mixer_volume_set;
|
||||
extern E_Mixer_Mute_Get_Cb e_mod_mixer_mute_get;
|
||||
extern E_Mixer_Mute_Set_Cb e_mod_mixer_mute_set;
|
||||
extern E_Mixer_Capture_Cb e_mod_mixer_mutable_get;
|
||||
extern E_Mixer_State_Get_Cb e_mod_mixer_state_get;
|
||||
extern E_Mixer_Capture_Cb e_mod_mixer_capture_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_new;
|
||||
extern E_Mixer_Cb e_mod_mixer_del;
|
||||
extern E_Mixer_Cb e_mod_mixer_channel_default_name_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_channel_get_by_name;
|
||||
extern E_Mixer_Cb e_mod_mixer_channel_name_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_channel_del;
|
||||
extern E_Mixer_Cb e_mod_mixer_channel_free;
|
||||
extern E_Mixer_Cb e_mod_mixer_channels_free;
|
||||
extern E_Mixer_Cb e_mod_mixer_channels_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_channels_names_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_card_name_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_cards_get;
|
||||
extern E_Mixer_Cb e_mod_mixer_cards_free;
|
||||
extern E_Mixer_Cb e_mod_mixer_card_default_get;
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,7 @@ void e_mixer_system_free_cards(Eina_List *cards);
|
|||
const char *e_mixer_system_get_default_card(void);
|
||||
const char *e_mixer_system_get_card_name(const char *card);
|
||||
|
||||
Eina_Bool e_mixer_pulse_init(void);
|
||||
|
||||
E_Mixer_System *e_mixer_system_new(const char *card);
|
||||
void e_mixer_system_del(E_Mixer_System *self);
|
||||
|
@ -44,5 +45,31 @@ int e_mixer_system_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int
|
|||
int e_mixer_system_can_mute(E_Mixer_System *self, E_Mixer_Channel *channel);
|
||||
int e_mixer_system_has_capture(E_Mixer_System *self, E_Mixer_Channel *channel);
|
||||
|
||||
int pulse_init(void);
|
||||
Eina_Bool e_mixer_pulse_ready(void);
|
||||
Eina_Bool e_mixer_pulse_init(void);
|
||||
void e_mixer_pulse_shutdown(void);
|
||||
E_Mixer_System *e_mixer_pulse_new(const char *name);
|
||||
void e_mixer_pulse_del(E_Mixer_System *self);
|
||||
Eina_List *e_mixer_pulse_get_cards(void);
|
||||
void e_mixer_pulse_free_cards(Eina_List *cards);
|
||||
const char *e_mixer_pulse_get_default_card(void);
|
||||
const char *e_mixer_pulse_get_card_name(const char *card);
|
||||
Eina_List *e_mixer_pulse_get_channels(E_Mixer_System *self);
|
||||
void e_mixer_pulse_free_channels(Eina_List *channels);
|
||||
Eina_List *e_mixer_pulse_get_channels_names(E_Mixer_System *self);
|
||||
void e_mixer_pulse_free_channels_names(Eina_List *channels_names);
|
||||
const char *e_mixer_pulse_get_default_channel_name(E_Mixer_System *self);
|
||||
E_Mixer_Channel *e_mixer_pulse_get_channel_by_name(E_Mixer_System *self, const char *name);
|
||||
void e_mixer_pulse_channel_del(E_Mixer_Channel *channel);
|
||||
const char *e_mixer_pulse_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel);
|
||||
int e_mixer_pulse_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right);
|
||||
int e_mixer_pulse_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right);
|
||||
int e_mixer_pulse_can_mute(E_Mixer_System *self, E_Mixer_Channel *channel);
|
||||
int e_mixer_pulse_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int *mute);
|
||||
int e_mixer_pulse_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int mute);
|
||||
int e_mixer_pulse_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state);
|
||||
int e_mixer_pulse_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state);
|
||||
int e_mixer_pulse_has_capture(E_Mixer_System *self, E_Mixer_Channel *channel);
|
||||
|
||||
#endif /* E_MOD_SYSTEM_H */
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#include "pa.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void
|
||||
msg_recv_creds(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
int r;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
} cmsg;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = &tag->header[tag->pos];
|
||||
iov.iov_len = sizeof(tag->header) - tag->pos;
|
||||
|
||||
memset(&cmsg, 0, sizeof(cmsg));
|
||||
memset(&mh, 0, sizeof(mh));
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = &cmsg;
|
||||
mh.msg_controllen = sizeof(cmsg);
|
||||
|
||||
r = recvmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, 0);
|
||||
if ((!r) || (r == sizeof(tag->header))) tag->auth = EINA_TRUE;
|
||||
else if (r < 0)
|
||||
{
|
||||
if (errno != EAGAIN) ecore_main_fd_handler_del(conn->fdh);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG("%zu bytes left", sizeof(tag->header) - r);
|
||||
tag->pos += r;
|
||||
}
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
msg_recv(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
long r;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
} cmsg;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = tag->data + tag->pos;
|
||||
iov.iov_len = tag->dsize - tag->pos;
|
||||
|
||||
memset(&cmsg, 0, sizeof(cmsg));
|
||||
memset(&mh, 0, sizeof(mh));
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = &cmsg;
|
||||
mh.msg_controllen = sizeof(cmsg);
|
||||
|
||||
r = recvmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, 0);
|
||||
DBG("recv %li bytes", r);
|
||||
/* things we don't really care about: credentials */
|
||||
if ((!r) || ((unsigned int)r == tag->dsize))
|
||||
{
|
||||
conn->iq = eina_list_remove(conn->iq, tag);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
else if (r < 0)
|
||||
{
|
||||
if (errno != EAGAIN) ecore_main_fd_handler_del(conn->fdh);
|
||||
}
|
||||
else
|
||||
tag->pos += r;
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
msg_sendmsg_creds(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
int r;
|
||||
struct msghdr mh;
|
||||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
|
||||
} cmsg;
|
||||
struct ucred *u;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov.iov_base = (void*) &tag->header[tag->pos];
|
||||
iov.iov_len = sizeof(tag->header) - tag->pos;
|
||||
|
||||
memset(&cmsg, 0, sizeof(cmsg));
|
||||
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
cmsg.hdr.cmsg_level = SOL_SOCKET;
|
||||
cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
|
||||
|
||||
u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
|
||||
|
||||
u->pid = getpid();
|
||||
u->uid = getuid();
|
||||
u->gid = getgid();
|
||||
|
||||
memset(&mh, 0, sizeof(mh));
|
||||
mh.msg_iov = &iov;
|
||||
mh.msg_iovlen = 1;
|
||||
mh.msg_control = &cmsg;
|
||||
mh.msg_controllen = sizeof(cmsg);
|
||||
|
||||
r = sendmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, MSG_NOSIGNAL);
|
||||
if ((!r) || (r == (int)sizeof(tag->header))) tag->auth = EINA_TRUE;
|
||||
else if (r < 0)
|
||||
{
|
||||
if (errno != EAGAIN) ecore_main_fd_handler_del(conn->fdh);
|
||||
}
|
||||
else
|
||||
tag->pos += r;
|
||||
}
|
||||
|
||||
void
|
||||
msg_send_creds(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
int r;
|
||||
|
||||
INF("trying to send 20 byte auth header");
|
||||
r = send(ecore_main_fd_handler_fd_get(conn->fdh), &tag->header[tag->pos], sizeof(tag->header) - tag->pos, MSG_NOSIGNAL);
|
||||
INF("%i bytes sent!", r);
|
||||
if ((!r) || (r == (int)sizeof(tag->header))) tag->auth = EINA_TRUE;
|
||||
else if (r < 0)
|
||||
{
|
||||
if (errno != EAGAIN) ecore_main_fd_handler_del(conn->fdh);
|
||||
}
|
||||
else
|
||||
tag->pos += r;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
msg_send(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
int r;
|
||||
|
||||
INF("trying to send %zu bytes", tag->dsize - tag->pos);
|
||||
r = send(ecore_main_fd_handler_fd_get(conn->fdh), tag->data + tag->pos, tag->dsize - tag->pos, MSG_NOSIGNAL);
|
||||
INF("%i bytes sent!", r);
|
||||
if ((!r) || ((unsigned int)r == tag->dsize - tag->pos))
|
||||
{
|
||||
DBG("Send complete! Deleting tag...");
|
||||
conn->oq = eina_list_remove(conn->oq, tag);
|
||||
pulse_tag_free(tag);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
if (r < 0)
|
||||
{
|
||||
if (errno != EAGAIN) ecore_main_fd_handler_del(conn->fdh);
|
||||
}
|
||||
else
|
||||
tag->pos += r;
|
||||
return EINA_FALSE;
|
||||
}
|
|
@ -0,0 +1,673 @@
|
|||
#include <Ecore.h>
|
||||
#include <Ecore_Con.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pa.h"
|
||||
|
||||
static int pulse_init_count = 0;
|
||||
int pa_log_dom = -1;
|
||||
int PULSE_EVENT_CONNECTED = -1;
|
||||
int PULSE_EVENT_DISCONNECTED = -1;
|
||||
int PULSE_EVENT_CHANGE = -1;
|
||||
Eina_Hash *pulse_sinks = NULL;
|
||||
|
||||
void
|
||||
pulse_fake_free(void *d __UNUSED__, void *d2 __UNUSED__)
|
||||
{}
|
||||
|
||||
static void
|
||||
proplist_init(Pulse_Tag *tag)
|
||||
{
|
||||
Eina_File *file;
|
||||
size_t size;
|
||||
const char *str;
|
||||
int argc;
|
||||
char **argv;
|
||||
char pid[32];
|
||||
tag->props = eina_hash_string_superfast_new((void*)eina_stringshare_del);
|
||||
/*
|
||||
"L\0\0\0\tL\0\0\0\1"
|
||||
"P"
|
||||
"tapplication.process.id\0"
|
||||
"L\0\0\0\5x\0\0\0\0052742\0"
|
||||
"tapplication.process.user\0"
|
||||
"L\0\0\0\5x\0\0\0\5root\0"
|
||||
"tapplication.process.host\0"
|
||||
"L\0\0\0\fx\0\0\0\fdarc.ath.cx\0"
|
||||
"tapplication.process.binary\0"
|
||||
"L\0\0\0\6x\0\0\0\6pactl\0"
|
||||
"tapplication.name\0"
|
||||
"L\0\0\0\6x\0\0\0\6pactl\0"
|
||||
"tapplication.language\0"
|
||||
"L\0\0\0\vx\0\0\0\ven_US.utf8\0"
|
||||
"twindow.x11.display\0"
|
||||
"L\0\0\0\5x\0\0\0\5:0.0\0"
|
||||
"tapplication.process.machine_id\0"
|
||||
"L\0\0\0!x\0\0\0!16ebb73850fbfdfff2b0079400000030\0"
|
||||
"N"
|
||||
*/
|
||||
tag->dsize += PA_TAG_SIZE_PROPLIST;
|
||||
snprintf(pid, sizeof(pid), "%"PRIu32, getpid());
|
||||
eina_hash_add(tag->props, "application.process.id", eina_stringshare_add(pid));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.id") + strlen(pid) + 2 + PA_TAG_SIZE_U32;
|
||||
|
||||
str = getenv("USER");
|
||||
eina_hash_add(tag->props, "application.process.user", eina_stringshare_add(str));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.user") + strlen(str) + 2 + PA_TAG_SIZE_U32;
|
||||
|
||||
file = eina_file_open("/etc/hostname", EINA_FALSE);
|
||||
if (file)
|
||||
{
|
||||
size = eina_file_size_get(file);
|
||||
str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
|
||||
eina_hash_add(tag->props, "application.process.host", eina_stringshare_add_length(str, size - 1));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + size + 1 + PA_TAG_SIZE_U32;
|
||||
eina_file_map_free(file, (void*)str);
|
||||
eina_file_close(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
eina_hash_add(tag->props, "application.process.host", eina_stringshare_add(""));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + 2 + PA_TAG_SIZE_U32;
|
||||
}
|
||||
|
||||
ecore_app_args_get(&argc, &argv);
|
||||
str = strrchr(argv[0], '/');
|
||||
str = (str) ? str + 1 : argv[0];
|
||||
eina_hash_add(tag->props, "application.process.binary", eina_stringshare_add(str));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.binary") + strlen(str) + 2 + PA_TAG_SIZE_U32;
|
||||
eina_hash_add(tag->props, "application.name", eina_stringshare_add(str));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.name") + strlen(str) + 2 + PA_TAG_SIZE_U32;
|
||||
|
||||
str = getenv("LANG");
|
||||
if (str)
|
||||
{
|
||||
eina_hash_add(tag->props, "application.language", eina_stringshare_add(str));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.language") + strlen(str) + 2 + PA_TAG_SIZE_U32;
|
||||
}
|
||||
|
||||
str = getenv("DISPLAY");
|
||||
if (str)
|
||||
{
|
||||
eina_hash_add(tag->props, "window.x11.display", eina_stringshare_add(str));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("window.x11.display") + strlen(str) + 2 + PA_TAG_SIZE_U32;
|
||||
}
|
||||
|
||||
file = eina_file_open(PA_MACHINE_ID, EINA_FALSE);
|
||||
if (file)
|
||||
{
|
||||
size = eina_file_size_get(file);
|
||||
str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
|
||||
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add_length(str, size - 1));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + size + 1 + PA_TAG_SIZE_U32;
|
||||
eina_file_map_free(file, (void*)str);
|
||||
eina_file_close(file);
|
||||
return;
|
||||
}
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
errno = 0;
|
||||
gethostname(buf, sizeof(buf));
|
||||
if (!errno)
|
||||
{
|
||||
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
|
||||
return;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%08lx", (unsigned long)gethostid());
|
||||
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
|
||||
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cookie_file(uint8_t *cookie)
|
||||
{
|
||||
char buf[4096];
|
||||
Eina_File *file;
|
||||
size_t size;
|
||||
void *cookie_data;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/.pulse-cookie", getenv("HOME"));
|
||||
file = eina_file_open(buf, EINA_FALSE);
|
||||
size = eina_file_size_get(file);
|
||||
cookie_data = eina_file_map_all(file, EINA_FILE_WILLNEED);
|
||||
memcpy(cookie, cookie_data, size);
|
||||
eina_file_map_free(file, cookie_data);
|
||||
eina_file_close(file);
|
||||
}
|
||||
|
||||
static Pulse_Tag *
|
||||
login_setup(Pulse *conn)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
uint32_t x;
|
||||
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
||||
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
tag->dsize = 4 * PA_TAG_SIZE_U32 + sizeof(cookie);
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag_simple_init(conn, tag, PA_COMMAND_AUTH, PA_TAG_U32);
|
||||
DBG("%zu bytes", tag->dsize);
|
||||
|
||||
if (!getuid())
|
||||
x = PA_PROTOCOL_VERSION;
|
||||
else
|
||||
x = PA_PROTOCOL_VERSION | 0x80000000U;
|
||||
tag_uint32(tag, x);
|
||||
DBG("%zu bytes", tag->dsize);
|
||||
|
||||
cookie_file(cookie);
|
||||
tag_arbitrary(tag, cookie, sizeof(cookie));
|
||||
DBG("%zu bytes", tag->dsize);
|
||||
|
||||
tag_finish(tag);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static Pulse_Tag *
|
||||
pulse_recv(Pulse *conn, Ecore_Fd_Handler *fdh)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
uint32_t x;
|
||||
|
||||
tag = eina_list_data_get(conn->iq);
|
||||
if (!tag)
|
||||
{
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
conn->iq = eina_list_append(conn->iq, tag);
|
||||
}
|
||||
if (!tag->auth)
|
||||
{
|
||||
msg_recv_creds(conn, tag);
|
||||
if (!tag->auth) return NULL;
|
||||
}
|
||||
if (!tag->data)
|
||||
{
|
||||
tag->dsize = ntohl(tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH]);
|
||||
if (!tag->dsize)
|
||||
{
|
||||
ERR("Kicked!");
|
||||
conn->state = PA_STATE_INIT;
|
||||
ecore_main_fd_handler_del(conn->fdh);
|
||||
close(conn->fd);
|
||||
ecore_event_add(PULSE_EVENT_DISCONNECTED, conn, pulse_fake_free, NULL);
|
||||
return NULL;
|
||||
}
|
||||
tag->data = malloc(tag->dsize);
|
||||
}
|
||||
if (tag->pos < tag->dsize)
|
||||
{
|
||||
if (!msg_recv(conn, tag))
|
||||
return NULL;
|
||||
}
|
||||
untag_uint32(tag, &x);
|
||||
EINA_SAFETY_ON_TRUE_GOTO((x != PA_COMMAND_REPLY) && (x != PA_COMMAND_SUBSCRIBE_EVENT), error);
|
||||
tag->command = x;
|
||||
if (x == PA_COMMAND_REPLY)
|
||||
untag_uint32(tag, &tag->tag_count);
|
||||
else
|
||||
tag->size += PA_TAG_SIZE_U32;
|
||||
if (conn->state != PA_STATE_CONNECTED)
|
||||
{
|
||||
ecore_main_fd_handler_active_set(fdh, ECORE_FD_WRITE);
|
||||
pulse_tag_free(tag);
|
||||
}
|
||||
return tag;
|
||||
error:
|
||||
CRI("Received error command %"PRIu32"!", x);
|
||||
pulse_tag_free(tag);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
login_finish(Pulse *conn, Ecore_Fd_Handler *fdh)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
tag->dsize = 2 * PA_TAG_SIZE_U32; /* tag_simple_init */
|
||||
proplist_init(tag);
|
||||
DBG("prep %zu bytes", tag->dsize);
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag_simple_init(conn, tag, PA_COMMAND_SET_CLIENT_NAME, PA_TAG_U32);
|
||||
tag_proplist(tag);
|
||||
tag_finish(tag);
|
||||
msg_send_creds(conn, tag);
|
||||
conn->state++;
|
||||
if (msg_send(conn, tag))
|
||||
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
|
||||
else
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
fdh_func(Pulse *conn, Ecore_Fd_Handler *fdh)
|
||||
{
|
||||
Pulse_Tag *rprev, *wprev;
|
||||
int read, write;
|
||||
|
||||
if (conn->watching) read = ECORE_FD_READ;
|
||||
else
|
||||
read = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
write = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_WRITE) * ECORE_FD_WRITE;
|
||||
rprev = eina_list_data_get(conn->iq);
|
||||
wprev = eina_list_data_get(conn->oq);
|
||||
|
||||
switch (conn->state)
|
||||
{
|
||||
case PA_STATE_INIT:
|
||||
if (!wprev)
|
||||
{
|
||||
wprev = login_setup(conn);
|
||||
conn->oq = eina_list_append(conn->oq, wprev);
|
||||
}
|
||||
|
||||
if (!wprev->auth)
|
||||
msg_sendmsg_creds(conn, wprev);
|
||||
|
||||
if (wprev->auth && msg_send(conn, wprev))
|
||||
{
|
||||
conn->state++;
|
||||
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
|
||||
}
|
||||
break;
|
||||
case PA_STATE_AUTH:
|
||||
if (pulse_recv(conn, fdh))
|
||||
login_finish(conn, fdh);
|
||||
break;
|
||||
case PA_STATE_MOREAUTH:
|
||||
if (write)
|
||||
{
|
||||
if (msg_send(conn, wprev))
|
||||
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
|
||||
break;
|
||||
}
|
||||
if (pulse_recv(conn, fdh))
|
||||
{
|
||||
conn->state++;
|
||||
INF("Login complete!");
|
||||
ecore_main_fd_handler_active_set(fdh, 0);
|
||||
ecore_event_add(PULSE_EVENT_CONNECTED, conn, pulse_fake_free, NULL);
|
||||
}
|
||||
break;
|
||||
case PA_STATE_CONNECTED:
|
||||
if (write)
|
||||
{
|
||||
if (wprev)
|
||||
{
|
||||
DBG("write");
|
||||
if (!wprev->auth)
|
||||
msg_send_creds(conn, wprev);
|
||||
if (wprev->auth)
|
||||
{
|
||||
if (msg_send(conn, wprev))
|
||||
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | (!!conn->oq * ECORE_FD_WRITE));
|
||||
}
|
||||
}
|
||||
else
|
||||
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ);
|
||||
}
|
||||
if (read)
|
||||
{
|
||||
DBG("read");
|
||||
if ((!rprev) || (!rprev->auth) || (rprev->pos < rprev->dsize))
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
PA_Commands command;
|
||||
tag = pulse_recv(conn, fdh);
|
||||
if (!tag) break;
|
||||
|
||||
command = (uintptr_t)eina_hash_find(conn->tag_handlers, &tag->tag_count);
|
||||
eina_hash_del_by_key(conn->tag_handlers, &tag->tag_count);
|
||||
deserialize_tag(conn, command, tag);
|
||||
if (!eina_list_count(conn->oq))
|
||||
ecore_main_fd_handler_active_set(conn->fdh, write | conn->watching * ECORE_FD_READ);
|
||||
pulse_tag_free(tag);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
con(Pulse *conn, int type __UNUSED__, Ecore_Con_Event_Server_Add *ev)
|
||||
{
|
||||
int on = 1;
|
||||
|
||||
if (conn != ecore_con_server_data_get(ev->server)) return ECORE_CALLBACK_PASS_ON;
|
||||
INF("connected to %s", ecore_con_server_name_get(ev->server));
|
||||
|
||||
conn->fd = dup(ecore_con_server_fd_get(ev->server));
|
||||
setsockopt(conn->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
|
||||
setsockopt(conn->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
fcntl(conn->fd, F_SETFL, O_NONBLOCK | FD_CLOEXEC);
|
||||
conn->fdh = ecore_main_fd_handler_add(conn->fd, ECORE_FD_WRITE, (Ecore_Fd_Cb)fdh_func, conn, NULL, NULL);
|
||||
ecore_con_server_del(conn->svr);
|
||||
conn->svr = NULL;
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_cards_get(Pulse *conn)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_GET_CARD_INFO_LIST;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 2 * PA_TAG_SIZE_U32;
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
void
|
||||
pulse_cb_set(Pulse *conn, uint32_t tagnum, Pulse_Cb cb)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN(conn);
|
||||
|
||||
if (cb) eina_hash_set(conn->tag_cbs, &tagnum, cb);
|
||||
else eina_hash_del_by_key(conn->tag_cbs, &tagnum);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sink_get(Pulse *conn, uint32_t sink_num)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_GET_SINK_INFO;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL;
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_uint32(tag, sink_num);
|
||||
tag_string(tag, NULL);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sinks_get(Pulse *conn)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_GET_SINK_INFO_LIST;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 2 * PA_TAG_SIZE_U32;
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sink_mute_set(Pulse *conn, uint32_t sink_num, Eina_Bool mute)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_SET_SINK_MUTE;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_BOOLEAN;
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_uint32(tag, sink_num);
|
||||
tag_string(tag, NULL);
|
||||
tag_bool(tag, !!mute);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
if (pulse_sinks)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
|
||||
sink = eina_hash_find(pulse_sinks, &sink_num);
|
||||
if (sink) sink->mute = !!mute;
|
||||
}
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sink_volume_set(Pulse *conn, uint32_t sink_num, uint8_t channels, double vol)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_SET_SINK_VOLUME;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + channels * sizeof(uint32_t);
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_uint32(tag, sink_num);
|
||||
tag_string(tag, NULL);
|
||||
tag_volume(tag, channels, vol);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sink_channel_volume_set(Pulse *conn, Pulse_Sink *sink, uint32_t id, double vol)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
int read;
|
||||
uint32_t type = PA_COMMAND_SET_SINK_VOLUME;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, 0);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
|
||||
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + sink->channel_map.channels * sizeof(uint32_t);
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
if (vol <= 0.0) sink->volume.values[id] = PA_VOLUME_MUTED;
|
||||
else sink->volume.values[id] = ((vol * PA_VOLUME_NORM) - (PA_VOLUME_NORM / 2)) / 100;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_uint32(tag, sink->index);
|
||||
tag_string(tag, NULL);
|
||||
tag_cvol(tag, &sink->volume);
|
||||
tag_finish(tag);
|
||||
read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
|
||||
ecore_main_fd_handler_active_set(conn->fdh, read | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return tag->tag_count;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
pulse_sinks_watch(Pulse *conn)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
uint32_t type = PA_COMMAND_SUBSCRIBE;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
|
||||
tag = calloc(1, sizeof(Pulse_Tag));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, EINA_FALSE);
|
||||
tag->dsize = 3 * PA_TAG_SIZE_U32;
|
||||
tag->data = malloc(tag->dsize);
|
||||
tag->tag_count = conn->tag_count;
|
||||
tag_simple_init(conn, tag, type, PA_TAG_U32);
|
||||
tag_uint32(tag, PA_SUBSCRIPTION_MASK_ALL);
|
||||
tag_finish(tag);
|
||||
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | ECORE_FD_WRITE);
|
||||
conn->oq = eina_list_append(conn->oq, tag);
|
||||
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
pulse_init(void)
|
||||
{
|
||||
if (pulse_init_count++) return pulse_init_count;
|
||||
|
||||
eina_init();
|
||||
ecore_init();
|
||||
ecore_con_init();
|
||||
pa_log_dom = eina_log_domain_register("pulse", EINA_COLOR_HIGH EINA_COLOR_BLUE);
|
||||
|
||||
PULSE_EVENT_CONNECTED = ecore_event_type_new();
|
||||
PULSE_EVENT_DISCONNECTED = ecore_event_type_new();
|
||||
PULSE_EVENT_CHANGE = ecore_event_type_new();
|
||||
|
||||
return pulse_init_count;
|
||||
}
|
||||
|
||||
void
|
||||
pulse_shutdown(void)
|
||||
{
|
||||
if ((!pulse_init_count) || (!--pulse_init_count)) return;
|
||||
|
||||
eina_log_domain_unregister(pa_log_dom);
|
||||
ecore_con_shutdown();
|
||||
ecore_shutdown();
|
||||
eina_shutdown();
|
||||
}
|
||||
|
||||
Pulse *
|
||||
pulse_new(void)
|
||||
{
|
||||
Pulse *conn;
|
||||
Eina_Iterator *it;
|
||||
const char *dir, *prev, *buf = NULL;;
|
||||
time_t time = 0;
|
||||
char *home, h[4096];
|
||||
|
||||
conn = calloc(1, sizeof(Pulse));
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
|
||||
home = getenv("PULSE_RUNTIME_PATH");
|
||||
|
||||
if (!home)
|
||||
{
|
||||
home = getenv("HOME");
|
||||
snprintf(h, sizeof(h), "%s/.pulse", home);
|
||||
home = h;
|
||||
}
|
||||
|
||||
it = eina_file_ls(home);
|
||||
EINA_ITERATOR_FOREACH(it, dir)
|
||||
{
|
||||
const char *rt = NULL;
|
||||
|
||||
rt = strrchr(dir, '-');
|
||||
if (rt)
|
||||
{
|
||||
if (!strcmp(rt + 1, "runtime"))
|
||||
{
|
||||
struct stat st;
|
||||
buf = eina_stringshare_printf("%s/native", dir);
|
||||
if (stat(buf, &st))
|
||||
{
|
||||
eina_stringshare_del(buf);
|
||||
buf = NULL;
|
||||
continue;
|
||||
}
|
||||
if (!time)
|
||||
{
|
||||
time = st.st_atime;
|
||||
prev = buf;
|
||||
continue;
|
||||
}
|
||||
if (time > st.st_atime)
|
||||
{
|
||||
eina_stringshare_del(buf);
|
||||
buf = NULL;
|
||||
continue;
|
||||
}
|
||||
eina_stringshare_del(prev);
|
||||
prev = buf;
|
||||
time = st.st_atime;
|
||||
}
|
||||
}
|
||||
eina_stringshare_del(dir);
|
||||
}
|
||||
eina_iterator_free(it);
|
||||
if ((!buf) || (!prev))
|
||||
{
|
||||
struct stat st;
|
||||
buf = eina_stringshare_add(STATEDIR "/run/pulse/native");
|
||||
if (stat(buf, &st))
|
||||
{
|
||||
CRI("could not locate local socket!");
|
||||
free(conn);
|
||||
return NULL;
|
||||
}
|
||||
conn->socket = buf;
|
||||
}
|
||||
else conn->socket = buf;
|
||||
conn->con = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)con, conn);
|
||||
conn->tag_handlers = eina_hash_int32_new(NULL);
|
||||
conn->tag_cbs = eina_hash_int32_new(NULL);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void
|
||||
pulse_free(Pulse *conn)
|
||||
{
|
||||
Pulse_Tag *tag;
|
||||
if (!conn) return;
|
||||
if (conn->fdh) ecore_main_fd_handler_del(conn->fdh);
|
||||
else if (conn->svr) ecore_con_server_del(conn->svr);
|
||||
ecore_event_handler_del(conn->con);
|
||||
eina_stringshare_del(conn->socket);
|
||||
EINA_LIST_FREE(conn->oq, tag)
|
||||
pulse_tag_free(tag);
|
||||
EINA_LIST_FREE(conn->iq, tag)
|
||||
pulse_tag_free(tag);
|
||||
eina_hash_free(conn->tag_handlers);
|
||||
eina_hash_free(conn->tag_cbs);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
pulse_connect(Pulse *conn)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
|
||||
conn->svr = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM, conn->socket, -1, conn);
|
||||
return !!conn->svr;
|
||||
}
|
|
@ -0,0 +1,541 @@
|
|||
#ifndef PA_HACKS_H
|
||||
#define PA_HACKS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <Ecore.h>
|
||||
#include <Ecore_Con.h>
|
||||
#include <inttypes.h>
|
||||
#include "Pulse.h"
|
||||
#include <math.h>
|
||||
|
||||
#ifndef STATEDIR
|
||||
# define STATEDIR "/var"
|
||||
#endif
|
||||
|
||||
#ifndef __UNUSED__
|
||||
# define __UNUSED__ __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#define PA_PROTOCOL_VERSION 16
|
||||
#define PA_NATIVE_COOKIE_LENGTH 256
|
||||
#ifndef PA_MACHINE_ID
|
||||
# define PA_MACHINE_ID "/var/lib/dbus/machine-id"
|
||||
#endif
|
||||
|
||||
#ifndef _
|
||||
# define _(X) (X)
|
||||
#endif
|
||||
|
||||
#define DBG(...) EINA_LOG_DOM_DBG(pa_log_dom, __VA_ARGS__)
|
||||
#define INF(...) EINA_LOG_DOM_INFO(pa_log_dom, __VA_ARGS__)
|
||||
#define WRN(...) EINA_LOG_DOM_WARN(pa_log_dom, __VA_ARGS__)
|
||||
#define ERR(...) EINA_LOG_DOM_ERR(pa_log_dom, __VA_ARGS__)
|
||||
#define CRI(...) EINA_LOG_DOM_CRIT(pa_log_dom, __VA_ARGS__)
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PA_COMMAND_ERROR,
|
||||
PA_COMMAND_TIMEOUT, /* pseudo command */
|
||||
PA_COMMAND_REPLY,
|
||||
PA_COMMAND_CREATE_PLAYBACK_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
|
||||
PA_COMMAND_DELETE_PLAYBACK_STREAM,
|
||||
PA_COMMAND_CREATE_RECORD_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
|
||||
PA_COMMAND_DELETE_RECORD_STREAM,
|
||||
PA_COMMAND_EXIT,
|
||||
PA_COMMAND_AUTH,
|
||||
PA_COMMAND_SET_CLIENT_NAME,
|
||||
PA_COMMAND_LOOKUP_SINK,
|
||||
PA_COMMAND_LOOKUP_SOURCE,
|
||||
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
|
||||
PA_COMMAND_STAT,
|
||||
PA_COMMAND_GET_PLAYBACK_LATENCY,
|
||||
PA_COMMAND_CREATE_UPLOAD_STREAM,
|
||||
PA_COMMAND_DELETE_UPLOAD_STREAM,
|
||||
PA_COMMAND_FINISH_UPLOAD_STREAM,
|
||||
PA_COMMAND_PLAY_SAMPLE,
|
||||
PA_COMMAND_REMOVE_SAMPLE,
|
||||
PA_COMMAND_GET_SERVER_INFO,
|
||||
PA_COMMAND_GET_SINK_INFO,
|
||||
PA_COMMAND_GET_SINK_INFO_LIST,
|
||||
PA_COMMAND_GET_SOURCE_INFO,
|
||||
PA_COMMAND_GET_SOURCE_INFO_LIST,
|
||||
PA_COMMAND_GET_MODULE_INFO,
|
||||
PA_COMMAND_GET_MODULE_INFO_LIST,
|
||||
PA_COMMAND_GET_CLIENT_INFO,
|
||||
PA_COMMAND_GET_CLIENT_INFO_LIST,
|
||||
PA_COMMAND_GET_SINK_INPUT_INFO, /* Payload changed in v11 (0.9.7) */
|
||||
PA_COMMAND_GET_SINK_INPUT_INFO_LIST, /* Payload changed in v11 (0.9.7) */
|
||||
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
|
||||
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
|
||||
PA_COMMAND_GET_SAMPLE_INFO,
|
||||
PA_COMMAND_GET_SAMPLE_INFO_LIST,
|
||||
PA_COMMAND_SUBSCRIBE,
|
||||
PA_COMMAND_SET_SINK_VOLUME,
|
||||
PA_COMMAND_SET_SINK_INPUT_VOLUME,
|
||||
PA_COMMAND_SET_SOURCE_VOLUME,
|
||||
PA_COMMAND_SET_SINK_MUTE,
|
||||
PA_COMMAND_SET_SOURCE_MUTE,
|
||||
PA_COMMAND_CORK_PLAYBACK_STREAM,
|
||||
PA_COMMAND_FLUSH_PLAYBACK_STREAM,
|
||||
PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
|
||||
PA_COMMAND_SET_DEFAULT_SINK,
|
||||
PA_COMMAND_SET_DEFAULT_SOURCE,
|
||||
PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
|
||||
PA_COMMAND_SET_RECORD_STREAM_NAME,
|
||||
PA_COMMAND_KILL_CLIENT,
|
||||
PA_COMMAND_KILL_SINK_INPUT,
|
||||
PA_COMMAND_KILL_SOURCE_OUTPUT,
|
||||
PA_COMMAND_LOAD_MODULE,
|
||||
PA_COMMAND_UNLOAD_MODULE,
|
||||
PA_COMMAND_ADD_AUTOLOAD___OBSOLETE,
|
||||
PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE,
|
||||
PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE,
|
||||
PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE,
|
||||
PA_COMMAND_GET_RECORD_LATENCY,
|
||||
PA_COMMAND_CORK_RECORD_STREAM,
|
||||
PA_COMMAND_FLUSH_RECORD_STREAM,
|
||||
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
|
||||
PA_COMMAND_REQUEST,
|
||||
PA_COMMAND_OVERFLOW,
|
||||
PA_COMMAND_UNDERFLOW,
|
||||
PA_COMMAND_PLAYBACK_STREAM_KILLED,
|
||||
PA_COMMAND_RECORD_STREAM_KILLED,
|
||||
PA_COMMAND_SUBSCRIBE_EVENT,
|
||||
PA_COMMAND_MOVE_SINK_INPUT,
|
||||
PA_COMMAND_MOVE_SOURCE_OUTPUT,
|
||||
PA_COMMAND_SET_SINK_INPUT_MUTE,
|
||||
PA_COMMAND_SUSPEND_SINK,
|
||||
PA_COMMAND_SUSPEND_SOURCE,
|
||||
PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
|
||||
PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
|
||||
PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
|
||||
PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
|
||||
PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
|
||||
PA_COMMAND_RECORD_STREAM_SUSPENDED,
|
||||
PA_COMMAND_PLAYBACK_STREAM_MOVED,
|
||||
PA_COMMAND_RECORD_STREAM_MOVED,
|
||||
PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
|
||||
PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
|
||||
PA_COMMAND_UPDATE_CLIENT_PROPLIST,
|
||||
PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
|
||||
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
|
||||
PA_COMMAND_REMOVE_CLIENT_PROPLIST,
|
||||
PA_COMMAND_STARTED,
|
||||
PA_COMMAND_EXTENSION,
|
||||
PA_COMMAND_GET_CARD_INFO,
|
||||
PA_COMMAND_GET_CARD_INFO_LIST,
|
||||
PA_COMMAND_SET_CARD_PROFILE,
|
||||
PA_COMMAND_CLIENT_EVENT,
|
||||
PA_COMMAND_PLAYBACK_STREAM_EVENT,
|
||||
PA_COMMAND_RECORD_STREAM_EVENT,
|
||||
PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
|
||||
PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
|
||||
PA_COMMAND_SET_SINK_PORT,
|
||||
PA_COMMAND_SET_SOURCE_PORT,
|
||||
PA_COMMAND_MAX
|
||||
} PA_Commands;
|
||||
|
||||
typedef enum pa_subscription_event_type {
|
||||
PA_SUBSCRIPTION_EVENT_SINK = 0x0000U,
|
||||
PA_SUBSCRIPTION_EVENT_SOURCE = 0x0001U,
|
||||
PA_SUBSCRIPTION_EVENT_SINK_INPUT = 0x0002U,
|
||||
PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 0x0003U,
|
||||
PA_SUBSCRIPTION_EVENT_MODULE = 0x0004U,
|
||||
PA_SUBSCRIPTION_EVENT_CLIENT = 0x0005U,
|
||||
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 0x0006U,
|
||||
PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U,
|
||||
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
|
||||
PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
|
||||
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
|
||||
PA_SUBSCRIPTION_EVENT_NEW = 0x0000U,
|
||||
PA_SUBSCRIPTION_EVENT_CHANGE = 0x0010U,
|
||||
PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
|
||||
PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
|
||||
} pa_subscription_event_type_t;
|
||||
|
||||
/** Subscription event mask, as used by pa_context_subscribe() */
|
||||
typedef enum pa_subscription_mask {
|
||||
PA_SUBSCRIPTION_MASK_NULL = 0x0000U,
|
||||
PA_SUBSCRIPTION_MASK_SINK = 0x0001U,
|
||||
PA_SUBSCRIPTION_MASK_SOURCE = 0x0002U,
|
||||
PA_SUBSCRIPTION_MASK_SINK_INPUT = 0x0004U,
|
||||
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 0x0008U,
|
||||
PA_SUBSCRIPTION_MASK_MODULE = 0x0010U,
|
||||
PA_SUBSCRIPTION_MASK_CLIENT = 0x0020U,
|
||||
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 0x0040U,
|
||||
PA_SUBSCRIPTION_MASK_SERVER = 0x0080U,
|
||||
PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
|
||||
PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
|
||||
PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
|
||||
} pa_subscription_mask_t;
|
||||
|
||||
/* The sequence descriptor header consists of 5 32bit integers: */
|
||||
typedef enum {
|
||||
PA_PSTREAM_DESCRIPTOR_LENGTH,
|
||||
PA_PSTREAM_DESCRIPTOR_CHANNEL,
|
||||
PA_PSTREAM_DESCRIPTOR_OFFSET_HI,
|
||||
PA_PSTREAM_DESCRIPTOR_OFFSET_LO,
|
||||
PA_PSTREAM_DESCRIPTOR_FLAGS,
|
||||
PA_PSTREAM_DESCRIPTOR_MAX
|
||||
} PA_Header;
|
||||
|
||||
/* Due to a stupid design flaw, proplists may only be at the END of a
|
||||
* packet or not before a STRING! Don't forget that! We can't really
|
||||
* fix this without breaking compat. */
|
||||
|
||||
typedef enum {
|
||||
PA_TAG_INVALID = 0,
|
||||
PA_TAG_STRING = 't',
|
||||
PA_TAG_STRING_NULL = 'N',
|
||||
PA_TAG_U32 = 'L',
|
||||
PA_TAG_U8 = 'B',
|
||||
PA_TAG_U64 = 'R',
|
||||
PA_TAG_S64 = 'r',
|
||||
PA_TAG_SAMPLE_SPEC = 'a',
|
||||
PA_TAG_ARBITRARY = 'x',
|
||||
PA_TAG_BOOLEAN_TRUE = '1',
|
||||
PA_TAG_BOOLEAN_FALSE = '0',
|
||||
PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE,
|
||||
PA_TAG_TIMEVAL = 'T',
|
||||
PA_TAG_USEC = 'U' /* 64bit unsigned */,
|
||||
PA_TAG_CHANNEL_MAP = 'm',
|
||||
PA_TAG_CVOLUME = 'v',
|
||||
PA_TAG_PROPLIST = 'P',
|
||||
PA_TAG_VOLUME = 'V'
|
||||
} PA_Tag;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PA_TAG_SIZE_INVALID = 0,
|
||||
PA_TAG_SIZE_STRING = 2,
|
||||
PA_TAG_SIZE_STRING_NULL = 1,
|
||||
PA_TAG_SIZE_U32 = 5,
|
||||
PA_TAG_SIZE_U8 = 'B',
|
||||
PA_TAG_SIZE_U64 = 'R',
|
||||
PA_TAG_SIZE_S64 = 'r',
|
||||
PA_TAG_SIZE_SAMPLE_SPEC = 7,
|
||||
PA_TAG_SIZE_ARBITRARY = PA_TAG_SIZE_U32,
|
||||
PA_TAG_SIZE_BOOLEAN = 1,
|
||||
PA_TAG_SIZE_TIMEVAL = 'T',
|
||||
PA_TAG_SIZE_USEC = 9 /* 64bit unsigned */,
|
||||
PA_TAG_SIZE_CHANNEL_MAP = 2,
|
||||
PA_TAG_SIZE_CVOLUME = 2,
|
||||
PA_TAG_SIZE_PROPLIST = 1 + PA_TAG_SIZE_STRING_NULL,
|
||||
PA_TAG_SIZE_VOLUME = 2
|
||||
} PA_Tag_Size;
|
||||
|
||||
/** Volume specification:
|
||||
* PA_VOLUME_MUTED: silence;
|
||||
* < PA_VOLUME_NORM: decreased volume;
|
||||
* PA_VOLUME_NORM: normal volume;
|
||||
* > PA_VOLUME_NORM: increased volume */
|
||||
typedef uint32_t pa_volume_t;
|
||||
|
||||
/** Normal volume (100%, 0 dB) */
|
||||
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
|
||||
|
||||
/** Muted (minimal valid) volume (0%, -inf dB) */
|
||||
#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
|
||||
|
||||
/** Maximum valid volume we can store. \since 0.9.15 */
|
||||
#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX-1)
|
||||
|
||||
/** Special 'invalid' volume. \since 0.9.16 */
|
||||
#define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX)
|
||||
#define PA_CHANNELS_MAX 32U
|
||||
|
||||
|
||||
/** Maximum allowed sample rate */
|
||||
#define PA_RATE_MAX (48000U*4U)
|
||||
|
||||
/** Sample format */
|
||||
typedef enum pa_sample_format {
|
||||
PA_SAMPLE_U8,
|
||||
/**< Unsigned 8 Bit PCM */
|
||||
|
||||
PA_SAMPLE_ALAW,
|
||||
/**< 8 Bit a-Law */
|
||||
|
||||
PA_SAMPLE_ULAW,
|
||||
/**< 8 Bit mu-Law */
|
||||
|
||||
PA_SAMPLE_S16LE,
|
||||
/**< Signed 16 Bit PCM, little endian (PC) */
|
||||
|
||||
PA_SAMPLE_S16BE,
|
||||
/**< Signed 16 Bit PCM, big endian */
|
||||
|
||||
PA_SAMPLE_FLOAT32LE,
|
||||
/**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
|
||||
|
||||
PA_SAMPLE_FLOAT32BE,
|
||||
/**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
|
||||
|
||||
PA_SAMPLE_S32LE,
|
||||
/**< Signed 32 Bit PCM, little endian (PC) */
|
||||
|
||||
PA_SAMPLE_S32BE,
|
||||
/**< Signed 32 Bit PCM, big endian */
|
||||
|
||||
PA_SAMPLE_S24LE,
|
||||
/**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
|
||||
|
||||
PA_SAMPLE_S24BE,
|
||||
/**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
|
||||
|
||||
PA_SAMPLE_S24_32LE,
|
||||
/**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
|
||||
|
||||
PA_SAMPLE_S24_32BE,
|
||||
/**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
|
||||
|
||||
PA_SAMPLE_MAX,
|
||||
/**< Upper limit of valid sample types */
|
||||
|
||||
PA_SAMPLE_INVALID = -1
|
||||
/**< An invalid value */
|
||||
} pa_sample_format_t;
|
||||
|
||||
/** A list of channel labels */
|
||||
typedef enum pa_channel_position {
|
||||
PA_CHANNEL_POSITION_INVALID = -1,
|
||||
PA_CHANNEL_POSITION_MONO = 0,
|
||||
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, /* Apple, Dolby call this 'Left' */
|
||||
PA_CHANNEL_POSITION_FRONT_RIGHT, /* Apple, Dolby call this 'Right' */
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER, /* Apple, Dolby call this 'Center' */
|
||||
|
||||
/** \cond fulldocs */
|
||||
PA_CHANNEL_POSITION_LEFT = PA_CHANNEL_POSITION_FRONT_LEFT,
|
||||
PA_CHANNEL_POSITION_RIGHT = PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_CENTER = PA_CHANNEL_POSITION_FRONT_CENTER,
|
||||
/** \endcond */
|
||||
|
||||
PA_CHANNEL_POSITION_REAR_CENTER, /* Microsoft calls this 'Back Center', Apple calls this 'Center Surround', Dolby calls this 'Surround Rear Center' */
|
||||
PA_CHANNEL_POSITION_REAR_LEFT, /* Microsoft calls this 'Back Left', Apple calls this 'Left Surround' (!), Dolby calls this 'Surround Rear Left' */
|
||||
PA_CHANNEL_POSITION_REAR_RIGHT, /* Microsoft calls this 'Back Right', Apple calls this 'Right Surround' (!), Dolby calls this 'Surround Rear Right' */
|
||||
|
||||
PA_CHANNEL_POSITION_LFE, /* Microsoft calls this 'Low Frequency', Apple calls this 'LFEScreen' */
|
||||
/** \cond fulldocs */
|
||||
PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
|
||||
/** \endcond */
|
||||
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, /* Apple, Dolby call this 'Left Center' */
|
||||
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, /* Apple, Dolby call this 'Right Center */
|
||||
|
||||
PA_CHANNEL_POSITION_SIDE_LEFT, /* Apple calls this 'Left Surround Direct', Dolby calls this 'Surround Left' (!) */
|
||||
PA_CHANNEL_POSITION_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct', Dolby calls this 'Surround Right' (!) */
|
||||
|
||||
PA_CHANNEL_POSITION_AUX0,
|
||||
PA_CHANNEL_POSITION_AUX1,
|
||||
PA_CHANNEL_POSITION_AUX2,
|
||||
PA_CHANNEL_POSITION_AUX3,
|
||||
PA_CHANNEL_POSITION_AUX4,
|
||||
PA_CHANNEL_POSITION_AUX5,
|
||||
PA_CHANNEL_POSITION_AUX6,
|
||||
PA_CHANNEL_POSITION_AUX7,
|
||||
PA_CHANNEL_POSITION_AUX8,
|
||||
PA_CHANNEL_POSITION_AUX9,
|
||||
PA_CHANNEL_POSITION_AUX10,
|
||||
PA_CHANNEL_POSITION_AUX11,
|
||||
PA_CHANNEL_POSITION_AUX12,
|
||||
PA_CHANNEL_POSITION_AUX13,
|
||||
PA_CHANNEL_POSITION_AUX14,
|
||||
PA_CHANNEL_POSITION_AUX15,
|
||||
PA_CHANNEL_POSITION_AUX16,
|
||||
PA_CHANNEL_POSITION_AUX17,
|
||||
PA_CHANNEL_POSITION_AUX18,
|
||||
PA_CHANNEL_POSITION_AUX19,
|
||||
PA_CHANNEL_POSITION_AUX20,
|
||||
PA_CHANNEL_POSITION_AUX21,
|
||||
PA_CHANNEL_POSITION_AUX22,
|
||||
PA_CHANNEL_POSITION_AUX23,
|
||||
PA_CHANNEL_POSITION_AUX24,
|
||||
PA_CHANNEL_POSITION_AUX25,
|
||||
PA_CHANNEL_POSITION_AUX26,
|
||||
PA_CHANNEL_POSITION_AUX27,
|
||||
PA_CHANNEL_POSITION_AUX28,
|
||||
PA_CHANNEL_POSITION_AUX29,
|
||||
PA_CHANNEL_POSITION_AUX30,
|
||||
PA_CHANNEL_POSITION_AUX31,
|
||||
|
||||
PA_CHANNEL_POSITION_TOP_CENTER, /* Apple calls this 'Top Center Surround' */
|
||||
|
||||
PA_CHANNEL_POSITION_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */
|
||||
PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */
|
||||
PA_CHANNEL_POSITION_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */
|
||||
|
||||
PA_CHANNEL_POSITION_TOP_REAR_LEFT, /* Microsoft and Apple call this 'Top Back Left' */
|
||||
PA_CHANNEL_POSITION_TOP_REAR_RIGHT, /* Microsoft and Apple call this 'Top Back Right' */
|
||||
PA_CHANNEL_POSITION_TOP_REAR_CENTER, /* Microsoft and Apple call this 'Top Back Center' */
|
||||
|
||||
PA_CHANNEL_POSITION_MAX
|
||||
} pa_channel_position_t;
|
||||
|
||||
/** A mask of channel positions. \since 0.9.16 */
|
||||
typedef uint64_t pa_channel_position_mask_t;
|
||||
|
||||
/** Makes a bit mask from a channel position. \since 0.9.16 */
|
||||
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
|
||||
|
||||
/** A list of channel mapping definitions for pa_channel_map_init_auto() */
|
||||
typedef enum pa_channel_map_def {
|
||||
PA_CHANNEL_MAP_AIFF,
|
||||
/**< The mapping from RFC3551, which is based on AIFF-C */
|
||||
|
||||
/** \cond fulldocs */
|
||||
PA_CHANNEL_MAP_ALSA,
|
||||
/**< The default mapping used by ALSA. This mapping is probably
|
||||
* not too useful since ALSA's default channel mapping depends on
|
||||
* the device string used. */
|
||||
/** \endcond */
|
||||
|
||||
PA_CHANNEL_MAP_AUX,
|
||||
/**< Only aux channels */
|
||||
|
||||
PA_CHANNEL_MAP_WAVEEX,
|
||||
/**< Microsoft's WAVEFORMATEXTENSIBLE mapping. This mapping works
|
||||
* as if all LSBs of dwChannelMask are set. */
|
||||
|
||||
/** \cond fulldocs */
|
||||
PA_CHANNEL_MAP_OSS,
|
||||
/**< The default channel mapping used by OSS as defined in the OSS
|
||||
* 4.0 API specs. This mapping is probably not too useful since
|
||||
* the OSS API has changed in this respect and no longer knows a
|
||||
* default channel mapping based on the number of channels. */
|
||||
/** \endcond */
|
||||
|
||||
/**< Upper limit of valid channel mapping definitions */
|
||||
PA_CHANNEL_MAP_DEF_MAX,
|
||||
|
||||
PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF
|
||||
/**< The default channel map */
|
||||
} pa_channel_map_def_t;
|
||||
|
||||
typedef struct pa_sample_spec {
|
||||
pa_sample_format_t format;
|
||||
/**< The sample format */
|
||||
|
||||
uint32_t rate;
|
||||
/**< The sample rate. (e.g. 44100) */
|
||||
|
||||
uint8_t channels;
|
||||
/**< Audio channels. (1 for mono, 2 for stereo, ...) */
|
||||
} pa_sample_spec;
|
||||
/** A structure encapsulating a per-channel volume */
|
||||
typedef struct pa_cvolume {
|
||||
uint8_t channels; /**< Number of channels */
|
||||
pa_volume_t values[PA_CHANNELS_MAX]; /**< Per-channel volume */
|
||||
} pa_cvolume;
|
||||
|
||||
typedef struct pa_sink_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 */
|
||||
} pa_sink_port_info;
|
||||
|
||||
typedef struct pa_channel_map {
|
||||
uint8_t channels;
|
||||
/**< Number of channels */
|
||||
|
||||
pa_channel_position_t map[PA_CHANNELS_MAX];
|
||||
/**< Channel labels */
|
||||
} pa_channel_map;
|
||||
|
||||
/** Type for usec specifications (unsigned). Always 64 bit. */
|
||||
typedef uint64_t pa_usec_t;
|
||||
|
||||
/** Stores information about sinks. Please note that this structure
|
||||
* can be extended as part of evolutionary API updates at any time in
|
||||
* any new release. */
|
||||
struct Pulse_Sink {
|
||||
const char *name; /**< Name of the sink */
|
||||
uint32_t index; /**< Index of the sink */
|
||||
const char *description; /**< Description of this sink */
|
||||
// pa_sample_spec sample_spec; /**< Sample spec of this sink */
|
||||
pa_channel_map channel_map; /**< Channel map */
|
||||
// uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
|
||||
pa_cvolume volume; /**< Volume of the sink */
|
||||
Eina_Bool mute : 1; /**< Mute switch of the sink */
|
||||
Eina_Bool update : 1;
|
||||
};
|
||||
|
||||
typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pa_pstream_descriptor header;
|
||||
uint8_t *data;
|
||||
|
||||
size_t dsize;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
PA_Commands command;
|
||||
uint32_t tag_count;
|
||||
Eina_Bool auth : 1;
|
||||
Eina_Hash *props;
|
||||
} Pulse_Tag;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PA_STATE_INIT,
|
||||
PA_STATE_AUTH,
|
||||
PA_STATE_MOREAUTH,
|
||||
PA_STATE_CONNECTED,
|
||||
} PA_State;
|
||||
|
||||
struct Pulse
|
||||
{
|
||||
PA_State state;
|
||||
int fd;
|
||||
Ecore_Fd_Handler *fdh;
|
||||
Ecore_Con_Server *svr;
|
||||
Ecore_Event_Handler *con;
|
||||
const char *socket;
|
||||
Eina_List *oq;
|
||||
Eina_List *iq;
|
||||
Eina_Hash *tag_handlers;
|
||||
Eina_Hash *tag_cbs;
|
||||
uint32_t tag_count;
|
||||
Eina_Bool watching : 1;
|
||||
};
|
||||
|
||||
extern int pa_log_dom;
|
||||
|
||||
uint8_t *tag_bool(Pulse_Tag *tag, Eina_Bool val);
|
||||
uint8_t *untag_bool(Pulse_Tag *tag, Eina_Bool *val);
|
||||
uint8_t *tag_uint32(Pulse_Tag *tag, uint32_t val);
|
||||
uint8_t *untag_uint32(Pulse_Tag *tag, uint32_t *val);
|
||||
uint8_t *tag_string(Pulse_Tag *tag, const char *val);
|
||||
uint8_t *untag_string(Pulse_Tag *tag, const char **val);
|
||||
uint8_t *tag_arbitrary(Pulse_Tag *tag, const void *val, uint32_t size);
|
||||
uint8_t *untag_arbitrary(Pulse_Tag *tag, Eina_Binbuf **val);
|
||||
uint8_t *untag_sample(Pulse_Tag *tag, pa_sample_spec *spec);
|
||||
uint8_t *untag_channel_map(Pulse_Tag *tag, pa_channel_map *map);
|
||||
uint8_t *untag_cvol(Pulse_Tag *tag, pa_cvolume *cvol);
|
||||
uint8_t *untag_usec(Pulse_Tag *tag, uint64_t *val);
|
||||
uint8_t *untag_proplist(Pulse_Tag *tag, Eina_Hash **props);
|
||||
uint8_t *tag_simple_init(Pulse *conn, Pulse_Tag *tag, uint32_t val, PA_Tag type);
|
||||
uint8_t *tag_proplist(Pulse_Tag *tag);
|
||||
uint8_t *tag_volume(Pulse_Tag *tag, uint8_t channels, double vol);
|
||||
uint8_t *tag_cvol(Pulse_Tag *tag, pa_cvolume *c);
|
||||
void tag_finish(Pulse_Tag *tag);
|
||||
|
||||
Eina_Bool deserialize_tag(Pulse *conn, PA_Commands command, Pulse_Tag *tag);
|
||||
|
||||
void pulse_fake_free(void *d __UNUSED__, void *d2 __UNUSED__);
|
||||
void pulse_tag_free(Pulse_Tag *tag);
|
||||
|
||||
void msg_recv_creds(Pulse *conn, Pulse_Tag *tag);
|
||||
Eina_Bool msg_recv(Pulse *conn, Pulse_Tag *tag);
|
||||
void msg_sendmsg_creds(Pulse *conn, Pulse_Tag *tag);
|
||||
void msg_send_creds(Pulse *conn, Pulse_Tag *tag);
|
||||
Eina_Bool msg_send(Pulse *conn, Pulse_Tag *tag);
|
||||
|
||||
extern Eina_Hash *pulse_sinks;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,131 @@
|
|||
#include "pa.h"
|
||||
|
||||
static void
|
||||
deserialize_sinks_watcher(Pulse *conn, Pulse_Tag *tag)
|
||||
{
|
||||
pa_subscription_event_type_t e;
|
||||
uint32_t idx;
|
||||
|
||||
EINA_SAFETY_ON_FALSE_RETURN(untag_uint32(tag, &e));
|
||||
EINA_SAFETY_ON_FALSE_RETURN(untag_uint32(tag, &idx));
|
||||
|
||||
if (e & PA_SUBSCRIPTION_EVENT_CHANGE)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
|
||||
sink = eina_hash_find(pulse_sinks, &idx);
|
||||
if (!sink) return;
|
||||
if (pulse_sink_get(conn, idx))
|
||||
sink->update = EINA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Pulse_Sink *
|
||||
deserialize_sink(Pulse *conn __UNUSED__, Pulse_Tag *tag)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
Eina_Bool mute, exist;
|
||||
pa_sample_spec spec;
|
||||
uint32_t owner_module, monitor_source, flags, base_volume, state, n_volume_steps, card, n_ports;
|
||||
uint64_t latency, configured_latency;
|
||||
const char *monitor_source_name, *driver, *active_port;
|
||||
Eina_Hash *props;
|
||||
unsigned int x;
|
||||
|
||||
monitor_source_name = driver = active_port = NULL;
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &x), error);
|
||||
sink = eina_hash_find(pulse_sinks, &x);
|
||||
exist = !!sink;
|
||||
if (!sink) sink = calloc(1, sizeof(Pulse_Sink));
|
||||
sink->index = x;
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &sink->name), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &sink->description), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_sample(tag, &spec), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_channel_map(tag, &sink->channel_map), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &owner_module), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_cvol(tag, &sink->volume), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_bool(tag, &mute), error);
|
||||
sink->mute = !!mute;
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &monitor_source), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &monitor_source_name), error);
|
||||
eina_stringshare_del(monitor_source_name);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_usec(tag, &latency), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &driver), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &flags), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_proplist(tag, &props), error);
|
||||
eina_hash_free(props);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_usec(tag, &configured_latency), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &base_volume), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &state), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &n_volume_steps), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &card), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &n_ports), error);
|
||||
|
||||
for (x = 0; x < n_ports; x++)
|
||||
{
|
||||
pa_sink_port_info pi = {NULL, NULL, 0};
|
||||
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &pi.name), error);
|
||||
eina_stringshare_del(pi.name);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &pi.description), error);
|
||||
eina_stringshare_del(pi.description);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &pi.priority), error);
|
||||
}
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &active_port), error);
|
||||
if (exist)
|
||||
ecore_event_add(PULSE_EVENT_CHANGE, sink, pulse_fake_free, NULL);
|
||||
else
|
||||
{
|
||||
if (!pulse_sinks) pulse_sinks = eina_hash_int32_new(NULL);
|
||||
eina_hash_add(pulse_sinks, (uintptr_t*)&sink->index, sink);
|
||||
}
|
||||
return sink;
|
||||
error:
|
||||
pulse_sink_free(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
deserialize_tag(Pulse *conn, PA_Commands command, Pulse_Tag *tag)
|
||||
{
|
||||
Pulse_Cb cb;
|
||||
void *ev = (!command) ? NULL : PULSE_SUCCESS;
|
||||
|
||||
cb = eina_hash_find(conn->tag_cbs, &tag->tag_count);
|
||||
if (command == PA_COMMAND_SUBSCRIBE)
|
||||
conn->watching = EINA_TRUE;
|
||||
switch (command)
|
||||
{
|
||||
case PA_COMMAND_GET_SINK_INFO_LIST:
|
||||
if (!cb) return EINA_TRUE;
|
||||
ev = NULL;
|
||||
while (tag->size < tag->dsize - PA_TAG_SIZE_STRING_NULL)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
|
||||
sink = deserialize_sink(conn, tag);
|
||||
if (!sink)
|
||||
{
|
||||
EINA_LIST_FREE(ev, sink)
|
||||
pulse_sink_free(sink);
|
||||
break;
|
||||
}
|
||||
ev = eina_list_append(ev, sink);
|
||||
}
|
||||
break;
|
||||
case PA_COMMAND_GET_SINK_INFO:
|
||||
if ((!cb) && (!conn->watching)) return EINA_TRUE;
|
||||
ev = deserialize_sink(conn, tag);
|
||||
break;
|
||||
case 0:
|
||||
deserialize_sinks_watcher(conn, tag);
|
||||
return EINA_TRUE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!cb) return EINA_TRUE;
|
||||
eina_hash_del_by_key(conn->tag_cbs, &tag->tag_count);
|
||||
cb(conn, tag->tag_count, ev);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
#include "pa.h"
|
||||
|
||||
|
||||
/** Makes a bit mask from a channel position. \since 0.9.16 */
|
||||
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
|
||||
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_LEFT \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_RIGHT \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_CENTER \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_FRONT \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_REAR \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_TOP \
|
||||
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
|
||||
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
|
||||
|
||||
#define PA_CHANNEL_POSITION_MASK_ALL \
|
||||
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
|
||||
|
||||
const char *const channel_name_table[PA_CHANNEL_POSITION_MAX] = {
|
||||
[PA_CHANNEL_POSITION_MONO] = _("Mono"),
|
||||
|
||||
[PA_CHANNEL_POSITION_FRONT_CENTER] = _("Front Center"),
|
||||
[PA_CHANNEL_POSITION_FRONT_LEFT] = _("Front Left"),
|
||||
[PA_CHANNEL_POSITION_FRONT_RIGHT] = _("Front Right"),
|
||||
|
||||
[PA_CHANNEL_POSITION_REAR_CENTER] = _("Rear Center"),
|
||||
[PA_CHANNEL_POSITION_REAR_LEFT] = _("Rear Left"),
|
||||
[PA_CHANNEL_POSITION_REAR_RIGHT] = _("Rear Right"),
|
||||
|
||||
[PA_CHANNEL_POSITION_LFE] = _("Subwoofer"),
|
||||
|
||||
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = _("Front Left-of-center"),
|
||||
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = _("Front Right-of-center"),
|
||||
|
||||
[PA_CHANNEL_POSITION_SIDE_LEFT] = _("Side Left"),
|
||||
[PA_CHANNEL_POSITION_SIDE_RIGHT] = _("Side Right"),
|
||||
|
||||
[PA_CHANNEL_POSITION_AUX0] = _("Auxiliary 0"),
|
||||
[PA_CHANNEL_POSITION_AUX1] = _("Auxiliary 1"),
|
||||
[PA_CHANNEL_POSITION_AUX2] = _("Auxiliary 2"),
|
||||
[PA_CHANNEL_POSITION_AUX3] = _("Auxiliary 3"),
|
||||
[PA_CHANNEL_POSITION_AUX4] = _("Auxiliary 4"),
|
||||
[PA_CHANNEL_POSITION_AUX5] = _("Auxiliary 5"),
|
||||
[PA_CHANNEL_POSITION_AUX6] = _("Auxiliary 6"),
|
||||
[PA_CHANNEL_POSITION_AUX7] = _("Auxiliary 7"),
|
||||
[PA_CHANNEL_POSITION_AUX8] = _("Auxiliary 8"),
|
||||
[PA_CHANNEL_POSITION_AUX9] = _("Auxiliary 9"),
|
||||
[PA_CHANNEL_POSITION_AUX10] = _("Auxiliary 10"),
|
||||
[PA_CHANNEL_POSITION_AUX11] = _("Auxiliary 11"),
|
||||
[PA_CHANNEL_POSITION_AUX12] = _("Auxiliary 12"),
|
||||
[PA_CHANNEL_POSITION_AUX13] = _("Auxiliary 13"),
|
||||
[PA_CHANNEL_POSITION_AUX14] = _("Auxiliary 14"),
|
||||
[PA_CHANNEL_POSITION_AUX15] = _("Auxiliary 15"),
|
||||
[PA_CHANNEL_POSITION_AUX16] = _("Auxiliary 16"),
|
||||
[PA_CHANNEL_POSITION_AUX17] = _("Auxiliary 17"),
|
||||
[PA_CHANNEL_POSITION_AUX18] = _("Auxiliary 18"),
|
||||
[PA_CHANNEL_POSITION_AUX19] = _("Auxiliary 19"),
|
||||
[PA_CHANNEL_POSITION_AUX20] = _("Auxiliary 20"),
|
||||
[PA_CHANNEL_POSITION_AUX21] = _("Auxiliary 21"),
|
||||
[PA_CHANNEL_POSITION_AUX22] = _("Auxiliary 22"),
|
||||
[PA_CHANNEL_POSITION_AUX23] = _("Auxiliary 23"),
|
||||
[PA_CHANNEL_POSITION_AUX24] = _("Auxiliary 24"),
|
||||
[PA_CHANNEL_POSITION_AUX25] = _("Auxiliary 25"),
|
||||
[PA_CHANNEL_POSITION_AUX26] = _("Auxiliary 26"),
|
||||
[PA_CHANNEL_POSITION_AUX27] = _("Auxiliary 27"),
|
||||
[PA_CHANNEL_POSITION_AUX28] = _("Auxiliary 28"),
|
||||
[PA_CHANNEL_POSITION_AUX29] = _("Auxiliary 29"),
|
||||
[PA_CHANNEL_POSITION_AUX30] = _("Auxiliary 30"),
|
||||
[PA_CHANNEL_POSITION_AUX31] = _("Auxiliary 31"),
|
||||
|
||||
[PA_CHANNEL_POSITION_TOP_CENTER] = _("Top Center"),
|
||||
|
||||
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = _("Top Front Center"),
|
||||
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = _("Top Front Left"),
|
||||
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = _("Top Front Right"),
|
||||
|
||||
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = _("Top Rear Center"),
|
||||
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = _("Top Rear Left"),
|
||||
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = _("Top Rear Right")
|
||||
};
|
||||
|
||||
static Eina_Bool on_left(pa_channel_position_t p) {
|
||||
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
|
||||
}
|
||||
|
||||
static Eina_Bool on_right(pa_channel_position_t p) {
|
||||
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
|
||||
}
|
||||
#if 0
|
||||
static Eina_Bool on_center(pa_channel_position_t p) {
|
||||
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
|
||||
}
|
||||
|
||||
static Eina_Bool on_lfe(pa_channel_position_t p) {
|
||||
return p == PA_CHANNEL_POSITION_LFE;
|
||||
}
|
||||
#endif
|
||||
static Eina_Bool on_front(pa_channel_position_t p) {
|
||||
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
|
||||
}
|
||||
|
||||
static Eina_Bool on_rear(pa_channel_position_t p) {
|
||||
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
|
||||
}
|
||||
|
||||
void
|
||||
pulse_sink_free(Pulse_Sink *sink)
|
||||
{
|
||||
if (!sink) return;
|
||||
eina_stringshare_del(sink->name);
|
||||
eina_stringshare_del(sink->description);
|
||||
eina_hash_del_by_key(pulse_sinks, (uintptr_t*)&sink->index);
|
||||
free(sink);
|
||||
}
|
||||
|
||||
const char *
|
||||
pulse_sink_name_get(Pulse_Sink *sink)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
|
||||
|
||||
return sink->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
pulse_sink_desc_get(Pulse_Sink *sink)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
|
||||
|
||||
return sink->description;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pulse_sink_idx_get(Pulse_Sink *sink)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, 0);
|
||||
|
||||
return sink->index;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
pulse_sink_muted_get(Pulse_Sink *sink)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, EINA_FALSE);
|
||||
|
||||
return sink->mute;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
pulse_sink_channels_count(Pulse_Sink *sink)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, 0);
|
||||
|
||||
return sink->volume.channels;
|
||||
}
|
||||
|
||||
double
|
||||
pulse_sink_avg_get_pct(Pulse_Sink *sink)
|
||||
{
|
||||
double avg;
|
||||
int x;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
|
||||
|
||||
for (avg = 0, x = 0; x < sink->volume.channels; x++)
|
||||
avg += sink->volume.values[x];
|
||||
avg /= sink->volume.channels;
|
||||
|
||||
if (avg <= PA_VOLUME_MUTED) return 0.0;
|
||||
|
||||
if (avg == PA_VOLUME_NORM) return 100.0;
|
||||
|
||||
return (avg * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM;
|
||||
}
|
||||
|
||||
Eina_List *
|
||||
pulse_sink_channel_names_get(Pulse_Sink *sink)
|
||||
{
|
||||
Eina_List *ret = NULL;
|
||||
unsigned int x;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
|
||||
|
||||
for (x = 0; x < sink->channel_map.channels; x++)
|
||||
ret = eina_list_append(ret, eina_stringshare_add(channel_name_table[x]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
pulse_sink_channel_name_get_id(Pulse_Sink *sink, const char *name)
|
||||
{
|
||||
unsigned int x;
|
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, UINT_MAX);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(name, UINT_MAX);
|
||||
for (x = 0; x < sink->channel_map.channels; x++)
|
||||
if (!strcmp(name, channel_name_table[sink->channel_map.map[x]])) return x;
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
const char *
|
||||
pulse_sink_channel_id_get_name(Pulse_Sink *sink, unsigned int id)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, NULL);
|
||||
return eina_stringshare_add(channel_name_table[sink->channel_map.map[id]]);
|
||||
}
|
||||
|
||||
double
|
||||
pulse_sink_channel_volume_get(Pulse_Sink *sink, unsigned int id)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1);
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1);
|
||||
return (sink->volume.values[id] * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM;
|
||||
}
|
||||
|
||||
float
|
||||
pulse_sink_channel_balance_get(Pulse_Sink *sink, unsigned int id)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1.0);
|
||||
|
||||
if (on_left(sink->channel_map.map[id])) return 0.0;
|
||||
if (on_right(sink->channel_map.map[id])) return 1.0;
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
float
|
||||
pulse_sink_channel_depth_get(Pulse_Sink *sink, unsigned int id)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1.0);
|
||||
|
||||
if (on_rear(sink->channel_map.map[id])) return 0.0;
|
||||
if (on_front(sink->channel_map.map[id])) return 1.0;
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
float
|
||||
pulse_sink_balance_get(Pulse_Sink *sink)
|
||||
{
|
||||
int c;
|
||||
float l, r;
|
||||
pa_volume_t left = 0, right = 0;
|
||||
unsigned n_left = 0, n_right = 0;
|
||||
|
||||
for (c = 0; c < sink->channel_map.channels; c++) {
|
||||
if (on_left(sink->channel_map.map[c])) {
|
||||
left += sink->volume.values[c];
|
||||
n_left++;
|
||||
} else if (on_right(sink->channel_map.map[c])) {
|
||||
right += sink->volume.values[c];
|
||||
n_right++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_left <= 0)
|
||||
l = PA_VOLUME_NORM;
|
||||
else
|
||||
l = left / n_left;
|
||||
|
||||
if (n_right <= 0)
|
||||
r = PA_VOLUME_NORM;
|
||||
else
|
||||
r = right / n_right;
|
||||
l /= (float)PA_VOLUME_NORM;
|
||||
r /= (float)PA_VOLUME_NORM;
|
||||
return r - l;
|
||||
}
|
|
@ -0,0 +1,427 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "e_mod_system.h"
|
||||
#include "e_mod_main.h"
|
||||
#include <Ecore.h>
|
||||
#include "Pulse.h"
|
||||
|
||||
#define PULSE_BUS "org.PulseAudio.Core1"
|
||||
#define PULSE_PATH "/org/pulseaudio/core1"
|
||||
#define PULSE_INTERFACE "org.PulseAudio.Core1"
|
||||
|
||||
static Pulse *conn = NULL;
|
||||
static Ecore_Event_Handler *ph = NULL;
|
||||
static Ecore_Event_Handler *pch = NULL;
|
||||
static Ecore_Event_Handler *pdh = NULL;
|
||||
static Eina_List *sinks = NULL;
|
||||
|
||||
static E_DBus_Connection *dbus = NULL;
|
||||
static E_DBus_Signal_Handler *dbus_handler = NULL;
|
||||
static double last_disc = 0;
|
||||
|
||||
static void
|
||||
_dbus_poll(void *data __UNUSED__,
|
||||
DBusMessage *msg)
|
||||
{
|
||||
DBusError err;
|
||||
const char *name, *from, *to;
|
||||
|
||||
dbus_error_init(&err);
|
||||
if (!dbus_message_get_args(msg, &err,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_STRING, &from,
|
||||
DBUS_TYPE_STRING, &to,
|
||||
DBUS_TYPE_INVALID))
|
||||
dbus_error_free(&err);
|
||||
|
||||
//printf("name: %s\nfrom: %s\nto: %s\n", name, from, to);
|
||||
if ((name) && !strcmp(name, PULSE_BUS))
|
||||
e_mixer_pulse_init();
|
||||
if (dbus_handler)
|
||||
{
|
||||
e_dbus_signal_handler_del(dbus, dbus_handler);
|
||||
dbus_handler = NULL;
|
||||
}
|
||||
if (dbus)
|
||||
{
|
||||
e_dbus_connection_close(dbus);
|
||||
dbus = NULL;
|
||||
e_dbus_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_dbus_test(void *data __UNUSED__,
|
||||
DBusMessage *msg __UNUSED__,
|
||||
DBusError *error)
|
||||
{
|
||||
if ((error) && (dbus_error_is_set(error)))
|
||||
{
|
||||
dbus_error_free(error);
|
||||
if (dbus_handler)
|
||||
{
|
||||
e_dbus_signal_handler_del(dbus, dbus_handler);
|
||||
dbus_handler = NULL;
|
||||
}
|
||||
if (dbus)
|
||||
{
|
||||
e_dbus_connection_close(dbus);
|
||||
dbus = NULL;
|
||||
e_dbus_shutdown();
|
||||
}
|
||||
e_mod_mixer_pulse_ready(EINA_FALSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_pulse_update(Pulse *d __UNUSED__, int type __UNUSED__, Pulse_Sink *ev __UNUSED__)
|
||||
{
|
||||
e_mod_mixer_pulse_update();
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_pulse_sinks_get(Pulse *p __UNUSED__, Pulse_Tag_Id id __UNUSED__, Eina_List *ev)
|
||||
{
|
||||
Eina_List *l;
|
||||
Pulse_Sink *sink;
|
||||
sinks = ev;
|
||||
EINA_LIST_FOREACH(ev, l, sink)
|
||||
{
|
||||
printf("Sink:\n");
|
||||
printf("\tname: %s\n", pulse_sink_name_get(sink));
|
||||
printf("\tidx: %"PRIu32"\n", pulse_sink_idx_get(sink));
|
||||
printf("\tdesc: %s\n", pulse_sink_desc_get(sink));
|
||||
printf("\tchannels: %u\n", pulse_sink_channels_count(sink));
|
||||
printf("\tmuted: %s\n", pulse_sink_muted_get(sink) ? "yes" : "no");
|
||||
printf("\tavg: %g\n", pulse_sink_avg_get_pct(sink));
|
||||
printf("\tbalance: %f\n", pulse_sink_balance_get(sink));
|
||||
}
|
||||
pulse_sinks_watch(conn);
|
||||
e_mod_mixer_pulse_ready(EINA_TRUE);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_pulse_connected(Pulse *d, int type __UNUSED__, Pulse *ev)
|
||||
{
|
||||
Pulse_Tag_Id id;
|
||||
if (d != ev) return ECORE_CALLBACK_PASS_ON;
|
||||
id = pulse_sinks_get(conn);
|
||||
if (!id)
|
||||
{
|
||||
e_mixer_pulse_shutdown();
|
||||
e_mixer_default_setup();
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_sinks_get);
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_pulse_disconnected(Pulse *d, int type __UNUSED__, Pulse *ev)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
|
||||
if (d != ev) return ECORE_CALLBACK_PASS_ON;
|
||||
|
||||
EINA_LIST_FREE(sinks, sink)
|
||||
pulse_sink_free(sink);
|
||||
|
||||
if (last_disc && (ecore_time_unix_get() - last_disc < 1))
|
||||
{
|
||||
e_mixer_pulse_shutdown();
|
||||
last_disc = 0;
|
||||
e_mod_mixer_pulse_ready(EINA_FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pulse_connect(conn);
|
||||
last_disc = ecore_time_unix_get();
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Pulse_Sink *
|
||||
_pulse_sink_find(const char *name)
|
||||
{
|
||||
Eina_List *l;
|
||||
Pulse_Sink *sink;
|
||||
EINA_LIST_FOREACH(sinks, l, sink)
|
||||
{
|
||||
const char *sink_name;
|
||||
|
||||
sink_name = pulse_sink_name_get(sink);
|
||||
if ((sink_name == name) || (!strcmp(sink_name, name)))
|
||||
return sink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
_pulse_result_cb(Pulse *p __UNUSED__, Pulse_Tag_Id id, void *ev)
|
||||
{
|
||||
if (!ev) fprintf(stderr, "Command %u failed!\n", id);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
e_mixer_pulse_ready(void)
|
||||
{
|
||||
return !!sinks;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
e_mixer_pulse_init(void)
|
||||
{
|
||||
pulse_init();
|
||||
conn = pulse_new();
|
||||
EINA_SAFETY_ON_NULL_GOTO(conn, error);
|
||||
if (dbus) goto error;
|
||||
if (!pulse_connect(conn))
|
||||
{
|
||||
DBusMessage *msg;
|
||||
|
||||
e_dbus_init();
|
||||
dbus = e_dbus_bus_get(DBUS_BUS_SESSION);
|
||||
|
||||
if (!dbus)
|
||||
{
|
||||
e_dbus_shutdown();
|
||||
return EINA_FALSE;
|
||||
}
|
||||
if (!dbus_handler)
|
||||
dbus_handler = e_dbus_signal_handler_add(dbus,
|
||||
E_DBUS_FDO_BUS, E_DBUS_FDO_PATH,
|
||||
E_DBUS_FDO_INTERFACE,
|
||||
"NameOwnerChanged", (E_DBus_Signal_Cb)_dbus_poll, NULL);
|
||||
|
||||
msg = dbus_message_new_method_call(PULSE_BUS, PULSE_PATH, PULSE_INTERFACE, "suuuuuup");
|
||||
e_dbus_method_call_send(dbus, msg, NULL, (E_DBus_Callback_Func)_dbus_test, NULL, -1, NULL); /* test for not running pulse */
|
||||
dbus_message_unref(msg);
|
||||
pulse_free(conn);
|
||||
conn = NULL;
|
||||
pulse_shutdown();
|
||||
return EINA_TRUE;
|
||||
}
|
||||
ph = ecore_event_handler_add(PULSE_EVENT_CONNECTED, (Ecore_Event_Handler_Cb)_pulse_connected, conn);
|
||||
pch = ecore_event_handler_add(PULSE_EVENT_CHANGE, (Ecore_Event_Handler_Cb)_pulse_update, conn);
|
||||
pdh = ecore_event_handler_add(PULSE_EVENT_DISCONNECTED, (Ecore_Event_Handler_Cb)_pulse_disconnected, conn);
|
||||
return EINA_TRUE;
|
||||
error:
|
||||
pulse_free(conn);
|
||||
conn = NULL;
|
||||
pulse_shutdown();
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_shutdown(void)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
EINA_LIST_FREE(sinks, sink)
|
||||
pulse_sink_free(sink);
|
||||
|
||||
pulse_free(conn);
|
||||
conn = NULL;
|
||||
ecore_event_handler_del(ph);
|
||||
ph = NULL;
|
||||
ecore_event_handler_del(pch);
|
||||
pch = NULL;
|
||||
ecore_event_handler_del(pdh);
|
||||
pdh = NULL;
|
||||
if (dbus_handler)
|
||||
{
|
||||
e_dbus_signal_handler_del(dbus, dbus_handler);
|
||||
dbus_handler = NULL;
|
||||
}
|
||||
if (dbus)
|
||||
{
|
||||
e_dbus_connection_close(dbus);
|
||||
dbus = NULL;
|
||||
e_dbus_shutdown();
|
||||
}
|
||||
pulse_shutdown();
|
||||
}
|
||||
|
||||
E_Mixer_System *
|
||||
e_mixer_pulse_new(const char *name)
|
||||
{
|
||||
return (E_Mixer_System*)_pulse_sink_find(name);
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_del(E_Mixer_System *self __UNUSED__)
|
||||
{
|
||||
}
|
||||
|
||||
Eina_List *
|
||||
e_mixer_pulse_get_cards(void)
|
||||
{
|
||||
Eina_List *l, *ret = NULL;
|
||||
Pulse_Sink *sink;
|
||||
|
||||
EINA_LIST_FOREACH(sinks, l, sink)
|
||||
ret = eina_list_append(ret, pulse_sink_name_get(sink));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_free_cards(Eina_List *cards)
|
||||
{
|
||||
eina_list_free(cards);
|
||||
}
|
||||
|
||||
const char *
|
||||
e_mixer_pulse_get_default_card(void)
|
||||
{
|
||||
if (eina_list_data_get(sinks))
|
||||
return eina_stringshare_ref(pulse_sink_name_get(eina_list_last(sinks)->data));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
e_mixer_pulse_get_card_name(const char *card)
|
||||
{
|
||||
Pulse_Sink *sink;
|
||||
sink = _pulse_sink_find(card);
|
||||
return eina_stringshare_add(pulse_sink_name_get(sink));
|
||||
}
|
||||
|
||||
Eina_List *
|
||||
e_mixer_pulse_get_channels(E_Mixer_System *self)
|
||||
{
|
||||
uintptr_t id;
|
||||
Eina_List *ret = NULL;
|
||||
|
||||
for (id=0; id < pulse_sink_channels_count((void*)self); id++)
|
||||
ret = eina_list_append(ret, (void*)id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_free_channels(Eina_List *channels)
|
||||
{
|
||||
eina_list_free(channels);
|
||||
}
|
||||
|
||||
Eina_List *
|
||||
e_mixer_pulse_get_channels_names(E_Mixer_System *self)
|
||||
{
|
||||
return pulse_sink_channel_names_get((void*)self);;
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_free_channels_names(Eina_List *channels_names)
|
||||
{
|
||||
const char *str;
|
||||
EINA_LIST_FREE(channels_names, str)
|
||||
eina_stringshare_del(str);
|
||||
}
|
||||
|
||||
const char *
|
||||
e_mixer_pulse_get_default_channel_name(E_Mixer_System *self __UNUSED__)
|
||||
{
|
||||
return eina_stringshare_add("Front");
|
||||
}
|
||||
|
||||
E_Mixer_Channel *
|
||||
e_mixer_pulse_get_channel_by_name(E_Mixer_System *self, const char *name)
|
||||
{
|
||||
unsigned int x;
|
||||
x = pulse_sink_channel_name_get_id((void*)self, name);
|
||||
if (x == UINT_MAX) return (intptr_t*)-2;
|
||||
return (uintptr_t*)((uintptr_t)x);
|
||||
}
|
||||
|
||||
void
|
||||
e_mixer_pulse_channel_del(E_Mixer_Channel *channel __UNUSED__)
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
e_mixer_pulse_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel)
|
||||
{
|
||||
if (channel == (E_Mixer_Channel *)-2) return NULL;
|
||||
return pulse_sink_channel_id_get_name((void*)self, (uintptr_t)channel);
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right)
|
||||
{
|
||||
double volume;
|
||||
|
||||
volume = pulse_sink_channel_volume_get((void*)self, (uintptr_t)channel);
|
||||
if (left) *left = (int)volume;
|
||||
if (right) *right = (int)volume;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right)
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
id = pulse_sink_channel_volume_set(conn, (void*)self, (uintptr_t)channel, (left + right) / 2);
|
||||
if (!id) return 0;
|
||||
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_result_cb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_can_mute(E_Mixer_System *self __UNUSED__, E_Mixer_Channel *channel __UNUSED__)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel __UNUSED__, int *mute)
|
||||
{
|
||||
if (mute) *mute = pulse_sink_muted_get((void*)self);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel __UNUSED__, int mute)
|
||||
{
|
||||
uint32_t idx, id;
|
||||
|
||||
idx = pulse_sink_idx_get((void*)self);
|
||||
id = pulse_sink_mute_set(conn, pulse_sink_idx_get((void*)self), mute);
|
||||
if (!id) return 0;
|
||||
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_result_cb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state)
|
||||
{
|
||||
double vol;
|
||||
|
||||
if (!state) return 0;
|
||||
|
||||
vol = pulse_sink_channel_volume_get((void*)self, (uintptr_t)channel);
|
||||
state->mute = pulse_sink_muted_get((void*)self);
|
||||
state->left = state->right = (int)vol;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state)
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
id = pulse_sink_channel_volume_set(conn, (void*)self, (uintptr_t)channel, (state->left + state->right) / 2);
|
||||
if (!id) return 0;
|
||||
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_result_cb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
e_mixer_pulse_has_capture(E_Mixer_System *self __UNUSED__, E_Mixer_Channel *channel __UNUSED__)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
#include "pa.h"
|
||||
|
||||
uint8_t *
|
||||
tag_uint32(Pulse_Tag *tag, uint32_t val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
*ret = PA_TAG_U32;
|
||||
val = htonl(val);
|
||||
memcpy(ret + 1, &val, sizeof(val));
|
||||
ret += PA_TAG_SIZE_U32;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_uint32(Pulse_Tag *tag, uint32_t *val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if ((*ret != PA_TAG_U32) && (*ret != PA_TAG_VOLUME)) return 0;
|
||||
memcpy(val, ret + 1, sizeof(uint32_t));
|
||||
*val = ntohl(*val);
|
||||
ret += PA_TAG_SIZE_U32;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_bool(Pulse_Tag *tag, Eina_Bool val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
*ret = (uint8_t)(val ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
|
||||
ret += PA_TAG_SIZE_BOOLEAN;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_bool(Pulse_Tag *tag, Eina_Bool *val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if ((*ret != PA_TAG_BOOLEAN_TRUE) && (*ret != PA_TAG_BOOLEAN_FALSE)) return 0;
|
||||
*val = !!(*ret == PA_TAG_BOOLEAN_TRUE) ? EINA_TRUE : EINA_FALSE;
|
||||
ret += PA_TAG_SIZE_BOOLEAN;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_string(Pulse_Tag *tag, const char *val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (val)
|
||||
{
|
||||
*ret = PA_TAG_STRING;
|
||||
strcpy((char*)ret + 1, val);
|
||||
ret += PA_TAG_SIZE_STRING + strlen(val);
|
||||
tag->size = ret - tag->data;
|
||||
}
|
||||
else
|
||||
*ret = PA_TAG_STRING_NULL, tag->size++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_string(Pulse_Tag *tag, const char **val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
switch (*ret)
|
||||
{
|
||||
case PA_TAG_STRING:
|
||||
eina_stringshare_replace(val, (char*)ret + 1);
|
||||
ret += PA_TAG_SIZE_STRING + strlen(*val);
|
||||
break;
|
||||
case PA_TAG_STRING_NULL:
|
||||
*val = NULL;
|
||||
ret += PA_TAG_SIZE_STRING_NULL;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_sample(Pulse_Tag *tag, pa_sample_spec *spec)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_SAMPLE_SPEC) return 0;
|
||||
|
||||
spec->format = ret[1];
|
||||
spec->channels = ret[2];
|
||||
memcpy(&spec->rate, ret + 3, sizeof(spec->rate));
|
||||
spec->rate = ntohl(spec->rate);
|
||||
ret += PA_TAG_SIZE_SAMPLE_SPEC, tag->size += PA_TAG_SIZE_SAMPLE_SPEC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_channel_map(Pulse_Tag *tag, pa_channel_map *map)
|
||||
{
|
||||
uint8_t *ret;
|
||||
unsigned int x;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_CHANNEL_MAP) return 0;
|
||||
|
||||
map->channels = ret[1];
|
||||
if (map->channels > PA_CHANNELS_MAX) return 0;
|
||||
if (map->channels + 2 + tag->size > tag->dsize) return 0;
|
||||
|
||||
for (ret += 2, x = 0; x < map->channels; ret++, x++)
|
||||
map->map[x] = (int8_t)*ret;
|
||||
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_usec(Pulse_Tag *tag, uint64_t *val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
uint32_t tmp;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_USEC) return 0;
|
||||
|
||||
memcpy(&tmp, ret + 1, 4);
|
||||
*val = (uint64_t)ntohl(tmp) << 32;
|
||||
memcpy(&tmp, ret + 5, 4);
|
||||
*val |= (uint64_t)ntohl(tmp);
|
||||
ret += PA_TAG_SIZE_USEC;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_arbitrary(Pulse_Tag *tag, const void *val, uint32_t size)
|
||||
{
|
||||
uint8_t *ret;
|
||||
uint32_t tmp;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
*ret = PA_TAG_ARBITRARY;
|
||||
tmp = htonl(size);
|
||||
memcpy(ret + 1, &tmp, sizeof(uint32_t));
|
||||
memcpy(ret + PA_TAG_SIZE_U32, val, size);
|
||||
ret += PA_TAG_SIZE_ARBITRARY + size;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_arbitrary(Pulse_Tag *tag, Eina_Binbuf **val)
|
||||
{
|
||||
uint8_t *ret;
|
||||
uint32_t len;
|
||||
|
||||
if (!untag_uint32(tag, &len)) return 0;
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_ARBITRARY) return 0;
|
||||
ret += PA_TAG_SIZE_ARBITRARY;
|
||||
|
||||
*val = eina_binbuf_new();
|
||||
eina_binbuf_append_length(*val, ret, len);
|
||||
ret += len;
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_simple_init(Pulse *conn, Pulse_Tag *tag, uint32_t val, PA_Tag type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PA_TAG_U32:
|
||||
tag_uint32(tag, val);
|
||||
return tag_uint32(tag, conn->tag_count++);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
tag_proplist_foreach(const Eina_Hash *h __UNUSED__, const char *key, const char *val, Pulse_Tag *tag)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
tag_string(tag, key);
|
||||
size = strlen(val) + 1;
|
||||
tag_uint32(tag, size);
|
||||
tag_arbitrary(tag, val, size);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_proplist(Pulse_Tag *tag)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
*ret = PA_TAG_PROPLIST, tag->size++;
|
||||
|
||||
eina_hash_foreach(tag->props, (Eina_Hash_Foreach)tag_proplist_foreach, tag);
|
||||
return tag_string(tag, NULL);
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_proplist(Pulse_Tag *tag, Eina_Hash **props)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_PROPLIST) return 0;
|
||||
|
||||
*props = eina_hash_string_superfast_new((Eina_Free_Cb)eina_binbuf_free);
|
||||
for (++tag->size; *ret != PA_TAG_STRING_NULL && tag->size < tag->dsize - 1;)
|
||||
{
|
||||
const char *key = NULL;
|
||||
Eina_Binbuf *val;
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &key), error);
|
||||
EINA_SAFETY_ON_FALSE_GOTO(untag_arbitrary(tag, &val), error);
|
||||
#if 0
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "key='%%s', val='%%.%zus'", eina_binbuf_length_get(val));
|
||||
DBG(buf, key, eina_binbuf_string_get(val));
|
||||
}
|
||||
#endif
|
||||
eina_hash_add(*props, key, val);
|
||||
eina_stringshare_del(key);
|
||||
ret = tag->data + tag->size;
|
||||
}
|
||||
tag->size++;
|
||||
return ++ret;
|
||||
error:
|
||||
eina_hash_free(*props);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_volume(Pulse_Tag *tag, uint8_t channels, double vol)
|
||||
{
|
||||
uint32_t pa_vol;
|
||||
uint8_t *ret, x;
|
||||
|
||||
if (vol <= 0.0) pa_vol = PA_VOLUME_MUTED;
|
||||
else pa_vol = ((vol * PA_VOLUME_NORM) - (PA_VOLUME_NORM / 2)) / 100;
|
||||
pa_vol = htonl(pa_vol);
|
||||
ret = tag->data + tag->size;
|
||||
*ret++ = PA_TAG_CVOLUME;
|
||||
*ret++ = channels;
|
||||
for (x = 0; x < channels; x++, ret += sizeof(pa_vol))
|
||||
memcpy(ret, &pa_vol, sizeof(pa_vol));
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
tag_cvol(Pulse_Tag *tag, pa_cvolume *c)
|
||||
{
|
||||
uint8_t *ret, x;
|
||||
uint32_t pa_vol;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
*ret++ = PA_TAG_CVOLUME;
|
||||
*ret++ = c->channels;
|
||||
for (x = 0; x < c->channels; x++, ret += sizeof(pa_vol))
|
||||
{
|
||||
pa_vol = htonl(c->values[x]);
|
||||
memcpy(ret, &pa_vol, sizeof(pa_vol));
|
||||
}
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
untag_cvol(Pulse_Tag *tag, pa_cvolume *cvol)
|
||||
{
|
||||
uint32_t pa_vol;
|
||||
uint8_t *ret, x;
|
||||
|
||||
ret = tag->data + tag->size;
|
||||
if (*ret != PA_TAG_CVOLUME) return 0;
|
||||
cvol->channels = ret[1];
|
||||
for (x = 0, ret += 2; x < cvol->channels; x++, ret += sizeof(pa_vol))
|
||||
{
|
||||
memcpy(&pa_vol, ret, sizeof(pa_vol));
|
||||
cvol->values[x] = ntohl(pa_vol);
|
||||
}
|
||||
|
||||
tag->size = ret - tag->data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
tag_finish(Pulse_Tag *tag)
|
||||
{
|
||||
EINA_SAFETY_ON_NULL_RETURN(tag);
|
||||
tag->header[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
|
||||
tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(tag->dsize);
|
||||
}
|
||||
|
||||
void
|
||||
pulse_tag_free(Pulse_Tag *tag)
|
||||
{
|
||||
if (!tag) return;
|
||||
free(tag->data);
|
||||
if (tag->props) eina_hash_free(tag->props);
|
||||
free(tag);
|
||||
}
|
Loading…
Reference in New Issue