diff --git a/configure.in b/configure.in index 8e9fca779..133117857 100644 --- a/configure.in +++ b/configure.in @@ -262,6 +262,35 @@ AC_SUBST(edje_cc) AC_DEFINE(E_INTERNAL, 1, "This define can be used to wrap internal E stuff, as config.h isn't exported") +dnl Check for Alsa +AC_ARG_ENABLE(alsa, AS_HELP_STRING([--enable-alsa], + [enable support for alsa(default=autodetect)]), + [ enable_alsa=$enableval ], [ enable_alsa=default ]) + +AM_CONDITIONAL(HAVE_ALSA, false) +if test "x$enable_alsa" = "xdefault" || test "x$enable_alsa" = "xyes"; then + PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.8], + [ SOUND_CFLAGS="$ALSA_CFLAGS -DHAVE_ALSA $SOUND_CFLAGS" + SOUND_LDFLAGS="$ALSA_LIBS $SOUND_LDFLAGS" + AM_CONDITIONAL(HAVE_ALSA, true) + have_alsa=yes ], + [ if test "x$enable_alsa" = "xyes"; then + AC_MSG_ERROR([alsa library >= 1.0.8 not found]) + fi + ]) +else + have_alsa=no +fi + +if test "$have_alsa" = "yes"; then + AC_DEFINE(HAVE_ALSA, 1, [Define if the ALSA output plugin should be built]) +else + have_alsa=no +fi + +AC_SUBST(SOUND_CFLAGS) +AC_SUBST(SOUND_LDFLAGS) + AC_OUTPUT([ Makefile enlightenment.spec @@ -375,6 +404,8 @@ src/modules/conf_interaction/Makefile src/modules/conf_interaction/module.desktop src/modules/gadman/Makefile src/modules/gadman/module.desktop +src/modules/mixer/Makefile +src/modules/mixer/module.desktop src/preload/Makefile data/Makefile data/fonts/Makefile diff --git a/data/themes/Makefile.am b/data/themes/Makefile.am index 56a8e8bef..a03529afa 100644 --- a/data/themes/Makefile.am +++ b/data/themes/Makefile.am @@ -62,7 +62,8 @@ default_deskpreview.edc \ default_fontpreview.edc \ default_wizard.edc \ default_toolbar.edc \ -default_slidesel.edc +default_slidesel.edc \ +default_mixer.edc default.edj: Makefile $(EXTRA_DIST) $(EDJE_CC) $(EDJE_FLAGS) \ diff --git a/data/themes/default.edc b/data/themes/default.edc index c7dc249b5..cf64f43c3 100644 --- a/data/themes/default.edc +++ b/data/themes/default.edc @@ -86,4 +86,5 @@ collections { #include "default_wizard.edc" #include "default_toolbar.edc" #include "default_slidesel.edc" +#include "default_mixer.edc" } diff --git a/data/themes/default_mixer.edc b/data/themes/default_mixer.edc new file mode 100644 index 000000000..dbe41f301 --- /dev/null +++ b/data/themes/default_mixer.edc @@ -0,0 +1,246 @@ +images { + image: "e17_mixer_base.png" COMP; + image: "e17_mixer_mute.png" COMP; + image: "e17_mixer_left_low.png" COMP; + image: "e17_mixer_left_medium.png" COMP; + image: "e17_mixer_left_high.png" COMP; + image: "e17_mixer_right_low.png" COMP; + image: "e17_mixer_right_medium.png" COMP; + image: "e17_mixer_right_high.png" COMP; +} + +group { + name: "e/modules/mixer/main"; + max: 128 128; + min: 1 1; + + script { + public message(Msg_Type:type, id, ...) { + if ((type == MSG_INT_SET) && (id == 0)) { + new mute, left, right; + + mute = getarg(2); + left = getarg(3); + right = getarg(4); + + if (mute) + run_program(PROGRAM:"mute"); + else + run_program(PROGRAM:"unmute"); + + if (left <= 0) + run_program(PROGRAM:"left_none"); + else if (left < 33) + run_program(PROGRAM:"left_low"); + else if (left < 66) + run_program(PROGRAM:"left_medium"); + else if (left >= 66) + run_program(PROGRAM:"left_high"); + + if (right <= 0) + run_program(PROGRAM:"right_none"); + else if (right < 33) + run_program(PROGRAM:"right_low"); + else if (right < 66) + run_program(PROGRAM:"right_medium"); + else if (right >= 66) + run_program(PROGRAM:"right_high"); + } + } + } + + parts { + part { + name: "base"; + mouse_events: 0; + type: RECT; + description { + state: "default" 0.0; + color: 255 255 255 0; + aspect: 1 1; + aspect_preference: BOTH; + } + } + + part { + name: "speaker"; + mouse_events: 0; + type: IMAGE; + description { + state: "default" 0.0; + aspect: 1 1; + aspect_preference: BOTH; + rel1.to: "base"; + rel2.to: "base"; + image.normal: "e17_mixer_base.png"; + } + } + + part { + name: "left"; + mouse_events: 0; + type: IMAGE; + description { + state: "default" 0.0; + visible: 0; + aspect: 1 1; + aspect_preference: BOTH; + rel1.to: "base"; + rel2.to: "base"; + image.normal: "e17_mixer_left_low.png"; + } + description { + state: "low" 0.0; + inherit: "default" 0.0; + visible: 1; + } + description { + state: "medium" 0.0; + inherit: "default" 0.0; + visible: 1; + image.normal: "e17_mixer_left_medium.png"; + } + description { + state: "high" 0.0; + inherit: "default" 0.0; + visible: 1; + image.normal: "e17_mixer_left_high.png"; + } + } + + part { + name: "right"; + mouse_events: 0; + type: IMAGE; + description { + state: "default" 0.0; + visible: 0; + aspect: 1 1; + aspect_preference: BOTH; + rel1.to: "base"; + rel2.to: "base"; + image.normal: "e17_mixer_right_low.png"; + } + description { + state: "low" 0.0; + inherit: "default" 0.0; + visible: 1; + } + description { + state: "medium" 0.0; + inherit: "default" 0.0; + visible: 1; + image.normal: "e17_mixer_right_medium.png"; + } + description { + state: "high" 0.0; + inherit: "default" 0.0; + visible: 1; + image.normal: "e17_mixer_right_high.png"; + } + } + + part { + name: "mute"; + mouse_events: 0; + type: IMAGE; + description { + state: "default" 0.0; + aspect: 1 1; + aspect_preference: BOTH; + visible: 0; + rel1.to: "base"; + rel2.to: "base"; + image.normal: "e17_mixer_mute.png"; + } + description { + state: "active" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + + part { + name: "over"; + type: RECT; + description { + state: "default" 0.0; + rel1.to: "base"; + rel2.to: "base"; + color: 255 255 255 0; + } + } + } + + programs { + program { + name: "mute"; + action: STATE_SET "active" 0.0; + transition: LINEAR 0.1; + target: "mute"; + } + + program { + name: "unmute"; + action: STATE_SET "default" 0.0; + transition: LINEAR 0.1; + target: "mute"; + } + + program { + name: "left_none"; + action: STATE_SET "default" 0.0; + transition: LINEAR 0.1; + target: "left"; + } + + program { + name: "left_low"; + action: STATE_SET "low" 0.0; + transition: LINEAR 0.1; + target: "left"; + } + + program { + name: "left_medium"; + action: STATE_SET "medium" 0.0; + transition: LINEAR 0.1; + target: "left"; + } + + program { + name: "left_high"; + action: STATE_SET "high" 0.0; + transition: LINEAR 0.1; + target: "left"; + } + + program { + name: "right_none"; + action: STATE_SET "default" 0.0; + transition: LINEAR 0.1; + target: "right"; + } + + program { + name: "right_low"; + action: STATE_SET "low" 0.0; + transition: LINEAR 0.1; + target: "right"; + } + + program { + name: "right_medium"; + action: STATE_SET "medium" 0.0; + transition: LINEAR 0.1; + target: "right"; + } + + program { + name: "right_high"; + action: STATE_SET "high" 0.0; + transition: LINEAR 0.1; + target: "right"; + } + } +} diff --git a/data/themes/images/e17_mixer_base.png b/data/themes/images/e17_mixer_base.png new file mode 100644 index 000000000..d4b216307 Binary files /dev/null and b/data/themes/images/e17_mixer_base.png differ diff --git a/data/themes/images/e17_mixer_left_high.png b/data/themes/images/e17_mixer_left_high.png new file mode 100644 index 000000000..f7f060a5b Binary files /dev/null and b/data/themes/images/e17_mixer_left_high.png differ diff --git a/data/themes/images/e17_mixer_left_low.png b/data/themes/images/e17_mixer_left_low.png new file mode 100644 index 000000000..97425a784 Binary files /dev/null and b/data/themes/images/e17_mixer_left_low.png differ diff --git a/data/themes/images/e17_mixer_left_medium.png b/data/themes/images/e17_mixer_left_medium.png new file mode 100644 index 000000000..63e846819 Binary files /dev/null and b/data/themes/images/e17_mixer_left_medium.png differ diff --git a/data/themes/images/e17_mixer_module_icon.png b/data/themes/images/e17_mixer_module_icon.png new file mode 100644 index 000000000..50f9e451a Binary files /dev/null and b/data/themes/images/e17_mixer_module_icon.png differ diff --git a/data/themes/images/e17_mixer_mute.png b/data/themes/images/e17_mixer_mute.png new file mode 100644 index 000000000..43ccfb03a Binary files /dev/null and b/data/themes/images/e17_mixer_mute.png differ diff --git a/data/themes/images/e17_mixer_right_high.png b/data/themes/images/e17_mixer_right_high.png new file mode 100644 index 000000000..359159fc6 Binary files /dev/null and b/data/themes/images/e17_mixer_right_high.png differ diff --git a/data/themes/images/e17_mixer_right_low.png b/data/themes/images/e17_mixer_right_low.png new file mode 100644 index 000000000..bc33659ce Binary files /dev/null and b/data/themes/images/e17_mixer_right_low.png differ diff --git a/data/themes/images/e17_mixer_right_medium.png b/data/themes/images/e17_mixer_right_medium.png new file mode 100644 index 000000000..89feab5a0 Binary files /dev/null and b/data/themes/images/e17_mixer_right_medium.png differ diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index bd756b9f1..19d0fb10b 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -51,6 +51,7 @@ conf_paths \ conf_mime \ conf_interaction \ gadman \ +mixer \ conf_window_remembers if HAVE_TEMPERATURE diff --git a/src/modules/mixer/Makefile.am b/src/modules/mixer/Makefile.am new file mode 100644 index 000000000..cb550e7ae --- /dev/null +++ b/src/modules/mixer/Makefile.am @@ -0,0 +1,39 @@ +MAINTAINERCLEANFILES = Makefile.in +MODULE = mixer + +# data files for the module +filesdir = $(libdir)/enlightenment/modules/$(MODULE) +files_DATA = e-module-$(MODULE).edj module.desktop + +EXTRA_DIST = $(files_DATA) + +# the module .so file +INCLUDES = -I. \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/modules/$(MODULE) \ + -I$(top_srcdir)/src/bin \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/modules \ + @e_cflags@ @SOUND_CFLAGS@ +pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = e_mod_main.h \ + e_mod_main.c \ + e_mod_system.h \ + conf_gadget.c \ + conf_module.c \ + app_mixer.c + +if HAVE_ALSA +module_la_SOURCES += sys_alsa.c +else +module_la_SOURCES += sys_dummy.c +endif + +module_la_LIBADD = @SOUND_LDFLAGS@ @e_libs@ @dlopen_libs@ +module_la_LDFLAGS = -module -avoid-version +module_la_DEPENDENCIES = $(top_builddir)/config.h + +uninstall: + rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE) diff --git a/src/modules/mixer/app_mixer.c b/src/modules/mixer/app_mixer.c new file mode 100644 index 000000000..85e37bebd --- /dev/null +++ b/src/modules/mixer/app_mixer.c @@ -0,0 +1,614 @@ +#include "e_mod_main.h" + +extern const char _Name[]; + +typedef struct E_Mixer_App_Dialog_Data +{ + E_Mixer_System *sys; + char *card; + char *channel_name; + int lock_sliders; + Evas_List *cards; + Evas_List *channels_infos; + struct channel_info *channel_info; + E_Mixer_Channel_State state; + + struct e_mixer_app_ui + { + Evas_Object *hlayout; + struct e_mixer_app_ui_cards + { + Evas_Object *frame; + Evas_Object *list; + } cards; + struct e_mixer_app_ui_channels + { + Evas_Object *frame; + Evas_Object *list; + } channels; + struct e_mixer_app_ui_channel_editor + { + Evas_Object *frame; + Evas_Object *label_card; + Evas_Object *card; + Evas_Object *label_channel; + Evas_Object *channel; + Evas_Object *label_type; + Evas_Object *type; + Evas_Object *label_left; + Evas_Object *left; + Evas_Object *label_right; + Evas_Object *right; + Evas_Object *mute; + Evas_Object *lock_sliders; + } channel_editor; + } ui; + + struct + { + void *data; + void (*func)(E_Dialog *dialog, void *data); + } del; +} E_Mixer_App_Dialog_Data; + +struct channel_info +{ + int has_capture; + char *name; + E_Mixer_Channel *id; + E_Mixer_App_Dialog_Data *app; +}; + +static void +_cb_changed_left(void *data, Evas_Object *obj) +{ + E_Mixer_App_Dialog_Data *app = data; + E_Mixer_Channel_State *state; + + state = &app->state; + if (app->lock_sliders && (state->left != state->right)) + { + state->right = 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, + state->left, state->right); +} + +static void +_cb_changed_right(void *data, Evas_Object *obj) +{ + E_Mixer_App_Dialog_Data *app = data; + E_Mixer_Channel_State *state; + + state = &app->state; + if (app->lock_sliders && (state->right != state->left)) + { + state->left = state->right; + e_widget_slider_value_int_set(app->ui.channel_editor.left, + state->left); + } + + e_mixer_system_set_volume(app->sys, app->channel_info->id, + state->left, state->right); +} + +static void +_cb_changed_mute(void *data, Evas_Object *obj) +{ + E_Mixer_App_Dialog_Data *app = data; + + e_mixer_system_set_mute(app->sys, app->channel_info->id, app->state.mute); +} + +static void +_cb_changed_lock_sliders(void *data, Evas_Object *obj) +{ + E_Mixer_App_Dialog_Data *app = data; + E_Mixer_Channel_State *state; + + if (!app->lock_sliders) + return; + + state = &app->state; + if (state->left == state->right) + return; + + state->left = state->right = (state->left + state->right) / 2; + + 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, + state->left, state->right); +} + +static void +_update_channel_editor_state(E_Mixer_App_Dialog_Data *app, const E_Mixer_Channel_State state) +{ + struct e_mixer_app_ui_channel_editor *ui; + + ui = &app->ui.channel_editor; + + 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)) + { + e_widget_disabled_set(ui->mute, 0); + e_widget_check_checked_set(ui->mute, state.mute); + } + else + { + e_widget_disabled_set(ui->mute, 1); + e_widget_check_checked_set(ui->mute, 0); + } +} + +static void +_populate_channel_editor(E_Mixer_App_Dialog_Data *app) +{ + struct e_mixer_app_ui_channel_editor *ui; + E_Mixer_Channel_State state; + char *card_name; + + ui = &app->ui.channel_editor; + + card_name = e_mixer_system_get_card_name(app->card); + e_widget_entry_text_set(ui->card, card_name); + if (card_name) + free(card_name); + + e_widget_entry_text_set(ui->channel, app->channel_name); + + if (e_mixer_system_has_capture(app->sys, app->channel_info->id)) + e_widget_entry_text_set(ui->type, D_("Capture")); + else + e_widget_entry_text_set(ui->type, D_("Playback")); + + e_mixer_system_get_state(app->sys, app->channel_info->id, &state); + _update_channel_editor_state(app, state); + + app->lock_sliders = (state.left == state.right); + e_widget_check_checked_set(ui->lock_sliders, app->lock_sliders); +} + +static void +_cb_channel_selected(void *data) +{ + struct channel_info *info = data; + E_Mixer_App_Dialog_Data *app; + + app = info->app; + app->channel_info = info; + _populate_channel_editor(app); +} + +static int +_channel_info_cmp(void *data_a, void *data_b) +{ + struct channel_info *a = data_a, *b = data_b; + + if (a->has_capture < b->has_capture) + return -1; + else if (a->has_capture > b->has_capture) + return 1; + + return strcmp(a->name, b->name); +} + +static Evas_List * +_channels_info_new(E_Mixer_System *sys) +{ + Evas_List *channels, *channels_infos, *l; + + channels = e_mixer_system_get_channels(sys); + channels_infos = NULL; + for (l = channels; l != NULL; l = l->next) + { + struct channel_info *info; + + 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); + + channels_infos = evas_list_append(channels_infos, info); + } + e_mixer_system_free_channels(channels); + + return evas_list_sort(channels_infos, -1, _channel_info_cmp); +} + +static void +_channels_info_free(Evas_List *list) +{ + Evas_List *l; + + for (l = list; l != NULL; l = l->next) + { + struct channel_info *info = l->data; + free(info->name); + free(info); + } + + evas_list_free(list); +} + +static int +_cb_system_update(void *data, E_Mixer_System *sys) +{ + E_Mixer_App_Dialog_Data *app; + E_Mixer_Channel_State state; + + app = data; + + if ((!app->sys) || (!app->channel_info)) + return 1; + + e_mixer_system_get_state(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) +{ + Evas_List *l; + Evas_Object *ilist; + int header_input; + int i; + + ilist = app->ui.channels.list; + edje_freeze(); + e_widget_ilist_freeze(ilist); + 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); + + if (app->channel_name) + free(app->channel_name); + app->channel_name = e_mixer_system_get_default_channel_name(app->sys); + + if (app->channels_infos) + _channels_info_free(app->channels_infos); + app->channels_infos = _channels_info_new(app->sys); + + if (app->channels_infos) + { + struct channel_info *info = app->channels_infos->data; + if (info->has_capture) + { + e_widget_ilist_header_append(ilist, NULL, D_("Input")); + header_input = 1; + i = 1; + } + else + { + e_widget_ilist_header_append(ilist, NULL, D_("Output")); + header_input = 0; + i = 1; + } + } + + for (l = app->channels_infos; l != NULL; l = l->next, i++) + { + struct channel_info *info = l->data; + + if ((!header_input) && info->has_capture) + { + e_widget_ilist_header_append(ilist, NULL, D_("Input")); + header_input = 1; + i++; + } + + info->app = app; + e_widget_ilist_append(ilist, NULL, info->name, _cb_channel_selected, + info, info->name); + if (app->channel_name && info->name && + (strcmp(app->channel_name, info->name) == 0)) + { + e_widget_ilist_selected_set(ilist, i); + app->channel_info = info; + } + } + + e_widget_ilist_go(ilist); + e_widget_ilist_thaw(ilist); + edje_thaw(); +} + +static void +select_card(E_Mixer_App_Dialog_Data *app) +{ + _populate_channels(app); + e_widget_ilist_selected_set(app->ui.channels.list, 1); +} + +static void +_cb_card_selected(void *data) +{ + select_card(data); +} + +static void +_create_cards(E_Dialog *dialog, Evas *evas, E_Mixer_App_Dialog_Data *app) +{ + struct e_mixer_app_ui_cards *ui; + Evas_List *l; + + app->card = e_mixer_system_get_default_card(); + app->cards = e_mixer_system_get_cards(); + if (evas_list_count(app->cards) < 2) + return; + + ui = &app->ui.cards; + ui->list = e_widget_tlist_add(evas, &app->card); + e_widget_tlist_go(ui->list); + for (l = app->cards; l != NULL; l = l->next) + { + char *card, *card_name; + + card = l->data; + card_name = e_mixer_system_get_card_name(card); + + e_widget_tlist_append(ui->list, card_name, _cb_card_selected, + app, card); + + free(card_name); + } + + ui->frame = e_widget_framelist_add(evas, D_("Cards"), 0); + e_widget_framelist_object_append(ui->frame, ui->list); + e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 0, 0.0); +} + +static void +_create_channels(E_Dialog *dialog, Evas *evas, E_Mixer_App_Dialog_Data *app) +{ + struct e_mixer_app_ui_channels *ui; + + ui = &app->ui.channels; + ui->list = e_widget_ilist_add(evas, 24, 24, &app->channel_name); + e_widget_min_size_set(ui->list, 180, 100); + e_widget_ilist_go(ui->list); + + ui->frame = e_widget_framelist_add(evas, D_("Channels"), 0); + e_widget_framelist_object_append(ui->frame, ui->list); + e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.0); +} + +static void +_create_channel_editor(E_Dialog *dialog, Evas *evas, E_Mixer_App_Dialog_Data *app) +{ + struct e_mixer_app_ui_channel_editor *ui; + + ui = &app->ui.channel_editor; + + ui->label_card = e_widget_label_add(evas, D_("Card:")); + ui->card = e_widget_entry_add(evas, NULL, NULL, NULL, NULL); + e_widget_entry_readonly_set(ui->card, 1); + + ui->label_channel = e_widget_label_add(evas, D_("Channel:")); + ui->channel = e_widget_entry_add(evas, NULL, NULL, NULL, NULL); + e_widget_entry_readonly_set(ui->channel, 1); + + ui->label_type = e_widget_label_add(evas, D_("Type:")); + ui->type = e_widget_entry_add(evas, NULL, NULL, NULL, NULL); + e_widget_entry_readonly_set(ui->type, 1); + + ui->label_left = e_widget_label_add(evas, D_("Left:")); + ui->left = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0, + NULL, &app->state.left, 150); + e_widget_on_change_hook_set(ui->left, _cb_changed_left, app); + + ui->label_right = e_widget_label_add(evas, D_("Right:")); + ui->right = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0, + NULL, &app->state.right, 150); + e_widget_on_change_hook_set(ui->right, _cb_changed_right, app); + + ui->mute = e_widget_check_add(evas, D_("Mute"), &app->state.mute); + e_widget_on_change_hook_set(ui->mute, _cb_changed_mute, app); + + ui->lock_sliders = e_widget_check_add(evas, D_("Lock Sliders"), + &app->lock_sliders); + e_widget_on_change_hook_set(ui->lock_sliders, _cb_changed_lock_sliders, app); + + ui->frame = e_widget_framelist_add(evas, D_("Edit"), 0); + e_widget_framelist_object_append(ui->frame, ui->label_card); + e_widget_framelist_object_append(ui->frame, ui->card); + e_widget_framelist_object_append(ui->frame, ui->label_channel); + e_widget_framelist_object_append(ui->frame, ui->channel); + e_widget_framelist_object_append(ui->frame, ui->label_type); + e_widget_framelist_object_append(ui->frame, ui->type); + e_widget_framelist_object_append(ui->frame, ui->label_left); + e_widget_framelist_object_append(ui->frame, ui->left); + e_widget_framelist_object_append(ui->frame, ui->label_right); + e_widget_framelist_object_append(ui->frame, ui->right); + e_widget_framelist_object_append(ui->frame, ui->mute); + e_widget_framelist_object_append(ui->frame, ui->lock_sliders); + + e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.5); +} + +static void +_create_ui(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app) +{ + struct e_mixer_app_ui *ui; + Evas *evas; + int mw, mh; + + evas = e_win_evas_get(dialog->win); + + ui = &app->ui; + + ui->hlayout = e_widget_list_add(evas, 0, 1); + _create_cards(dialog, evas, app); + _create_channels(dialog, evas, app); + _create_channel_editor(dialog, evas, app); + + if (ui->cards.list) + e_widget_tlist_selected_set(ui->cards.list, 0); + else + select_card(app); + e_widget_ilist_selected_set(ui->channels.list, 1); + + e_widget_min_size_get(ui->hlayout, &mw, &mh); + if (mw < 300) + mw = 300; + if (mh < 200) + mh = 200; + e_dialog_content_set(dialog, ui->hlayout, mw, mh); +} + +static void +_mixer_app_dialog_del(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app) +{ + if (app->del.func) + app->del.func(dialog, app->del.data); + + if (app->card) + free(app->card); + if (app->channel_name) + free(app->channel_name); + if (app->cards) + e_mixer_system_free_cards(app->cards); + if (app->channels_infos) + _channels_info_free(app->channels_infos); + e_mixer_system_del(app->sys); + + e_util_defer_object_del(E_OBJECT(dialog)); + dialog->data = NULL; + E_FREE(app); +} + +static void +_cb_win_del(E_Win *win) +{ + E_Dialog *dialog; + E_Mixer_App_Dialog_Data *app; + + dialog = win->data; + app = dialog->data; + + _mixer_app_dialog_del(dialog, app); +} + +static void +_cb_dialog_dismiss(void *data, E_Dialog *dialog) +{ + _mixer_app_dialog_del(dialog, data); +} + +E_Dialog * +e_mixer_app_dialog_new(E_Container *con, void (*func)(E_Dialog *dialog, void *data), void *data) +{ + E_Mixer_App_Dialog_Data *app; + E_Dialog *dialog; + + dialog = e_dialog_new(con, _Name, "e_mixer_app_dialog"); + if (!dialog) + return NULL; + + app = E_NEW(E_Mixer_App_Dialog_Data, 1); + if (!app) + { + e_object_del(E_OBJECT(dialog)); + return NULL; + } + + dialog->data = app; + app->del.data = data; + app->del.func = func; + + e_dialog_title_set(dialog, D_(_Name)); + e_dialog_resizable_set(dialog, 1); + + e_win_delete_callback_set(dialog->win, _cb_win_del); + + _create_ui(dialog, app); + + e_dialog_button_add(dialog, D_("Close"), NULL, _cb_dialog_dismiss, app); + e_dialog_button_focus_num(dialog, 1); + e_win_centered_set(dialog->win, 1); + e_dialog_show(dialog); + + return dialog; +} + +static inline int +_find_card_by_name(E_Mixer_App_Dialog_Data *app, const char *card_name) +{ + Evas_List *l; + int i; + + for (i = 0, l = app->cards; l != NULL; i++, l = l->next) + if (strcmp(card_name, l->data) == 0) + return i; + + return -1; +} + +static inline int +_find_channel_by_name(E_Mixer_App_Dialog_Data *app, const char *channel_name) +{ + Evas_List *l; + int i, header_input; + + if (app->channels_infos) + { + struct channel_info *info = app->channels_infos->data; + + header_input = !!info->has_capture; + i = 1; + } + + for (l = app->channels_infos; l != NULL; l = l->next, i++) + { + struct channel_info *info = l->data; + + if ((!header_input) && info->has_capture) + { + header_input = 1; + i++; + } + + if (strcmp(channel_name, info->name) == 0) + return i; + } + + return -1; +} + +int +e_mixer_app_dialog_select(E_Dialog *dialog, const char *card_name, const char *channel_name) +{ + E_Mixer_App_Dialog_Data *app; + int n; + + if (!dialog) + return 0; + + app = dialog->data; + if (!app) + return 0; + + n = _find_card_by_name(app, card_name); + if (n < 0) + return 0; + if (app->ui.cards.list) + e_widget_tlist_selected_set(app->ui.cards.list, n); + + n = _find_channel_by_name(app, channel_name); + if (n < 0) + return 0; + e_widget_ilist_selected_set(app->ui.channels.list, n); + + return 1; +} diff --git a/src/modules/mixer/conf_gadget.c b/src/modules/mixer/conf_gadget.c new file mode 100644 index 000000000..928dda856 --- /dev/null +++ b/src/modules/mixer/conf_gadget.c @@ -0,0 +1,389 @@ +#include "e_mod_main.h" + +extern const char _Name[]; + +struct _E_Config_Dialog_Data +{ + int lock_sliders; + int show_locked; + int card_num; + int channel; + const char *card; + const char *channel_name; + Evas_List *cards; + Evas_List *cards_names; + Evas_List *channels_names; + struct mixer_config_ui + { + Evas_Object *table; + struct mixer_config_ui_general + { + Evas_Object *frame; + Evas_Object *lock_sliders; + Evas_Object *show_locked; + } general; + struct mixer_config_ui_cards + { + Evas_Object *frame; + E_Radio_Group *radio; + } cards; + struct mixer_config_ui_channels + { + Evas_Object *frame; + Evas_Object *scroll; + Evas_Object *list; + E_Radio_Group *radio; + Evas_List *radios; + } channels; + } ui; + E_Mixer_Gadget_Config *conf; +}; + +static void +_mixer_fill_cards_info(E_Config_Dialog_Data *cfdata) +{ + Evas_List *l; + int i; + + cfdata->card_num = -1; + cfdata->cards = e_mixer_system_get_cards(); + cfdata->cards_names = NULL; + for (l = cfdata->cards, i = 0; l != NULL; l = l->next, i++) + { + char *card, *name; + + card = l->data; + name = e_mixer_system_get_card_name(card); + if ((cfdata->card_num < 0) && card && cfdata->card && + (strcmp(card, cfdata->card) == 0)) + cfdata->card_num = i; + + cfdata->cards_names = evas_list_append(cfdata->cards_names, name); + } + + if (cfdata->card_num < 0) + cfdata->card_num = 0; +} + +static void +_mixer_fill_channels_info(E_Config_Dialog_Data *cfdata) +{ + E_Mixer_System *sys; + Evas_List *l; + int i; + + sys = e_mixer_system_new(cfdata->card); + if (!sys) + return; + + cfdata->channel = 0; + cfdata->channel_name = evas_stringshare_add(cfdata->conf->channel_name); + cfdata->channels_names = e_mixer_system_get_channels_names(sys); + for (l = cfdata->channels_names, i = 0; l != NULL; l = l->next, i++) + { + char *channel; + + channel = l->data; + if (channel && cfdata->channel_name && + (strcmp(channel, cfdata->channel_name) == 0)) + { + cfdata->channel = i; + break; + } + } + e_mixer_system_del(sys); +} + +static void * +_create_data(E_Config_Dialog *dialog) +{ + E_Config_Dialog_Data *cfdata; + E_Mixer_Gadget_Config *conf; + + cfdata = E_NEW(E_Config_Dialog_Data, 1); + if (!cfdata) + return NULL; + + conf = dialog->data; + cfdata->conf = conf; + cfdata->lock_sliders = conf->lock_sliders; + cfdata->show_locked = conf->show_locked; + cfdata->card = evas_stringshare_add(conf->card); + _mixer_fill_cards_info(cfdata); + _mixer_fill_channels_info(cfdata); + + return cfdata; +} + +static void +_free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata) +{ + E_Mixer_Gadget_Config *conf; + Evas_List *l; + + conf = dialog->data; + if (conf) + conf->dialog = NULL; + + if (!cfdata) + return; + + for (l = cfdata->cards_names; l != NULL; l = l->next) + if (l->data) + free(l->data); + evas_list_free(cfdata->cards_names); + + if (cfdata->channels_names) + e_mixer_system_free_channels_names(cfdata->channels_names); + if (cfdata->cards) + e_mixer_system_free_cards(cfdata->cards); + + if (cfdata->card) + evas_stringshare_del(cfdata->card); + if (cfdata->channel_name) + evas_stringshare_del(cfdata->channel_name); + + evas_list_free(cfdata->ui.channels.radios); + + E_FREE(cfdata); +} + +static int +_basic_apply(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata) +{ + E_Mixer_Gadget_Config *conf; + const char *card, *channel; + + conf = dialog->data; + conf->lock_sliders = cfdata->lock_sliders; + conf->show_locked = cfdata->show_locked; + + card = evas_list_nth(cfdata->cards, cfdata->card_num); + if (card) + { + if (conf->card && (strcmp(card, conf->card) != 0)) + evas_stringshare_del(conf->card); + conf->card = evas_stringshare_add(card); + } + + channel = evas_list_nth(cfdata->channels_names, cfdata->channel); + if (channel) + { + if (conf->channel_name && (strcmp(channel, conf->channel_name) != 0)) + evas_stringshare_del(conf->channel_name); + conf->channel_name = evas_stringshare_add(channel); + } + + e_mixer_update(conf->instance); + return 1; +} + +static void +_lock_change(void *data, Evas_Object *obj, void *event) +{ + E_Config_Dialog_Data *cfdata; + + cfdata = data; + e_widget_disabled_set(cfdata->ui.general.show_locked, !cfdata->lock_sliders); +} + +static void +_basic_create_general(Evas *evas, E_Config_Dialog_Data *cfdata) +{ + struct mixer_config_ui_general *ui; + + ui = &cfdata->ui.general; + + ui->frame = e_widget_framelist_add(evas, D_("General Settings"), 0); + + ui->lock_sliders = e_widget_check_add( + evas, D_("Lock Sliders"), &cfdata->lock_sliders); + evas_object_smart_callback_add( + ui->lock_sliders, "changed", _lock_change, cfdata); + e_widget_framelist_object_append(ui->frame, ui->lock_sliders); + + ui->show_locked = e_widget_check_add( + evas, D_("Show both sliders when locked"), &cfdata->show_locked); + e_widget_disabled_set(ui->show_locked, !cfdata->lock_sliders); + e_widget_framelist_object_append(ui->frame, ui->show_locked); +} + +static void +_clear_channels(E_Config_Dialog_Data *cfdata) +{ + Evas_List *l; + + for (l = cfdata->ui.channels.radios; l != NULL; l = l->next) + evas_object_del(l->data); + cfdata->ui.channels.radios = evas_list_free(cfdata->ui.channels.radios); +} + +static void +_fill_channels(Evas *evas, E_Config_Dialog_Data *cfdata) +{ + struct mixer_config_ui_channels *ui; + Evas_Object *selected; + Evas_Coord mw, mh; + Evas_List *l; + int i; + + ui = &cfdata->ui.channels; + ui->radio = e_widget_radio_group_new(&cfdata->channel); + for (i = 0, l = cfdata->channels_names; l != NULL; l = l->next, i++) + { + Evas_Object *ow; + const char *name; + + name = l->data; + if (!name) + continue; + + ow = e_widget_radio_add(evas, name, i, ui->radio); + ui->radios = evas_list_append(ui->radios, ow); + e_widget_list_object_append(ui->list, ow, 1, 1, 0.0); + } + + e_widget_min_size_get(ui->list, &mw, &mh); + evas_object_resize(ui->list, mw, mh); + + selected = evas_list_nth(ui->radios, cfdata->channel); + if (selected) + { + Evas_Coord x, y, w, h, lx, ly; + evas_object_geometry_get(selected, &x, &y, &w, &h); + evas_object_geometry_get(ui->list, &lx, &ly, NULL, NULL); + x -= lx; + y -= ly - 10; + h += 20; + e_widget_scrollframe_child_region_show(ui->scroll, x, y, w, h); + } +} + +static void +_channel_scroll_set_min_size(struct mixer_config_ui_channels *ui) +{ + Evas_Coord w, h; + int len; + + len = evas_list_count(ui->radios); + if (len < 1) + return; + + e_widget_min_size_get(ui->list, &w, &h); + h = 4 * h / len; + e_widget_min_size_set(ui->scroll, w, h); +} + +static void +_basic_create_channels(Evas *evas, E_Config_Dialog_Data *cfdata) +{ + struct mixer_config_ui_channels *ui; + + ui = &cfdata->ui.channels; + ui->list = e_widget_list_add(evas, 1, 0); + ui->scroll = e_widget_scrollframe_simple_add(evas, ui->list); + ui->frame = e_widget_framelist_add(evas, D_("Channels"), 0); + + _fill_channels(evas, cfdata); + + _channel_scroll_set_min_size(ui); + e_widget_framelist_object_append(ui->frame, ui->scroll); +} + +static void +_card_change(void *data, Evas_Object *obj, void *event) +{ + E_Config_Dialog_Data *cfdata; + Evas *evas; + char *card; + + cfdata = data; + + if (cfdata->card) + evas_stringshare_del(cfdata->card); + + e_mixer_system_free_channels_names(cfdata->channels_names); + if (cfdata->channel_name) + evas_stringshare_del(cfdata->channel_name); + + card = evas_list_nth(cfdata->cards, cfdata->card_num); + cfdata->card = evas_stringshare_add(card); + _mixer_fill_channels_info(cfdata); + + evas = evas_object_evas_get(obj); + _clear_channels(cfdata); + _fill_channels(evas, cfdata); +} + +static void +_basic_create_cards(Evas *evas, E_Config_Dialog_Data *cfdata) +{ + struct mixer_config_ui_cards *ui; + Evas_List *l; + int i; + + ui = &cfdata->ui.cards; + + ui->frame = e_widget_framelist_add(evas, D_("Sound Cards"), 0); + ui->radio = e_widget_radio_group_new(&cfdata->card_num); + for (i = 0, l = cfdata->cards_names; l != NULL; l = l->next, i++) + { + Evas_Object *ow; + const char *card; + + card = l->data; + if (!card) + continue; + + ow = e_widget_radio_add(evas, card, i, ui->radio); + e_widget_framelist_object_append(ui->frame, ow); + evas_object_smart_callback_add(ow, "changed", _card_change, cfdata); + } +} + +static Evas_Object * +_basic_create(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata) +{ + if (!cfdata) + return NULL; + + cfdata->ui.table = e_widget_table_add(evas, 0); + _basic_create_general(evas, cfdata); + _basic_create_cards(evas, cfdata); + _basic_create_channels(evas, cfdata); + + e_widget_table_object_append(cfdata->ui.table, cfdata->ui.general.frame, + 0, 0, 1, 1, 1, 1, 1, 0); + e_widget_table_object_append(cfdata->ui.table, cfdata->ui.cards.frame, + 0, 1, 1, 1, 1, 1, 1, 0); + e_widget_table_object_append(cfdata->ui.table, cfdata->ui.channels.frame, + 0, 2, 1, 1, 1, 1, 1, 1); + + return cfdata->ui.table; +} + +E_Config_Dialog * +e_mixer_config_dialog_new(E_Container *con, E_Mixer_Gadget_Config *conf) +{ + E_Config_Dialog *dialog; + E_Config_Dialog_View *view; + + if (e_config_dialog_find(_Name, "e_mixer_config_dialog_new")) + return NULL; + + view = E_NEW(E_Config_Dialog_View, 1); + if (!view) + return NULL; + + view->create_cfdata = _create_data; + view->free_cfdata = _free_data; + view->basic.create_widgets = _basic_create; + view->basic.apply_cfdata = _basic_apply; + + dialog = e_config_dialog_new(con, D_("Mixer Configuration"), + _Name, "e_mixer_config_dialog_new", + e_mixer_theme_path(), 0, view, conf); + e_dialog_resizable_set(dialog->dia, 1); + + return dialog; +} diff --git a/src/modules/mixer/conf_module.c b/src/modules/mixer/conf_module.c new file mode 100644 index 000000000..80fa88306 --- /dev/null +++ b/src/modules/mixer/conf_module.c @@ -0,0 +1,192 @@ +#include "e_mod_main.h" + +extern const char _Name[]; + +struct _E_Config_Dialog_Data +{ + int default_instance; + struct mixer_config_ui + { + Evas_Object *list; + struct mixer_config_ui_general + { + Evas_Object *frame; + E_Radio_Group *radio; + } general; + } ui; +}; + +static int +_find_default_instance_index(E_Mixer_Module_Context *ctxt) +{ + Evas_List *l; + int i; + + for (i = 0, l = ctxt->instances; l != NULL; l = l->next, i++) + if (l->data == ctxt->default_instance) + return i; + + return 0; +} + +static void * +_create_data(E_Config_Dialog *dialog) +{ + E_Config_Dialog_Data *cfdata; + E_Mixer_Module_Context *ctxt; + + cfdata = E_NEW(E_Config_Dialog_Data, 1); + if (!cfdata) + return NULL; + + ctxt = dialog->data; + cfdata->default_instance = _find_default_instance_index(ctxt); + + return cfdata; +} + +static void +_free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata) +{ + E_Mixer_Module_Context *ctxt; + + ctxt = dialog->data; + if (ctxt) + ctxt->conf_dialog = NULL; + + E_FREE(cfdata); +} + +static int +_basic_apply(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata) +{ + E_Mixer_Module_Context *ctxt; + + ctxt = dialog->data; + ctxt->default_instance = evas_list_nth(ctxt->instances, + cfdata->default_instance); + if (ctxt->default_instance) + { + E_Mixer_Module_Config *conf; + const char *id; + + conf = ctxt->conf; + if (conf->default_gc_id) + evas_stringshare_del(conf->default_gc_id); + + id = ctxt->default_instance->gcc->cf->id; + conf->default_gc_id = evas_stringshare_add(id); + } + + return 1; +} + +static void +_basic_create_general(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata) +{ + struct mixer_config_ui_general *ui; + E_Mixer_Module_Context *ctxt; + Evas_Object *label; + Evas_List *l; + int i; + + ui = &cfdata->ui.general; + ctxt = dialog->data; + + ui->frame = e_widget_framelist_add(evas, D_("General Settings"), 0); + + label = e_widget_label_add(evas, D_("Mixer to use for global actions:")); + e_widget_framelist_object_append(ui->frame, label); + + ui->radio = e_widget_radio_group_new(&cfdata->default_instance); + for (i = 0, l = ctxt->instances; l != NULL; l = l->next, i++) + { + E_Mixer_Instance *inst; + E_Mixer_Gadget_Config *conf; + Evas_Object *o; + char name[128]; + char *card_name; + + inst = l->data; + conf = inst->conf; + + card_name = e_mixer_system_get_card_name(conf->card); + snprintf(name, sizeof(name), "%s: %s", card_name, conf->channel_name); + free(card_name); + + o = e_widget_radio_add(evas, name, i, ui->radio); + e_widget_framelist_object_append(ui->frame, o); + } + + e_widget_list_object_append(cfdata->ui.list, ui->frame, 1, 1, 0.5); +} + +static void +cb_mixer_app_del(E_Dialog *dialog, void *data) +{ + E_Mixer_Module_Context *ctxt = data; + ctxt->mixer_dialog = NULL; +} + +static void +cb_mixer_call(void *data, void *data2) +{ + E_Mixer_Module_Context *ctxt = data; + E_Container *con; + + if (ctxt->mixer_dialog) + { + e_dialog_show(ctxt->mixer_dialog); + return; + } + + con = e_container_current_get(e_manager_current_get()); + ctxt->mixer_dialog = e_mixer_app_dialog_new(con, cb_mixer_app_del, ctxt); +} + +static void +_basic_create_mixer_call(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata) +{ + Evas_Object *button; + + button = e_widget_button_add(evas, D_("Launch mixer..."), NULL, + cb_mixer_call, dialog->data, NULL); + e_widget_list_object_append(cfdata->ui.list, button, 0, 0, 0.0); +} + +static Evas_Object * +_basic_create(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata) +{ + if (!cfdata) + return NULL; + + cfdata->ui.list = e_widget_list_add(evas, 0, 0); + _basic_create_general(dialog, evas, cfdata); + _basic_create_mixer_call(dialog, evas, cfdata); + return cfdata->ui.list; +} + +E_Config_Dialog * +e_mixer_config_module_dialog_new(E_Container *con, E_Mixer_Module_Context *ctxt) +{ + E_Config_Dialog *dialog; + E_Config_Dialog_View *view; + + if (e_config_dialog_find(_Name, "e_mixer_config_module_dialog_new")) + return NULL; + + view = E_NEW(E_Config_Dialog_View, 1); + if (!view) + return NULL; + + view->create_cfdata = _create_data; + view->free_cfdata = _free_data; + view->basic.create_widgets = _basic_create; + view->basic.apply_cfdata = _basic_apply; + + dialog = e_config_dialog_new(con, D_("Mixer Module Configuration"), + _Name, "e_mixer_config_module_dialog_new", + e_mixer_theme_path(), 0, view, ctxt); + + return dialog; +} diff --git a/src/modules/mixer/e-module-mixer.edj b/src/modules/mixer/e-module-mixer.edj new file mode 100644 index 000000000..6b2108ab0 Binary files /dev/null and b/src/modules/mixer/e-module-mixer.edj differ diff --git a/src/modules/mixer/e_mod_main.c b/src/modules/mixer/e_mod_main.c new file mode 100644 index 000000000..035365b49 --- /dev/null +++ b/src/modules/mixer/e_mod_main.c @@ -0,0 +1,1252 @@ +#include "e_mod_main.h" +#include "e_mod_system.h" + +static E_Module *mixer_mod = NULL; +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 _Name[] = "Mixer"; + +const char * +e_mixer_theme_path(void) +{ +#define TF "/e-module-mixer.edj" + int dirlen; + + dirlen = strlen(mixer_mod->dir); + if (dirlen >= sizeof(tmpbuf) - sizeof(TF)) + return NULL; + + memcpy(tmpbuf, mixer_mod->dir, dirlen); + memcpy(tmpbuf + dirlen, TF, sizeof(TF)); + + return tmpbuf; +#undef TF +} + +static int +_mixer_gadget_configuration_defaults(E_Mixer_Gadget_Config *conf) +{ + E_Mixer_System *sys; + char *card, *channel; + + card = e_mixer_system_get_default_card(); + if (!card) + return 0; + + sys = e_mixer_system_new(card); + if (!sys) + { + free(card); + return 0; + } + + channel = e_mixer_system_get_default_channel_name(sys); + e_mixer_system_del(sys); + + if (!channel) + { + free(card); + return 0; + } + + conf->card = evas_stringshare_add(card); + conf->channel_name = evas_stringshare_add(channel); + conf->lock_sliders = 1; + conf->show_locked = 0; + + free(card); + free(channel); + + return 1; +} + +static E_Mixer_Gadget_Config * +_mixer_gadget_configuration_new(E_Mixer_Module_Config *mod_conf, const char *id) +{ + E_Mixer_Gadget_Config *conf; + + conf = E_NEW(E_Mixer_Gadget_Config, 1); + if (!conf) + return NULL; + + if (!_mixer_gadget_configuration_defaults(conf)) + { + E_FREE(conf); + return NULL; + } + + conf->id = evas_stringshare_add(id); + mod_conf->gadgets = evas_hash_direct_add(mod_conf->gadgets, conf->id, conf); + + return conf; +} + +static inline void +_mixer_gadget_configuration_free_int(E_Mixer_Gadget_Config *conf) +{ + if (conf->dialog) + e_object_del(E_OBJECT(conf->dialog)); + + if (conf->card) + evas_stringshare_del(conf->card); + if (conf->channel_name) + evas_stringshare_del(conf->channel_name); + + evas_stringshare_del(conf->id); + free(conf); +} + +static void +_mixer_gadget_configuration_free(E_Mixer_Module_Config *mod_conf, E_Mixer_Gadget_Config *conf) +{ + if (!mod_conf) + return; + if (!conf) + return; + mod_conf->gadgets = evas_hash_del(mod_conf->gadgets, conf->id, conf); + _mixer_gadget_configuration_free_int(conf); +} + +static Evas_Bool +_mixer_gadget_configuration_free_foreach(const Evas_Hash *hash, const char *key, void *hdata, void *fdata) +{ + _mixer_gadget_configuration_free_int(hdata); + return 1; +} + +static int +_mixer_module_configuration_alert(void *data) +{ + e_util_dialog_show(D_("Mixer Configuration Updated"), data); + return 0; +} + +static E_Mixer_Module_Config * +_mixer_module_configuration_new(void) +{ + E_Mixer_Module_Config *conf; + + conf = E_NEW(E_Mixer_Module_Config, 1); + if (!conf) + return NULL; + + conf->version = MOD_CONF_VERSION; + + return conf; +} + +static void +_mixer_module_configuration_free(E_Mixer_Module_Config *conf) +{ + if (!conf) + return; + + evas_hash_foreach(conf->gadgets, + _mixer_gadget_configuration_free_foreach, NULL); + evas_hash_free(conf->gadgets); + free(conf); +} + +static void +_mixer_popup_update(E_Mixer_Instance *inst) +{ + E_Mixer_Channel_State *state; + + state = &inst->mixer_state; + + if (inst->ui.left) + e_slider_value_set(inst->ui.left, state->left); + if (inst->ui.right) + e_slider_value_set(inst->ui.right, state->right); + if (inst->ui.mute) + e_widget_check_checked_set(inst->ui.mute, state->mute); +} + +static void +_mixer_gadget_update(E_Mixer_Instance *inst) +{ + Edje_Message_Int_Set *msg; + + if (!inst) + return; + + e_mixer_system_get_state(inst->sys, inst->channel, &inst->mixer_state); + + msg = alloca(sizeof(Edje_Message_Int_Set) + 2 * sizeof(int)); + msg->count = 3; + msg->val[0] = inst->mixer_state.mute; + msg->val[1] = inst->mixer_state.left; + msg->val[2] = inst->mixer_state.right; + edje_object_message_send(inst->ui.gadget, EDJE_MESSAGE_INT_SET, 0, msg); + + edje_object_signal_emit(inst->ui.gadget, "e,action,volume,change", ""); + + if (inst->popup) + _mixer_popup_update(inst); +} + +static void +_mixer_volume_increase(E_Mixer_Instance *inst) +{ + E_Mixer_Channel_State *state; + + state = &inst->mixer_state; + e_mixer_system_get_volume(inst->sys, inst->channel, + &state->left, &state->right); + if (state->left >= 0) + { + if (state->left < 95) + state->left += 5; + else + state->left = 100; + } + + if (state->right >= 0) + { + if (state->right < 95) + state->right += 5; + else + state->right = 100; + } + + e_mixer_system_set_volume(inst->sys, inst->channel, + state->left, state->right); + _mixer_gadget_update(inst); +} + +static void +_mixer_volume_decrease(E_Mixer_Instance *inst) +{ + E_Mixer_Channel_State *state; + + state = &inst->mixer_state; + e_mixer_system_get_volume(inst->sys, inst->channel, + &state->left, &state->right); + if (state->left >= 0) + { + if (state->left > 5) + state->left -= 5; + else + state->left = 0; + } + if (state->right >= 0) + { + if (state->right > 5) + state->right -= 5; + else + state->right = 0; + } + + e_mixer_system_set_volume(inst->sys, inst->channel, + state->left, state->right); + _mixer_gadget_update(inst); +} + +static void +_mixer_toggle_mute(E_Mixer_Instance *inst) +{ + E_Mixer_Channel_State *state; + + if (!e_mixer_system_can_mute(inst->sys, inst->channel)) + return; + + state = &inst->mixer_state; + e_mixer_system_get_mute(inst->sys, inst->channel, &state->mute); + state->mute = !state->mute; + e_mixer_system_set_mute(inst->sys, inst->channel, state->mute); + _mixer_gadget_update(inst); +} + +static void +_mixer_popup_cb_volume_left_change(void *data, Evas_Object *obj, void *event) +{ + E_Mixer_Instance *inst; + E_Mixer_Channel_State *state; + + inst = data; + if (!inst) + return; + + state = &inst->mixer_state; + e_mixer_system_get_volume(inst->sys, inst->channel, + &state->left, &state->right); + + state->left = (int)e_slider_value_get(obj); + if (inst->conf->lock_sliders) + { + state->right = state->left; + e_slider_value_set(inst->ui.right, state->right); + } + + e_mixer_system_set_volume(inst->sys, inst->channel, + state->left, state->right); + _mixer_gadget_update(inst); +} + +static void +_mixer_popup_cb_volume_right_change(void *data, Evas_Object *obj, void *event) +{ + E_Mixer_Instance *inst; + E_Mixer_Channel_State *state; + + inst = data; + if (!inst) + return; + + state = &inst->mixer_state; + e_mixer_system_get_volume(inst->sys, inst->channel, + &state->left, &state->right); + + state->right = (int)e_slider_value_get(obj); + if (inst->conf->lock_sliders) + { + state->left = state->right; + e_slider_value_set(inst->ui.left, state->left); + } + + e_mixer_system_set_volume(inst->sys, inst->channel, + state->left, state->right); + _mixer_gadget_update(inst); +} + +static void +_mixer_popup_cb_mute_change(void *data, Evas_Object *obj, void *event) +{ + E_Mixer_Instance *inst; + E_Mixer_Channel_State *state; + + inst = data; + if (!inst) + return; + + state = &inst->mixer_state; + state->mute = e_widget_check_checked_get(obj); + e_mixer_system_set_mute(inst->sys, inst->channel, state->mute); + + _mixer_gadget_update(inst); +} + +static void +_mixer_popup_cb_resize(Evas_Object *obj, int *w, int *h) +{ + int mw, mh; + + e_widget_min_size_get(obj, &mw, &mh); + if (mh < 200) mh = 200; + if (mw < 60) mw = 60; + if (*w) *w = (mw + 8); + if (*h) *h = (mh + 8); +} + +static Evas_Object * +_mixer_popup_add_slider(E_Mixer_Instance *inst, int value, void (*cb) (void *data, Evas_Object *obj, void *event_info)) +{ + Evas_Object *slider; + + slider = e_slider_add(inst->popup->win->evas); + evas_object_show(slider); + e_slider_orientation_set(slider, 0); + e_slider_value_set(slider, value); + e_slider_value_range_set(slider, 0.0, 100.0); + e_slider_value_format_display_set(slider, NULL); + evas_object_smart_callback_add(slider, "changed", cb, inst); + + return slider; +} + +static void +_mixer_app_cb_del(E_Dialog *dialog, void *data) +{ + E_Mixer_Module_Context *ctxt = data; + ctxt->mixer_dialog = NULL; +} + +static void _mixer_popup_del(E_Mixer_Instance *inst); + +static int +_mixer_popup_input_window_mouse_up_cb(void *data, int type, void *event) +{ + Ecore_X_Event_Mouse_Button_Up *ev = event; + E_Mixer_Instance *inst = data; + + if (ev->win != inst->ui.input.win) + return 1; + + _mixer_popup_del(inst); + + return 1; +} + +static int +_mixer_popup_input_window_key_down_cb(void *data, int type, void *event) +{ + Ecore_X_Event_Key_Down *ev = event; + E_Mixer_Instance *inst = data; + const char *keysym; + + if (ev->win != inst->ui.input.win) + return 1; + + keysym = ev->keysymbol; + if (strcmp(keysym, "Escape") == 0) + _mixer_popup_del(inst); + else if (strcmp(keysym, "Up") == 0) + _mixer_volume_increase(inst); + else if (strcmp(keysym, "Down") == 0) + _mixer_volume_decrease(inst); + else if ((strcmp(keysym, "Return") == 0) || + (strcmp(keysym, "KP_Enter") == 0)) + _mixer_toggle_mute(inst); + else + _mixer_popup_del(inst); /* XXX really? */ + + return 1; +} + +static void +_mixer_popup_input_window_destroy(E_Mixer_Instance *inst) +{ + ecore_x_window_del(inst->ui.input.win); + inst->ui.input.win = 0; + + ecore_event_handler_del(inst->ui.input.mouse_up); + inst->ui.input.mouse_up = NULL; + + ecore_event_handler_del(inst->ui.input.key_down); + inst->ui.input.key_down = NULL; +} + +static void +_mixer_popup_input_window_create(E_Mixer_Instance *inst) +{ + Ecore_X_Window_Configure_Mask mask; + Ecore_X_Window w, popup_w; + E_Manager *man; + + man = e_manager_current_get(); + + w = ecore_x_window_input_new(man->root, 0, 0, man->w, man->h); + mask = (ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE | + ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING); + popup_w = inst->popup->win->evas_win; + ecore_x_window_configure(w, mask, 0, 0, 0, 0, 0, popup_w, + ECORE_X_WINDOW_STACK_BELOW); + ecore_x_window_show(w); + + inst->ui.input.mouse_up = + ecore_event_handler_add(ECORE_X_EVENT_MOUSE_BUTTON_UP, + _mixer_popup_input_window_mouse_up_cb, inst); + + inst->ui.input.key_down = + ecore_event_handler_add(ECORE_X_EVENT_KEY_DOWN, + _mixer_popup_input_window_key_down_cb, inst); + + inst->ui.input.win = w; +} + +static void +_mixer_popup_del(E_Mixer_Instance *inst) +{ + _mixer_popup_input_window_destroy(inst); + e_object_del(E_OBJECT(inst->popup)); + inst->ui.label = NULL; + inst->ui.left = NULL; + inst->ui.right = NULL; + inst->ui.mute = NULL; + inst->ui.table = NULL; + inst->ui.button = NULL; + inst->popup = NULL; +} + +static void +_mixer_app_select_current(E_Dialog *dialog, E_Mixer_Instance *inst) +{ + E_Mixer_Gadget_Config *conf = inst->conf; + + e_mixer_app_dialog_select(dialog, conf->card, conf->channel_name); +} + + +static void +_mixer_popup_cb_mixer(void *data, void *data2) +{ + E_Mixer_Instance *inst = data; + E_Mixer_Module_Context *ctxt; + E_Container *con; + + _mixer_popup_del(inst); + + ctxt = mixer_mod->data; + if (ctxt->mixer_dialog) + { + _mixer_app_select_current(ctxt->mixer_dialog, inst); + e_dialog_show(ctxt->mixer_dialog); + return; + } + + con = e_container_current_get(e_manager_current_get()); + ctxt->mixer_dialog = e_mixer_app_dialog_new( + con, _mixer_app_cb_del, ctxt); + + _mixer_app_select_current(ctxt->mixer_dialog, inst); +} + +static void +_mixer_popup_new(E_Mixer_Instance *inst) +{ + E_Mixer_Channel_State *state; + Evas *evas; + int colspan; + + if (inst->conf->dialog) + return; + + state = &inst->mixer_state; + e_mixer_system_get_state(inst->sys, inst->channel, state); + + if ((state->right >= 0) && + (inst->conf->show_locked || (!inst->conf->lock_sliders))) + colspan = 2; + else + colspan = 1; + + inst->popup = e_gadcon_popup_new(inst->gcc, _mixer_popup_cb_resize); + evas = inst->popup->win->evas; + + inst->ui.table = e_widget_table_add(evas, 0); + + inst->ui.label = e_widget_label_add(evas, inst->conf->channel_name); + e_widget_table_object_append(inst->ui.table, inst->ui.label, + 0, 0, colspan, 1, 0, 0, 0, 0); + + if (state->left >= 0) + { + inst->ui.left = _mixer_popup_add_slider( + inst, state->left, _mixer_popup_cb_volume_left_change); + e_widget_table_object_append(inst->ui.table, inst->ui.left, + 0, 1, 1, 1, 1, 1, 1, 1); + } + else + inst->ui.left = NULL; + + if ((state->right >= 0) && + (inst->conf->show_locked || (!inst->conf->lock_sliders))) + { + inst->ui.right = _mixer_popup_add_slider( + inst, state->right, _mixer_popup_cb_volume_right_change); + e_widget_table_object_append(inst->ui.table, inst->ui.right, + 1, 1, 1, 1, 1, 1, 1, 1); + } + else + inst->ui.right = NULL; + + if (e_mixer_system_can_mute(inst->sys, inst->channel)) + { + inst->ui.mute = e_widget_check_add(evas, D_("Mute"), &state->mute); + evas_object_show(inst->ui.mute); + e_widget_table_object_append(inst->ui.table, inst->ui.mute, + 0, 2, colspan, 1, 1, 1, 1, 0); + evas_object_smart_callback_add(inst->ui.mute, "changed", + _mixer_popup_cb_mute_change, inst); + } + else + inst->ui.mute = NULL; + + inst->ui.button = e_widget_button_add(evas, D_(_Name), NULL, + _mixer_popup_cb_mixer, inst, NULL); + e_widget_table_object_append(inst->ui.table, inst->ui.button, + 0, 7, colspan, 1, 1, 1, 1, 0); + + e_gadcon_popup_content_set(inst->popup, inst->ui.table); + e_gadcon_popup_show(inst->popup); + _mixer_popup_input_window_create(inst); +} + +static void +_mixer_menu_cb_post(void *data, E_Menu *menu) +{ + E_Mixer_Instance *inst; + + inst = data; + if ((!inst) || (!inst->menu)) + return; + if (inst->menu) + { + e_object_del(E_OBJECT(inst->menu)); + inst->menu = NULL; + } +} + +static void +_mixer_menu_cb_cfg(void *data, E_Menu *menu, E_Menu_Item *mi) +{ + E_Mixer_Instance *inst; + E_Container *con; + + inst = data; + if (!inst) + return; + if (inst->popup) + _mixer_popup_del(inst); + con = e_container_current_get(e_manager_current_get()); + inst->conf->dialog = e_mixer_config_dialog_new(con, inst->conf); +} + +static void +_mixer_menu_new(E_Mixer_Instance *inst, Evas_Event_Mouse_Down *ev) +{ + E_Zone *zone; + E_Menu *mn; + E_Menu_Item *mi; + int x, y; + + zone = e_util_zone_current_get(e_manager_current_get()); + + mn = e_menu_new(); + e_menu_post_deactivate_callback_set(mn, _mixer_menu_cb_post, inst); + inst->menu = mn; + + mi = e_menu_item_new(mn); + e_menu_item_label_set(mi, D_("Configuration")); + e_util_menu_item_edje_icon_set(mi, "enlightenment/configuration"); + e_menu_item_callback_set(mi, _mixer_menu_cb_cfg, inst); + + e_gadcon_client_util_menu_items_append(inst->gcc, mn, 0); + e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL); + e_menu_activate_mouse(mn, zone, x + ev->output.x, y + ev->output.y, + 1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp); + evas_event_feed_mouse_up(inst->gcc->gadcon->evas, ev->button, + EVAS_BUTTON_NONE, ev->timestamp, NULL); +} + +static void +_mixer_cb_mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event) +{ + E_Mixer_Instance *inst; + Evas_Event_Mouse_Down *ev; + + inst = data; + if (!inst) + return; + + ev = event; + if (ev->button == 1) + { + if (!inst->popup) + _mixer_popup_new(inst); + else + _mixer_popup_del(inst); + } + else if (ev->button == 2) + _mixer_toggle_mute(inst); + else if ((ev->button == 3) && (!inst->menu)) + _mixer_menu_new(inst, ev); +} + +static void +_mixer_cb_mouse_wheel(void *data, Evas *evas, Evas_Object *obj, void *event) +{ + E_Mixer_Instance *inst; + Evas_Event_Mouse_Wheel *ev; + + inst = data; + if (!inst) + return; + + ev = event; + if (ev->direction == 0) + { + if (ev->z > 0) + _mixer_volume_decrease(inst); + else if (ev->z < 0) + _mixer_volume_increase(inst); + } +} + +static int +_mixer_sys_setup(E_Mixer_Instance *inst) +{ + E_Mixer_Gadget_Config *conf; + + conf = inst->conf; + + if (inst->sys) + e_mixer_system_del(inst->sys); + + inst->sys = e_mixer_system_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); + return inst->channel != NULL; +} + +static int +_mixer_system_cb_update(void *data, E_Mixer_System *sys) +{ + E_Mixer_Instance *inst; + + inst = data; + e_mixer_system_get_state(inst->sys, inst->channel, &inst->mixer_state); + _mixer_gadget_update(inst); + + return 1; +} + +int +e_mixer_update(E_Mixer_Instance *inst) +{ + int r; + + e_modapi_save(mixer_mod); + if ((!inst) || (!inst->conf)) + return 0; + + r = _mixer_sys_setup(inst); + if (r) + e_mixer_system_callback_set(inst->sys, _mixer_system_cb_update, inst); + + return r; +} + +static int +_mixer_sys_setup_default_card(E_Mixer_Instance *inst) +{ + E_Mixer_Gadget_Config *conf; + char *card; + + conf = inst->conf; + if (conf->card) + evas_stringshare_del(conf->card); + + card = e_mixer_system_get_default_card(); + if (!card) + goto error; + + inst->sys = e_mixer_system_new(card); + if (!inst->sys) + goto system_error; + + conf->card = evas_stringshare_add(card); + free(card); + return 1; + + + system_error: + free(card); + error: + conf->card = NULL; + return 0; +} + +static int +_mixer_sys_setup_default_channel(E_Mixer_Instance *inst) +{ + E_Mixer_Gadget_Config *conf; + char *channel_name; + + conf = inst->conf; + if (conf->channel_name) + evas_stringshare_del(conf->channel_name); + + channel_name = e_mixer_system_get_default_channel_name(inst->sys); + if (!channel_name) + goto error; + + inst->channel = e_mixer_system_get_channel_by_name(inst->sys, channel_name); + if (!inst->channel) + goto system_error; + + conf->channel_name = evas_stringshare_add(channel_name); + free(channel_name); + return 1; + + system_error: + free(channel_name); + error: + conf->channel_name = NULL; + return 0; +} + +static int +_mixer_sys_setup_defaults(E_Mixer_Instance *inst) +{ + if ((!inst->sys) && (!_mixer_sys_setup_default_card(inst))) + return 0; + + return _mixer_sys_setup_default_channel(inst); +} + +/* Gadcon Api Functions */ +static void _mixer_module_configuration_setup(E_Mixer_Module_Context *ctxt); + +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + E_Mixer_Instance *inst; + E_Mixer_Module_Context *ctxt; + E_Mixer_Gadget_Config *conf; + + if (!mixer_mod) + return NULL; + + ctxt = mixer_mod->data; + if (!ctxt->conf) + { + _mixer_module_configuration_setup(ctxt); + if (!ctxt->conf) + return NULL; + } + + conf = evas_hash_find(ctxt->conf->gadgets, id); + if (!conf) + { + conf = _mixer_gadget_configuration_new(ctxt->conf, id); + if (!conf) + return NULL; + } + + inst = E_NEW(E_Mixer_Instance, 1); + inst->conf = conf; + conf->instance = inst; + if ((!_mixer_sys_setup(inst)) && (!_mixer_sys_setup_defaults(inst))) + { + if (inst->sys) + e_mixer_system_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); + + inst->ui.gadget = edje_object_add(gc->evas); + if (!e_theme_edje_object_set(inst->ui.gadget, "base/theme/modules/mixer", + "e/modules/mixer/main")) + edje_object_file_set(inst->ui.gadget, e_mixer_theme_path(), + "e/modules/mixer/main"); + + inst->gcc = e_gadcon_client_new(gc, name, id, style, inst->ui.gadget); + inst->gcc->data = inst; + + evas_object_event_callback_add(inst->ui.gadget, EVAS_CALLBACK_MOUSE_DOWN, + _mixer_cb_mouse_down, inst); + 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 (!ctxt->conf->default_gc_id) + { + ctxt->conf->default_gc_id = evas_stringshare_add(id); + ctxt->default_instance = inst; + } + else if ((!ctxt->default_instance) || + (strcmp(id, ctxt->conf->default_gc_id) == 0)) + ctxt->default_instance = inst; + + ctxt->instances = evas_list_append(ctxt->instances, inst); + + return inst->gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + E_Mixer_Module_Context *ctxt; + E_Mixer_Instance *inst; + + if (!mixer_mod) + return; + + ctxt = mixer_mod->data; + if (!ctxt) + return; + + inst = gcc->data; + if (!inst) + return; + + if (inst->menu) + { + e_menu_post_deactivate_callback_set(inst->menu, NULL, NULL); + 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); + + inst->conf->instance = NULL; + ctxt->instances = evas_list_remove(ctxt->instances, inst); + + E_FREE(inst); +} + +static void +_gc_orient(E_Gadcon_Client *gcc) +{ + e_gadcon_client_aspect_set(gcc, 16, 16); + e_gadcon_client_min_size_set(gcc, 16, 16); +} + +static char * +_gc_label(void) +{ + return D_(_Name); +} + +static Evas_Object * +_gc_icon(Evas *evas) +{ + Evas_Object *o; + + o = edje_object_add(evas); + edje_object_file_set(o, e_mixer_theme_path(), "icon"); + return o; +} + +static const char * +_gc_id_new(void) +{ + E_Mixer_Module_Context *ctxt; + Evas_List *instances; + + if (!mixer_mod) + return NULL; + + ctxt = mixer_mod->data; + if (!ctxt) + return NULL; + + instances = ctxt->instances; + snprintf(tmpbuf, sizeof(tmpbuf), "mixer.%d", evas_list_count(instances)); + return tmpbuf; +} + +static const E_Gadcon_Client_Class _gc_class = +{ + GADCON_CLIENT_CLASS_VERSION, _name, + {_gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL}, + E_GADCON_CLIENT_STYLE_PLAIN +}; + + + +EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, _Name}; + +static void +_mixer_cb_volume_increase(E_Object *obj, const char *params) +{ + E_Mixer_Module_Context *ctxt; + + if (!mixer_mod) + return; + + ctxt = mixer_mod->data; + if (!ctxt->conf) + return; + + if (ctxt->default_instance) + _mixer_volume_increase(ctxt->default_instance); +} + +static void +_mixer_cb_volume_decrease(E_Object *obj, const char *params) +{ + E_Mixer_Module_Context *ctxt; + + if (!mixer_mod) + return; + + ctxt = mixer_mod->data; + if (!ctxt->conf) + return; + + if (ctxt->default_instance) + _mixer_volume_decrease(ctxt->default_instance); +} + +static void +_mixer_cb_volume_mute(E_Object *obj, const char *params) +{ + E_Mixer_Module_Context *ctxt; + + if (!mixer_mod) + return; + + ctxt = mixer_mod->data; + if (!ctxt->conf) + return; + + if (ctxt->default_instance) + _mixer_toggle_mute(ctxt->default_instance); +} + +static E_Config_Dialog * +_mixer_module_config(E_Container *con, const char *params __UNUSED__) +{ + E_Mixer_Module_Context *ctxt; + + if (!mixer_mod) + return NULL; + + ctxt = mixer_mod->data; + if (!ctxt) + return NULL; + + if (ctxt->conf_dialog) + return NULL; + + if (!ctxt->conf) + { + _mixer_module_configuration_setup(ctxt); + if (!ctxt->conf) + return NULL; + } + + ctxt->conf_dialog = e_mixer_config_module_dialog_new(con, ctxt); + return ctxt->conf_dialog; +} + +static const char _reg_cat[] = "extensions"; +static const char _reg_item[] = "extensions/e"; + +static void +_mixer_configure_registry_register(void) +{ + e_configure_registry_category_add(_reg_cat, 90, D_("Extensions"), NULL, + "enlightenment/extensions"); + e_configure_registry_item_add(_reg_item, 30, D_(_Name), NULL, + "enlightenment/e", + _mixer_module_config); +} + +static void +_mixer_configure_registry_unregister(void) +{ + e_configure_registry_item_del(_reg_item); + e_configure_registry_category_del(_reg_cat); +} + +static E_Config_DD * +_mixer_module_configuration_descriptor_new(E_Config_DD *gadget_conf_edd) +{ + E_Config_DD *conf_edd; + + conf_edd = E_CONFIG_DD_NEW("Mixer_Module_Config", E_Mixer_Module_Config); + if (!conf_edd) + return NULL; + E_CONFIG_VAL(conf_edd, E_Mixer_Module_Config, version, INT); + E_CONFIG_VAL(conf_edd, E_Mixer_Module_Config, default_gc_id, STR); + E_CONFIG_HASH(conf_edd, E_Mixer_Module_Config, gadgets, gadget_conf_edd); + + return conf_edd; +} + +static inline void +_mixer_module_configuration_descriptor_free(E_Config_DD *conf_edd) +{ + if (!conf_edd) + return; + E_CONFIG_DD_FREE(conf_edd); +} + +static E_Config_DD * +_mixer_gadget_configuration_descriptor_new(void) +{ + E_Config_DD *conf_edd; + + conf_edd = E_CONFIG_DD_NEW("Mixer_Gadget_Config", E_Mixer_Gadget_Config); + if (!conf_edd) + return NULL; + E_CONFIG_VAL(conf_edd, E_Mixer_Gadget_Config, lock_sliders, INT); + E_CONFIG_VAL(conf_edd, E_Mixer_Gadget_Config, show_locked, INT); + E_CONFIG_VAL(conf_edd, E_Mixer_Gadget_Config, card, STR); + E_CONFIG_VAL(conf_edd, E_Mixer_Gadget_Config, channel_name, STR); + + return conf_edd; +} + +static inline void +_mixer_gadget_configuration_descriptor_free(E_Config_DD *conf_edd) +{ + if (!conf_edd) + return; + E_CONFIG_DD_FREE(conf_edd); +} + +static E_Mixer_Module_Config * +_mixer_module_configuration_load(E_Config_DD *module_conf_edd) +{ + E_Mixer_Module_Config *conf; + + conf = e_config_domain_load(_conf_domain, module_conf_edd); + if (!conf) + return _mixer_module_configuration_new(); + + if (conf->version != MOD_CONF_VERSION) + { + _mixer_module_configuration_free(conf); + conf = _mixer_module_configuration_new(); + if (!conf) + return NULL; + + ecore_timer_add(1.0, _mixer_module_configuration_alert, + D_("Mixer Module Configuration data changed.
" + "Your old configuration has been replaced with " + "new default.
Sorry for the inconvenience.")); + return conf; + } + + return conf; +} + +static void +_mixer_module_configuration_setup(E_Mixer_Module_Context *ctxt) +{ + E_Config_DD *module_edd, *gadget_edd; + + gadget_edd = _mixer_gadget_configuration_descriptor_new(); + module_edd = _mixer_module_configuration_descriptor_new(gadget_edd); + ctxt->gadget_conf_edd = gadget_edd; + ctxt->module_conf_edd = module_edd; + ctxt->conf = _mixer_module_configuration_load(module_edd); +} + +static const char _act_increase[] = "volume_increase"; +static const char _act_decrease[] = "volume_decrease"; +static const char _act_mute[] = "volume_mute"; +static const char _lbl_increase[] = "Increase Volume"; +static const char _lbl_decrease[] = "Decrease Volume"; +static const char _lbl_mute[] = "Mute Volume"; + +static void +_mixer_actions_register(E_Mixer_Module_Context *ctxt) +{ + ctxt->actions.incr = e_action_add(_act_increase); + if (ctxt->actions.incr) + { + ctxt->actions.incr->func.go = _mixer_cb_volume_increase; + e_action_predef_name_set(D_(_Name), D_(_lbl_increase), _act_increase, + NULL, NULL, 0); + } + + ctxt->actions.decr = e_action_add(_act_decrease); + if (ctxt->actions.decr) + { + ctxt->actions.decr->func.go = _mixer_cb_volume_decrease; + e_action_predef_name_set(D_(_Name), D_(_lbl_decrease), _act_decrease, + NULL, NULL, 0); + } + + ctxt->actions.mute = e_action_add(_act_mute); + if (ctxt->actions.mute) + { + ctxt->actions.mute->func.go = _mixer_cb_volume_mute; + e_action_predef_name_set(D_(_Name), D_(_lbl_mute), _act_mute, + NULL, NULL, 0); + } +} + +static void +_mixer_actions_unregister(E_Mixer_Module_Context *ctxt) +{ + if (ctxt->actions.incr) + { + e_action_predef_name_del(D_(_Name), D_(_lbl_increase)); + e_action_del(_act_increase); + } + + if (ctxt->actions.decr) + { + e_action_predef_name_del(D_(_Name), D_(_lbl_decrease)); + e_action_del(_act_decrease); + } + + if (ctxt->actions.mute) + { + e_action_predef_name_del(D_(_Name), D_(_lbl_mute)); + e_action_del(_act_mute); + } +} + +EAPI void * +e_modapi_init(E_Module *m) +{ + E_Mixer_Module_Context *ctxt; + + ctxt = E_NEW(E_Mixer_Module_Context, 1); + if (!ctxt) + return NULL; + + _mixer_configure_registry_register(); + _mixer_actions_register(ctxt); + e_gadcon_provider_register(&_gc_class); + mixer_mod = m; + return ctxt; +} + +static void +_mixer_instances_free(E_Mixer_Module_Context *ctxt) +{ + while (ctxt->instances) + { + E_Mixer_Instance *inst; + + inst = ctxt->instances->data; + e_object_del(E_OBJECT(inst->gcc)); + } +} + +EAPI int +e_modapi_shutdown(E_Module *m) +{ + E_Mixer_Module_Context *ctxt; + + ctxt = m->data; + if (!ctxt) + return 0; + + _mixer_instances_free(ctxt); + + if (ctxt->conf_dialog) + e_object_del(E_OBJECT(ctxt->conf_dialog)); + + if (ctxt->mixer_dialog) + e_object_del(E_OBJECT(ctxt->mixer_dialog)); + + _mixer_configure_registry_unregister(); + _mixer_actions_unregister(ctxt); + e_gadcon_provider_unregister(&_gc_class); + + if (ctxt->conf) + { + _mixer_module_configuration_free(ctxt->conf); + _mixer_gadget_configuration_descriptor_free(ctxt->gadget_conf_edd); + _mixer_module_configuration_descriptor_free(ctxt->module_conf_edd); + } + + E_FREE(ctxt); + mixer_mod = NULL; + return 1; +} + +EAPI int +e_modapi_save(E_Module *m) +{ + E_Mixer_Module_Context *ctxt; + + ctxt = m->data; + if (!ctxt) + return 0; + if (!ctxt->conf) + return 1; + + return e_config_domain_save(_conf_domain, ctxt->module_conf_edd, ctxt->conf); +} diff --git a/src/modules/mixer/e_mod_main.h b/src/modules/mixer/e_mod_main.h new file mode 100644 index 000000000..5bc2f022c --- /dev/null +++ b/src/modules/mixer/e_mod_main.h @@ -0,0 +1,89 @@ +#define D_(str) dgettext(PACKAGE, str) + +#ifndef E_MOD_MAIN_H +#define E_MOD_MAIN_H + +#include "config.h" +#include "e_mod_system.h" +#include + +#define MOD_CONF_VERSION 3 + +typedef struct E_Mixer_Gadget_Config +{ + int lock_sliders; + int show_locked; + const char *card; + const char *channel_name; + const char *id; + E_Config_Dialog *dialog; + struct E_Mixer_Instance *instance; +} E_Mixer_Gadget_Config; + +typedef struct E_Mixer_Module_Config +{ + int version; + const char *default_gc_id; + Evas_Hash *gadgets; +} E_Mixer_Module_Config; + +typedef struct E_Mixer_Instance +{ + E_Gadcon_Client *gcc; + E_Gadcon_Popup *popup; + E_Menu *menu; + + struct + { + Evas_Object *gadget; + Evas_Object *label; + Evas_Object *left; + Evas_Object *right; + Evas_Object *mute; + Evas_Object *table; + Evas_Object *button; + struct + { + Ecore_X_Window win; + Ecore_Event_Handler *mouse_up; + Ecore_Event_Handler *key_down; + } input; + } ui; + + E_Mixer_System *sys; + E_Mixer_Channel *channel; + E_Mixer_Channel_State mixer_state; + E_Mixer_Gadget_Config *conf; +} E_Mixer_Instance; + +typedef struct E_Mixer_Module_Context +{ + E_Config_DD *module_conf_edd; + E_Config_DD *gadget_conf_edd; + E_Mixer_Module_Config *conf; + E_Config_Dialog *conf_dialog; + E_Mixer_Instance *default_instance; + Evas_List *instances; + E_Dialog *mixer_dialog; + struct st_mixer_actions + { + E_Action *incr; + E_Action *decr; + E_Action *mute; + } actions; +} E_Mixer_Module_Context; + +EAPI extern E_Module_Api e_modapi; +EAPI void *e_modapi_init(E_Module *m); +EAPI int e_modapi_shutdown(E_Module *m); +EAPI int e_modapi_save(E_Module *m); + +E_Config_Dialog *e_mixer_config_module_dialog_new(E_Container *con, E_Mixer_Module_Context *ctxt); +E_Config_Dialog *e_mixer_config_dialog_new(E_Container *con, E_Mixer_Gadget_Config *conf); +E_Dialog *e_mixer_app_dialog_new(E_Container *con, void (*func)(E_Dialog *dialog, void *data), void *data); +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); +const char *e_mixer_theme_path(void); + +#endif diff --git a/src/modules/mixer/e_mod_system.h b/src/modules/mixer/e_mod_system.h new file mode 100644 index 000000000..e65566115 --- /dev/null +++ b/src/modules/mixer/e_mod_system.h @@ -0,0 +1,48 @@ +#ifndef E_MOD_SYSTEM_H +#define E_MOD_SYSTEM_H + +#include + +typedef void E_Mixer_System; +typedef void E_Mixer_Channel; + +struct E_Mixer_Channel_State +{ + int mute; + int left; + int right; +}; +typedef struct E_Mixer_Channel_State E_Mixer_Channel_State; + +Evas_List *e_mixer_system_get_cards(void); +void e_mixer_system_free_cards(Evas_List *cards); +char *e_mixer_system_get_default_card(void); +char *e_mixer_system_get_card_name(const char *card); + + +E_Mixer_System *e_mixer_system_new(const char *card); +void e_mixer_system_del(E_Mixer_System *self); + +int e_mixer_system_callback_set(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data); + +Evas_List *e_mixer_system_get_channels(E_Mixer_System *self); +void e_mixer_system_free_channels(Evas_List *channels); +Evas_List *e_mixer_system_get_channels_names(E_Mixer_System *self); +void e_mixer_system_free_channels_names(Evas_List *channels_names); +char *e_mixer_system_get_default_channel_name(E_Mixer_System *self); +E_Mixer_Channel *e_mixer_system_get_channel_by_name(E_Mixer_System *self, const char *name); +char *e_mixer_system_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel); +void e_mixer_system_channel_del(E_Mixer_Channel *channel); + + +int e_mixer_system_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state); +int e_mixer_system_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state); +int e_mixer_system_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right); +int e_mixer_system_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right); +int e_mixer_system_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int *mute); +int e_mixer_system_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int mute); +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); + + +#endif /* E_MOD_SYSTEM_H */ diff --git a/src/modules/mixer/module.desktop.in b/src/modules/mixer/module.desktop.in new file mode 100644 index 000000000..54ff6d45b --- /dev/null +++ b/src/modules/mixer/module.desktop.in @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Link +Name=Mixer +Icon=e-module-mixer +Comment=Mixer Gadget

A module to provide a mixer for changing volume. diff --git a/src/modules/mixer/sys_alsa.c b/src/modules/mixer/sys_alsa.c new file mode 100644 index 000000000..75fd7b0a4 --- /dev/null +++ b/src/modules/mixer/sys_alsa.c @@ -0,0 +1,618 @@ +#include +#include +#include +#include +#include +#include +#include "e_mod_system.h" + +struct e_mixer_callback_desc +{ + int (*func)(void *data, E_Mixer_System *self); + void *data; + E_Mixer_System *self; + Ecore_Idler *idler; + Evas_List *handlers; +}; + + +static int _mixer_callback_add(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data); +static int _mixer_callback_del(E_Mixer_System *self, struct e_mixer_callback_desc *desc); + + +static int +_cb_dispatch(void *data) +{ + struct e_mixer_callback_desc *desc; + int r; + + desc = data; + snd_mixer_handle_events(desc->self); + r = desc->func(desc->data, desc->self); + desc->idler = NULL; + + if (!r) + _mixer_callback_del(desc->self, desc); /* desc is invalid then. */ + + return 0; +} + +static int +_cb_fd_handler(void *data, Ecore_Fd_Handler *fd_handler) +{ + struct e_mixer_callback_desc *desc; + + desc = data; + + if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) + { + desc->handlers = evas_list_remove(desc->handlers, fd_handler); + if (!desc->handlers) + { + E_Mixer_System *s; + int (*f)(void *, E_Mixer_System *); + void *d; + + s = desc->self; + f = desc->func; + d = desc->data; + _mixer_callback_del(s, desc); + _mixer_callback_add(s, f, d); + } + return 0; + } + + if (!desc->idler) + desc->idler = ecore_idler_add(_cb_dispatch, desc); + return 1; +} + +static int +_mixer_callback_add(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data) +{ + struct e_mixer_callback_desc *desc; + struct pollfd *pfds; + int len; + + len = snd_mixer_poll_descriptors_count(self); + if (len <= 0) + return 0; + + desc = malloc(sizeof(struct e_mixer_callback_desc)); + if (!desc) + return 0; + + desc->func = func; + desc->data = data; + desc->self = self; + desc->idler = NULL; + desc->handlers = NULL; + + pfds = alloca(len * sizeof(struct pollfd)); + len = snd_mixer_poll_descriptors(self, pfds, len); + if (len <= 0) + { + free(desc); + return 0; + } + + while (len > 0) + { + Ecore_Fd_Handler *fd_handler; + + len--; + fd_handler = ecore_main_fd_handler_add( + pfds[len].fd, ECORE_FD_READ, _cb_fd_handler, desc, NULL, NULL); + desc->handlers = evas_list_prepend(desc->handlers, fd_handler); + } + + snd_mixer_set_callback_private(self, desc); + + return 1; +} + +static int +_mixer_callback_del(E_Mixer_System *self, struct e_mixer_callback_desc *desc) +{ + Evas_List *l; + + snd_mixer_set_callback_private(self, NULL); + + for (l = desc->handlers; l != NULL; l = l->next) + ecore_main_fd_handler_del(l->data); + + evas_list_free(desc->handlers); + free(desc); + + return 1; +} + +static int +_mixer_callback_replace(E_Mixer_System *self, struct e_mixer_callback_desc *desc, int (*func)(void *data, E_Mixer_System *self), void *data) +{ + desc->func = func; + desc->data = data; + + return 1; +} + +E_Mixer_System * +e_mixer_system_new(const char *name) +{ + snd_mixer_t *handle; + int err; + + if (!name) + return NULL; + + err = snd_mixer_open(&handle, 0); + if (err < 0) + goto error_open; + + err = snd_mixer_attach(handle, name); + if (err < 0) + goto error_load; + + err = snd_mixer_selem_register(handle, NULL, NULL); + if (err < 0) + goto error_load; + + err = snd_mixer_load(handle); + if (err < 0) + goto error_load; + + return handle; + + error_load: + snd_mixer_close(handle); + error_open: + fprintf(stderr, "MIXER: Cannot get hardware info: %s\n", snd_strerror(err)); + return NULL; +} + +void +e_mixer_system_del(E_Mixer_System *self) +{ + struct e_mixer_callback_desc *desc; + + if (self <= 0) + return; + + desc = snd_mixer_get_callback_private(self); + if (desc) + _mixer_callback_del(self, desc); + + snd_mixer_close(self); +} + +int +e_mixer_system_callback_set(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data) +{ + struct e_mixer_callback_desc *desc; + + if (!self) + return 0; + + desc = snd_mixer_get_callback_private(self); + if (!desc) + { + if (func) + return _mixer_callback_add(self, func, data); + return 1; + } + else + { + if (func) + return _mixer_callback_replace(self, desc, func, data); + else + return _mixer_callback_del(self, desc); + } +} + +Evas_List * +e_mixer_system_get_cards(void) +{ + int err, card_num; + Evas_List *cards; + + cards = NULL; + card_num = -1; + while (((err = snd_card_next(&card_num)) == 0) && (card_num >= 0)) + { + snd_ctl_t *control; + char buf[256]; + + snprintf(buf, sizeof(buf), "hw:%d", card_num); + + if (snd_ctl_open(&control, buf, 0) < 0) + break; + snd_ctl_close(control); + cards = evas_list_append(cards, strdup(buf)); + } + + if (err < 0) + fprintf(stderr, "MIXER: Cannot get available card number: %s\n", + snd_strerror(err)); + + return cards; +} + +void +e_mixer_system_free_cards(Evas_List *cards) +{ + Evas_List *e; + + for (e = cards; e != NULL; e = e->next) + free(e->data); + + evas_list_free(cards); +} + +char * +e_mixer_system_get_default_card(void) +{ + static const char buf[] = "hw:0"; + snd_ctl_t *control; + + if (snd_ctl_open(&control, buf, 0) < 0) + return NULL; + snd_ctl_close(control); + return strdup(buf); +} + +char * +e_mixer_system_get_card_name(const char *card) +{ + snd_ctl_card_info_t *hw_info; + const char *name; + snd_ctl_t *control; + int err; + + if (!card) + return NULL; + + snd_ctl_card_info_alloca(&hw_info); + + err = snd_ctl_open(&control, card, 0); + if (err < 0) + return NULL; + + err = snd_ctl_card_info(control, hw_info); + if (err < 0) + { + fprintf(stderr, "MIXER: Cannot get hardware info: %s: %s\n", card, + snd_strerror(err)); + snd_ctl_close(control); + return NULL; + } + + snd_ctl_close(control); + name = snd_ctl_card_info_get_name(hw_info); + if (!name) + { + fprintf(stderr, "MIXER: Cannot get hardware name: %s\n", card); + return NULL; + } + + return strdup(name); +} + +Evas_List * +e_mixer_system_get_channels(E_Mixer_System *self) +{ + Evas_List *channels; + snd_mixer_elem_t *elem; + + if (!self) + return NULL; + + channels = NULL; + + elem = snd_mixer_first_elem(self); + for (; elem != NULL; elem = snd_mixer_elem_next(elem)) + { + if ((!snd_mixer_selem_is_active(elem)) || + (!snd_mixer_selem_has_playback_volume(elem))) + continue; + + channels = evas_list_append(channels, elem); + } + + return channels; +} + +void +e_mixer_system_free_channels(Evas_List *channels) +{ + evas_list_free(channels); +} + +Evas_List * +e_mixer_system_get_channels_names(E_Mixer_System *self) +{ + Evas_List *channels; + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + + if (!self) + return NULL; + + channels = NULL; + snd_mixer_selem_id_alloca(&sid); + + elem = snd_mixer_first_elem(self); + for (; elem != NULL; elem = snd_mixer_elem_next(elem)) + { + const char *name; + if ((!snd_mixer_selem_is_active(elem)) || + (!snd_mixer_selem_has_playback_volume(elem))) + continue; + + snd_mixer_selem_get_id(elem, sid); + name = snd_mixer_selem_id_get_name(sid); + if (name) + channels = evas_list_append(channels, strdup(name)); + } + + return channels; +} + +void +e_mixer_system_free_channels_names(Evas_List *channels_names) +{ + Evas_List *e; + + for (e = channels_names; e != NULL; e = e->next) + free(e->data); + + evas_list_free(channels_names); +} + +char * +e_mixer_system_get_default_channel_name(E_Mixer_System *self) +{ + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + + if (!self) + return NULL; + + snd_mixer_selem_id_alloca(&sid); + + elem = snd_mixer_first_elem(self); + for (; elem != NULL; elem = snd_mixer_elem_next(elem)) + { + const char *name; + if ((!snd_mixer_selem_is_active(elem)) || + (!snd_mixer_selem_has_playback_volume(elem))) + continue; + + snd_mixer_selem_get_id(elem, sid); + name = snd_mixer_selem_id_get_name(sid); + if (name) + return strdup(name); + } + + return NULL; +} + +E_Mixer_Channel * +e_mixer_system_get_channel_by_name(E_Mixer_System *self, const char *name) +{ + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + + if ((!self) || (!name)) + return NULL; + + snd_mixer_selem_id_alloca(&sid); + + elem = snd_mixer_first_elem(self); + for (; elem != NULL; elem = snd_mixer_elem_next(elem)) + { + const char *n; + if ((!snd_mixer_selem_is_active(elem)) || + (!snd_mixer_selem_has_playback_volume(elem))) + continue; + + snd_mixer_selem_get_id(elem, sid); + n = snd_mixer_selem_id_get_name(sid); + if (n && (strcmp(n, name) == 0)) + return elem; + } + + return NULL; +} + +void +e_mixer_system_channel_del(E_Mixer_Channel *channel) +{ +} + +char * +e_mixer_system_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + snd_mixer_selem_id_t *sid; + const char *n; + char *name; + + if ((!self) || (!channel)) + return NULL; + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_get_id(channel, sid); + n = snd_mixer_selem_id_get_name(sid); + if (n) + name = strdup(n); + else + name = NULL; + + return name; +} + +int +e_mixer_system_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right) +{ + long lvol, rvol, range, min, max; + + if ((!self) || (!channel) || (!left) || (!right)) + return 0; + + snd_mixer_handle_events(self); + snd_mixer_selem_get_playback_volume_range(channel, &min, &max); + range = max - min; + if (range < 1) + return 0; + + if (snd_mixer_selem_has_playback_channel(channel, 0)) + snd_mixer_selem_get_playback_volume(channel, 0, &lvol); + else + lvol = min; + + if (snd_mixer_selem_has_playback_channel(channel, 1)) + snd_mixer_selem_get_playback_volume(channel, 1, &rvol); + else + rvol = min; + + if (snd_mixer_selem_is_playback_mono(channel) || + snd_mixer_selem_has_playback_volume_joined(channel)) + rvol = lvol; + + *left = rint((double)(lvol - min) * 100 / (double)range); + *right = rint((double)(rvol - min) * 100 / (double)range); + + return 1; +} + +int +e_mixer_system_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right) +{ + long range, min, max, div; + int mode; + + if ((!self) || (!channel)) + return 0; + + snd_mixer_handle_events(self); + snd_mixer_selem_get_playback_volume_range(channel, &min, &max); + div = 100 + min; + if (div == 0) + { + div = 1; /* no zero-division */ + min++; + } + + range = max - min; + if (range < 1) + return 0; + + mode = 0; + if (left >= 0) + { + left = (((range * left) + (range / 2)) / div) - min; + mode |= 1; + } + + if (right >= 0) + { + right = (((range * right) + (range / 2)) / div) - min; + mode |= 2; + } + + if (mode & 1) + snd_mixer_selem_set_playback_volume(channel, 0, left); + + if ((!snd_mixer_selem_is_playback_mono(channel)) && + (!snd_mixer_selem_has_playback_volume_joined(channel)) && + (mode & 2)) + { + if (snd_mixer_selem_has_playback_channel(channel, 1)) + snd_mixer_selem_set_playback_volume(channel, 1, right); + } + + return 1; +} + +int +e_mixer_system_can_mute(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + if ((!self) || (!channel)) + return 0; + + snd_mixer_handle_events(self); + return (snd_mixer_selem_has_playback_switch(channel) || + snd_mixer_selem_has_playback_switch_joined(channel)); +} + +int +e_mixer_system_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int *mute) +{ + if ((!self) || (!channel) || (!mute)) + return 0; + + snd_mixer_handle_events(self); + if (snd_mixer_selem_has_playback_switch(channel) || + snd_mixer_selem_has_playback_switch_joined(channel)) + { + int m; + + /* XXX: not checking for return, always returns 0 even if it worked. + * alsamixer also don't check it. Bug? + */ + snd_mixer_selem_get_playback_switch(channel, 0, &m); + *mute = !m; + } + else + *mute = 0; + + return 1; +} + +int +e_mixer_system_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int mute) +{ + if ((!self) || (!channel)) + return 0; + + snd_mixer_handle_events(self); + if (snd_mixer_selem_has_playback_switch(channel) || + snd_mixer_selem_has_playback_switch_joined(channel)) + return snd_mixer_selem_set_playback_switch_all(channel, !mute); + else + return 0; +} + +int +e_mixer_system_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state) +{ + int r; + + if (!state) + return 0; + + r = e_mixer_system_get_mute(self, channel, &state->mute); + r &= e_mixer_system_get_volume(self, channel, &state->left, &state->right); + return r; +} + +int +e_mixer_system_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state) +{ + int r; + + if (!state) + return 0; + + r = e_mixer_system_set_mute(self, channel, state->mute); + r &= e_mixer_system_set_volume(self, channel, state->left, state->right); + return r; +} + +int +e_mixer_system_has_capture(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + if ((!self) || (!channel)) + return 0; + + return snd_mixer_selem_has_capture_switch(channel); +} diff --git a/src/modules/mixer/sys_dummy.c b/src/modules/mixer/sys_dummy.c new file mode 100644 index 000000000..783e4a123 --- /dev/null +++ b/src/modules/mixer/sys_dummy.c @@ -0,0 +1,166 @@ +#include "e_mod_system.h" +#include +#include + +static const char _name[] = "dummy"; + +E_Mixer_System * +e_mixer_system_new(const char *name) +{ + if (strcmp(name, _name) == 0) + return (E_Mixer_System *)-1; + else + return NULL; +} + +void +e_mixer_system_del(E_Mixer_System *self) +{ +} + +int +e_mixer_system_callback_set(E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data) +{ + return 0; +} + +Evas_List * +e_mixer_system_get_cards(void) +{ + return evas_list_append(NULL, _name); +} + +void +e_mixer_system_free_cards(Evas_List *cards) +{ + evas_list_free(cards); +} + +char * +e_mixer_system_get_default_card(void) +{ + return strdup(_name); +} + +char * +e_mixer_system_get_card_name(const char *card) +{ + if (strcmp(card, _name) == 0) + return strdup(_name); + else + return NULL; +} + +Evas_List * +e_mixer_system_get_channels(E_Mixer_System *self) +{ + return evas_list_append(NULL, (void *)-2); +} + +void +e_mixer_system_free_channels(Evas_List *channels) +{ + evas_list_free(channels); +} + +Evas_List * +e_mixer_system_get_channels_names(E_Mixer_System *self) +{ + return evas_list_append(NULL, _name); +} + +void +e_mixer_system_free_channels_names(Evas_List *channels_names) +{ + evas_list_free(channels_names); +} + +char * +e_mixer_system_get_default_channel_name(E_Mixer_System *self) +{ + return strdup(_name); +} + +E_Mixer_Channel * +e_mixer_system_get_channel_by_name(E_Mixer_System *self, const char *name) +{ + if (strcmp(name, _name) == 0) + return (E_Mixer_Channel *)-2; + else + return NULL; +} + +void +e_mixer_system_channel_del(E_Mixer_Channel *channel) +{ +} + +char * +e_mixer_system_get_channel_name(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + if (channel == (E_Mixer_Channel *)-2) + return strdup(_name); + else + return NULL; +} + +int +e_mixer_system_get_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int *left, int *right) +{ + if (left) + *left = 0; + if (right) + *right = 0; + + return 1; +} + +int +e_mixer_system_set_volume(E_Mixer_System *self, E_Mixer_Channel *channel, int left, int right) +{ + return 0; +} + +int +e_mixer_system_can_mute(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + return 1; +} + +int +e_mixer_system_get_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int *mute) +{ + if (mute) + *mute = 1; + + return 1; +} + +int +e_mixer_system_set_mute(E_Mixer_System *self, E_Mixer_Channel *channel, int mute) +{ + return 0; +} + +int +e_mixer_system_get_state(E_Mixer_System *self, E_Mixer_Channel *channel, E_Mixer_Channel_State *state) +{ + const E_Mixer_Channel_State def = {1, 0, 0}; + + if (state) + *state = def; + + return 1; +} + +int +e_mixer_system_set_state(E_Mixer_System *self, E_Mixer_Channel *channel, const E_Mixer_Channel_State *state) +{ + return 0; +} + +int +e_mixer_system_has_capture(E_Mixer_System *self, E_Mixer_Channel *channel) +{ + return 0; +}