full pulseaudio mixer support: no external dependencies

SVN revision: 63557
This commit is contained in:
Mike Blumenkrantz 2011-09-23 07:56:05 +00:00
parent 277cbfdcaf
commit e4b65bce0b
14 changed files with 2901 additions and 75 deletions

View File

@ -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

48
src/modules/mixer/Pulse.h Normal file
View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

162
src/modules/mixer/msg.c Normal file
View File

@ -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;
}

673
src/modules/mixer/pa.c Normal file
View File

@ -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;
}

541
src/modules/mixer/pa.h Normal file
View File

@ -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

131
src/modules/mixer/serial.c Normal file
View File

@ -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;
}

315
src/modules/mixer/sink.c Normal file
View File

@ -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;
}

View File

@ -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;
}

326
src/modules/mixer/tag.c Normal file
View File

@ -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);
}