mixer - show a mic/recording slider if someothing is and icons

so now display icons for clients/apps that are playing anything back
right now next to the vu meter. also if some app is recording
something, display a slider for mic/input source volume and a list of
icons that are recording too and a vu meter too for this (95% of the
time things wont record so hide it then and only show when something
does).
devs/bu5hm4n/pointer-fix
Carsten Haitzler 1 year ago
parent c5c994658d
commit 9376490ba8
  1. 219
      src/modules/mixer/backend.c
  2. 11
      src/modules/mixer/backend.h
  3. 437
      src/modules/mixer/e_mod_main.c
  4. 14
      src/modules/mixer/lib/backends/alsa/alsa.c
  5. 309
      src/modules/mixer/lib/backends/pulseaudio/pulse.c
  6. 39
      src/modules/mixer/lib/emix.c
  7. 29
      src/modules/mixer/lib/emix.h

@ -43,7 +43,6 @@ static void _sink_input_set(int volume, Eina_Bool muted, void *data);
static int _sink_input_min_get(void *data);
static int _sink_input_max_get(void *data);
static const char *_sink_input_name_get(void *data);
static pid_t _get_ppid(pid_t pid);
static void _sink_input_event(int type, Emix_Sink_Input *input);
static void _events_cb(void *data, enum Emix_Event type, void *event_info);
static Eina_Bool _desklock_cb(void *data, int type, void *info);
@ -85,6 +84,8 @@ static int _notification_id = 0;
static Ecore_Exe *_emixer_exe = NULL;
static Ecore_Event_Handler *_emix_exe_event_del_handler = NULL;
static const Emix_Source *_source_default = NULL;
static E_Action *_action_incr = NULL;
static E_Action *_action_decr = NULL;
static E_Action *_action_mute = NULL;
@ -133,7 +134,7 @@ _notify(const int val)
}
static void
_volume_increase_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
_volume_adjust(int step)
{
unsigned int i;
Emix_Volume volume;
@ -143,19 +144,21 @@ _volume_increase_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
if (!s->volume.channel_count) return;
if (BARRIER_CHECK(s->volume.volumes[0], s->volume.volumes[0] + VOLUME_STEP))
return;
if (step > 0)
{
if (BARRIER_CHECK(s->volume.volumes[0], s->volume.volumes[0] + step))
return;
}
volume.channel_count = s->volume.channel_count;
volume.volumes = calloc(s->volume.channel_count, sizeof(int));
for (i = 0; i < volume.channel_count; i++)
{
if (s->volume.volumes[i] < (emix_max_volume_get()) - VOLUME_STEP)
volume.volumes[i] = s->volume.volumes[i] + VOLUME_STEP;
else if (s->volume.volumes[i] < emix_max_volume_get())
volume.volumes[i] = s->volume.volumes[i] + step;
if (volume.volumes[i] < 0)
volume.volumes[i] = 0;
else if (volume.volumes[i] > emix_max_volume_get())
volume.volumes[i] = emix_max_volume_get();
else
volume.volumes[i] = s->volume.volumes[i];
}
emix_sink_volume_set(s, &volume);
@ -164,27 +167,47 @@ _volume_increase_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
free(volume.volumes);
}
static void
_volume_increase_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
_volume_adjust(VOLUME_STEP);
}
static void
_volume_decrease_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
_volume_adjust(-VOLUME_STEP);
}
static void
_volume_source_adjust(int step)
{
unsigned int i;
Emix_Volume volume;
EINA_SAFETY_ON_NULL_RETURN(_sink_default);
Emix_Sink *s = (Emix_Sink *)_sink_default;
Emix_Source *s = (Emix_Source *)_source_default;
if (!s->volume.channel_count) return;
if (step > 0)
{
if (BARRIER_CHECK(s->volume.volumes[0], s->volume.volumes[0] + step))
return;
}
volume.channel_count = s->volume.channel_count;
volume.volumes = calloc(s->volume.channel_count, sizeof(int));
for (i = 0; i < volume.channel_count; i++)
{
if (s->volume.volumes[i] > VOLUME_STEP)
volume.volumes[i] = s->volume.volumes[i] - VOLUME_STEP;
else if (s->volume.volumes[i] < VOLUME_STEP)
volume.volumes[i] = s->volume.volumes[i] + step;
if (volume.volumes[i] < 0)
volume.volumes[i] = 0;
else
volume.volumes[i] = s->volume.volumes[i];
else if (volume.volumes[i] > emix_max_volume_get())
volume.volumes[i] = emix_max_volume_get();
}
emix_sink_volume_set((Emix_Sink *)_sink_default, &volume);
emix_source_volume_set(s, &volume);
emix_config_save_state_get();
if (emix_config_save_get()) e_config_save_queue();
free(volume.volumes);
@ -497,8 +520,8 @@ _sink_input_name_get(void *data)
return input->name;
}
static pid_t
_get_ppid(pid_t pid)
EINTERN pid_t
backend_util_get_ppid(pid_t pid)
{
int fd;
char buf[128];
@ -564,7 +587,7 @@ _sink_input_event(int type, Emix_Sink_Input *input)
}
}
if (found) break;
pid = _get_ppid(pid);
pid = backend_util_get_ppid(pid);
}
break;
case EMIX_SINK_INPUT_REMOVED_EVENT:
@ -589,6 +612,75 @@ _sink_input_event(int type, Emix_Sink_Input *input)
}
}
static void
_source_event(int type, Emix_Source *source)
{
const Eina_List *l;
if (type == EMIX_SOURCE_REMOVED_EVENT)
{
if (source == _source_default)
{
l = emix_sources_get();
if (l) _source_default = l->data;
else _source_default = NULL;
if (emix_config_save_get()) e_config_save_queue();
_backend_changed();
}
}
else if (type == EMIX_SOURCE_CHANGED_EVENT)
{
/* If pulseaudio changed the default sink, swap the UI to display it
instead of previously selected sink */
if (source->default_source)
_source_default = source;
if (_source_default == source)
{
static int prev_vol = -1;
int vol;
_backend_changed();
if (source->mute || !source->volume.channel_count) vol = 0;
else vol = source->volume.volumes[0];
if (vol != prev_vol)
{
// _notify(vol);
prev_vol = vol;
}
}
}
else
{
DBG("Source added");
}
/*
Only safe the state if we are not in init mode,
If we are in init mode, this is a result of the restore call.
Restore iterates over a list of sinks which would get deleted in the
save_state_get call.
*/
if (!_backend_init_flag)
{
emix_config_save_state_get();
if (emix_config_save_get()) e_config_save_queue();
// ecore_event_add(E_EVENT_MIXER_SINKS_CHANGED, NULL, NULL, NULL);
}
}
static void
_source_output_event(int type, Emix_Source_Output *output EINA_UNUSED)
{
switch (type)
{
case EMIX_SOURCE_OUTPUT_ADDED_EVENT:
break;
case EMIX_SOURCE_OUTPUT_REMOVED_EVENT:
break;
case EMIX_SOURCE_OUTPUT_CHANGED_EVENT:
break;
}
}
static void
_events_cb(void *data EINA_UNUSED, enum Emix_Event type, void *event_info)
{
@ -610,6 +702,16 @@ _events_cb(void *data EINA_UNUSED, enum Emix_Event type, void *event_info)
case EMIX_SINK_INPUT_CHANGED_EVENT:
_sink_input_event(type, event_info);
break;
case EMIX_SOURCE_ADDED_EVENT:
case EMIX_SOURCE_CHANGED_EVENT:
case EMIX_SOURCE_REMOVED_EVENT:
_source_event(type, event_info);
break;
case EMIX_SOURCE_OUTPUT_ADDED_EVENT:
case EMIX_SOURCE_OUTPUT_CHANGED_EVENT:
case EMIX_SOURCE_OUTPUT_REMOVED_EVENT:
_source_output_event(type, event_info);
break;
default:
break;
@ -988,7 +1090,7 @@ _e_client_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
_client_sinks = eina_list_append(_client_sinks, sink);
return ECORE_CALLBACK_PASS_ON;
}
pid = _get_ppid(pid);
pid = backend_util_get_ppid(pid);
}
}
return ECORE_CALLBACK_PASS_ON;
@ -1186,6 +1288,9 @@ backend_init(void)
if (emix_sink_default_support())
_sink_default = emix_sink_default_get();
if (emix_source_default_support())
_source_default = emix_source_default_get();
_actions_register();
_border_hook = e_int_client_menu_hook_add(_bd_hook, NULL);
@ -1348,3 +1453,77 @@ backend_sink_default_get(void)
return _sink_default;
}
//////////////////////////////////////////////////////////////////////////////
EINTERN Eina_Bool
backend_source_active_get(void)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(_source_default, EINA_FALSE);
if (emix_source_outputs_get()) return EINA_TRUE;
return EINA_FALSE;
}
EINTERN void
backend_source_volume_decrease(void)
{
EINA_SAFETY_ON_NULL_RETURN(_source_default);
_volume_source_adjust(-VOLUME_STEP);
}
EINTERN void
backend_source_volume_increase(void)
{
EINA_SAFETY_ON_NULL_RETURN(_source_default);
_volume_source_adjust(VOLUME_STEP);
}
EINTERN void
backend_source_mute_set(Eina_Bool mute)
{
EINA_SAFETY_ON_NULL_RETURN(_source_default);
DBG("Source default mute set %d", mute);
emix_source_mute_set((Emix_Source *)_source_default, mute);
emix_config_save_state_get();
if (emix_config_save_get()) e_config_save_queue();
}
EINTERN Eina_Bool
backend_source_mute_get(void)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(_source_default, EINA_FALSE);
return _source_default->mute;
}
EINTERN void
backend_source_volume_set(unsigned int volume)
{
EINA_SAFETY_ON_NULL_RETURN(_source_default);
DBG("Sink default mute set %d", volume);
VOLSET(volume, ((Emix_Source *)_source_default)->volume,
(Emix_Source *)_source_default, emix_source_volume_set);
emix_config_save_state_get();
if (emix_config_save_get()) e_config_save_queue();
}
EINTERN unsigned int
backend_source_volume_get(void)
{
unsigned int volume = 0, i;
EINA_SAFETY_ON_NULL_RETURN_VAL(_source_default, 0);
for (i = 0; i < _source_default->volume.channel_count; i++)
volume += _source_default->volume.volumes[i];
if (_source_default->volume.channel_count)
volume = volume / _source_default->volume.channel_count;
DBG("Source default volume get %d", volume);
return volume;
}
EINTERN const Emix_Source *
backend_source_default_get(void)
{
return _source_default;
}

