mixer: use the new e_client api to export volume control by app.

We use the pulseaudio backend to export volume control by app.
This commit introduce 3 news shortcuts to control the volume with the current
focused window.

@features
This commit is contained in:
Michaël Bouchaud (yoz) 2016-10-14 12:30:57 +02:00
parent 06ea6cadbd
commit 307f0831ce
4 changed files with 251 additions and 20 deletions

View File

@ -14,6 +14,7 @@
int _e_emix_log_domain;
static Eina_Bool init;
static Eina_List *_client_sinks = NULL;
/* module requirements */
E_API E_Module_Api e_modapi =
@ -62,6 +63,9 @@ struct _Context
E_Action *incr;
E_Action *decr;
E_Action *mute;
E_Action *incr_app;
E_Action *decr_app;
E_Action *mute_app;
} actions;
};
@ -241,6 +245,42 @@ _volume_mute_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
if (emix_config_save_get()) e_config_save_queue();
}
static void
_volume_increase_app_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
E_Client *ec;
ec = e_client_focused_get();
if (ec && ec->volume_control_enabled)
{
e_client_volume_set(ec, ec->volume + VOLUME_STEP);
}
}
static void
_volume_decrease_app_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
E_Client *ec;
ec = e_client_focused_get();
if (ec && ec->volume_control_enabled)
{
e_client_volume_set(ec, ec->volume - VOLUME_STEP);
}
}
static void
_volume_mute_app_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
E_Client *ec;
ec = e_client_focused_get();
if (ec && ec->volume_control_enabled)
{
e_client_volume_mute_set(ec, !ec->mute);
}
}
static void
_actions_register(void)
{
@ -267,6 +307,30 @@ _actions_register(void)
e_action_predef_name_set("Mixer", _("Mute volume"), "volume_mute",
NULL, NULL, 0);
}
mixer_context->actions.incr_app = e_action_add("volume_increase_app");
if (mixer_context->actions.incr_app)
{
mixer_context->actions.incr_app->func.go = _volume_increase_app_cb;
e_action_predef_name_set("Mixer",
_("Increase Volume of Focused Application"),
"volume_increase_app", NULL, NULL, 0);
}
mixer_context->actions.decr_app = e_action_add("volume_decrease_app");
if (mixer_context->actions.decr_app)
{
mixer_context->actions.decr_app->func.go = _volume_decrease_app_cb;
e_action_predef_name_set("Mixer",
_("Decrease Volume of Focused Application"),
"volume_decrease_app", NULL, NULL, 0);
}
mixer_context->actions.mute_app = e_action_add("volume_mute_app");
if (mixer_context->actions.mute_app)
{
mixer_context->actions.mute_app->func.go = _volume_mute_app_cb;
e_action_predef_name_set("Mixer",
_("Mute Volume of Focused Application"),
"volume_mute_app", NULL, NULL, 0);
}
e_comp_canvas_keys_ungrab();
e_comp_canvas_keys_grab();
@ -296,6 +360,30 @@ _actions_unregister(void)
mixer_context->actions.mute = NULL;
}
if (mixer_context->actions.incr_app)
{
e_action_predef_name_del("Mixer",
_("Increase Volume of Focuse Application"));
e_action_del("volume_increase_app");
mixer_context->actions.incr_app = NULL;
}
if (mixer_context->actions.decr_app)
{
e_action_predef_name_del("Mixer",
_("Decrease Volume of Focuse Application"));
e_action_del("volume_decrease_app");
mixer_context->actions.decr_app = NULL;
}
if (mixer_context->actions.incr_app)
{
e_action_predef_name_del("Mixer",
_("Mute Volume of Focuse Application"));
e_action_del("volume_mute_app");
mixer_context->actions.mute_app = NULL;
}
e_comp_canvas_keys_ungrab();
e_comp_canvas_keys_grab();
}
@ -758,6 +846,126 @@ _ready(void)
init = EINA_FALSE;
}
static void
_sink_input_get(int *volume, Eina_Bool *muted, void *data)
{
Emix_Sink_Input *input;
input = data;
if (volume) *volume = input->volume.volumes[0];
if (muted) *muted = input->mute;
}
static void
_sink_input_set(int volume, Eina_Bool muted, void *data)
{
Emix_Sink_Input *input;
input = data;
VOLSET(volume, input->volume, input, emix_sink_input_volume_set);
emix_sink_input_mute_set(input, muted);
}
static int
_sink_input_min_get(void *data)
{
return 0;
}
static int
_sink_input_max_get(void *data)
{
return emix_max_volume_get();
}
static pid_t
_get_ppid(pid_t pid)
{
int fd;
char buf[128];
char *s;
pid_t ppid;
/* Open the status info process file provided by kernel to get the parent
* process id. 'man 5 proc' and go to /proc/[pid]/stat to get information
* about the content of this file.
*/
snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
fd = open(buf, O_RDONLY);
if (fd == -1)
{
ERR("Can't open %s, maybee the process exited.", buf);
return -1;
}
read(fd, buf, sizeof(buf));
buf[sizeof(buf) - 1] = '0';
s = strrchr(buf, ')');
s += 3;
ppid = atoi(s);
close(fd);
return ppid;
}
static void
_sink_input_event(int type, Emix_Sink_Input *input)
{
Eina_List *clients, *l, *ll;
E_Client *ec;
E_Client_Volume_Sink *sink;
pid_t pid;
switch (type)
{
case EMIX_SINK_INPUT_ADDED_EVENT:
pid = input->pid;
while (42)
{
if (pid <= 1 || pid == getpid()) return;
clients = e_client_focus_stack_get();
EINA_LIST_FOREACH(clients, l, ec)
{
if ((ec->netwm.pid == pid) && (!ec->parent))
{
DBG("Sink found the client %s",
e_client_util_name_get(ec));
sink = e_client_volume_sink_new(_sink_input_get,
_sink_input_set,
_sink_input_min_get,
_sink_input_max_get,
input);
e_client_volume_sink_append(ec, sink);
_client_sinks = eina_list_append(_client_sinks, sink);
return;
}
}
pid = _get_ppid(pid);
}
break;
case EMIX_SINK_INPUT_REMOVED_EVENT:
EINA_LIST_FOREACH(_client_sinks, l, sink)
{
if (sink->data == input)
{
e_client_volume_sink_del(sink);
_client_sinks = eina_list_remove_list(_client_sinks, l);
break;
}
}
break;
case EMIX_SINK_INPUT_CHANGED_EVENT:
EINA_LIST_FOREACH(_client_sinks, l, sink)
{
if (sink->data == input)
{
e_client_volume_sink_update(sink);
}
}
break;
}
}
static void
_events_cb(void *data EINA_UNUSED, enum Emix_Event type, void *event_info)
{
@ -774,6 +982,12 @@ _events_cb(void *data EINA_UNUSED, enum Emix_Event type, void *event_info)
case EMIX_READY_EVENT:
_ready();
break;
case EMIX_SINK_INPUT_ADDED_EVENT:
case EMIX_SINK_INPUT_REMOVED_EVENT:
case EMIX_SINK_INPUT_CHANGED_EVENT:
_sink_input_event(type, event_info);
break;
default:
break;
}
@ -892,6 +1106,8 @@ err:
E_API int
e_modapi_shutdown(E_Module *m EINA_UNUSED)
{
E_Client_Volume_Sink *sink;
_actions_unregister();
e_gadcon_provider_unregister((const E_Gadcon_Client_Class *)&_gadcon_class);
@ -901,6 +1117,9 @@ e_modapi_shutdown(E_Module *m EINA_UNUSED)
E_FREE(mixer_context);
}
EINA_LIST_FREE(_client_sinks, sink)
e_client_volume_sink_del(sink);
emix_event_callback_del(_events_cb);
emix_shutdown();
emix_config_shutdown();

