From 307f0831ce0be4d8dd075c14d020dc6106f7aa0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Bouchaud=20=28yoz=29?= Date: Fri, 14 Oct 2016 12:30:57 +0200 Subject: [PATCH] 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 --- src/modules/mixer/e_mod_main.c | 219 ++++++++++++++++++ src/modules/mixer/emixer.c | 20 -- .../mixer/lib/backends/pulseaudio/pulse.c | 11 + src/modules/mixer/lib/emix.h | 21 ++ 4 files changed, 251 insertions(+), 20 deletions(-) diff --git a/src/modules/mixer/e_mod_main.c b/src/modules/mixer/e_mod_main.c index ef53d2efb..8c7efb1ec 100644 --- a/src/modules/mixer/e_mod_main.c +++ b/src/modules/mixer/e_mod_main.c @@ -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(); diff --git a/src/modules/mixer/emixer.c b/src/modules/mixer/emixer.c index 61ae83021..5cde88127 100644 --- a/src/modules/mixer/emixer.c +++ b/src/modules/mixer/emixer.c @@ -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, diff --git a/src/modules/mixer/lib/backends/pulseaudio/pulse.c b/src/modules/mixer/lib/backends/pulseaudio/pulse.c index 5d7ab71a8..42f0f9360 100644 --- a/src/modules/mixer/lib/backends/pulseaudio/pulse.c +++ b/src/modules/mixer/lib/backends/pulseaudio/pulse.c @@ -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, diff --git a/src/modules/mixer/lib/emix.h b/src/modules/mixer/lib/emix.h index 1f1ee5e15..29dc17da2 100644 --- a/src/modules/mixer/lib/emix.h +++ b/src/modules/mixer/lib/emix.h @@ -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);