summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlavio Ceolin <flavio.ceolin@gmail.com>2013-09-01 19:41:13 -0300
committerFlavio Ceolin <flavio.ceolin@gmail.com>2014-04-03 16:23:04 -0300
commit196cdb70b84958dd827797f86e78abd798e48cf7 (patch)
treefa94c67c0794b8dd8d64b8e216daccc20801c584
parentc984cf740b8625571a5d1617e0c3911fc27c11f9 (diff)
Adding common code
Init/shutdown used libraries and including common headers.
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am59
-rw-r--r--Makefile_Theme.am78
-rw-r--r--README4
-rw-r--r--configure.ac22
-rw-r--r--data/themes/default.edc6
-rw-r--r--data/themes/images/inset_round_hilight.pngbin0 -> 1383 bytes
-rw-r--r--data/themes/images/inset_round_shading.pngbin0 -> 6801 bytes
-rw-r--r--data/themes/images/inset_round_shadow.pngbin0 -> 2050 bytes
-rw-r--r--data/themes/images/led_dot_white.pngbin0 -> 525 bytes
-rw-r--r--data/themes/images/module_icon.pngbin0 -> 4940 bytes
-rw-r--r--data/themes/images/speaker.pngbin0 -> 19745 bytes
-rw-r--r--data/themes/main.edc24
-rw-r--r--data/themes/mixer.edc245
-rw-r--r--data/themes/naviframe.edc155
-rw-r--r--data/themes/playbacks.edc199
-rw-r--r--data/xml/module.desktop.in12
-rw-r--r--src/bin/main.c31
-rw-r--r--src/bin/main_window.c160
-rw-r--r--src/bin/main_window.h12
-rw-r--r--src/bin/playbacks_view.c375
-rw-r--r--src/bin/playbacks_view.h12
-rw-r--r--src/bin/sinks_view.c320
-rw-r--r--src/bin/sinks_view.h13
-rw-r--r--src/bin/sources_view.c263
-rw-r--r--src/bin/sources_view.h13
-rw-r--r--src/lib/common.c64
-rw-r--r--src/lib/common.h31
-rw-r--r--src/lib/epulse.c781
-rw-r--r--src/lib/epulse.h63
-rw-r--r--src/lib/epulse_ml.c (renamed from src/epulse_ml.c)14
-rw-r--r--src/main.c18
-rw-r--r--src/module/e_mod_main.c586
-rw-r--r--src/module/e_mod_main.h12
34 files changed, 3532 insertions, 43 deletions
diff --git a/.gitignore b/.gitignore
index b4b2bb9..e6fb1b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,4 +31,5 @@ missing
31stamp-h1 31stamp-h1
32*.edj 32*.edj
33*.eet 33*.eet
34src/epulse \ No newline at end of file 34src/bin/epulse
35*.desktop \ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
index b83945d..63de1e8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,7 @@ AM_CFLAGS = \
4 -DPACKAGE_DATA_DIR=\"$(pkgdatadir)/\" \ 4 -DPACKAGE_DATA_DIR=\"$(pkgdatadir)/\" \
5 -DPACKAGE_BIN_DIR=\"$(bindir)\" \ 5 -DPACKAGE_BIN_DIR=\"$(bindir)\" \
6 -DPACKAGE_LIB_DIR=\"$(libdir)\" \ 6 -DPACKAGE_LIB_DIR=\"$(libdir)\" \
7 -I$(top_srcdir)/src/lib/ \
7 @EFL_CFLAGS@ \ 8 @EFL_CFLAGS@ \
8 @PULSE_CFLAGS@ 9 @PULSE_CFLAGS@
9 10
@@ -22,21 +23,54 @@ MAINTAINERCLEANFILES = \
22 mkinstalldirs 23 mkinstalldirs
23EXTRA_DIST = 24EXTRA_DIST =
24 25
26lib_LTLIBRARIES = \
27 src/lib/libepulse.la
28
29src_lib_libepulse_la_SOURCES = \
30 src/lib/common.c \
31 src/lib/common.h \
32 src/lib/epulse_ml.c \
33 src/lib/epulse.c \
34 src/lib/epulse.h
35
36src_lib_libepulse_la_LIBADD = @EFL_LIBS@ @PULSE_LIBS@
37src_lib_libepulse_la_LDFLAGS = -no-undefined -avoid-version
38src_lib_libepulse_la_LIBTOOLFLAGS = --tag=disable-static
39
25bin_PROGRAMS = \ 40bin_PROGRAMS = \
26 src/epulse 41 src/bin/epulse
27 42
28src_epulse_LDADD = \ 43src_bin_epulse_LDADD = \
29 @EFL_LIBS@ \ 44 $(top_builddir)/src/lib/libepulse.la \
45 @EFL_LIBS@ \
30 @PULSE_LIBS@ 46 @PULSE_LIBS@
31 47
32src_epulse_SOURCES = \ 48src_bin_epulse_SOURCES = \
33 src/main.c 49 src/bin/main_window.h \
50 src/bin/main_window.c \
51 src/bin/playbacks_view.h \
52 src/bin/playbacks_view.c \
53 src/bin/sinks_view.h \
54 src/bin/sinks_view.c \
55 src/bin/sources_view.h \
56 src/bin/sources_view.c \
57 src/bin/main.c
58
59moduledir = $(pkgdir)/$(MODULE_ARCH)
60module_LTLIBRARIES = src/module/module.la
61
62src_module_module_la_SOURCES = \
63 src/module/e_mod_main.c \
64 src/module/e_mod_main.h
65
66src_module_module_la_LIBADD = \
67 $(top_builddir)/src/lib/libepulse.la \
68 @EFL_LIBS@ \
69 @PULSE_LIBS@
34 70
35# Themes are compiled with edje_cc given by user (cross-compile) 71src_module_module_la_LDFLAGS = -module -avoid-version
36EDJE_CC = @edje_cc@ 72
37EDJE_FLAGS_VERBOSE_ = 73include Makefile_Theme.am
38EDJE_FLAGS_VERBOSE_0 =
39EDJE_FLAGS_VERBOSE_1 = -v
40 74
41EXTRA_DIST += \ 75EXTRA_DIST += \
42 mksnapshot \ 76 mksnapshot \
@@ -44,8 +78,3 @@ EXTRA_DIST += \
44 autogen.sh 78 autogen.sh
45 79
46clean-local: 80clean-local:
47
48
49
50
51
diff --git a/Makefile_Theme.am b/Makefile_Theme.am
new file mode 100644
index 0000000..076f017
--- /dev/null
+++ b/Makefile_Theme.am
@@ -0,0 +1,78 @@
1# Themes are compiled with edje_cc given by user (cross-compile)
2EDJE_CC = @edje_cc@
3EDJE_FLAGS_VERBOSE_ =
4EDJE_FLAGS_VERBOSE_0 =
5EDJE_FLAGS_VERBOSE_1 = -v
6EDJE_FLAGS = $(EDJE_FLAGS_VERBOSE_$(V)) -id $(srcdir)/data/themes/images -fd $(srcdir)/data/themes/fonts
7
8filesdir = $(pkgdatadir)/data/themes
9files_DATA = data/themes/default.edj
10
11modulethemedir = @pkgdir@
12moduletheme_DATA = data/themes/mixer.edj \
13 data/xml/module.desktop
14
15AM_V_EDJ = $(am__v_EDJ_$(V))
16am__v_EDJ_ = $(am__v_EDJ_$(AM_DEFAULT_VERBOSITY))
17am__v_EDJ_0 = @echo " EDJ " $@;
18
19THEME_IMAGES =
20 inset_round_hilight.png \
21 inset_round_shading.png \
22 inset_round_shadow.png \
23 led_dot_white.png \
24 module_icon.png \
25 speaker.png
26
27THEME_FONTS =
28
29THEMES = \
30 data/themes/default.edc \
31 data/themes/playbacks.edc
32
33THEMES_MODULE = \
34 data/themes/mixer.edc
35
36EXTRA_DIST += $(THEMES) $(THEME_IMAGES) $(THEME_FONTS)
37
38data/themes/mixer.edj: $(THEMES_MODULE) $(THEME_IMAGES) $(THEME_FONTS)
39 $(AM_V_EDJ)$(EDJE_CC) $(EDJE_FLAGS) \
40 $< $
41
42
43data/themes/default.edj: $(THEMES) $(THEME_IMAGES) $(THEME_FONTS)
44 $(AM_V_EDJ)$(EDJE_CC) $(EDJE_FLAGS) \
45 $< $@
46
47clean-local:
48 rm -f $(builddir)/data/themes/default.edj $(builddir)/data/themes/e-module-pulse-mixer.edj
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
diff --git a/README b/README
index e69de29..25bc81c 100644
--- a/README
+++ b/README
@@ -0,0 +1,4 @@
1Epulse - Enlightenment volume control
2
3A volume manager using efl and pulseaudio technologies.
4To build is necessary efl, elementary and pulseaudio devel packages in the system. \ No newline at end of file
diff --git a/configure.ac b/configure.ac
index 5642869..a86f54a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
1AC_PREREQ([2.69]) 1ppAC_PREREQ([2.69])
2AC_INIT([epulse], [0.1], [flavio.ceolin@gmail.com]) 2AC_INIT([epulse], [0.1], [flavio.ceolin@gmail.com])
3 3
4AM_INIT_AUTOMAKE([foreign subdir-objects]) 4AM_INIT_AUTOMAKE([foreign subdir-objects])
@@ -35,6 +35,7 @@ PKG_CHECK_MODULES([EFL],
35 eet 35 eet
36 ecore 36 ecore
37 ecore-file 37 ecore-file
38 enlightenment
38 ]) 39 ])
39 40
40PKG_CHECK_MODULES([PULSE], 41PKG_CHECK_MODULES([PULSE],
@@ -43,6 +44,20 @@ PKG_CHECK_MODULES([PULSE],
43 libpulse 44 libpulse
44 ]) 45 ])
45 46
47release=$(pkg-config --variable=release enlightenment)
48MODULE_ARCH="$host_os-$host_cpu-$release"
49AC_SUBST(MODULE_ARCH)
50AC_DEFINE_UNQUOTED(MODULE_ARCH, "$MODULE_ARCH", "Module architecture")
51AC_DEFINE_UNQUOTED(MODULE_VERSION, "$VERSION", "Module version")
52
53pkgdir=$(pkg-config --variable=modules enlightenment)/${PACKAGE}
54AC_ARG_ENABLE(homedir-install,
55 AS_HELP_STRING([--enable-homedir-install], [Install module in homedir]),
56 [ pkgdir="${HOME}/.e/e/modules/${PACKAGE}" ]
57)
58
59AC_SUBST(pkgdir)
60
46# Checks for header files. 61# Checks for header files.
47 62
48# Checks for typedefs, structures, and compiler characteristics. 63# Checks for typedefs, structures, and compiler characteristics.
@@ -52,7 +67,9 @@ PKG_CHECK_MODULES([PULSE],
52AC_CONFIG_FILES([ 67AC_CONFIG_FILES([
53Makefile 68Makefile
54]) 69])
55AC_OUTPUT 70AC_OUTPUT([
71data/xml/module.desktop
72])
56 73
57 74
58echo 75echo
@@ -74,4 +91,5 @@ echo " LDFLAGS..................: $LDFLAGS"
74echo 91echo
75echo "Installation...............: make install (as root if needed, with 'su' or 'sudo')" 92echo "Installation...............: make install (as root if needed, with 'su' or 'sudo')"
76echo " prefix...................: $prefix" 93echo " prefix...................: $prefix"
94echo " module...................: $pkgdir"
77echo 95echo
diff --git a/data/themes/default.edc b/data/themes/default.edc
new file mode 100644
index 0000000..dfc410b
--- /dev/null
+++ b/data/themes/default.edc
@@ -0,0 +1,6 @@
1collections {
2
3#include "main.edc"
4#include "naviframe.edc"
5#include "playbacks.edc"
6}
diff --git a/data/themes/images/inset_round_hilight.png b/data/themes/images/inset_round_hilight.png
new file mode 100644
index 0000000..c2cf394
--- /dev/null
+++ b/data/themes/images/inset_round_hilight.png
Binary files differ
diff --git a/data/themes/images/inset_round_shading.png b/data/themes/images/inset_round_shading.png
new file mode 100644
index 0000000..e56ac25
--- /dev/null
+++ b/data/themes/images/inset_round_shading.png
Binary files differ
diff --git a/data/themes/images/inset_round_shadow.png b/data/themes/images/inset_round_shadow.png
new file mode 100644
index 0000000..37e1e89
--- /dev/null
+++ b/data/themes/images/inset_round_shadow.png
Binary files differ
diff --git a/data/themes/images/led_dot_white.png b/data/themes/images/led_dot_white.png
new file mode 100644
index 0000000..4c1883e
--- /dev/null
+++ b/data/themes/images/led_dot_white.png
Binary files differ
diff --git a/data/themes/images/module_icon.png b/data/themes/images/module_icon.png
new file mode 100644
index 0000000..e14cb7b
--- /dev/null
+++ b/data/themes/images/module_icon.png
Binary files differ
diff --git a/data/themes/images/speaker.png b/data/themes/images/speaker.png
new file mode 100644
index 0000000..3db381c
--- /dev/null
+++ b/data/themes/images/speaker.png
Binary files differ
diff --git a/data/themes/main.edc b/data/themes/main.edc
new file mode 100644
index 0000000..47eb3ff
--- /dev/null
+++ b/data/themes/main.edc
@@ -0,0 +1,24 @@
1group {
2 name: "elm/layout/main/default";
3
4 parts {
5 part {
6 name: "bg";
7 type: RECT;
8 mouse_events: 1;
9 description {
10 state: "default";
11 color: 0 0 0 0;
12 }
13 }
14
15 part {
16 name: "main_content";
17 type: SWALLOW;
18 mouse_events: 1;
19 description {
20 state: "default";
21 }
22 }
23 }
24}
diff --git a/data/themes/mixer.edc b/data/themes/mixer.edc
new file mode 100644
index 0000000..75fd82b
--- /dev/null
+++ b/data/themes/mixer.edc
@@ -0,0 +1,245 @@
1collections {
2 group {
3 name: "icon";
4 max: 24 24;
5 images {
6 image: "module_icon.png" COMP;
7 }
8 parts {
9 part {
10 name: "image";
11 mouse_events: 0;
12 type: IMAGE;
13 description {
14 state: "default" 0.0;
15 image.normal: "module_icon.png";
16 }
17 }
18 }
19 }
20
21 group { name: "e/modules/mixer/main";
22 images.image: "speaker.png" COMP;
23 images.image: "inset_round_hilight.png" COMP;
24 images.image: "inset_round_shadow.png" COMP;
25 images.image: "inset_round_shading.png" COMP;
26 images.image: "led_dot_white.png" COMP;
27 max: 160 160;
28 min: 16 16;
29 script {
30 public message(Msg_Type:type, id, ...) {
31 if ((type == MSG_INT_SET) && (id == 0)) {
32 new m, l, r;
33
34 m = getarg(2);
35 l = getarg(3);
36 r = getarg(4);
37
38 if (m) {
39 run_program(PROGRAM:"mute");
40 } else {
41 run_program(PROGRAM:"unmute");
42 }
43
44 if (l <= 0) {
45 run_program(PROGRAM:"l0-off");
46 run_program(PROGRAM:"l1-off");
47 run_program(PROGRAM:"l2-off");
48 run_program(PROGRAM:"l3-off");
49 run_program(PROGRAM:"l4-off");
50 } else if (l <= 20) {
51 run_program(PROGRAM:"l0-on");
52 run_program(PROGRAM:"l1-off");
53 run_program(PROGRAM:"l2-off");
54 run_program(PROGRAM:"l3-off");
55 run_program(PROGRAM:"l4-off");
56 } else if (l <= 40) {
57 run_program(PROGRAM:"l0-on");
58 run_program(PROGRAM:"l1-on");
59 run_program(PROGRAM:"l2-off");
60 run_program(PROGRAM:"l3-off");
61 run_program(PROGRAM:"l4-off");
62 } else if (l <= 60) {
63 run_program(PROGRAM:"l0-on");
64 run_program(PROGRAM:"l1-on");
65 run_program(PROGRAM:"l2-on");
66 run_program(PROGRAM:"l3-off");
67 run_program(PROGRAM:"l4-off");
68 } else if (l <= 80) {
69 run_program(PROGRAM:"l0-on");
70 run_program(PROGRAM:"l1-on");
71 run_program(PROGRAM:"l2-on");
72 run_program(PROGRAM:"l3-on");
73 run_program(PROGRAM:"l4-off");
74 } else {
75 run_program(PROGRAM:"l0-on");
76 run_program(PROGRAM:"l1-on");
77 run_program(PROGRAM:"l2-on");
78 run_program(PROGRAM:"l3-on");
79 run_program(PROGRAM:"l4-on");
80 }
81
82 if (r <= 0) {
83 run_program(PROGRAM:"r0-off");
84 run_program(PROGRAM:"r1-off");
85 run_program(PROGRAM:"r2-off");
86 run_program(PROGRAM:"r3-off");
87 run_program(PROGRAM:"r4-off");
88 } else if (r <= 20) {
89 run_program(PROGRAM:"r0-on");
90 run_program(PROGRAM:"r1-off");
91 run_program(PROGRAM:"r2-off");
92 run_program(PROGRAM:"r3-off");
93 run_program(PROGRAM:"r4-off");
94 } else if (r <= 40) {
95 run_program(PROGRAM:"r0-on");
96 run_program(PROGRAM:"r1-on");
97 run_program(PROGRAM:"r2-off");
98 run_program(PROGRAM:"r3-off");
99 run_program(PROGRAM:"r4-off");
100 } else if (r <= 60) {
101 run_program(PROGRAM:"r0-on");
102 run_program(PROGRAM:"r1-on");
103 run_program(PROGRAM:"r2-on");
104 run_program(PROGRAM:"r3-off");
105 run_program(PROGRAM:"r4-off");
106 } else if (r <= 80) {
107 run_program(PROGRAM:"r0-on");
108 run_program(PROGRAM:"r1-on");
109 run_program(PROGRAM:"r2-on");
110 run_program(PROGRAM:"r3-on");
111 run_program(PROGRAM:"r4-off");
112 } else {
113 run_program(PROGRAM:"r0-on");
114 run_program(PROGRAM:"r1-on");
115 run_program(PROGRAM:"r2-on");
116 run_program(PROGRAM:"r3-on");
117 run_program(PROGRAM:"r4-on");
118 }
119 }
120 }
121 }
122 parts {
123 part { name: "base-sh";
124 description { state: "default" 0.0;
125 rel1.to: "base";
126 rel1.offset: 0 -1;
127 rel2.to: "base";
128 rel2.offset: -1 -2;
129 image.normal: "inset_round_shadow.png";
130 }
131 }
132 part { name: "base-hi";
133 description { state: "default" 0.0;
134 rel1.to: "base";
135 rel1.offset: 0 1;
136 rel2.to: "base";
137 rel2.offset: -1 0;
138 image.normal: "inset_round_hilight.png";
139 }
140 }
141 part { name: "base";
142 description { state: "default" 0.0;
143 rel1.relative: (25/380) (25/380);
144 rel2.relative: (365/380) (365/380);
145 aspect: 1.0 1.0; aspect_preference: BOTH;
146 image.normal: "speaker.png";
147 }
148 }
149 part { name: "state"; type: RECT;
150 description { state: "default" 0.0;
151 rel1.relative: -1.0 -1.0;
152 rel2.relative: 2.0 2.0;
153 color: 51 153 255 255;
154 }
155 description { state: "mute" 0.0;
156 inherit: "default" 0.0;
157 color: 255 153 51 255;
158 }
159 }
160#define LED(_NAME, _BASE, _X, _SIZE) \
161 part { name: _NAME; type: RECT; \
162 clip_to: "state"; \
163 description { state: "default" 0.0; \
164 rel1.relative: ((_BASE+(_X*8))/160) (80/160); \
165 rel2.relative: ((_BASE+(_X*8))/160) (80/160); \
166 min: 1 1; \
167 max: 1 1; \
168 visible: 0; \
169 color: 255 255 255 0; \
170 } \
171 description { state: "active" 0.0; \
172 inherit: "default" 0.0; \
173 visible: 1; \
174 color: 255 255 255 255; \
175 } \
176 } \
177 part { name: _NAME"g"; \
178 clip_to: "state"; \
179 description { state: "default" 0.0; \
180 rel1.to: _NAME; \
181 rel2.to: _NAME; \
182 image.normal: "led_dot_white.png"; \
183 min: _SIZE _SIZE; \
184 visible: 0; \
185 color: 255 255 255 0; \
186 } \
187 description { state: "active" 0.0; \
188 inherit: "default" 0.0; \
189 visible: 1; \
190 color: 255 255 255 255; \
191 } \
192 }
193 LED("r0", 99, 0, 1)
194 LED("r1", 99, 1, 9)
195 LED("r2", 99, 2, 15)
196 LED("r3", 99, 3, 21)
197 LED("r4", 99, 4, 27)
198 LED("l0", 61, 0, 1)
199 LED("l1", 61, -1, 9)
200 LED("l2", 61, -2, 15)
201 LED("l3", 61, -3, 21)
202 LED("l4", 61, -4, 27)
203#undef LED
204 part { name: "over"; type: RECT;
205 description { state: "default" 0.0;
206 color: 255 255 255 0;
207 }
208 }
209 }
210 programs {
211 program { name: "mute";
212 action: STATE_SET "mute" 0.0;
213 transition: LINEAR 0.3;
214 target: "state";
215 }
216 program { name: "unmute";
217 action: STATE_SET "default" 0.0;
218 transition: LINEAR 0.2;
219 target: "state";
220 }
221#define PROG(_NAME) \
222 program { name: _NAME"-on"; \
223 action: STATE_SET "active" 0.0; \
224 target: _NAME; \
225 target: _NAME"g"; \
226 } \
227 program { name: _NAME"-off"; \
228 action: STATE_SET "default" 0.0; \
229 target: _NAME; \
230 target: _NAME"g"; \
231 }
232 PROG("r0")
233 PROG("r1")
234 PROG("r2")
235 PROG("r3")
236 PROG("r4")
237 PROG("l0")
238 PROG("l1")
239 PROG("l2")
240 PROG("l3")
241 PROG("l4")
242#undef PROG
243 }
244 }
245}
diff --git a/data/themes/naviframe.edc b/data/themes/naviframe.edc
new file mode 100644
index 0000000..0afeb0b
--- /dev/null
+++ b/data/themes/naviframe.edc
@@ -0,0 +1,155 @@
1group {
2 name:"elm/naviframe/base/no_transition";
3 parts {
4 part {
5 name: "base";
6 type: RECT;
7 description {
8 state: "default" 0.0;
9 }
10 }
11 }
12}
13
14group {
15 name: "elm/naviframe/item/basic/no_transition";
16 parts {
17 part {
18 name: "clip";
19 type: RECT;
20 description {
21 state: "default" 0.0;
22 }
23 }
24 part {
25 name: "base";
26 type: SPACER;
27 description {
28 state: "default" 0.0;
29 }
30 description {
31 state: "prev" 0.0;
32 inherit: "default" 0.0;
33 rel1.relative: -1.0 0.0;
34 rel2.relative: 0.0 1.0;
35 }
36 description {
37 state: "next" 0.0;
38 inherit: "default" 0.0;
39 rel1.relative: 1.0 0.0;
40 rel2.relative: 2.0 1.0;
41 }
42 }
43 part {
44 name: "elm.swallow.content";
45 type: SWALLOW;
46 clip_to: "elements_clip";
47 description {
48 state: "default" 0.0;
49 rel1.to: "base";
50 rel2.to: "base";
51 }
52 }
53 part {
54 name: "elements_clip";
55 type: RECT;
56 clip_to: "clip";
57 description {
58 state: "default" 0.0;
59 rel1.to: "base";
60 rel2.to: "base";
61 }
62 description {
63 state: "hidden" 0.0;
64 inherit: "default" 0.0;
65 color: 255 255 255 0;
66 visible: 0;
67 }
68 description { state: "next" 0.0;
69 inherit: "hidden" 0.0;
70 }
71 description { state: "prev" 0.0;
72 inherit: "hidden" 0.0;
73 }
74 }
75 }
76 programs {
77 program {
78 signal: "elm,state,visible"; source: "elm";
79 action: STATE_SET "default" 0.0;
80 target: "elm.swallow.content";
81 target: "elements_clip";
82 }
83 program {
84 signal: "elm,state,cur,pushed"; source: "elm";
85 action: STATE_SET "default" 0.0;
86 target: "base";
87 target: "elements_clip";
88 after: "pushed2";
89 }
90 program {
91 name: "pushed2";
92 action: STATE_SET "prev" 0.0;
93 target: "base";
94 target: "elements_clip";
95 after: "pushed3";
96 }
97 program {
98 name: "pushed3";
99 action: SIGNAL_EMIT "elm,action,pushed,finished" "elm";
100 }
101 program {
102 signal: "elm,state,cur,popped"; source: "elm";
103 action: STATE_SET "default" 0.0;
104 target: "base";
105 target: "elements_clip";
106 after: "popped2";
107 }
108 program {
109 name: "popped2";
110 action: STATE_SET "next" 0.0;
111 target: "base";
112 target: "elements_clip";
113 after: "popped3";
114 }
115 program {
116 name: "popped3";
117 action: SIGNAL_EMIT "elm,action,popped,finished" "elm";
118 }
119 program {
120 signal: "elm,state,new,pushed"; source: "elm";
121 action: STATE_SET "next" 0.0;
122 target: "base";
123 target: "elements_clip";
124 after: "pushedb2";
125 }
126 program {
127 name: "pushedb2";
128 action: STATE_SET "default" 0.0;
129 target: "base";
130 target: "elements_clip";
131 after: "pushedb3";
132 }
133 program {
134 name: "pushedb3";
135 action: SIGNAL_EMIT "elm,action,show,finished" "elm";
136 }
137 program {
138 signal: "elm,state,prev,popped"; source: "elm";
139 action: STATE_SET "prev" 0.0;
140 target: "base";
141 target: "elements_clip";
142 after: "poppedb2";
143 }
144 program { name: "poppedb2";
145 action: STATE_SET "default" 0.0;
146 target: "base";
147 target: "elements_clip";
148 after: "poppedb3";
149 }
150 program {
151 name: "poppedb3";
152 action: SIGNAL_EMIT "elm,action,show,finished" "elm";
153 }
154 }
155}
diff --git a/data/themes/playbacks.edc b/data/themes/playbacks.edc
new file mode 100644
index 0000000..302d38c
--- /dev/null
+++ b/data/themes/playbacks.edc
@@ -0,0 +1,199 @@
1#define ADD_SPACER(_name, _min_x, _min_y, _max_x, _max_y, _align_x, \
2 _align_y, _rel1_to_x, _rel1_to_y, _rel1_x, _rel1_y, \
3 _rel2_to_x, _rel2_to_y, _rel2_x, _rel2_y) \
4 part { \
5 name: _name; \
6 type: SPACER; \
7 scale: 1; \
8 description { \
9 state: "default" 0.0; \
10 min: _min_x _min_y; \
11 max: _max_x _max_y; \
12 fixed: 1 1; \
13 align: _align_x _align_y; \
14 rel1 { \
15 to_x: _rel1_to_x; \
16 to_y: _rel1_to_y; \
17 relative: _rel1_x _rel1_y; \
18 } \
19 rel2 { \
20 to_x: _rel2_to_x; \
21 to_y: _rel2_to_y; \
22 relative: _rel2_x _rel2_y; \
23 } \
24 }
25
26group {
27 name: "elm/layout/playbacks/default";
28 alias: "elm/layout/sinks/default";
29 alias: "elm/layout/sources/default";
30
31 parts {
32 part {
33 name: "bg";
34 type: RECT;
35 mouse_events: 1;
36 description {
37 state: "default";
38 color: 0 0 0 0;
39 }
40 }
41
42 part {
43 name: "list";
44 type: SWALLOW;
45 description {
46 state: "default";
47 }
48 }
49 }
50}
51
52group {
53 name: "elm/genlist/item/playbacks/default";
54 alias: "elm/genlist/item/sinks/default";
55 alias: "elm/genlist/item/sources/default";
56
57 data {
58 item: "texts" "name";
59 item: "contents" "slider mute icon hover";
60 }
61
62 color_classes {
63 color_class {
64 name: "playback_name";
65 color: 0 0 0 255;
66 }
67 }
68
69 parts {
70 part {
71 name: "base";
72 type: RECT;
73 mouse_events: 1;
74 scale: 1;
75 description {
76 state: "default";
77 min: 0 120;
78 max: -1 -1;
79 color: 64 64 64 255;
80 }
81 description {
82 state: "odd";
83 min: 0 120;
84 max: -1 -1;
85 color: 56 56 56 255;
86 }
87 }
88 part {
89 name: "mute";
90 type: SWALLOW;
91 scale: 1;
92 description {
93 state: "default";
94 align: 1.0 0.0;
95 fixed: 1 1;
96 rel1.relative: 1.0 0.0;
97 rel2.relative: 1.0 0.0;
98 }
99 }
100
101 part {
102 name: "hover";
103 type: SWALLOW;
104 scale: 1;
105 description {
106 state: "default";
107 align: 1.0 0.0;
108 fixed: 1 1;
109 rel1 {
110 relative: 0.0 0.0;
111 to: "mute";
112 }
113 rel2 {
114 relative: 0.0 0.0;
115 to: "mute";
116 }
117 }
118 }
119
120 part {
121 name: "icon";
122 type: SWALLOW;
123 scale: 1;
124 description {
125 state: "default";
126 align: 0.0 0.0;
127 fixed: 1 1;
128 min: 40 40;
129 max: 40 40;
130 rel1.relative: 0.0 0.0;
131 rel2.relative: 0.0 0.0;
132 }
133 }
134
135 ADD_SPACER("spacer.icon", 20, 20, 20, 20, 0.0, 0.5, "icon", "icon",
136 1.0, 0.0, "icon", "icon", 1.0, 1.0)
137
138 part {
139 name: "name";
140 type: TEXT;
141 mouse_events: 0;
142 scale: 1;
143 description {
144 state: "default";
145 align: 0.0 0.5;
146 fixed: 1 1;
147 rel1 {
148 to: "spacer.icon";
149 relative: 1.0 0.0;
150 }
151 rel2 {
152 to_x: "hover";
153 to_y: "spacer.icon";
154 relative: 0.0 1.0;
155 }
156 color_class: "playback_name";
157 text {
158 size: 10;
159 align: 0.0 0.5;
160 min: 1 1;
161 }
162 }
163
164 ADD_SPACER("spacer.mute", 0, 20, 0, 20, 0.0, 0.0, "base", "mute",
165 0.0, 1.0, "base", "mute", 0.0, 1.0)
166
167 part {
168 name: "slider";
169 type: SWALLOW;
170 scale: 1;
171 description {
172 state: "default";
173 fixed: 1 1;
174 align: 0.5 0.0;
175 rel1 {
176 to_y: "spacer.mute";
177 relative: 0.0 1.0;
178 }
179 rel2 {
180 to: "base";
181 relative: 1.0 1.0;
182 }
183 }
184 }
185 }
186
187 programs {
188 program {
189 signal: "elm,state,odd"; source: "elm";
190 action: STATE_SET "odd" 1.0;
191 target: "base";
192 }
193 program {
194 signal: "elm,state,even"; source: "elm";
195 action: STATE_SET "default" 0.0;
196 target: "base";
197 }
198 }
199}
diff --git a/data/xml/module.desktop.in b/data/xml/module.desktop.in
new file mode 100644
index 0000000..bd1441c
--- /dev/null
+++ b/data/xml/module.desktop.in
@@ -0,0 +1,12 @@
1[Desktop Entry]
2Encoding=UTF-8
3Type=Link
4Version=1.0
5Name=Pulse Mixer
6Comment=A module to control audio volume and streams.
7Icon=e-module-mixer
8Exec=enlightenment
9StartupNotify=true
10X-Enlightenment-IconClass=epulse-mixer,gadget
11X-Enlightenment-ModuleType=utils
12X-Enlightenment-WaitExit=false
diff --git a/src/bin/main.c b/src/bin/main.c
new file mode 100644
index 0000000..66cd35a
--- /dev/null
+++ b/src/bin/main.c
@@ -0,0 +1,31 @@
1#include <common.h>
2#include <epulse.h>
3#include "main_window.h"
4
5#define DEFAULT_HEIGHT 600
6#define DEFAULT_WIDTH 800
7
8EAPI int
9elm_main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
10{
11 Evas_Object *win;
12
13 EINA_SAFETY_ON_FALSE_RETURN_VAL(epulse_common_init("epulse"), EXIT_FAILURE);
14 EINA_SAFETY_ON_FALSE_RETURN_VAL(epulse_init() > 0, EXIT_FAILURE);
15
16 win = main_window_add();
17 evas_object_resize(win, DEFAULT_WIDTH, DEFAULT_HEIGHT);
18 evas_object_show(win);
19
20 elm_run();
21
22 epulse_common_shutdown();
23 epulse_shutdown();
24 return 0;
25}
26
27/*
28 * Create the default main() that will work with both quicklaunch or
29 * regular applications.
30 */
31ELM_MAIN()
diff --git a/src/bin/main_window.c b/src/bin/main_window.c
new file mode 100644
index 0000000..6783384
--- /dev/null
+++ b/src/bin/main_window.c
@@ -0,0 +1,160 @@
1#include "main_window.h"
2
3#include "epulse.h"
4#include "playbacks_view.h"
5#include "sinks_view.h"
6#include "sources_view.h"
7
8#define MAIN_WINDOW_DATA "mainwindow.data"
9
10enum MAIN_SUBVIEWS {
11 PLAYBACKS,
12 OUTPUTS,
13 INPUTS
14};
15
16static const char *_views[] = {
17 "Playback",
18 "Output Devices",
19 "Input Devices"
20};
21
22typedef struct _Main_Window Main_Window;
23struct _Main_Window
24{
25 Evas_Object *win;
26 Evas_Object *toolbar;
27 Evas_Object *layout;
28 Evas_Object *naviframe;
29 Evas_Object *playbacks;
30 Evas_Object *inputs;
31 Evas_Object *outputs;
32 Elm_Object_Item *toolbar_items[3];
33 Elm_Object_Item *views[3];
34};
35
36static void
37_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED,
38 void *event_info EINA_UNUSED)
39{
40 Main_Window *mw = data;
41
42 free(mw);
43}
44
45static void
46_delete_request_cb(void *data EINA_UNUSED, Evas_Object *o EINA_UNUSED,
47 void *event_info EINA_UNUSED)
48{
49 elm_exit();
50}
51
52static void
53_toolbar_item_cb(void *data, Evas_Object *obj EINA_UNUSED,
54 void *event_info EINA_UNUSED)
55{
56 Main_Window *mw = data;
57 Elm_Object_Item *it = elm_toolbar_selected_item_get(obj);
58
59 if (!mw->views[PLAYBACKS] || !mw->views[OUTPUTS] ||
60 !mw->views[INPUTS])
61 return;
62
63 if (it == mw->toolbar_items[PLAYBACKS])
64 elm_naviframe_item_promote(mw->views[PLAYBACKS]);
65 else if (it == mw->toolbar_items[OUTPUTS])
66 elm_naviframe_item_promote(mw->views[OUTPUTS]);
67 else
68 elm_naviframe_item_promote(mw->views[INPUTS]);
69}
70
71Evas_Object *
72main_window_add(void)
73{
74 Main_Window *mw;
75 Evas_Object *tmp, *box;
76 unsigned int i;
77
78 elm_theme_extension_add(NULL, EPULSE_THEME);
79 mw = calloc(1, sizeof(Main_Window));
80 if (!mw)
81 {
82 ERR("Could not allocate memmory to main window");
83 return NULL;
84 }
85
86 tmp = elm_win_add(NULL, PACKAGE_NAME, ELM_WIN_BASIC);
87 EINA_SAFETY_ON_NULL_GOTO(tmp, win_err);
88 evas_object_data_set(tmp, MAIN_WINDOW_DATA, mw);
89 evas_object_event_callback_add(tmp, EVAS_CALLBACK_DEL, _del_cb, mw);
90 elm_win_autodel_set(tmp, EINA_TRUE);
91 elm_win_title_set(tmp, "Efl Volume Control");
92 mw->win = tmp;
93
94 tmp = elm_bg_add(mw->win);
95 evas_object_size_hint_weight_set(tmp, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
96 evas_object_size_hint_align_set(tmp, EVAS_HINT_FILL, EVAS_HINT_FILL);
97 elm_win_resize_object_add(mw->win, tmp);
98 evas_object_show(tmp);
99
100 box = elm_box_add(mw->win);
101 evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
102 elm_box_horizontal_set(box, EINA_FALSE);
103 elm_win_resize_object_add(mw->win, box);
104 evas_object_show(box);
105
106 tmp = elm_toolbar_add(mw->win);
107 evas_object_size_hint_weight_set(tmp, EVAS_HINT_EXPAND, 0);
108 evas_object_size_hint_align_set(tmp, EVAS_HINT_FILL, EVAS_HINT_FILL);
109 elm_toolbar_select_mode_set(tmp, ELM_OBJECT_SELECT_MODE_ALWAYS);
110 elm_box_pack_start(box, tmp);
111 mw->toolbar = tmp;
112 evas_object_show(tmp);
113
114 tmp = elm_naviframe_add(mw->win);
115 elm_object_style_set(tmp, "no_transition");
116 elm_naviframe_prev_btn_auto_pushed_set(tmp, EINA_FALSE);
117 evas_object_size_hint_weight_set(tmp, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
118 evas_object_size_hint_align_set(tmp, EVAS_HINT_FILL, EVAS_HINT_FILL);
119 elm_box_pack_end(box, tmp);
120 evas_object_show(tmp);
121 mw->naviframe = tmp;
122
123 for (i = 0; i < EINA_C_ARRAY_LENGTH(_views); i++)
124 {
125 mw->toolbar_items[i] = elm_toolbar_item_append(mw->toolbar, NULL,
126 _views[i],
127 _toolbar_item_cb, mw);
128 }
129
130 evas_object_smart_callback_add(mw->win, "delete,request",
131 _delete_request_cb, mw);
132
133 /* Creating the playbacks view */
134 mw->playbacks = playbacks_view_add(mw->win);
135 evas_object_size_hint_weight_set(mw->playbacks, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
136 evas_object_size_hint_align_set(mw->playbacks, EVAS_HINT_FILL, EVAS_HINT_FILL);
137 evas_object_show(mw->playbacks);
138
139 /* Creating the outputs view */
140 mw->outputs = sinks_view_add(mw->win);
141 evas_object_size_hint_weight_set(mw->outputs, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
142 evas_object_size_hint_align_set(mw->outputs, EVAS_HINT_FILL, EVAS_HINT_FILL);
143 evas_object_show(mw->outputs);
144
145 /* Creating the inputs view */
146 mw->inputs = sources_view_add(mw->win);
147 evas_object_size_hint_weight_set(mw->inputs, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
148 evas_object_size_hint_align_set(mw->inputs, EVAS_HINT_FILL, EVAS_HINT_FILL);
149 evas_object_show(mw->inputs);
150
151 mw->views[INPUTS] = elm_naviframe_item_simple_push(mw->naviframe, mw->inputs);
152 mw->views[OUTPUTS] = elm_naviframe_item_simple_push(mw->naviframe, mw->outputs);
153 mw->views[PLAYBACKS] = elm_naviframe_item_simple_push(mw->naviframe, mw->playbacks);
154
155 return mw->win;
156
157 win_err:
158 free(mw);
159 return NULL;
160}
diff --git a/src/bin/main_window.h b/src/bin/main_window.h
new file mode 100644
index 0000000..dd63ae7
--- /dev/null
+++ b/src/bin/main_window.h
@@ -0,0 +1,12 @@
1#ifndef _MAIN_WINDOW_H_
2#define _MAIN_WINDOW_H_
3
4#include <common.h>
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10Evas_Object *main_window_add(void);
11
12#endif /* _MAIN_WINDOW_H_ */
diff --git a/src/bin/playbacks_view.c b/src/bin/playbacks_view.c
new file mode 100644
index 0000000..98edff4
--- /dev/null
+++ b/src/bin/playbacks_view.c
@@ -0,0 +1,375 @@
1#include "playbacks_view.h"
2
3#include "epulse.h"
4
5#define PLAYBACKS_KEY "playbacks.key"
6
7struct Sink
8{
9 int index;
10 const char *name;
11};
12
13struct Playbacks_View
14{
15 Evas_Object *self;
16 Evas_Object *genlist;
17 Elm_Genlist_Item_Class *itc;
18
19 Eina_List *inputs;
20 Eina_List *sinks;
21
22 Ecore_Event_Handler *sink_input_added;
23 Ecore_Event_Handler *sink_input_changed;
24 Ecore_Event_Handler *sink_input_removed;
25
26 Ecore_Event_Handler *sink_added;
27 Ecore_Event_Handler *sink_removed;
28};
29
30struct Sink_Input
31{
32 struct Playbacks_View *pv;
33
34 int index;
35 int sink_index;
36 pa_cvolume volume;
37 const char *name;
38 const char *icon;
39 Eina_Bool mute;
40
41 Elm_Object_Item *item;
42};
43
44static Eina_Bool
45_sink_input_add_cb(void *data, int type EINA_UNUSED,
46 void *info)
47{
48 struct Playbacks_View *pv = data;
49 Epulse_Event_Sink_Input *ev = info;
50 struct Sink_Input *input = calloc(1, sizeof(struct Sink_Input));
51 EINA_SAFETY_ON_NULL_RETURN_VAL(input, ECORE_CALLBACK_PASS_ON);
52
53 input->name = eina_stringshare_add(ev->base.name);
54 input->icon = eina_stringshare_add(ev->icon);
55 input->index = ev->base.index;
56 input->sink_index = ev->sink;
57 input->volume = ev->base.volume;
58 input->mute = ev->base.mute;
59 input->pv = pv;
60
61 pv->inputs = eina_list_append(pv->inputs, input);
62 input->item = elm_genlist_item_append(pv->genlist, pv->itc, input, NULL,
63 ELM_GENLIST_ITEM_NONE, NULL, pv);
64
65 return ECORE_CALLBACK_DONE;
66}
67
68static Eina_Bool
69_sink_input_removed_cb(void *data, int type EINA_UNUSED,
70 void *info)
71{
72 struct Playbacks_View *pv = data;
73 Epulse_Event_Sink_Input *ev = info;
74 Eina_List *l, *ll;
75 struct Sink_Input *input;
76
77 EINA_LIST_FOREACH_SAFE(pv->inputs, l, ll, input)
78 {
79 if (input->index == ev->base.index)
80 {
81 pv->inputs = eina_list_remove_list(pv->inputs, l);
82 elm_object_item_del(input->item);
83 break;
84 }
85 }
86
87 return ECORE_CALLBACK_DONE;
88}
89
90static Eina_Bool
91_sink_input_changed_cb(void *data, int type EINA_UNUSED,
92 void *info)
93{
94 struct Playbacks_View *pv = data;
95 Epulse_Event_Sink_Input *ev = info;
96 Eina_List *l;
97 struct Sink_Input *input;
98 Evas_Object *item = NULL;
99
100 EINA_LIST_FOREACH(pv->inputs, l, input)
101 {
102 if (input->index == ev->base.index)
103 {
104 pa_volume_t vol = pa_cvolume_avg(&ev->base.volume);
105
106 item = elm_object_item_part_content_get(input->item, "item");
107 input->volume = ev->base.volume;
108 if (item)
109 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
110
111 item = elm_object_item_part_content_get(input->item, "mute");
112 input->mute = ev->base.mute;
113 if (item)
114 {
115 elm_check_state_set(item, input->mute);
116 }
117 break;
118 }
119 }
120
121 return ECORE_CALLBACK_DONE;
122}
123
124static Eina_Bool
125_sink_add_cb(void *data, int type EINA_UNUSED,
126 void *info)
127{
128 struct Playbacks_View *pv = data;
129 Epulse_Event_Sink *ev = info;
130 struct Sink *sink = calloc(1, sizeof(struct Sink));
131 EINA_SAFETY_ON_NULL_RETURN_VAL(sink, ECORE_CALLBACK_PASS_ON);
132
133 sink->name = eina_stringshare_add(ev->base.name);
134 sink->index = ev->base.index;
135
136 pv->sinks = eina_list_append(pv->sinks, sink);
137 elm_genlist_realized_items_update(pv->genlist);
138
139 return ECORE_CALLBACK_PASS_ON;
140}
141
142static Eina_Bool
143_sink_removed_cb(void *data, int type EINA_UNUSED,
144 void *info)
145{
146 struct Playbacks_View *pv = data;
147 Epulse_Event_Sink *ev = info;
148 Eina_List *l, *ll;
149 struct Sink *sink;
150
151 EINA_LIST_FOREACH_SAFE(pv->sinks, l, ll, sink)
152 {
153 if (sink->index == ev->base.index)
154 {
155 pv->sinks = eina_list_remove_list(pv->sinks, l);
156 break;
157 }
158 }
159
160 elm_genlist_realized_items_update(pv->genlist);
161 return ECORE_CALLBACK_PASS_ON;
162}
163
164static void
165_del_cb(void *data,
166 Evas *e EINA_UNUSED,
167 Evas_Object *o EINA_UNUSED,
168 void *event_info EINA_UNUSED)
169{
170 struct Playbacks_View *pv = data;
171 struct Sink *sink;
172 eina_list_free(pv->inputs);
173
174 EINA_LIST_FREE(pv->sinks, sink)
175 {
176 eina_stringshare_del(sink->name);
177 free(sink);
178 }
179
180#define ECORE_EVENT_HANDLER_DEL(_handle) \
181 if (pv->_handle) \
182 { \
183 ecore_event_handler_del(pv->_handle); \
184 pv->_handle = NULL; \
185 }
186
187 ECORE_EVENT_HANDLER_DEL(sink_input_added)
188 ECORE_EVENT_HANDLER_DEL(sink_input_changed)
189 ECORE_EVENT_HANDLER_DEL(sink_input_removed)
190 ECORE_EVENT_HANDLER_DEL(sink_added)
191 ECORE_EVENT_HANDLER_DEL(sink_removed)
192#undef ECORE_EVENT_HANDLER_DEL
193}
194
195static char *
196_item_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part)
197{
198 struct Sink_Input *input = data;
199
200 if (!strcmp(part, "name"))
201 {
202 return strdup(input->name);
203 }
204
205 return NULL;
206}
207
208static void
209_item_del(void *data, Evas_Object *obj EINA_UNUSED)
210{
211 struct Sink_Input *input = data;
212
213 eina_stringshare_del(input->name);
214 eina_stringshare_del(input->icon);
215 free(input);
216}
217
218static void
219_volume_changed_cb(void *data, Evas_Object *o,
220 void *event_info EINA_UNUSED)
221{
222 struct Sink_Input *input = data;
223 double val = elm_slider_value_get(o);
224 pa_volume_t v = INT_TO_PA_VOLUME(val);
225
226 pa_cvolume_set(&input->volume, input->volume.channels, v);
227
228 epulse_sink_input_volume_set(input->index, input->volume);
229}
230
231static void
232_mute_changed_cb(void *data, Evas_Object *o EINA_UNUSED,
233 void *event_info EINA_UNUSED)
234{
235 struct Sink_Input *input = data;
236
237 if (!epulse_sink_input_mute_set(input->index, input->mute))
238 {
239 ERR("Could not mute the input: %d", input->index);
240 input->mute = !input->mute;
241 return;
242 }
243}
244
245static void
246_sink_selected(void *data, Evas_Object *obj, void *event_info)
247{
248 struct Sink_Input *input = data;
249 Elm_Object_Item *item = event_info;
250 struct Sink *sink = elm_object_item_data_get(item);
251
252 epulse_sink_input_move(input->index, sink->index);
253 elm_object_text_set(obj, sink->name);
254}
255
256static Evas_Object *
257_item_content_get(void *data, Evas_Object *obj, const char *part)
258{
259 Evas_Object *item = NULL;
260 struct Sink_Input *input = data;
261
262 if (!strcmp(part, "slider"))
263 {
264 pa_volume_t vol = pa_cvolume_avg(&input->volume);
265 item = elm_slider_add(obj);
266 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
267
268 elm_slider_unit_format_set(item, "%1.0f");
269 elm_slider_indicator_format_set(item, "%1.0f");
270 elm_slider_span_size_set(item, 120);
271 elm_slider_min_max_set(item, 0.0, 100.0);
272 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
273
274 evas_object_smart_callback_add(item, "delay,changed",
275 _volume_changed_cb, input);
276 }
277 else if (!strcmp(part, "mute"))
278 {
279 item = elm_check_add(obj);
280 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
281
282 elm_object_style_set(item, "toggle");
283 elm_object_part_text_set(item, "off", "Mute");
284 elm_object_part_text_set(item, "on", "Unmute");
285
286 elm_check_state_set(item, input->mute);
287 elm_check_state_pointer_set(item, &input->mute);
288 evas_object_smart_callback_add(item, "changed", _mute_changed_cb,
289 input);
290 }
291 else if (!strcmp(part, "icon"))
292 {
293 EINA_SAFETY_ON_NULL_RETURN_VAL(input->icon, NULL);
294
295 item = elm_icon_add(obj);
296 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
297
298 elm_icon_standard_set(item, input->icon);
299 }
300 else if (!strcmp(part, "hover"))
301 {
302 Eina_List *l;
303 struct Sink *sink;
304 item = elm_hoversel_add(obj);
305
306 EINA_LIST_FOREACH(input->pv->sinks, l, sink)
307 {
308 if (sink->index == input->sink_index)
309 elm_object_text_set(item, sink->name);
310 elm_hoversel_item_add(item, sink->name, NULL,
311 ELM_ICON_NONE, NULL, sink);
312 }
313 evas_object_smart_callback_add(item, "selected",
314 _sink_selected, input);
315 }
316
317 return item;
318}
319
320Evas_Object *
321playbacks_view_add(Evas_Object *parent)
322{
323 Evas_Object *layout;
324 struct Playbacks_View *pv;
325
326 pv = calloc(1, sizeof(struct Playbacks_View));
327 EINA_SAFETY_ON_NULL_RETURN_VAL(pv, NULL);
328
329 layout = epulse_layout_add(parent, "playbacks", "default");
330 EINA_SAFETY_ON_NULL_GOTO(layout, err);
331
332 evas_object_event_callback_add(layout, EVAS_CALLBACK_DEL, _del_cb, pv);
333
334 pv->genlist = elm_genlist_add(layout);
335 EINA_SAFETY_ON_NULL_GOTO(pv->genlist, err_genlist);
336
337 pv->sink_input_added = ecore_event_handler_add(SINK_INPUT_ADDED,
338 _sink_input_add_cb, pv);
339 pv->sink_input_added = ecore_event_handler_add(SINK_INPUT_CHANGED,
340 _sink_input_changed_cb, pv);
341 pv->sink_input_removed = ecore_event_handler_add(SINK_INPUT_REMOVED,
342 _sink_input_removed_cb, pv);
343 pv->sink_added = ecore_event_handler_add(SINK_ADDED,
344 _sink_add_cb, pv);
345 pv->sink_removed = ecore_event_handler_add(SINK_REMOVED,
346 _sink_removed_cb, pv);
347
348 pv->itc = elm_genlist_item_class_new();
349 EINA_SAFETY_ON_NULL_GOTO(pv->itc, err_genlist);
350 pv->itc->item_style = "playbacks";
351 pv->itc->func.text_get = _item_text_get;
352 pv->itc->func.content_get = _item_content_get;
353 pv->itc->func.del = _item_del;
354
355 evas_object_data_set(layout, PLAYBACKS_KEY, pv);
356 elm_layout_content_set(layout, "list", pv->genlist);
357
358 evas_object_size_hint_weight_set(pv->genlist, EVAS_HINT_EXPAND,
359 EVAS_HINT_EXPAND);
360 evas_object_size_hint_align_set(pv->genlist, EVAS_HINT_FILL,
361 EVAS_HINT_FILL);
362
363 return layout;
364
365 err_genlist:
366 ecore_event_handler_del(pv->sink_input_added);
367 ecore_event_handler_del(pv->sink_input_changed);
368 ecore_event_handler_del(pv->sink_input_removed);
369 ecore_event_handler_del(pv->sink_added);
370 ecore_event_handler_del(pv->sink_removed);
371 free(layout);
372 err:
373 free(pv);
374 return NULL;
375}
diff --git a/src/bin/playbacks_view.h b/src/bin/playbacks_view.h
new file mode 100644
index 0000000..a58d327
--- /dev/null
+++ b/src/bin/playbacks_view.h
@@ -0,0 +1,12 @@
1#ifndef _PLAYBACKS_VIEW_H_
2#define _PLAYBACKS_VIEW_H_
3
4#include "common.h"
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10Evas_Object *playbacks_view_add(Evas_Object *parent);
11
12#endif /* _PLAYBACKS_VIEW_H_ */
diff --git a/src/bin/sinks_view.c b/src/bin/sinks_view.c
new file mode 100644
index 0000000..7ddea2c
--- /dev/null
+++ b/src/bin/sinks_view.c
@@ -0,0 +1,320 @@
1#include "sinks_view.h"
2
3#include "epulse.h"
4
5#define SINKS_KEY "sinks.key"
6
7struct Sink
8{
9 int index;
10 pa_cvolume volume;
11 const char *name;
12 Eina_Bool mute;
13
14 Elm_Object_Item *item;
15 Eina_List *ports;
16};
17
18struct Sink_Port
19{
20 char *name;
21 Eina_Bool active;
22};
23
24struct Sinks_View
25{
26 Evas_Object *self;
27 Evas_Object *genlist;
28 Elm_Genlist_Item_Class *itc;
29
30 Eina_List *sinks;
31 Ecore_Event_Handler *sink_added;
32 Ecore_Event_Handler *sink_changed;
33 Ecore_Event_Handler *sink_removed;
34};
35
36
37static Eina_Bool
38_sink_add_cb(void *data, int type EINA_UNUSED, void *info)
39{
40 struct Sinks_View *sv = data;
41 struct Sink_Port *sp;
42 Epulse_Event_Sink *ev = info;
43 Port *port;
44 Eina_List *l;
45 struct Sink *sink = calloc(1, sizeof(struct Sink));
46 EINA_SAFETY_ON_NULL_RETURN_VAL(sink, ECORE_CALLBACK_PASS_ON);
47
48 sink->name = eina_stringshare_add(ev->base.name);
49 sink->index = ev->base.index;
50 sink->volume = ev->base.volume;
51 sink->mute = ev->base.mute;
52
53 EINA_LIST_FOREACH(ev->ports, l, port)
54 {
55 sp = calloc(1, sizeof(struct Sink_Port));
56 sp->name = strdup(port->name);
57 if (port->active)
58 sp->active = EINA_TRUE;
59 sink->ports = eina_list_append(sink->ports, sp);
60 }
61
62 sv->sinks = eina_list_append(sv->sinks, sink);
63 sink->item = elm_genlist_item_append(sv->genlist, sv->itc, sink, NULL,
64 ELM_GENLIST_ITEM_NONE, NULL, sv);
65
66 return ECORE_CALLBACK_PASS_ON;
67}
68
69static Eina_Bool
70_sink_removed_cb(void *data, int type EINA_UNUSED, void *info)
71{
72 struct Sinks_View *sv = data;
73 Epulse_Event_Sink *ev = info;
74 Eina_List *l, *ll;
75 struct Sink *sink;
76
77 EINA_LIST_FOREACH_SAFE(sv->sinks, l, ll, sink)
78 {
79 if (sink->index == ev->base.index)
80 {
81 sv->sinks = eina_list_remove_list(sv->sinks, l);
82 elm_object_item_del(sink->item);
83 break;
84 }
85 }
86
87 return ECORE_CALLBACK_PASS_ON;
88}
89
90static Eina_Bool
91_sink_changed_cb(void *data, int type EINA_UNUSED, void *info)
92{
93 struct Sinks_View *sv = data;
94 Epulse_Event_Sink *ev = info;
95 Eina_List *l;
96 struct Sink *sink;
97 Evas_Object *item = NULL;
98
99 EINA_LIST_FOREACH(sv->sinks, l, sink)
100 {
101 if (sink->index == ev->base.index)
102 {
103 pa_volume_t vol = pa_cvolume_avg(&ev->base.volume);
104
105 item = elm_object_item_part_content_get(sink->item, "slider");
106 sink->volume = ev->base.volume;
107 if (item)
108 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
109
110 item = elm_object_item_part_content_get(sink->item, "mute");
111 sink->mute = ev->base.mute;
112 if (item)
113 elm_check_state_set(item, sink->mute);
114
115 break;
116 }
117 }
118
119 return ECORE_CALLBACK_DONE;
120}
121
122static void
123_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED,
124 void *event_info EINA_UNUSED)
125{
126 struct Sinks_View *sv = data;
127
128 eina_list_free(sv->sinks);
129 if (sv->sink_added)
130 {
131 ecore_event_handler_del(sv->sink_added);
132 sv->sink_added = NULL;
133 }
134 if (sv->sink_changed)
135 {
136 ecore_event_handler_del(sv->sink_changed);
137 sv->sink_changed = NULL;
138 }
139 if (sv->sink_removed)
140 {
141 ecore_event_handler_del(sv->sink_removed);
142 sv->sink_removed = NULL;
143 }
144}
145
146static char *
147_item_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part)
148{
149 struct Sink *sink = data;
150
151 if (!strcmp(part, "name"))
152 {
153 return strdup(sink->name);
154 }
155
156 return NULL;
157}
158
159static void
160_item_del(void *data, Evas_Object *obj EINA_UNUSED)
161{
162 struct Sink *sink = data;
163 struct Sink_Port *port;
164
165 eina_stringshare_del(sink->name);
166 EINA_LIST_FREE(sink->ports, port)
167 {
168 free(port->name);
169 free(port);
170 }
171 free(sink);
172}
173
174static void
175_volume_changed_cb(void *data, Evas_Object *o,
176 void *event_info EINA_UNUSED)
177{
178 struct Sink *sink = data;
179 double val = elm_slider_value_get(o);
180 pa_volume_t v = INT_TO_PA_VOLUME(val);
181
182 pa_cvolume_set(&sink->volume, sink->volume.channels, v);
183
184 epulse_sink_volume_set(sink->index, sink->volume);
185}
186
187static void
188_mute_changed_cb(void *data, Evas_Object *o EINA_UNUSED,
189 void *event_info EINA_UNUSED)
190{
191 struct Sink *sink = data;
192
193 if (!epulse_sink_mute_set(sink->index, sink->mute))
194 {
195 ERR("Could not mute the sink: %d", sink->index);
196 sink->mute = !sink->mute;
197 return;
198 }
199}
200
201static void
202_port_selected_cb(void *data, Evas_Object *o,
203 void *event_info EINA_UNUSED)
204{
205 struct Sink *sink = data;
206 Elm_Object_Item *item = event_info;
207 struct Sink_Port *port = elm_object_item_data_get(item);
208
209 if (!epulse_sink_port_set(sink->index, port->name))
210 ERR("Could not change the port");
211 else
212 elm_object_text_set(o, port->name);
213}
214
215static Evas_Object *
216_item_content_get(void *data, Evas_Object *obj, const char *part)
217{
218 Evas_Object *item = NULL;
219 struct Sink *sink = data;
220
221 if (!strcmp(part, "slider"))
222 {
223 pa_volume_t vol = pa_cvolume_avg(&sink->volume);
224 item = elm_slider_add(obj);
225 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
226
227 elm_slider_unit_format_set(item, "%1.0f");
228 elm_slider_indicator_format_set(item, "%1.0f");
229 elm_slider_span_size_set(item, 120);
230 elm_slider_min_max_set(item, 0.0, 100.0);
231 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
232 evas_object_smart_callback_add(item, "delay,changed",
233 _volume_changed_cb, sink);
234 }
235 else if (!strcmp(part, "mute"))
236 {
237 item = elm_check_add(obj);
238 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
239
240 elm_object_style_set(item, "toggle");
241 elm_object_part_text_set(item, "off", "Mute");
242 elm_object_part_text_set(item, "on", "Unmute");
243
244 elm_check_state_set(item, sink->mute);
245 elm_check_state_pointer_set(item, &sink->mute);
246 evas_object_smart_callback_add(item, "changed", _mute_changed_cb,
247 sink);
248 }
249 else if (!strcmp(part, "hover"))
250 {
251 Eina_List *l;
252 struct Sink_Port *sp;
253
254 if (sink->ports)
255 item = elm_hoversel_add(obj);
256
257 EINA_LIST_FOREACH(sink->ports, l, sp)
258 {
259 elm_hoversel_item_add(item, sp->name, NULL,
260 ELM_ICON_NONE, NULL, sp);
261 if (sp->active)
262 elm_object_text_set(item, sp->name);
263 }
264 evas_object_smart_callback_add(item, "selected",
265 _port_selected_cb, sink);
266 }
267
268 return item;
269}
270
271
272Evas_Object *
273sinks_view_add(Evas_Object *parent)
274{
275 Evas_Object *layout;
276 struct Sinks_View *sv;
277
278 sv = calloc(1, sizeof(struct Sinks_View));
279 EINA_SAFETY_ON_NULL_RETURN_VAL(sv, NULL);
280
281 layout = epulse_layout_add(parent, "sinks", "default");
282 EINA_SAFETY_ON_NULL_GOTO(layout, err);
283
284 evas_object_event_callback_add(layout, EVAS_CALLBACK_DEL, _del_cb, sv);
285
286 sv->genlist = elm_genlist_add(layout);
287 EINA_SAFETY_ON_NULL_GOTO(sv->genlist, err_genlist);
288
289 sv->sink_added = ecore_event_handler_add(SINK_ADDED, _sink_add_cb, sv);
290 sv->sink_added = ecore_event_handler_add(SINK_CHANGED, _sink_changed_cb, sv);
291 sv->sink_removed = ecore_event_handler_add(SINK_REMOVED,
292 _sink_removed_cb, sv);
293
294 sv->itc = elm_genlist_item_class_new();
295 EINA_SAFETY_ON_NULL_GOTO(sv->itc, err_genlist);
296 sv->itc->item_style = "sinks";
297 sv->itc->func.text_get = _item_text_get;
298 sv->itc->func.content_get = _item_content_get;
299 sv->itc->func.del = _item_del;
300
301 evas_object_data_set(layout, SINKS_KEY, sv);
302 elm_layout_content_set(layout, "list", sv->genlist);
303
304 evas_object_size_hint_weight_set(sv->genlist, EVAS_HINT_EXPAND,
305 EVAS_HINT_EXPAND);
306 evas_object_size_hint_align_set(sv->genlist, EVAS_HINT_FILL,
307 EVAS_HINT_FILL);
308
309 return layout;
310
311 err_genlist:
312 ecore_event_handler_del(sv->sink_added);
313 ecore_event_handler_del(sv->sink_changed);
314 ecore_event_handler_del(sv->sink_removed);
315 free(layout);
316 err:
317 free(sv);
318
319 return NULL;
320}
diff --git a/src/bin/sinks_view.h b/src/bin/sinks_view.h
new file mode 100644
index 0000000..f222234
--- /dev/null
+++ b/src/bin/sinks_view.h
@@ -0,0 +1,13 @@
1#ifndef _SINKS_VIEW_H_
2#define _SINKS_VIEW_H_
3
4#include "common.h"
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10Evas_Object *sinks_view_add(Evas_Object *parent);
11
12
13#endif /* _SINKS_VIEW_H_ */
diff --git a/src/bin/sources_view.c b/src/bin/sources_view.c
new file mode 100644
index 0000000..aa6dafb
--- /dev/null
+++ b/src/bin/sources_view.c
@@ -0,0 +1,263 @@
1#include "sources_view.h"
2
3#include "epulse.h"
4
5#define SOURCES_KEY "sources.key"
6
7struct Source
8{
9 int index;
10 pa_cvolume volume;
11 const char *name;
12 Eina_Bool mute;
13
14 Elm_Object_Item *item;
15};
16
17struct Sources_View
18{
19 Evas_Object *self;
20 Evas_Object *genlist;
21 Elm_Genlist_Item_Class *itc;
22
23 Eina_List *sources;
24 Ecore_Event_Handler *source_added;
25 Ecore_Event_Handler *source_changed;
26 Ecore_Event_Handler *source_removed;
27};
28
29static Eina_Bool
30_source_add_cb(void *data, int type EINA_UNUSED, void *info)
31{
32 struct Sources_View *sv = data;
33 Epulse_Event *ev = info;
34 struct Source *source = calloc(1, sizeof(struct Source));
35 EINA_SAFETY_ON_NULL_RETURN_VAL(source, ECORE_CALLBACK_PASS_ON);
36
37 source->name = eina_stringshare_add(ev->name);
38 source->index = ev->index;
39 source->volume = ev->volume;
40 source->mute = ev->mute;
41
42 sv->sources = eina_list_append(sv->sources, source);
43 source->item = elm_genlist_item_append(sv->genlist, sv->itc, source, NULL,
44 ELM_GENLIST_ITEM_NONE, NULL, sv);
45
46 return ECORE_CALLBACK_DONE;
47}
48
49static Eina_Bool
50_source_removed_cb(void *data, int type EINA_UNUSED, void *info)
51{
52 struct Sources_View *sv = data;
53 Epulse_Event *ev = info;
54 Eina_List *l, *ll;
55 struct Source *source;
56
57 EINA_LIST_FOREACH_SAFE(sv->sources, l, ll, source)
58 {
59 if (source->index == ev->index)
60 {
61 sv->sources = eina_list_remove_list(sv->sources, l);
62 elm_object_item_del(source->item);
63 break;
64 }
65 }
66
67 return ECORE_CALLBACK_DONE;
68}
69
70static Eina_Bool
71_source_changed_cb(void *data, int type EINA_UNUSED, void *info)
72{
73 struct Sources_View *sv = data;
74 Epulse_Event *ev = info;
75 Eina_List *l;
76 struct Source *source;
77 Evas_Object *item = NULL;
78
79 EINA_LIST_FOREACH(sv->sources, l, source)
80 {
81 if (source->index == ev->index)
82 {
83 pa_volume_t vol = pa_cvolume_avg(&ev->volume);
84
85 item = elm_object_item_part_content_get(source->item, "slider");
86 source->volume = ev->volume;
87 if (item)
88 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
89
90 item = elm_object_item_part_content_get(source->item, "mute");
91 source->mute = ev->mute;
92 if (item)
93 {
94 elm_check_state_set(item, source->mute);
95 }
96 break;
97 }
98 }
99
100 return ECORE_CALLBACK_DONE;
101}
102
103static void
104_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED,
105 void *event_info EINA_UNUSED)
106{
107 struct Sources_View *sv = data;
108
109 eina_list_free(sv->sources);
110 if (sv->source_added)
111 {
112 ecore_event_handler_del(sv->source_added);
113 sv->source_added = NULL;
114 }
115 if (sv->source_changed)
116 {
117 ecore_event_handler_del(sv->source_changed);
118 sv->source_changed = NULL;
119 }
120 if (sv->source_removed)
121 {
122 ecore_event_handler_del(sv->source_removed);
123 sv->source_removed = NULL;
124 }
125}
126
127static char *
128_item_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part)
129{
130 struct Source *source = data;
131
132 if (!strcmp(part, "name"))
133 {
134 return strdup(source->name);
135 }
136
137 return NULL;
138}
139
140static void
141_item_del(void *data, Evas_Object *obj EINA_UNUSED)
142{
143 struct Source *source = data;
144
145 eina_stringshare_del(source->name);
146 free(source);
147}
148
149static void
150_volume_changed_cb(void *data, Evas_Object *o,
151 void *event_info EINA_UNUSED)
152{
153 struct Source *source = data;
154 double val = elm_slider_value_get(o);
155 pa_volume_t v = INT_TO_PA_VOLUME(val);
156
157 pa_cvolume_set(&source->volume, source->volume.channels, v);
158
159 epulse_source_volume_set(source->index, source->volume);
160}
161
162static void
163_mute_changed_cb(void *data, Evas_Object *o EINA_UNUSED,
164 void *event_info EINA_UNUSED)
165{
166 struct Source *source = data;
167
168 if (!epulse_source_mute_set(source->index, source->mute))
169 {
170 ERR("Could not mute the source: %d", source->index);
171 source->mute = !source->mute;
172 return;
173 }
174}
175
176static Evas_Object *
177_item_content_get(void *data, Evas_Object *obj, const char *part)
178{
179 Evas_Object *item = NULL;
180 struct Source *source = data;
181
182 if (!strcmp(part, "slider"))
183 {
184 pa_volume_t vol = pa_cvolume_avg(&source->volume);
185 item = elm_slider_add(obj);
186 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
187
188 elm_slider_unit_format_set(item, "%1.0f");
189 elm_slider_indicator_format_set(item, "%1.0f");
190 elm_slider_span_size_set(item, 120);
191 elm_slider_min_max_set(item, 0.0, 100.0);
192 elm_slider_value_set(item, PA_VOLUME_TO_INT(vol));
193 evas_object_smart_callback_add(item, "delay,changed",
194 _volume_changed_cb, source);
195 }
196 else if (!strcmp(part, "mute"))
197 {
198 item = elm_check_add(obj);
199 EINA_SAFETY_ON_NULL_RETURN_VAL(item, NULL);
200
201 elm_object_style_set(item, "toggle");
202 elm_object_part_text_set(item, "off", "Mute");
203 elm_object_part_text_set(item, "on", "Unmute");
204
205 elm_check_state_set(item, source->mute);
206 elm_check_state_pointer_set(item, &source->mute);
207 evas_object_smart_callback_add(item, "changed", _mute_changed_cb,
208 source);
209 }
210
211 return item;
212}
213
214Evas_Object *
215sources_view_add(Evas_Object *parent)
216{
217 Evas_Object *layout;
218 struct Sources_View *sv;
219
220 sv = calloc(1, sizeof(struct Sources_View));
221 EINA_SAFETY_ON_NULL_RETURN_VAL(sv, NULL);
222
223 layout = epulse_layout_add(parent, "sources", "default");
224 EINA_SAFETY_ON_NULL_GOTO(layout, err);
225
226 evas_object_event_callback_add(layout, EVAS_CALLBACK_DEL, _del_cb, sv);
227
228 sv->genlist = elm_genlist_add(layout);
229 EINA_SAFETY_ON_NULL_GOTO(sv->genlist, err_genlist);
230
231 sv->source_added = ecore_event_handler_add(SOURCE_ADDED, _source_add_cb, sv);
232 sv->source_added = ecore_event_handler_add(SOURCE_CHANGED,
233 _source_changed_cb, sv);
234 sv->source_removed = ecore_event_handler_add(SOURCE_REMOVED,
235 _source_removed_cb, sv);
236
237 sv->itc = elm_genlist_item_class_new();
238 EINA_SAFETY_ON_NULL_GOTO(sv->itc, err_genlist);
239 sv->itc->item_style = "sources";
240 sv->itc->func.text_get = _item_text_get;
241 sv->itc->func.content_get = _item_content_get;
242 sv->itc->func.del = _item_del;
243
244 evas_object_data_set(layout, SOURCES_KEY, sv);
245 elm_layout_content_set(layout, "list", sv->genlist);
246
247 evas_object_size_hint_weight_set(sv->genlist, EVAS_HINT_EXPAND,
248 EVAS_HINT_EXPAND);
249 evas_object_size_hint_align_set(sv->genlist, EVAS_HINT_FILL,
250 EVAS_HINT_FILL);
251
252 return layout;
253
254 err_genlist:
255 ecore_event_handler_del(sv->source_added);
256 ecore_event_handler_del(sv->source_changed);
257 ecore_event_handler_del(sv->source_removed);
258 free(layout);
259 err:
260 free(sv);
261
262 return NULL;
263}
diff --git a/src/bin/sources_view.h b/src/bin/sources_view.h
new file mode 100644
index 0000000..62697fe
--- /dev/null
+++ b/src/bin/sources_view.h
@@ -0,0 +1,13 @@
1#ifndef _SOURCES_VIEW_H_
2#define _SOURCES_VIEW_H_
3
4#include "common.h"
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif
9
10Evas_Object *sources_view_add(Evas_Object *parent);
11
12
13#endif /* _SOURCES_VIEW_H_ */
diff --git a/src/lib/common.c b/src/lib/common.c
new file mode 100644
index 0000000..8b7f805
--- /dev/null
+++ b/src/lib/common.c
@@ -0,0 +1,64 @@
1#include "common.h"
2
3int _log_domain = -1;
4
5Eina_Bool
6epulse_common_init(const char *domain)
7{
8 EINA_SAFETY_ON_NULL_RETURN_VAL(domain, EINA_FALSE);
9 if (!eina_init())
10 {
11 fprintf(stderr, "Could not init eina\n");
12 return EINA_FALSE;
13 }
14
15 _log_domain = eina_log_domain_register(domain, NULL);
16 if (_log_domain < 0)
17 {
18 EINA_LOG_CRIT("Could not create log domain '%s'", domain);
19 goto err_log;
20 }
21
22 if (!ecore_init())
23 {
24 CRIT("Could not init ecore");
25 goto err_ecore;
26 }
27
28 return EINA_TRUE;
29
30 err_ecore:
31 eina_log_domain_unregister(_log_domain);
32 _log_domain = -1;
33 err_log:
34 eina_shutdown();
35 return EINA_FALSE;
36}
37
38void
39epulse_common_shutdown(void)
40{
41 eina_shutdown();
42
43 eina_log_domain_unregister(_log_domain);
44 _log_domain = -1;
45
46 ecore_shutdown();
47}
48
49Evas_Object *epulse_layout_add(Evas_Object *parent, const char *group,
50 const char *style)
51{
52 EINA_SAFETY_ON_NULL_RETURN_VAL(group, NULL);
53 EINA_SAFETY_ON_NULL_RETURN_VAL(style, NULL);
54
55 Evas_Object *layout = elm_layout_add(parent);
56 if (!elm_layout_theme_set(layout, "layout", group, style))
57 {
58 CRIT("No theme for 'elm/layout/%s/%s' at %s", group, style, EPULSE_THEME);
59 evas_object_del(layout);
60 return NULL;
61 }
62
63 return layout;
64}
diff --git a/src/lib/common.h b/src/lib/common.h
new file mode 100644
index 0000000..66b3925
--- /dev/null
+++ b/src/lib/common.h
@@ -0,0 +1,31 @@
1#ifndef __COMMON_H__
2#define __COMMON_H__
3
4#ifndef _GNU_SOURCE
5#define _GNU_SOURCE
6#endif
7
8#include <Ecore.h>
9#include <Evas.h>
10#include <Elementary.h>
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15
16#define EPULSE_THEME PACKAGE_DATA_DIR"/data/themes/default.edj"
17
18EAPI extern int _log_domain;
19
20#define CRIT(...) EINA_LOG_DOM_CRIT(_log_domain, __VA_ARGS__)
21#define ERR(...) EINA_LOG_DOM_ERR(_log_domain, __VA_ARGS__)
22#define WRN(...) EINA_LOG_DOM_WARN(_log_domain, __VA_ARGS__)
23#define INF(...) EINA_LOG_DOM_INFO(_log_domain, __VA_ARGS__)
24#define DBG(...) EINA_LOG_DOM_DBG(_log_domain, __VA_ARGS__)
25
26EAPI Eina_Bool epulse_common_init(const char *domain);
27EAPI void epulse_common_shutdown(void);
28EAPI Evas_Object *epulse_layout_add(Evas_Object *parent, const char *group,
29 const char *style);
30
31#endif /* __COMMON_H__ */
diff --git a/src/lib/epulse.c b/src/lib/epulse.c
new file mode 100644
index 0000000..f0e3af9
--- /dev/null
+++ b/src/lib/epulse.c
@@ -0,0 +1,781 @@
1#include "epulse.h"
2
3typedef struct _Epulse_Context Epulse_Context;
4struct _Epulse_Context {
5 pa_mainloop_api api;
6 pa_context *context;
7 pa_context_state_t state;
8 void *data;
9};
10
11static unsigned int _init_count = 0;
12static Epulse_Context *ctx = NULL;
13extern pa_mainloop_api functable;
14
15int SINK_ADDED = 0;
16int SINK_CHANGED = 0;
17int SINK_DEFAULT = 0;
18int SINK_REMOVED = 0;
19int SINK_INPUT_ADDED = 0;
20int SINK_INPUT_CHANGED = 0;
21int SINK_INPUT_REMOVED = 0;
22int SOURCE_ADDED = 0;
23int SOURCE_CHANGED = 0;
24int SOURCE_REMOVED = 0;
25int SOURCE_INPUT_ADDED = 0;
26int SOURCE_INPUT_REMOVED = 0;
27
28static void
29_event_free_cb(void *user_data EINA_UNUSED, void *func_data)
30{
31 Epulse_Event *ev = func_data;
32
33 if (ev->name)
34 free(ev->name);
35
36 free(ev);
37}
38
39static void
40_event_sink_input_free_cb(void *user_data EINA_UNUSED, void *func_data)
41{
42 Epulse_Event_Sink_Input *ev = func_data;
43
44 if (ev->base.name)
45 free(ev->base.name);
46
47 if (ev->icon)
48 free(ev->icon);
49
50 free(ev);
51}
52
53static void
54_event_sink_free_cb(void *user_data EINA_UNUSED, void *func_data)
55{
56 Epulse_Event_Sink *ev = func_data;
57 Port *port;
58
59 if (ev->base.name)
60 free(ev->base.name);
61
62 EINA_LIST_FREE(ev->ports, port)
63 {
64 free(port->name);
65 free(port);
66 }
67
68 free(ev);
69}
70
71static void
72_sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
73 void *userdata EINA_UNUSED)
74{
75 Epulse_Event_Sink *ev;
76 Port *port;
77 uint32_t i;
78
79 if (eol < 0)
80 {
81 if (pa_context_errno(c) == PA_ERR_NOENTITY)
82 return;
83
84 ERR("Sink callback failure");
85 return;
86 }
87
88 if (eol > 0)
89 return;
90
91 DBG("sink index: %d\nsink name: %s", info->index,
92 info->name);
93
94 ev = calloc(1, sizeof(Epulse_Event_Sink));
95 ev->base.index = info->index;
96 ev->base.name = strdup(info->description);
97 ev->base.volume = info->volume;
98 ev->base.mute = !!info->mute;
99
100 for (i = 0; i < info->n_ports; i++)
101 {
102 port = calloc(1, sizeof(Port));
103 EINA_SAFETY_ON_NULL_GOTO(port, error);
104
105 port->available = !!info->ports[i]->available;
106 port->priority = info->ports[i]->priority;
107 port->name = strdup(info->ports[i]->description ?:
108 info->ports[i]->name);
109 ev->ports = eina_list_append(ev->ports, port);
110 if (info->ports[i]->name == info->active_port->name)
111 port->active = EINA_TRUE;
112 }
113
114 ecore_event_add(SINK_ADDED, ev, _event_sink_free_cb, NULL);
115 return;
116
117 error:
118 _event_sink_free_cb(NULL, ev);
119}
120
121static void
122_sink_changed_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
123 void *userdata EINA_UNUSED)
124{
125 Epulse_Event_Sink *ev;
126 Port *port;
127 uint32_t i;
128
129 if (eol < 0)
130 {
131 if (pa_context_errno(c) == PA_ERR_NOENTITY)
132 return;
133
134 ERR("Sink callback failure");
135 return;
136 }
137
138 if (eol > 0)
139 return;
140
141 DBG("sink index: %d\nsink name: %s", info->index,
142 info->name);
143
144 ev = calloc(1, sizeof(Epulse_Event_Sink));
145 ev->base.index = info->index;
146 ev->base.name = strdup(info->name);
147 ev->base.volume = info->volume;
148 ev->base.mute = !!info->mute;
149
150 for (i = 0; i < info->n_ports; i++)
151 {
152 port = calloc(1, sizeof(Port));
153 EINA_SAFETY_ON_NULL_GOTO(port, error);
154
155 port->priority = info->ports[i]->priority;
156 port->available = !!info->ports[i]->available;
157 port->name = strdup(info->ports[i]->description ?:
158 info->ports[i]->name);
159 ev->ports = eina_list_append(ev->ports, port);
160 if (info->ports[i]->name == info->active_port->name)
161 port->active = EINA_TRUE;
162 }
163
164 ecore_event_add(SINK_CHANGED, ev, _event_sink_free_cb, NULL);
165 return;
166
167 error:
168 _event_sink_free_cb(NULL, ev);
169}
170
171static void
172_sink_remove_cb(int index, void *data EINA_UNUSED)
173{
174 Epulse_Event_Sink *ev;
175 DBG("Removing sink: %d", index);
176
177 ev = calloc(1, sizeof(Epulse_Event_Sink));
178 ev->base.index = index;
179
180 ecore_event_add(SINK_REMOVED, ev, _event_sink_free_cb, NULL);
181}
182
183static const char *
184_icon_from_properties(pa_proplist *l)
185{
186 const char *t;
187
188 if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ICON_NAME)))
189 return t;
190
191 if ((t = pa_proplist_gets(l, PA_PROP_WINDOW_ICON_NAME)))
192 return t;
193
194 if ((t = pa_proplist_gets(l, PA_PROP_APPLICATION_ICON_NAME)))
195 return t;
196
197 if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ROLE)))
198 {
199
200 if (strcmp(t, "video") == 0 ||
201 strcmp(t, "phone") == 0)
202 return t;
203
204 if (strcmp(t, "music") == 0)
205 return "audio";
206
207 if (strcmp(t, "game") == 0)
208 return "applications-games";
209
210 if (strcmp(t, "event") == 0)
211 return "dialog-information";
212 }
213
214 return "audio-card";
215}
216
217static void
218_sink_input_cb(pa_context *c EINA_UNUSED, const pa_sink_input_info *info,
219 int eol, void *userdata EINA_UNUSED)
220{
221 Epulse_Event_Sink_Input *ev;
222
223 if (eol < 0)
224 {
225 if (pa_context_errno(c) == PA_ERR_NOENTITY)
226 return;
227
228 ERR("Sink input callback failure");
229 return;
230 }
231
232 if (eol > 0)
233 return;
234
235 DBG("sink input index: %d\nsink input name: %s", info->index,
236 info->name);
237
238 ev = calloc(1, sizeof(Epulse_Event_Sink_Input));
239 ev->base.index = info->index;
240 ev->base.name = strdup(info->name);
241 ev->base.volume = info->volume;
242 ev->base.mute = !!info->mute;
243 ev->sink = info->sink;
244 ev->icon = strdup(_icon_from_properties(info->proplist));
245
246 ecore_event_add(SINK_INPUT_ADDED, ev, _event_sink_input_free_cb, NULL);
247}
248
249static void
250_sink_input_changed_cb(pa_context *c EINA_UNUSED,
251 const pa_sink_input_info *info, int eol,
252 void *userdata EINA_UNUSED)
253{
254 Epulse_Event_Sink_Input *ev;
255
256 if (eol < 0)
257 {
258 if (pa_context_errno(c) == PA_ERR_NOENTITY)
259 return;
260
261 ERR("Sink input changed callback failure");
262 return;
263 }
264
265 if (eol > 0)
266 return;
267
268 DBG("sink input changed index: %d\n", info->index);
269
270 ev = calloc(1, sizeof(Epulse_Event_Sink_Input));
271 ev->base.index = info->index;
272 ev->base.volume = info->volume;
273 ev->base.mute = !!info->mute;
274
275 ecore_event_add(SINK_INPUT_CHANGED, ev, _event_sink_input_free_cb,
276 NULL);
277}
278
279static void
280_sink_input_remove_cb(int index, void *data EINA_UNUSED)
281{
282 Epulse_Event_Sink_Input *ev;
283
284 DBG("Removing sink input: %d", index);
285
286 ev = calloc(1, sizeof(Epulse_Event_Sink_Input));
287 ev->base.index = index;
288
289 ecore_event_add(SINK_INPUT_REMOVED, ev, _event_sink_input_free_cb, NULL);
290}
291
292static void
293_source_cb(pa_context *c EINA_UNUSED, const pa_source_info *info,
294 int eol, void *userdata EINA_UNUSED)
295{
296 Epulse_Event *ev;
297
298 if (eol < 0)
299 {
300 if (pa_context_errno(c) == PA_ERR_NOENTITY)
301 return;
302
303 ERR("Source callback failure");
304 return;
305 }
306
307 if (eol > 0)
308 return;
309
310 ev = calloc(1, sizeof(Epulse_Event));
311 ev->index = info->index;
312 ev->name = strdup(info->name);
313 ev->volume = info->volume;
314 ev->mute = !!info->mute;
315
316 ecore_event_add(SOURCE_ADDED, ev, _event_free_cb, NULL);
317}
318
319static void
320_source_changed_cb(pa_context *c EINA_UNUSED,
321 const pa_source_info *info, int eol,
322 void *userdata EINA_UNUSED)
323{
324 Epulse_Event *ev;
325
326 if (eol < 0)
327 {
328 if (pa_context_errno(c) == PA_ERR_NOENTITY)
329 return;
330
331 ERR("Source changed callback failure");
332 return;
333 }
334
335 if (eol > 0)
336 return;
337
338 DBG("source changed index: %d\n", info->index);
339
340 ev = calloc(1, sizeof(Epulse_Event));
341 ev->index = info->index;
342 ev->volume = info->volume;
343 ev->mute = !!info->mute;
344
345 ecore_event_add(SOURCE_CHANGED, ev, _event_free_cb,
346 NULL);
347}
348
349static void
350_source_remove_cb(int index, void *data EINA_UNUSED)
351{
352 Epulse_Event *ev;
353
354 DBG("Removing source: %d", index);
355
356 ev = calloc(1, sizeof(Epulse_Event));
357 ev->index = index;
358
359 ecore_event_add(SOURCE_REMOVED, ev, _event_free_cb, NULL);
360}
361
362static void
363_sink_default_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
364 void *userdata EINA_UNUSED)
365{
366 Epulse_Event_Sink *ev;
367
368 if (eol < 0)
369 {
370 if (pa_context_errno(c) == PA_ERR_NOENTITY)
371 return;
372
373 ERR("Sink callback failure");
374 return;
375 }
376
377 if (eol > 0)
378 return;
379
380 DBG("sink index: %d\nsink name: %s", info->index,
381 info->name);
382
383 ev = calloc(1, sizeof(Epulse_Event_Sink));
384 ev->base.index = info->index;
385 ev->base.name = strdup(info->description);
386 ev->base.volume = info->volume;
387 ev->base.mute = !!info->mute;
388
389 ecore_event_add(SINK_DEFAULT, ev, _event_sink_free_cb, NULL);
390}
391
392static void
393_server_info_cb(pa_context *c, const pa_server_info *info,
394 void *userdata)
395{
396 pa_operation *o;
397
398 if (!(o = pa_context_get_sink_info_by_name(c, info->default_sink_name,
399 _sink_default_cb, userdata)))
400 {
401 ERR("pa_context_get_sink_info_by_name() failed");
402 return;
403 }
404 pa_operation_unref(o);
405}
406
407static void
408_subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
409 uint32_t index, void *data)
410{
411 pa_operation *o;
412
413 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
414 case PA_SUBSCRIPTION_EVENT_SINK:
415 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
416 PA_SUBSCRIPTION_EVENT_REMOVE)
417 _sink_remove_cb(index, data);
418 else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
419 PA_SUBSCRIPTION_EVENT_NEW)
420 {
421 if (!(o = pa_context_get_sink_info_by_index(c, index,
422 _sink_cb, data)))
423 {
424 ERR("pa_context_get_sink_info_by_index() failed");
425 return;
426 }
427 pa_operation_unref(o);
428 }
429 else
430 {
431 if (!(o = pa_context_get_sink_info_by_index(c, index,
432 _sink_changed_cb,
433 data)))
434 {
435 ERR("pa_context_get_sink_info_by_index() failed");
436 return;
437 }
438 pa_operation_unref(o);
439 }
440 break;
441
442 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
443 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
444 PA_SUBSCRIPTION_EVENT_REMOVE)
445 _sink_input_remove_cb(index, data);
446 else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
447 PA_SUBSCRIPTION_EVENT_NEW)
448 {
449 if (!(o = pa_context_get_sink_input_info(c, index,
450 _sink_input_cb, data)))
451 {
452 ERR("pa_context_get_sink_input_info() failed");
453 return;
454 }
455 pa_operation_unref(o);
456 }
457 else
458 {
459 if (!(o = pa_context_get_sink_input_info(c, index,
460 _sink_input_changed_cb,
461 data)))
462 {
463 ERR("pa_context_get_sink_input_info() failed");
464 return;
465 }
466 pa_operation_unref(o);
467 }
468 break;
469
470 case PA_SUBSCRIPTION_EVENT_SOURCE:
471 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
472 PA_SUBSCRIPTION_EVENT_REMOVE)
473 _source_remove_cb(index, data);
474 else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
475 PA_SUBSCRIPTION_EVENT_NEW)
476 {
477 if (!(o = pa_context_get_source_info_by_index(c, index,
478 _source_cb, data)))
479 {
480 ERR("pa_context_get_source_info() failed");
481 return;
482 }
483 pa_operation_unref(o);
484 }
485 else
486 {
487 if (!(o = pa_context_get_source_info_by_index(c, index,
488 _source_changed_cb,
489 data)))
490 {
491 ERR("pa_context_get_source_info() failed");
492 return;
493 }
494 pa_operation_unref(o);
495 }
496 break;
497
498 default:
499 WRN("Event not handled");
500 break;
501 }
502}
503
504static void
505_epulse_pa_state_cb(pa_context *context, void *data EINA_UNUSED)
506{
507 pa_operation *o;
508
509 switch (pa_context_get_state(context))
510 {
511 case PA_CONTEXT_UNCONNECTED:
512 case PA_CONTEXT_CONNECTING:
513 case PA_CONTEXT_AUTHORIZING:
514 case PA_CONTEXT_SETTING_NAME:
515 break;
516
517 case PA_CONTEXT_READY:
518 {
519 pa_context_set_subscribe_callback(context, _subscribe_cb, ctx);
520 if (!(o = pa_context_subscribe(context, (pa_subscription_mask_t)
521 (PA_SUBSCRIPTION_MASK_SINK|
522 PA_SUBSCRIPTION_MASK_SOURCE|
523 PA_SUBSCRIPTION_MASK_SINK_INPUT|
524 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
525 PA_SUBSCRIPTION_MASK_CLIENT|
526 PA_SUBSCRIPTION_MASK_SERVER|
527 PA_SUBSCRIPTION_MASK_CARD),
528 NULL, NULL)))
529 {
530 ERR("pa_context_subscribe() failed");
531 return;
532 }
533 pa_operation_unref(o);
534
535 if (!(o = pa_context_get_sink_info_list(context, _sink_cb, ctx)))
536 {
537 ERR("pa_context_get_sink_info_list() failed");
538 return;
539 }
540 pa_operation_unref(o);
541
542 if (!(o = pa_context_get_sink_input_info_list(context,
543 _sink_input_cb,
544 ctx)))
545 {
546 ERR("pa_context_get_sink_input_info_list() failed");
547 return;
548 }
549 pa_operation_unref(o);
550
551 if (!(o = pa_context_get_source_info_list(context, _source_cb,
552 ctx)))
553 {
554 ERR("pa_context_get_source_info_list() failed");
555 return;
556 }
557 pa_operation_unref(o);
558
559 if (!(o = pa_context_get_server_info(context, _server_info_cb,
560 ctx)))
561 {
562 ERR("pa_context_get_server_info() failed");
563 return;
564 }
565 pa_operation_unref(o);
566 break;
567 }
568
569 case PA_CONTEXT_FAILED:
570 pa_context_unref(context);
571 context = NULL;
572 return;
573
574 case PA_CONTEXT_TERMINATED:
575 default:
576 exit(0);
577 return;
578 }
579}
580
581int
582epulse_init(void)
583{
584 pa_proplist *proplist;
585
586 if (_init_count > 0)
587 goto end;
588
589 ctx = calloc(1, sizeof(Epulse_Context));
590 if (!ctx)
591 {
592 ERR("Could not create Epulse Context");
593 return 0;
594 }
595
596 SINK_ADDED = ecore_event_type_new();
597 SINK_CHANGED = ecore_event_type_new();
598 SINK_DEFAULT = ecore_event_type_new();
599 SINK_REMOVED = ecore_event_type_new();
600 SINK_INPUT_ADDED = ecore_event_type_new();
601 SINK_INPUT_CHANGED = ecore_event_type_new();
602 SINK_INPUT_REMOVED = ecore_event_type_new();
603 SOURCE_ADDED = ecore_event_type_new();
604 SOURCE_CHANGED = ecore_event_type_new();
605 SOURCE_REMOVED = ecore_event_type_new();
606 SOURCE_INPUT_ADDED = ecore_event_type_new();
607 SOURCE_INPUT_REMOVED = ecore_event_type_new();
608
609 ctx->api = functable;
610 ctx->api.userdata = ctx;
611
612 proplist = pa_proplist_new();
613 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "Efl Volume Control");
614 pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID,
615 "org.enlightenment.volumecontrol");
616 pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card");
617 ctx->context = pa_context_new_with_proplist(&(ctx->api), NULL, proplist);
618 if (!ctx->context)
619 {
620 WRN("Could not connect to pulse");
621 goto err;
622 }
623
624 pa_context_set_state_callback(ctx->context, _epulse_pa_state_cb, ctx);
625 pa_context_connect(ctx->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
626 pa_proplist_free(proplist);
627
628 end:
629 _init_count++;
630 return _init_count;
631
632 err:
633 pa_proplist_free(proplist);
634 free(ctx);
635 return 0;
636}
637
638void
639epulse_shutdown(void)
640{
641 if (_init_count == 0)
642 return;
643
644 _init_count--;
645 if (_init_count > 0)
646 return;
647
648 pa_context_unref(ctx->context);
649 free(ctx);
650 ctx = NULL;
651}
652
653Eina_Bool
654epulse_source_volume_set(int index, pa_cvolume volume)
655{
656 pa_operation* o;
657 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
658
659 if (!(o = pa_context_set_source_volume_by_index(ctx->context,
660 index, &volume, NULL, NULL)))
661 {
662 ERR("pa_context_set_source_volume_by_index() failed");
663 return EINA_FALSE;
664 }
665
666 return EINA_TRUE;
667}
668
669Eina_Bool
670epulse_source_mute_set(int index, Eina_Bool mute)
671{
672 pa_operation* o;
673 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
674
675 if (!(o = pa_context_set_source_mute_by_index(ctx->context,
676 index, mute, NULL, NULL)))
677 {
678 ERR("pa_context_set_source_mute() failed");
679 return EINA_FALSE;
680 }
681
682 return EINA_TRUE;
683}
684
685Eina_Bool
686epulse_sink_volume_set(int index, pa_cvolume volume)
687{
688 pa_operation* o;
689 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
690
691 if (!(o = pa_context_set_sink_volume_by_index(ctx->context,
692 index, &volume, NULL, NULL)))
693 {
694 ERR("pa_context_set_sink_volume_by_index() failed");
695 return EINA_FALSE;
696 }
697
698 return EINA_TRUE;
699}
700
701Eina_Bool
702epulse_sink_mute_set(int index, Eina_Bool mute)
703{
704 pa_operation* o;
705 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
706
707 if (!(o = pa_context_set_sink_mute_by_index(ctx->context,
708 index, mute, NULL, NULL)))
709 {
710 ERR("pa_context_set_sink_mute() failed");
711 return EINA_FALSE;
712 }
713
714 return EINA_TRUE;
715}
716
717Eina_Bool
718epulse_sink_input_volume_set(int index, pa_cvolume volume)
719{
720 pa_operation* o;
721 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
722
723 if (!(o = pa_context_set_sink_input_volume(ctx->context,
724 index, &volume, NULL, NULL)))
725 {
726 ERR("pa_context_set_sink_input_volume_by_index() failed");
727 return EINA_FALSE;
728 }
729
730 return EINA_TRUE;
731}
732
733Eina_Bool
734epulse_sink_input_mute_set(int index, Eina_Bool mute)
735{
736 pa_operation* o;
737 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
738
739 if (!(o = pa_context_set_sink_input_mute(ctx->context,
740 index, mute, NULL, NULL)))
741 {
742 ERR("pa_context_set_sink_input_mute() failed");
743 return EINA_FALSE;
744 }
745
746 return EINA_TRUE;
747}
748
749Eina_Bool
750epulse_sink_input_move(int index, int sink_index)
751{
752 pa_operation* o;
753 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
754
755 if (!(o = pa_context_move_sink_input_by_index(ctx->context,
756 index, sink_index, NULL,
757 NULL)))
758 {
759 ERR("pa_context_move_sink_input_by_index() failed");
760 return EINA_FALSE;
761 }
762
763 return EINA_TRUE;
764}
765
766Eina_Bool
767epulse_sink_port_set(int index, const char *port)
768{
769 pa_operation* o;
770 EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
771
772 if (!(o = pa_context_set_source_port_by_index(ctx->context,
773 index, port, NULL,
774 NULL)))
775 {
776 ERR("pa_context_set_source_port_by_index() failed");
777 return EINA_FALSE;
778 }
779
780 return EINA_TRUE;
781}
diff --git a/src/lib/epulse.h b/src/lib/epulse.h
new file mode 100644
index 0000000..aaace56
--- /dev/null
+++ b/src/lib/epulse.h
@@ -0,0 +1,63 @@
1#include "common.h"
2
3#include <pulse/pulseaudio.h>
4
5#define PA_VOLUME_TO_INT(_vol) \
6 ((_vol*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM)
7#define INT_TO_PA_VOLUME(_vol) \
8 ((PA_VOLUME_NORM*_vol-PA_VOLUME_NORM/2)/100)
9
10typedef struct _Port Port;
11struct _Port {
12 Eina_Bool active;
13 Eina_Bool available;
14 int priority;
15 char *name;
16 char *description;
17};
18
19typedef struct _Epulse_Event Epulse_Event;
20struct _Epulse_Event
21{
22 int index;
23 char *name;
24 pa_cvolume volume;
25 Eina_Bool mute;
26};
27
28typedef struct _Epulse_Event_Sink Epulse_Event_Sink;
29struct _Epulse_Event_Sink {
30 Epulse_Event base;
31 Eina_List *ports;
32};
33
34typedef struct _Epulse_Event_Sink_Input Epulse_Event_Sink_Input;
35struct _Epulse_Event_Sink_Input {
36 Epulse_Event base;
37 int sink;
38 char *icon;
39};
40
41EAPI extern int SINK_ADDED;
42EAPI extern int SINK_CHANGED;
43EAPI extern int SINK_DEFAULT;
44EAPI extern int SINK_REMOVED;
45EAPI extern int SINK_INPUT_ADDED;
46EAPI extern int SINK_INPUT_CHANGED;
47EAPI extern int SINK_INPUT_REMOVED;
48EAPI extern int SOURCE_ADDED;
49EAPI extern int SOURCE_CHANGED;
50EAPI extern int SOURCE_REMOVED;
51EAPI extern int SOURCE_INPUT_ADDED;
52EAPI extern int SOURCE_INPUT_REMOVED;
53
54EAPI int epulse_init(void);
55EAPI Eina_Bool epulse_source_volume_set(int index, pa_cvolume volume);
56EAPI Eina_Bool epulse_source_mute_set(int index, Eina_Bool mute);
57EAPI Eina_Bool epulse_sink_volume_set(int index, pa_cvolume volume);
58EAPI Eina_Bool epulse_sink_mute_set(int index, Eina_Bool mute);
59EAPI Eina_Bool epulse_sink_port_set(int index, const char *port);
60EAPI Eina_Bool epulse_sink_input_volume_set(int index, pa_cvolume volume);
61EAPI Eina_Bool epulse_sink_input_mute_set(int index, Eina_Bool mute);
62EAPI Eina_Bool epulse_sink_input_move(int index, int sink_index);
63EAPI void epulse_shutdown(void);
diff --git a/src/epulse_ml.c b/src/lib/epulse_ml.c
index 6686d3c..9c9a2b9 100644
--- a/src/epulse_ml.c
+++ b/src/lib/epulse_ml.c
@@ -30,9 +30,9 @@ static Ecore_Fd_Handler_Flags
30map_flags_to_ecore(pa_io_event_flags_t flags) 30map_flags_to_ecore(pa_io_event_flags_t flags)
31{ 31{
32 return (Ecore_Fd_Handler_Flags)((flags & PA_IO_EVENT_INPUT ? ECORE_FD_READ : 0) | 32 return (Ecore_Fd_Handler_Flags)((flags & PA_IO_EVENT_INPUT ? ECORE_FD_READ : 0) |
33 (flags & PA_IO_EVENT_OUTPUT ? ECORE_FD_WRITE : 0) | 33 (flags & PA_IO_EVENT_OUTPUT ? ECORE_FD_WRITE : 0) |
34 (flags & PA_IO_EVENT_ERROR ? ECORE_FD_ERROR : 0) | 34 (flags & PA_IO_EVENT_ERROR ? ECORE_FD_ERROR : 0) |
35 (flags & PA_IO_EVENT_HANGUP ? ECORE_FD_READ : 0)); 35 (flags & PA_IO_EVENT_HANGUP ? ECORE_FD_READ : 0));
36} 36}
37 37
38static Eina_Bool 38static Eina_Bool
@@ -62,9 +62,9 @@ _ecore_io_wrapper(void *data, Ecore_Fd_Handler *handler)
62 } 62 }
63 63
64 if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE)) 64 if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE))
65 flags |= PA_IO_EVENT_OUTPUT; 65 flags |= PA_IO_EVENT_OUTPUT;
66 if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR)) 66 if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR))
67 flags |= PA_IO_EVENT_ERROR; 67 flags |= PA_IO_EVENT_ERROR;
68 68
69 event->callback(event->mainloop, event, fd, flags, event->userdata); 69 event->callback(event->mainloop, event, fd, flags, event->userdata);
70 70
@@ -193,7 +193,7 @@ void
193_ecore_pa_time_free(pa_time_event *event) 193_ecore_pa_time_free(pa_time_event *event)
194{ 194{
195 if (event->timer) 195 if (event->timer)
196 ecore_timer_del(event->timer); 196 ecore_timer_del(event->timer);
197 197
198 event->timer = NULL; 198 event->timer = NULL;
199 199
@@ -262,7 +262,7 @@ void
262_ecore_pa_defer_free(pa_defer_event *event) 262_ecore_pa_defer_free(pa_defer_event *event)
263{ 263{
264 if (event->idler) 264 if (event->idler)
265 ecore_idler_del(event->idler); 265 ecore_idler_del(event->idler);
266 266
267 event->idler = NULL; 267 event->idler = NULL;
268 268
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index df0808b..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,18 +0,0 @@
1#include <Elementary.h>
2
3EAPI int elm_main(int argc, char **argv)
4{
5 int args;
6
7 elm_run();
8
9
10 return 0;
11
12}
13
14/*
15 * Create the default main() that will work with both quicklaunch or
16 * regular applications.
17 */
18ELM_MAIN()
diff --git a/src/module/e_mod_main.c b/src/module/e_mod_main.c
new file mode 100644
index 0000000..232b820
--- /dev/null
+++ b/src/module/e_mod_main.c
@@ -0,0 +1,586 @@
1#include <common.h>
2
3#include <e.h>
4#include <Eina.h>
5#include <epulse.h>
6#include "e_mod_main.h"
7
8/* module requirements */
9EAPI E_Module_Api e_modapi =
10 {
11 E_MODULE_API_VERSION,
12 "Pulse Mixer"
13 };
14
15/* necessary forward delcaration */
16static E_Gadcon_Client *_gc_init(E_Gadcon *gc, const char *name,
17 const char *id, const char *style);
18static void _gc_shutdown(E_Gadcon_Client *gcc);
19static void _gc_orient(E_Gadcon_Client *gcc,
20 E_Gadcon_Orient orient);
21static const char *_gc_label(const E_Gadcon_Client_Class *client_class);
22static Evas_Object *_gc_icon(const E_Gadcon_Client_Class *client_class,
23 Evas *evas);
24static const char *_gc_id_new(const E_Gadcon_Client_Class *client_class);
25
26static const E_Gadcon_Client_Class _gadcon_class =
27 {
28 GADCON_CLIENT_CLASS_VERSION,
29 "pulse_mixer",
30 {
31 _gc_init, _gc_shutdown,
32 _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL,
33 e_gadcon_site_is_not_toolbar
34 },
35 E_GADCON_CLIENT_STYLE_PLAIN
36 };
37
38typedef struct _Sink Sink;
39struct _Sink {
40 int index;
41 pa_cvolume volume;
42 int mute;
43 char *name;
44};
45
46typedef struct _Context Context;
47struct _Context
48{
49 char *theme;
50 Ecore_Exe *epulse;
51 Ecore_Event_Handler *epulse_event_handler;
52 Ecore_Event_Handler *sink_default_handler;
53 Ecore_Event_Handler *sink_changed_handler;
54 Ecore_Event_Handler *sink_added_handler;
55 Ecore_Event_Handler *sink_removed_handler;
56 Sink *sink_default;
57 E_Module *module;
58 Eina_List *instances;
59 Eina_List *sinks;
60 E_Menu *menu;
61};
62
63typedef struct _Instance Instance;
64struct _Instance
65{
66 E_Gadcon_Client *gcc;
67 E_Gadcon_Orient orient;
68
69 E_Gadcon_Popup *popup;
70 Evas_Object *gadget;
71 Evas_Object *list;
72 Evas_Object *slider;
73 Evas_Object *check;
74
75 int mute;
76};
77
78static Context *mixer_context = NULL;
79
80static void
81_mixer_popup_update(Instance *inst, int mute, int vol)
82{
83 e_widget_check_checked_set(inst->check, mute);
84 e_slider_value_set(inst->slider, vol);
85}
86
87static void
88_mixer_gadget_update(void)
89{
90 Edje_Message_Int_Set *msg;
91 Instance *inst;
92 Eina_List*l;
93
94 EINA_LIST_FOREACH(mixer_context->instances, l, inst)
95 {
96 pa_volume_t vol = pa_cvolume_avg(&mixer_context->sink_default->volume);
97 msg = alloca(sizeof(Edje_Message_Int_Set) + (2 * sizeof(int)));
98 msg->count = 3;
99 msg->val[0] = mixer_context->sink_default->mute;
100 msg->val[1] = PA_VOLUME_TO_INT(vol);
101 msg->val[2] = msg->val[1];
102 edje_object_message_send(inst->gadget, EDJE_MESSAGE_INT_SET, 0, msg);
103
104 edje_object_signal_emit(inst->gadget, "e,action,volume,change", "e");
105
106 if (inst->popup)
107 _mixer_popup_update(inst, mixer_context->sink_default->mute,
108 msg->val[1]);
109 }
110}
111
112static void
113_popup_del(Instance *inst)
114{
115 inst->slider = NULL;
116 inst->check = NULL;
117 E_FREE_FUNC(inst->popup, e_object_del);
118}
119
120static void
121_popup_del_cb(void *obj)
122{
123 _popup_del(e_object_data_get(obj));
124}
125
126static void
127_popup_comp_del_cb(void *data, Evas_Object *obj EINA_UNUSED)
128{
129 Instance *inst = data;
130
131 E_FREE_FUNC(inst->popup, e_object_del);
132}
133
134static Eina_Bool
135_epulse_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED,
136 void *info EINA_UNUSED)
137{
138 mixer_context->epulse = NULL;
139 if (mixer_context->epulse_event_handler)
140 ecore_event_handler_del(mixer_context->epulse_event_handler);
141
142 return EINA_TRUE;
143}
144
145static void
146_epulse_exec_cb(void *data, void *data2 EINA_UNUSED)
147{
148 Instance *inst = data;
149
150 _popup_del(inst);
151 if (mixer_context->epulse)
152 return;
153
154 mixer_context->epulse = ecore_exe_run("epulse", NULL);
155 if (mixer_context->epulse_event_handler)
156 ecore_event_handler_del(mixer_context->epulse_event_handler);
157 mixer_context->epulse_event_handler =
158 ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _epulse_del_cb, NULL);
159}
160
161static void
162_check_changed_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
163 void *event EINA_UNUSED)
164{
165 Sink *s = mixer_context->sink_default;
166 s->mute = !s->mute;
167 if (!epulse_sink_mute_set(s->index, s->mute))
168 {
169 WRN("Could not mute the sink: %d", s->index);
170 s->mute = !s->mute;
171 return;
172 }
173
174 _mixer_gadget_update();
175}
176
177static void
178_slider_changed_cb(void *data EINA_UNUSED, Evas_Object *obj,
179 void *event EINA_UNUSED)
180{
181 int val;
182 pa_volume_t v;
183 Sink *s = mixer_context->sink_default;
184
185 val = (int)e_slider_value_get(obj);
186 v = INT_TO_PA_VOLUME(val);
187
188 pa_cvolume_set(&s->volume, s->volume.channels, v);
189 epulse_sink_volume_set(s->index, s->volume);
190}
191
192static Evas_Object *
193_popup_add_slider(Instance *inst)
194{
195 pa_volume_t vol = pa_cvolume_avg(&mixer_context->sink_default->volume);
196 int value = PA_VOLUME_TO_INT(vol);
197 Evas_Object *slider = e_slider_add(e_comp_get(inst->popup)->evas);
198 EINA_SAFETY_ON_NULL_RETURN_VAL(slider, NULL);
199
200 evas_object_show(slider);
201 e_slider_orientation_set(slider, 1);
202 e_slider_value_range_set(slider, 0.0, 100.0);
203 e_slider_value_format_display_set(slider, NULL);
204 evas_object_smart_callback_add(slider, "changed", _slider_changed_cb,
205 NULL);
206
207 e_slider_value_set(slider, value);
208 return slider;
209}
210
211static void
212_sink_selected_cb(void *data)
213{
214 Sink *s = data;
215
216 mixer_context->sink_default = s;
217 _mixer_gadget_update();
218}
219
220static void
221_popup_new(Instance *inst)
222{
223 Evas_Object *button, *list;
224 Evas *evas;
225 Evas_Coord mw, mh;
226 Sink *s;
227 Eina_List *l;
228 int pos = 0;
229
230 inst->popup = e_gadcon_popup_new(inst->gcc, 0);
231 evas = e_comp_get(inst->gcc)->evas;
232
233 list = e_widget_list_add(evas, 0, 0);
234
235 inst->list = e_widget_ilist_add(evas, 24, 24, NULL);
236 e_widget_size_min_set(inst->list, 120, 100);
237 e_widget_list_object_append(list, inst->list, 1, 1, 0.5);
238
239 EINA_LIST_FOREACH(mixer_context->sinks, l, s)
240 {
241 e_widget_ilist_append_full(inst->list, NULL, NULL, s->name,
242 _sink_selected_cb,
243 s, NULL);
244 if (mixer_context->sink_default == s)
245 e_widget_ilist_selected_set(inst->list, pos);
246
247 pos++;
248 }
249
250 inst->slider = _popup_add_slider(inst);
251 e_widget_list_object_append(list, inst->slider, 1, 1, 0.5);
252
253 inst->mute = mixer_context->sink_default->mute;
254 inst->check = e_widget_check_add(evas, "Mute",
255 &inst->mute);
256 e_widget_list_object_append(list, inst->check, 1, 9, 0.5);
257 evas_object_smart_callback_add(inst->check, "changed", _check_changed_cb,
258 NULL);
259
260 button = e_widget_button_add(evas, NULL, "preferences-system",
261 _epulse_exec_cb, inst, NULL);
262 e_widget_list_object_append(list, button, 1, 0, 0.5);
263
264 e_widget_size_min_get(list, &mw, &mh);
265 if (mh < 208) mh = 208;
266 e_widget_size_min_set(list, 208, mh);
267
268 e_gadcon_popup_content_set(inst->popup, list);
269 e_comp_object_util_autoclose(inst->popup->comp_object,
270 _popup_comp_del_cb, NULL, inst);
271 e_gadcon_popup_show(inst->popup);
272 e_object_data_set(E_OBJECT(inst->popup), inst);
273 E_OBJECT_DEL_SET(inst->popup, _popup_del_cb);
274}
275
276static void
277_menu_cb(void *data, E_Menu *menu EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
278{
279 _epulse_exec_cb(data, NULL);
280}
281
282static void
283_menu_new(Instance *inst, Evas_Event_Mouse_Down *ev)
284{
285 E_Zone *zone;
286 E_Menu *m;
287 E_Menu_Item *mi;
288 int x, y;
289
290 zone = e_util_zone_current_get(e_manager_current_get());
291
292 m = e_menu_new();
293
294 mi = e_menu_item_new(m);
295 e_menu_item_label_set(mi, "Advanced");
296 e_util_menu_item_theme_icon_set(mi, "configure");
297 e_menu_item_callback_set(mi, _menu_cb, inst);
298
299 m = e_gadcon_client_util_menu_items_append(inst->gcc, m, 0);
300 e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL);
301 e_menu_activate_mouse(m, zone, x + ev->output.x, y + ev->output.y,
302 1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp);
303 evas_event_feed_mouse_up(inst->gcc->gadcon->evas, ev->button,
304 EVAS_BUTTON_NONE, ev->timestamp, NULL);
305}
306
307static void
308_mouse_down_cb(void *data, Evas *evas EINA_UNUSED,
309 Evas_Object *obj EINA_UNUSED, void *event)
310{
311 Instance *inst = data;
312 Evas_Event_Mouse_Down *ev = event;
313
314 if (ev->button == 1)
315 {
316 if (!inst->popup)
317 _popup_new(inst);
318 }
319 else if (ev->button == 3)
320 {
321 _menu_new(inst, ev);
322 }
323}
324
325/*
326 * Gadcon functions
327 */
328static E_Gadcon_Client *
329_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
330{
331 E_Gadcon_Client *gcc;
332 Instance *inst;
333
334 inst = E_NEW(Instance, 1);
335
336 inst->gadget = edje_object_add(gc->evas);
337 edje_object_file_set(inst->gadget, mixer_context->theme,
338 "e/modules/mixer/main");
339
340 gcc = e_gadcon_client_new(gc, name, id, style, inst->gadget);
341 gcc->data = inst;
342 inst->gcc = gcc;
343
344 evas_object_event_callback_add(inst->gadget, EVAS_CALLBACK_MOUSE_DOWN,
345 _mouse_down_cb, inst);
346 mixer_context->instances = eina_list_append(mixer_context->instances, inst);
347
348 return gcc;
349}
350
351static void
352_gc_shutdown(E_Gadcon_Client *gcc)
353{
354 Instance *inst;
355
356 inst = gcc->data;
357 evas_object_del(inst->gadget);
358 mixer_context->instances = eina_list_remove(mixer_context->instances, inst);
359 free(inst);
360}
361
362static void
363_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient)
364{
365 Instance *inst;
366 int w, h;
367
368 inst = gcc->data;
369 if (orient != E_GADCON_ORIENT_LAST)
370 inst->orient = orient;
371
372 switch (inst->orient)
373 {
374 case E_GADCON_ORIENT_VERT:
375 case E_GADCON_ORIENT_LEFT:
376 case E_GADCON_ORIENT_RIGHT:
377 case E_GADCON_ORIENT_CORNER_LT:
378 case E_GADCON_ORIENT_CORNER_RT:
379 case E_GADCON_ORIENT_CORNER_LB:
380 case E_GADCON_ORIENT_CORNER_RB:
381 w = 16;
382 h = 16;
383 default:
384 break;
385 }
386
387 e_gadcon_client_aspect_set(gcc, w, h);
388 e_gadcon_client_min_size_set(gcc, w, h);
389}
390
391static const char *
392_gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED)
393{
394 return "Pulse Mixer";
395}
396
397static Evas_Object *
398_gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas)
399{
400 Evas_Object *o;
401 char buf[4096] = { 0 };
402
403 o = edje_object_add(evas);
404 snprintf(buf, sizeof(buf), "%s/mixer.edj",
405 e_module_dir_get(mixer_context->module));
406 edje_object_file_set(o, buf, "icon");
407
408 return o;
409}
410
411static const char *
412_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED)
413{
414 return _gadcon_class.name;
415}
416
417static Eina_Bool
418_sink_default_cb(void *data EINA_UNUSED, int type EINA_UNUSED,
419 void *info)
420{
421 Epulse_Event *ev = info;
422 Eina_List *l;
423 Sink *s;
424
425 EINA_LIST_FOREACH(mixer_context->sinks, l, s)
426 {
427 if (s->index == ev->index)
428 {
429 mixer_context->sink_default = s;
430 goto end;
431 }
432 }
433
434 s = malloc(sizeof(*s));
435 s->index = ev->index;
436 s->volume = ev->volume;
437 s->name = strdup(ev->name);
438 s->mute = ev->mute;
439
440 mixer_context->sinks = eina_list_append(mixer_context->sinks, s);
441 mixer_context->sink_default = s;
442
443 end:
444 _mixer_gadget_update();
445 return ECORE_CALLBACK_DONE;
446}
447
448static Eina_Bool
449_sink_changed_cb(void *data EINA_UNUSED, int type EINA_UNUSED,
450 void *info)
451{
452 Epulse_Event *ev = info;
453 Eina_List *l;
454 Sink *s;
455
456 EINA_LIST_FOREACH(mixer_context->sinks, l, s)
457 {
458 if (ev->index == s->index)
459 {
460 s->mute = ev->mute;
461 s->volume = ev->volume;
462 if (ev->index == mixer_context->sink_default->index)
463 _mixer_gadget_update();
464 }
465 }
466
467 return ECORE_CALLBACK_DONE;
468}
469
470static Eina_Bool
471_sink_added_cb(void *data EINA_UNUSED, int type EINA_UNUSED,
472 void *info)
473{
474 Epulse_Event *ev = info;
475 Eina_List *l;
476 Sink *s;
477
478 EINA_LIST_FOREACH(mixer_context->sinks, l, s)
479 if (s->index == ev->index)
480 return ECORE_CALLBACK_DONE;
481
482 s = malloc(sizeof(*s));
483 s->index = ev->index;
484 s->volume = ev->volume;
485 s->name = strdup(ev->name);
486 s->mute = ev->mute;
487
488 mixer_context->sinks = eina_list_append(mixer_context->sinks, s);
489 return ECORE_CALLBACK_DONE;
490}
491
492static Eina_Bool
493_sink_removed_cb(void *data EINA_UNUSED, int type EINA_UNUSED,
494 void *info)
495{
496 Epulse_Event *ev = info;
497 Eina_List *l, *ll;
498 Sink *s;
499
500 EINA_LIST_FOREACH_SAFE(mixer_context->sinks, l, ll, s)
501 {
502 if (ev->index == s->index)
503 {
504 free(s->name);
505 free(s);
506 mixer_context->sinks =
507 eina_list_remove_list(mixer_context->sinks, l);
508 }
509 }
510
511 if (ev->index == mixer_context->sink_default->index)
512 {
513 s = mixer_context->sinks->data;
514 mixer_context->sink_default = s;
515 _mixer_gadget_update();
516 }
517
518 return ECORE_CALLBACK_DONE;
519}
520
521EAPI void *
522e_modapi_init(E_Module *m)
523{
524 char buf[4096];
525
526 EINA_SAFETY_ON_FALSE_RETURN_VAL(epulse_common_init("epulse_mod"),
527 NULL);
528 EINA_SAFETY_ON_FALSE_RETURN_VAL(epulse_init() > 0, NULL);
529 if (!mixer_context)
530 {
531 mixer_context = E_NEW(Context, 1);
532
533 mixer_context->sink_default_handler =
534 ecore_event_handler_add(SINK_DEFAULT, _sink_default_cb, NULL);
535 mixer_context->sink_changed_handler =
536 ecore_event_handler_add(SINK_CHANGED, _sink_changed_cb, NULL);
537 mixer_context->sink_added_handler =
538 ecore_event_handler_add(SINK_ADDED, _sink_added_cb, NULL);
539 mixer_context->sink_removed_handler =
540 ecore_event_handler_add(SINK_REMOVED, _sink_removed_cb, NULL);
541 mixer_context->module = m;
542 snprintf(buf, sizeof(buf), "%s/mixer.edj",
543 e_module_dir_get(mixer_context->module));
544 mixer_context->theme = strdup(buf);
545 }
546
547 e_gadcon_provider_register(&_gadcon_class);
548
549 return m;
550}
551
552EAPI int
553e_modapi_shutdown(E_Module *m EINA_UNUSED)
554{
555 Sink *s;
556 e_gadcon_provider_unregister((const E_Gadcon_Client_Class *)&_gadcon_class);
557
558 if (!mixer_context)
559 {
560 if (mixer_context->theme)
561 free(mixer_context->theme);
562
563 ecore_event_handler_del(mixer_context->sink_default_handler);
564 ecore_event_handler_del(mixer_context->sink_changed_handler);
565 ecore_event_handler_del(mixer_context->sink_added_handler);
566 ecore_event_handler_del(mixer_context->sink_removed_handler);
567
568 EINA_LIST_FREE(mixer_context->sinks, s)
569 {
570 free(s->name);
571 free(s);
572 }
573
574 E_FREE(mixer_context);
575 }
576
577 epulse_common_shutdown();
578 epulse_shutdown();
579 return 1;
580}
581
582EAPI int
583e_modapi_save(E_Module *m EINA_UNUSED)
584{
585 return 1;
586}
diff --git a/src/module/e_mod_main.h b/src/module/e_mod_main.h
new file mode 100644
index 0000000..01b0f35
--- /dev/null
+++ b/src/module/e_mod_main.h
@@ -0,0 +1,12 @@
1#ifndef _E_MOD_MAIN_H_
2#define _E_MOD_MAIN_H_
3
4#define CONFIG_VERSION 1
5
6EAPI extern E_Module_Api e_modapi;
7
8EAPI void *e_modapi_init(E_Module *m);
9EAPI int e_modapi_shutdown(E_Module *m);
10EAPI int e_modapi_save(E_Module *m);
11
12#endif /* _E_MOD_MAIN_H_ */