View File

@ -24,26 +24,6 @@ _backend_init(const char *back)
return EINA_FALSE;
}
//////////////////////////////////////////////////////////////////////////////
#define VOLSET(vol, srcvol, target, func) \
do { \
Emix_Volume _v; \
int _pvol = srcvol.volumes[0]; \
if ((_pvol > 80) && (_pvol <= 100) && \
(vol > 100) && (vol < 120)) vol = 100; \
_v.channel_count = srcvol.channel_count; \
_v.volumes = calloc(srcvol.channel_count, sizeof(int)); \
if (_v.volumes) { \
unsigned int _i; \
for (_i = 0; _i < _v.channel_count; _i++) _v.volumes[_i] = vol; \
func(target, _v); \
free(_v.volumes); \
} \
} while (0)
//////////////////////////////////////////////////////////////////////////////
static void
_cb_sink_port_change(void *data,
Evas_Object *obj,

View File

@ -308,6 +308,7 @@ _sink_input_cb(pa_context *c EINA_UNUSED, const pa_sink_input_info *info,
Sink_Input *input;
Eina_List *l;
Sink *s;
const char *t;
EINA_SAFETY_ON_NULL_RETURN(ctx);
if (eol < 0)
@ -340,6 +341,11 @@ _sink_input_cb(pa_context *c EINA_UNUSED, const pa_sink_input_info *info,
input->icon = eina_stringshare_add(_icon_from_properties(info->proplist));
ctx->inputs = eina_list_append(ctx->inputs, input);
if ((t = pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_PROCESS_ID)))
{
input->base.pid = atoi(t);
}
if (ctx->cb)
ctx->cb((void *)ctx->userdata, EMIX_SINK_INPUT_ADDED_EVENT,
(Emix_Sink_Input *)input);
@ -353,6 +359,7 @@ _sink_input_changed_cb(pa_context *c EINA_UNUSED,
Sink_Input *input = NULL, *i;
Sink *s = NULL;
Eina_List *l;
const char *t;
EINA_SAFETY_ON_NULL_RETURN(ctx);
if (eol < 0)
@ -393,6 +400,10 @@ _sink_input_changed_cb(pa_context *c EINA_UNUSED,
if (s->idx == (int)info->sink)
input->base.sink = (Emix_Sink *)s;
}
if ((t = pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_PROCESS_ID)))
{
input->base.pid = atoi(t);
}
if (ctx->cb)
ctx->cb((void *)ctx->userdata, EMIX_SINK_INPUT_CHANGED_EVENT,

View File

@ -64,6 +64,7 @@ typedef struct _Emix_Sink_Input {
Emix_Volume volume;
Eina_Bool mute;
Emix_Sink *sink;
pid_t pid;
} Emix_Sink_Input;
typedef struct _Emix_Source {
@ -110,6 +111,26 @@ typedef struct _Emix_Backend {
Evas_Object* (*ebackend_advanced_options_add)(Evas_Object *parent);
} Emix_Backend;
//////////////////////////////////////////////////////////////////////////////
#define VOLSET(vol, srcvol, target, func) \
do { \
Emix_Volume _v; \
int _pvol = srcvol.volumes[0]; \
if ((_pvol > 80) && (_pvol <= 100) && \
(vol > 100) && (vol < 120)) vol = 100; \
_v.channel_count = srcvol.channel_count; \
_v.volumes = calloc(srcvol.channel_count, sizeof(int)); \
if (_v.volumes) { \
unsigned int _i; \
for (_i = 0; _i < _v.channel_count; _i++) _v.volumes[_i] = vol; \
func(target, _v); \
free(_v.volumes); \
} \
} while (0)
//////////////////////////////////////////////////////////////////////////////
E_API Eina_Bool emix_init(void);
E_API void emix_shutdown(void);