@ -21,4 +21,15 @@ EINTERN Eina_Bool backend_mute_get(void);
EINTERN void backend_sink_default_set(const Emix_Sink *s);
EINTERN const Emix_Sink *backend_sink_default_get(void);
EINTERN Eina_Bool backend_source_active_get(void);
EINTERN void backend_source_volume_decrease(void);
EINTERN void backend_source_volume_increase(void);
EINTERN void backend_source_mute_set(Eina_Bool mute);
EINTERN Eina_Bool backend_source_mute_get(void);
EINTERN void backend_source_volume_set(unsigned int volume);
EINTERN unsigned int backend_source_volume_get(void);
EINTERN const Emix_Source *backend_source_default_get(void);
EINTERN pid_t backend_util_get_ppid(pid_t pid);
#endif /* MIXER_GADGET_BACKEND */

@ -51,6 +51,7 @@ typedef struct _Mon_Data Mon_Data;
struct _Mon_Data
{
Emix_Sink *sink;
Emix_Source *source;
Evas_Object *vu;
Ecore_Animator *animator;
float samp_max;
@ -70,9 +71,19 @@ struct _Instance
Evas_Object *gadget;
Evas_Object *list;
Evas_Object *slider;
Evas_Object *slider_ic;
Evas_Object *check;
Evas_Object *vu;
Evas_Object *playback_box;
Evas_Object *recbox;
Evas_Object *recslider;
Evas_Object *reccheck;
Evas_Object *recbx;
Evas_Object *recic;
Evas_Object *recvu;
Evas_Object *recording_box;
Mon_Data mon_data;
Mon_Data recmon_data;
};
static Context *mixer_context = NULL;
@ -122,6 +133,29 @@ _sink_icon_find(const char *name)
static void _sink_unmonitor(Instance *inst, Emix_Sink *s);
static void _sink_monitor(Instance *inst, Emix_Sink *s);
static void _source_unmonitor(Instance *inst, Emix_Source *s);
static void _source_monitor(Instance *inst, Emix_Source *s);
static void _popup_playback_box_refill(Instance *inst);
static void _popup_recording_fill(Instance *inst);
static void
_cb_emix_event(void *data, enum Emix_Event event, void *event_info EINA_UNUSED)
{
Instance *inst = data;
if ((event == EMIX_SINK_INPUT_ADDED_EVENT) ||
(event == EMIX_SINK_INPUT_REMOVED_EVENT))
{
_popup_playback_box_refill(inst);
}
else if ((event == EMIX_SOURCE_OUTPUT_ADDED_EVENT) ||
(event == EMIX_SOURCE_OUTPUT_REMOVED_EVENT))
{
_popup_recording_fill(inst);
}
}
static Eina_Bool
_cb_emix_monitor_update(void *data)
{
@ -171,9 +205,70 @@ _cb_emix_sink_monitor_event(void *data, enum Emix_Event event, void *event_info)
}
}
static Eina_Bool
_cb_emix_source_monitor_update(void *data)
{
Mon_Data *md = data;
if (md->mon_update == 0)
{
md->mon_skips++;
if (md->mon_skips > 5)
{
elm_progressbar_value_set(md->vu, 0.0);
md->animator = NULL;
return EINA_FALSE;
}
return EINA_TRUE;
}
elm_progressbar_value_set(md->vu, md->samp_max);
md->mon_update = 0;
md->samp_max = 0;
md->mon_skips = 0;
md->mon_samps = 0;
return EINA_TRUE;
}
static void
_cb_emix_source_monitor_event(void *data, enum Emix_Event event, void *event_info)
{
Mon_Data *md = data;
Emix_Source *source = event_info;
if (source != md->source) return;
if (event == EMIX_SOURCE_MONITOR_EVENT)
{
unsigned int i, num = source->mon_num * 2;
float samp, max = 0.0;
for (i = 0; i < num; i++)
{
samp = fabs(source->mon_buf[i]);
if (samp > max) max = samp;
}
md->mon_samps += num;
if (md->samp_max < max) md->samp_max = max;
md->mon_update++;
if (!md->animator)
md->animator = ecore_animator_add(_cb_emix_source_monitor_update, md);
}
}
static void
_mixer_popup_update(Instance *inst, int mute, int vol)
{
Emix_Sink *s;
char *icname = NULL;
s = (Emix_Sink *)backend_sink_default_get();
if (s)
{
if (s->name) icname = _sink_icon_find(s->name);
if (!icname) icname = strdup("audio-volume");
elm_icon_standard_set(inst->slider_ic, icname);
free(icname);
}
elm_check_state_set(inst->check, !!mute);
elm_slider_value_set(inst->slider, vol);
}
@ -235,9 +330,18 @@ static void
_popup_del(Instance *inst)
{
inst->slider = NULL;
inst->slider_ic = NULL;
inst->check = NULL;
inst->list = NULL;
inst->vu = NULL;
inst->playback_box = NULL;
inst->recbox = NULL;
inst->recslider = NULL;
inst->reccheck = NULL;
inst->recbx = NULL;
inst->recvu = NULL;
inst->recording_box = NULL;
emix_event_callback_del(_cb_emix_event, inst);
if (inst->mon_data.sink) _sink_unmonitor(inst, inst->mon_data.sink);
E_FREE_FUNC(inst->popup, e_object_del);
}
@ -321,6 +425,39 @@ _sink_monitor(Instance *inst, Emix_Sink *s)
emix_sink_monitor(md->sink, EINA_TRUE);
}
static void
_source_unmonitor(Instance *inst, Emix_Source *s)
{
Mon_Data *md = &(inst->recmon_data);
if (md->source != s) return;
emix_event_callback_del(_cb_emix_source_monitor_event, md);
if (md->animator)
{
ecore_animator_del(md->animator);
md->animator = NULL;
}
emix_source_monitor(md->source, EINA_FALSE);
md->source = NULL;
md->vu = NULL;
md->mon_update = 0;
md->samp_max = 0;
md->mon_skips = 0;
md->mon_samps = 0;
}
static void
_source_monitor(Instance *inst, Emix_Source *s)
{
Mon_Data *md = &(inst->recmon_data);
if (md->source == s) return;
if (md->source) _source_unmonitor(inst, md->source);
md->source = s;
md->vu = inst->recvu;
emix_event_callback_add(_cb_emix_source_monitor_event, md);
emix_source_monitor(md->source, EINA_TRUE);
}
static Eina_Bool
_mixer_sinks_changed(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
@ -372,13 +509,260 @@ _cb_vu_format_cb(double v EINA_UNUSED)
return "";
}
static void
_popup_playback_box_refill(Instance *inst)
{
Evas_Object *ic;
Eina_List *children, *l, *del_list = NULL;
Eina_List *playbacks;
Evas_Object *o;
Emix_Sink_Input *s;
int num = 0;
children = elm_box_children_get(inst->playback_box);
EINA_LIST_FOREACH(children, l, o)
{
// skip first item - it's the rect spacer
if (l->prev) del_list = eina_list_append(del_list, o);
}
EINA_LIST_FREE(del_list, o) evas_object_del(o);
playbacks = (Eina_List *)emix_sink_inputs_get();
printf("MX: playbacks %p\n", playbacks);
EINA_LIST_FOREACH(playbacks, l, s)
{
E_Client *ec = NULL;
Eina_List *clients, *ll;
pid_t pid;
ic = NULL;
pid = s->pid;
printf("MX: + PID %i\n", pid);
for (;;)
{
if ((pid <= 1) || (pid == getpid())) return;
clients = e_client_focus_stack_get();
EINA_LIST_FOREACH(clients, ll, ec)
{
if ((ec->netwm.pid == pid) && (!ec->parent))
{
ic = e_client_icon_add(ec, e_comp->evas);
break;
}
}
pid = backend_util_get_ppid(pid);
if (ic) break;
}
if (!ic)
{
if (s->icon)
{
ic = elm_icon_add(e_comp->elm);
elm_icon_standard_set(ic, s->icon);
}
}
if (ic)
{
printf("MX: + %p\n", ic);
evas_object_size_hint_min_set(ic, 20 * e_scale, 20 * e_scale);
elm_box_pack_end(inst->playback_box, ic);
evas_object_show(ic);
}
// max 8 app icons
num++;
if (num > 8) break;
}
}
static void
_reccheck_changed_cb(void *data EINA_UNUSED, Evas_Object *obj,
void *event EINA_UNUSED)
{
backend_source_mute_set(elm_check_state_get(obj));
}
static void
_recslider_changed_cb(void *data EINA_UNUSED, Evas_Object *obj,
void *event EINA_UNUSED)
{
int val;
val = (int)elm_slider_value_get(obj);
backend_source_volume_set(val);
}
static void
_popup_recording_box_refill(Instance *inst)
{
Evas_Object *ic;
Eina_List *children, *l, *del_list = NULL;
Eina_List *recordings;
Evas_Object *o;
Emix_Source_Output *s;
int num = 0;
children = elm_box_children_get(inst->recording_box);
EINA_LIST_FOREACH(children, l, o)
{
// skip first item - it's the rect spacer
if (l->prev) del_list = eina_list_append(del_list, o);
}
EINA_LIST_FREE(del_list, o) evas_object_del(o);
recordings = (Eina_List *)emix_source_outputs_get();
EINA_LIST_FOREACH(recordings, l, s)
{
E_Client *ec = NULL;
Eina_List *clients, *ll;
pid_t pid;
ic = NULL;
pid = s->pid;
for (;;)
{
if ((pid <= 1) || (pid == getpid())) return;
clients = e_client_focus_stack_get();
EINA_LIST_FOREACH(clients, ll, ec)
{
if ((ec->netwm.pid == pid) && (!ec->parent))
{
ic = e_client_icon_add(ec, e_comp->evas);
break;
}
}
pid = backend_util_get_ppid(pid);
if (ic) break;
}
if (!ic)
{
if (s->icon)
{
ic = elm_icon_add(e_comp->elm);
elm_icon_standard_set(ic, s->icon);
}
}
if (ic)
{
evas_object_size_hint_min_set(ic, 20 * e_scale, 20 * e_scale);
elm_box_pack_end(inst->recording_box, ic);
evas_object_show(ic);
}
// max 8 app icons
num++;
if (num > 8) break;
}
}
static void
_popup_recording_fill(Instance *inst)
{
Emix_Source *ss;
if (inst->recording_box) evas_object_del(inst->recording_box);
if (inst->recvu) evas_object_del(inst->recvu);
if (inst->recic) evas_object_del(inst->recic);
if (inst->recbx) evas_object_del(inst->recbx);
if (inst->reccheck) evas_object_del(inst->reccheck);
if (inst->recslider) evas_object_del(inst->recslider);
inst->recslider = NULL;
inst->reccheck = NULL;
inst->recbx = NULL;
inst->recvu = NULL;
inst->recording_box = NULL;
ss = (Emix_Source *)backend_source_default_get();
if (ss) _source_unmonitor(inst, ss);
if (backend_source_active_get())
{
Evas_Object *bx, *r, *slider, *ic;
bx = elm_box_add(e_comp->elm);
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5);
elm_box_pack_end(inst->recbox, bx);
evas_object_show(bx);
inst->recvu = elm_progressbar_add(e_comp->elm);
elm_progressbar_unit_format_function_set(inst->recvu, _cb_vu_format_cb, NULL);
evas_object_size_hint_weight_set(inst->recvu, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(inst->recvu, EVAS_HINT_FILL, 0.5);
elm_box_pack_end(bx, inst->recvu);
evas_object_show(inst->recvu);
inst->recording_box = elm_box_add(e_comp->elm);
elm_box_horizontal_set(inst->recording_box, EINA_TRUE);
evas_object_size_hint_weight_set(inst->recording_box, 0.0, 0.0);
evas_object_size_hint_align_set(inst->recording_box, 1.0, 0.5);
elm_box_pack_end(bx, inst->recording_box);
evas_object_show(inst->recording_box);
r = evas_object_rectangle_add(evas_object_evas_get(e_comp->elm));
evas_object_size_hint_min_set(r, 0, 20 * e_scale);
evas_object_color_set(r, 0, 0, 0, 0);
elm_box_pack_end(inst->recording_box, r);
_popup_recording_box_refill(inst);
bx = elm_box_add(e_comp->elm);
inst->recbx = bx;
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.0);
elm_box_pack_end(inst->recbox, bx);
evas_object_show(bx);
ss = (Emix_Source *)backend_source_default_get();
if (ss)
{
ic = elm_icon_add(e_comp->elm);
inst->recic = ic;
evas_object_size_hint_min_set(ic, 20 * e_scale, 20 * e_scale);
elm_icon_standard_set(ic, "audio-input-microphone");
elm_box_pack_end(bx, ic);
evas_object_show(ic);
}
slider = elm_slider_add(e_comp->elm);
inst->recslider = slider;
elm_slider_span_size_set(slider, 128 * elm_config_scale_get());
elm_slider_unit_format_set(slider, "%1.0f");
elm_slider_indicator_format_set(slider, "%1.0f");
evas_object_size_hint_align_set(slider, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(slider, EVAS_HINT_EXPAND, 0.0);
evas_object_show(slider);
elm_slider_min_max_set(slider, 0.0, emix_max_volume_get());
evas_object_smart_callback_add(slider, "changed", _recslider_changed_cb, NULL);
elm_slider_value_set(slider, backend_source_volume_get());
elm_box_pack_end(bx, slider);
evas_object_show(slider);
inst->reccheck = elm_check_add(e_comp->elm);
evas_object_size_hint_align_set(inst->reccheck, 0.5, EVAS_HINT_FILL);
elm_object_text_set(inst->reccheck, _("Mute"));
elm_check_state_set(inst->reccheck, backend_source_mute_get());
evas_object_smart_callback_add(inst->reccheck, "changed", _reccheck_changed_cb, NULL);
elm_box_pack_end(bx, inst->reccheck);
evas_object_show(inst->reccheck);
if (ss) _source_monitor(inst, ss);
}
}
static void
_popup_new(Instance *inst)
{
Evas_Object *button, *list, *slider, *bx, *ic;
Evas_Object *button, *list, *slider, *bx, *ic, *r;
Emix_Sink *s;
Eina_List *l;
Elm_Object_Item *default_it = NULL;
char *icname = NULL;
emix_event_callback_add(_cb_emix_event, inst);
inst->popup = e_gadcon_popup_new(inst->gcc, 0);
list = elm_box_add(e_comp->elm);
@ -391,19 +775,55 @@ _popup_new(Instance *inst)
elm_box_pack_end(list, inst->list);
bx = elm_box_add(e_comp->elm);
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5);
elm_box_pack_end(list, bx);
evas_object_show(bx);
inst->vu = elm_progressbar_add(e_comp->elm);
elm_progressbar_unit_format_function_set(inst->vu, _cb_vu_format_cb, NULL);
evas_object_size_hint_weight_set(inst->vu, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(inst->vu, EVAS_HINT_FILL, 0.0);
elm_box_pack_end(list, inst->vu);
evas_object_size_hint_align_set(inst->vu, EVAS_HINT_FILL, 0.5);
elm_box_pack_end(bx, inst->vu);
evas_object_show(inst->vu);
inst->playback_box = elm_box_add(e_comp->elm);
elm_box_horizontal_set(inst->playback_box, EINA_TRUE);
evas_object_size_hint_weight_set(inst->playback_box, 0.0, 0.0);
evas_object_size_hint_align_set(inst->playback_box, 1.0, 0.5);
elm_box_pack_end(bx, inst->playback_box);
evas_object_show(inst->playback_box);
r = evas_object_rectangle_add(evas_object_evas_get(e_comp->elm));
evas_object_size_hint_min_set(r, 0, 20 * e_scale);
evas_object_color_set(r, 0, 0, 0, 0);
elm_box_pack_end(inst->playback_box, r);
_popup_playback_box_refill(inst);
bx = elm_box_add(e_comp->elm);
elm_box_horizontal_set(bx, EINA_TRUE);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.0);
elm_box_pack_end(list, bx);
evas_object_show(bx);
s = (Emix_Sink *)backend_sink_default_get();
if (s)
{
if (s->name) icname = _sink_icon_find(s->name);
if (!icname) icname = strdup("audio-volume");
ic = elm_icon_add(e_comp->elm);
evas_object_size_hint_min_set(ic, 20 * e_scale, 20 * e_scale);
elm_icon_standard_set(ic, icname);
free(icname);
elm_box_pack_end(bx, ic);
evas_object_show(ic);
inst->slider_ic = ic;
}
slider = elm_slider_add(e_comp->elm);
inst->slider = slider;
elm_slider_span_size_set(slider, 128 * elm_config_scale_get());
@ -427,6 +847,14 @@ _popup_new(Instance *inst)
elm_box_pack_end(bx, inst->check);
evas_object_show(inst->check);
inst->recbox = elm_box_add(e_comp->elm);
evas_object_size_hint_align_set(inst->recbox, EVAS_HINT_FILL, 0.5);
evas_object_size_hint_weight_set(inst->recbox, EVAS_HINT_EXPAND, 0.0);
elm_box_pack_end(list, inst->recbox);
evas_object_show(inst->recbox);
_popup_recording_fill(inst);
button = elm_button_add(e_comp->elm);
evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0.0);
@ -438,7 +866,6 @@ _popup_new(Instance *inst)
EINA_LIST_FOREACH((Eina_List *)emix_sinks_get(), l, s)
{
Elm_Object_Item *it;
char *icname = NULL;
if (s->name) icname = _sink_icon_find(s->name);
if (!icname) icname = strdup("audio-volume");
@ -456,7 +883,7 @@ _popup_new(Instance *inst)
}
elm_list_go(inst->list);
evas_object_size_hint_min_set(list, 240 * e_scale, 240 * e_scale);
evas_object_size_hint_min_set(list, 240 * e_scale, 280 * e_scale);
e_gadcon_popup_content_set(inst->popup, list);
e_comp_object_util_autoclose(inst->popup->comp_object,

@ -525,22 +525,26 @@ _alsa_backend =
_alsa_sink_volume_set, /*volume_set*/
NULL, /* port set */
_alsa_support, /*change support*/
NULL, /*sink input get*/
NULL,/*sink input mute set*/
NULL,/*sink input volume set*/
NULL,/*sink input sink change*/
NULL, /*sink inputs get*/
NULL, /*sink input mute set*/
NULL, /*sink input volume set*/
NULL, /*sink input sink change*/
_alsa_sources_get,/*source*/
_alsa_support, /* source default support*/
NULL, /*get*/
NULL, /*set*/
_alsa_sources_mute_set,/* source mute set */
_alsa_sources_volume_set, /* source volume set */
NULL, /*source outputs get*/
NULL, /*source output mute set*/
NULL, /*source output volume set*/
NULL, /*source output source change*/
NULL, /* advanced options */
NULL, /* card list */
NULL, /* card profile set */
NULL, /* sink monitor set */
NULL, /* sink input monitor set */
NULL /* ssource monitor set */
NULL /* source monitor set */
};
E_API Emix_Backend *

