diff --git a/src/modules/mixer/Makefile.am b/src/modules/mixer/Makefile.am index 54a88551e..dc2b75d5c 100644 --- a/src/modules/mixer/Makefile.am +++ b/src/modules/mixer/Makefile.am @@ -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 diff --git a/src/modules/mixer/Pulse.h b/src/modules/mixer/Pulse.h new file mode 100644 index 000000000..0bc2f2405 --- /dev/null +++ b/src/modules/mixer/Pulse.h @@ -0,0 +1,48 @@ +#ifndef PULSE_H +#define PULSE_H + +#include +#include + +#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 diff --git a/src/modules/mixer/app_mixer.c b/src/modules/mixer/app_mixer.c index 7e44e89bc..80ced48e3 100644 --- a/src/modules/mixer/app_mixer.c +++ b/src/modules/mixer/app_mixer.c @@ -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; diff --git a/src/modules/mixer/conf_gadget.c b/src/modules/mixer/conf_gadget.c index 980f75664..574660740 100644 --- a/src/modules/mixer/conf_gadget.c +++ b/src/modules/mixer/conf_gadget.c @@ -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) { diff --git a/src/modules/mixer/e_mod_main.c b/src/modules/mixer/e_mod_main.c index 89b7b49e3..2675dfe4c 100644 --- a/src/modules/mixer/e_mod_main.c +++ b/src/modules/mixer/e_mod_main.c @@ -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; } diff --git a/src/modules/mixer/e_mod_main.h b/src/modules/mixer/e_mod_main.h index 875199d6e..d345de9ae 100644 --- a/src/modules/mixer/e_mod_main.h +++ b/src/modules/mixer/e_mod_main.h @@ -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 diff --git a/src/modules/mixer/e_mod_system.h b/src/modules/mixer/e_mod_system.h index 89bccade6..dbbf4ff90 100644 --- a/src/modules/mixer/e_mod_system.h +++ b/src/modules/mixer/e_mod_system.h @@ -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 */ diff --git a/src/modules/mixer/msg.c b/src/modules/mixer/msg.c new file mode 100644 index 000000000..450fb08bd --- /dev/null +++ b/src/modules/mixer/msg.c @@ -0,0 +1,162 @@ +#include "pa.h" +#include +#include +#include + +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; +} diff --git a/src/modules/mixer/pa.c b/src/modules/mixer/pa.c new file mode 100644 index 000000000..211152011 --- /dev/null +++ b/src/modules/mixer/pa.c @@ -0,0 +1,673 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/modules/mixer/pa.h b/src/modules/mixer/pa.h new file mode 100644 index 000000000..fdcce31dd --- /dev/null +++ b/src/modules/mixer/pa.h @@ -0,0 +1,541 @@ +#ifndef PA_HACKS_H +#define PA_HACKS_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include "Pulse.h" +#include + +#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 diff --git a/src/modules/mixer/serial.c b/src/modules/mixer/serial.c new file mode 100644 index 000000000..ad1ce278b --- /dev/null +++ b/src/modules/mixer/serial.c @@ -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; +} diff --git a/src/modules/mixer/sink.c b/src/modules/mixer/sink.c new file mode 100644 index 000000000..7959e6b34 --- /dev/null +++ b/src/modules/mixer/sink.c @@ -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; +} diff --git a/src/modules/mixer/sys_pulse.c b/src/modules/mixer/sys_pulse.c new file mode 100644 index 000000000..c7d90d389 --- /dev/null +++ b/src/modules/mixer/sys_pulse.c @@ -0,0 +1,427 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "e_mod_system.h" +#include "e_mod_main.h" +#include +#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; +} diff --git a/src/modules/mixer/tag.c b/src/modules/mixer/tag.c new file mode 100644 index 000000000..60ab7645f --- /dev/null +++ b/src/modules/mixer/tag.c @@ -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); +}