@ -26,7 +26,7 @@ typedef struct _Context
const void *userdata;
Ecore_Timer *connect;
Eina_List *sinks, *sources, *inputs, *cards;
Eina_List *sinks, *sources, *inputs, *outputs, *cards;
Eina_Bool connected;
} Context;
@ -59,6 +59,13 @@ typedef struct _Source
pa_stream *mon_stream;
} Source;
typedef struct _Source_Output
{
Emix_Source_Output base;
int idx, source_idx;
Eina_Bool running : 1;
} Source_Output;
typedef struct _Profile
{
Emix_Profile base;
@ -168,6 +175,21 @@ _source_del(Source *source)
free(source);
}
static void
_source_output_del(Source_Output *output)
{
unsigned int i;
EINA_SAFETY_ON_NULL_RETURN(output);
free(output->base.volume.volumes);
for(i = 0; i < output->base.volume.channel_count; ++i)
eina_stringshare_del(output->base.volume.channel_names[i]);
free(output->base.volume.channel_names);
eina_stringshare_del(output->base.name);
eina_stringshare_del(output->base.icon);
free(output);
}
static void
_card_del(Card *card)
{
@ -728,6 +750,189 @@ _source_remove_cb(int index, void *data EINA_UNUSED)
}
}
static void
_source_output_state_running_set(Source_Output *output, Eina_Bool running)
{
output->running = running;
}
static void
_source_output_cb(pa_context *c EINA_UNUSED, const pa_source_output_info *info,
int eol, void *userdata EINA_UNUSED)
{
Source_Output *output;
Eina_List *l;
Source *s;
const char *t;
unsigned int i;
EINA_SAFETY_ON_NULL_RETURN(ctx);
if (eol < 0)
{
if (pa_context_errno(c) == PA_ERR_NOENTITY)
return;
ERR("Source output callback failure");
return;
}
if (eol > 0)
return;
if ((info->name) && (!strcmp(info->name, "__e_mon"))) return;
output = calloc(1, sizeof(Source_Output));
EINA_SAFETY_ON_NULL_RETURN(output);
DBG("source output index: %d\nsink input name: %s", info->index,
info->name);
output->idx = info->index;
output->source_idx = info->source;
Eina_Strbuf *output_name;
output_name = eina_strbuf_new();
const char *application = pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_NAME);
if (application)
{
eina_strbuf_append(output_name, application);
eina_strbuf_append(output_name, ":");
eina_strbuf_append(output_name, info->name);
}
else if (info->name)
{
eina_strbuf_append(output_name, info->name);
}
output->base.name = eina_stringshare_add(eina_strbuf_string_get(output_name));
eina_strbuf_free(output_name);
_pa_cvolume_convert(&info->volume, &output->base.volume);
output->base.volume.channel_names = calloc(output->base.volume.channel_count, sizeof(Emix_Channel));
for (i = 0; i < output->base.volume.channel_count; ++i)
output->base.volume.channel_names[i] = eina_stringshare_add(pa_channel_position_to_pretty_string(info->channel_map.map[i]));
output->base.mute = !!info->mute;
EINA_LIST_FOREACH(ctx->sources, l, s)
{
if (s->idx == (int)info->source)
output->base.source = (Emix_Source *)s;
}
output->base.icon = eina_stringshare_add(_icon_from_properties(info->proplist));
ctx->outputs = eina_list_append(ctx->outputs, output);
if ((t = pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_PROCESS_ID)))
{
output->base.pid = atoi(t);
}
if (!info->corked) _source_output_state_running_set(output, EINA_TRUE);
else _source_output_state_running_set(output, EINA_FALSE);
if (ctx->cb)
ctx->cb((void *)ctx->userdata, EMIX_SOURCE_OUTPUT_ADDED_EVENT,
(Emix_Source_Output *)output);
}
static void
_source_output_changed_cb(pa_context *c EINA_UNUSED,
const pa_source_output_info *info, int eol,
void *userdata EINA_UNUSED)
{
Source_Output *output = NULL, *so;
Source *s = NULL;
Eina_List *l;
const char *t;
unsigned int i;
EINA_SAFETY_ON_NULL_RETURN(ctx);
if (eol < 0)
{
if (pa_context_errno(c) == PA_ERR_NOENTITY)
return;
ERR("Source output changed callback failure");
return;
}
if (eol > 0)
return;
if ((info->name) && (!strcmp(info->name, "__e_mon"))) return;
EINA_LIST_FOREACH(ctx->outputs, l, so)
{
if (so->idx == (int)info->index)
{
output = so;
break;
}
}
DBG("source output changed index: %d\n", info->index);
if (!output)
{
output = calloc(1, sizeof(Source_Output));
EINA_SAFETY_ON_NULL_RETURN(output);
ctx->outputs = eina_list_append(ctx->outputs, output);
}
output->idx = info->index;
output->source_idx = info->source;
if (output->base.volume.channel_count != info->volume.channels)
{
for (i = 0; i < output->base.volume.channel_count; ++i)
eina_stringshare_del(output->base.volume.channel_names[i]);
free(output->base.volume.channel_names);
output->base.volume.channel_names = calloc(info->volume.channels, sizeof(Emix_Channel));
}
_pa_cvolume_convert(&info->volume, &output->base.volume);
for (i = 0; i < output->base.volume.channel_count; ++i)
eina_stringshare_replace(&output->base.volume.channel_names[i],
pa_channel_position_to_pretty_string(info->channel_map.map[i]));
output->base.mute = !!info->mute;
EINA_LIST_FOREACH(ctx->sources, l, s)
{
if (s->idx == (int)info->source)
output->base.source = (Emix_Source *)s;
}
if ((t = pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_PROCESS_ID)))
{
output->base.pid = atoi(t);
}
if (!info->corked) _source_output_state_running_set(output, EINA_TRUE);
else _source_output_state_running_set(output, EINA_FALSE);
if (ctx->cb)
ctx->cb((void *)ctx->userdata, EMIX_SOURCE_OUTPUT_CHANGED_EVENT,
(Emix_Source_Output *)output);
}
static void
_source_output_remove_cb(int index, void *data EINA_UNUSED)
{
Source_Output *output;
Eina_List *l;
EINA_SAFETY_ON_NULL_RETURN(ctx);
DBG("Removing source output: %d", index);
EINA_LIST_FOREACH(ctx->outputs, l, output)
{
if (output->idx == index)
{
ctx->outputs = eina_list_remove_list(ctx->outputs, l);
if (ctx->cb)
ctx->cb((void *)ctx->userdata,
EMIX_SOURCE_OUTPUT_REMOVED_EVENT,
(Emix_Source_Output *)output);
_source_output_del(output);
break;
}
}
}
static void
_sink_default_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
void *userdata EINA_UNUSED)
@ -1093,6 +1298,33 @@ _subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_REMOVE)
_source_output_remove_cb(index, data);
else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_NEW)
{
if (!(o = pa_context_get_source_output_info(c, index,
_source_output_cb, data)))
{
ERR("pa_context_get_source_info() failed");
return;
}
pa_operation_unref(o);
}
else
{
if (!(o = pa_context_get_source_output_info(c, index,
_source_output_changed_cb,
data)))
{
ERR("pa_context_get_source_info() failed");
return;
}
pa_operation_unref(o);
}
break;
case PA_SUBSCRIPTION_EVENT_SERVER:
if (!(o = pa_context_get_server_info(c, _server_info_cb,
data)))
@ -1195,6 +1427,14 @@ _pulse_pa_state_cb(pa_context *context, void *data)
}
pa_operation_unref(o);
if (!(o = pa_context_get_source_output_info_list(context, _source_output_cb,
ctx)))
{
ERR("pa_context_get_source_output_info_list() failed");
return;
}
pa_operation_unref(o);
if (!(o = pa_context_get_server_info(context, _server_info_cb,
ctx)))
{
@ -1405,13 +1645,6 @@ _sources_get(void)
return ctx->sources;
}
static const Eina_List *
_sink_inputs_get(void)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
return ctx->inputs;
}
static void
_sink_volume_set(Emix_Sink *sink, Emix_Volume *volume)
{
@ -1437,6 +1670,13 @@ _sink_mute_set(Emix_Sink *sink, Eina_Bool mute)
ERR("pa_context_set_sink_mute() failed");
}
static const Eina_List *
_sink_inputs_get(void)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
return ctx->inputs;
}
static void
_sink_input_volume_set(Emix_Sink_Input *input, Emix_Volume *volume)
{
@ -1479,6 +1719,55 @@ _sink_input_move(Emix_Sink_Input *input, Emix_Sink *sink)
ERR("pa_context_move_sink_input_by_index() failed");
}
static const Eina_List *
_source_outputs_get(void)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
return ctx->outputs;
}
static void
_source_output_volume_set(Emix_Source_Output *output, Emix_Volume *volume)
{
pa_operation* o;
Source_Output *source_output = (Source_Output *)output;
pa_cvolume vol = _emix_volume_convert(volume);
EINA_SAFETY_ON_FALSE_RETURN(ctx && ctx->context && output != NULL);
if (!(o = pa_context_set_source_output_volume(ctx->context,
source_output->idx, &vol,
NULL, NULL)))
ERR("pa_context_set_source_output_volume_by_index() failed");
}
static void
_source_output_mute_set(Emix_Source_Output *output, Eina_Bool mute)
{
pa_operation* o;
Source_Output *source_output = (Source_Output *)output;
EINA_SAFETY_ON_FALSE_RETURN(ctx && ctx->context && output != NULL);
if (!(o = pa_context_set_source_output_mute(ctx->context,
source_output->idx, mute,
NULL, NULL)))
ERR("pa_context_set_source_output_mute() failed");
}
static void
_source_output_move(Emix_Source_Output *output, Emix_Source *source)
{
pa_operation* o;
Source *s = (Source *)source;
Source_Output *i = (Source_Output *)output;
EINA_SAFETY_ON_FALSE_RETURN(ctx && ctx->context && output != NULL
&& source != NULL);
if (!(o = pa_context_move_source_output_by_index(ctx->context,
i->idx, s->idx, NULL,
NULL)))
ERR("pa_context_move_source_output_by_index() failed");
}
static Eina_Bool
_sink_port_set(Emix_Sink *sink, const Emix_Port *port)
{
@ -1868,6 +2157,10 @@ _pulseaudio_backend =
_source_default_set,
_source_mute_set,
_source_volume_set,
_source_outputs_get,
_source_output_mute_set,
_source_output_volume_set,
_source_output_move,
NULL,
_cards_get,
_card_profile_set,

@ -395,6 +395,45 @@ emix_source_volume_set(Emix_Source *source, Emix_Volume *volume)
ctx->loaded->ebackend_source_volume_set(source, volume);
}
const Eina_List*
emix_source_outputs_get(void)
{
EINA_SAFETY_ON_FALSE_RETURN_VAL((ctx && ctx->loaded &&
ctx->loaded->ebackend_source_outputs_get),
NULL);
return ctx->loaded->ebackend_source_outputs_get();
}
void
emix_source_output_mute_set(Emix_Source_Output *output, Eina_Bool mute)
{
EINA_SAFETY_ON_FALSE_RETURN((ctx && ctx->loaded &&
ctx->loaded->ebackend_source_output_mute_set &&
output));
ctx->loaded->ebackend_source_output_mute_set(output, mute);
}
void