wmix: release memory for startup-only configuration stuff after startup
[dockapps.git] / wmix / wmix.c
blob1f7ddabae31daef63cbfe126a31a382dd812e72f
1 /* WMix 3.0 -- a mixer using the OSS mixer API.
2 * Copyright (C) 2000, 2001 timecop@japan.co.jp
3 * Mixer code in version 3.0 based on mixer api library by
4 * Daniel Richard G. <skunk@mit.edu>, which in turn was based on
5 * the mixer code in WMix 2.x releases.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <unistd.h>
30 #include <X11/X.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
34 #include <sys/soundcard.h>
36 #include "include/common.h"
37 #include "include/mixer.h"
38 #include "include/misc.h"
39 #include "include/ui_x.h"
40 #include "include/config.h"
43 static Display *display;
44 static bool button_pressed = false;
45 static bool slider_pressed = false;
46 static double prev_button_press_time = 0.0;
48 static float display_height;
49 static float display_width;
50 static int mouse_drag_home_x;
51 static int mouse_drag_home_y;
52 static int idle_loop;
54 /* local stuff */
55 static void signal_catch(int sig);
56 static void button_press_event(XButtonEvent *event);
57 static void button_release_event(XButtonEvent *event);
58 static void motion_event(XMotionEvent *event);
61 int main(int argc, char **argv)
63 XEvent event;
65 config_init();
66 parse_cli_options(argc, argv);
67 config_read();
69 mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
70 mixer_set_channel(0);
72 display = XOpenDisplay(config.display_name);
73 if (display == NULL) {
74 const char *name;
76 if (config.display_name) {
77 name = config.display_name;
78 } else {
79 name = getenv("DISPLAY");
80 if (name == NULL) {
81 fprintf(stderr, "wmix:error: Unable to open display, variable $DISPLAY not set\n");
82 return EXIT_FAILURE;
85 fprintf(stderr, "wmix:error: Unable to open display \"%s\"\n", name);
86 return EXIT_FAILURE;
88 display_width = (float)DisplayWidth(display, DefaultScreen(display)) / 4.0;
89 display_height = (float)DisplayHeight(display, DefaultScreen(display)) / 2.0;
91 dockapp_init(display);
92 new_window("wmix", 64, 64);
93 new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60);
95 config_release();
97 blit_string("wmix 3.0");
98 scroll_text(3, 4, 57, true);
99 ui_update();
101 /* add click regions */
102 add_region(1, 37, 36, 25, 25); /* knob */
103 add_region(2, 4, 42, 27, 15); /* balancer */
104 add_region(3, 2, 26, 7, 10); /* previous channel */
105 add_region(4, 10, 26, 7, 10); /* next channel */
106 add_region(5, 39, 14, 20, 7); /* mute toggle */
107 add_region(6, 4, 14, 13, 7); /* rec toggle */
108 add_region(10, 3, 4, 56, 7); /* re-scroll current channel name */
110 /* setup up/down signal handler */
111 create_pid_file();
112 signal(SIGUSR1, (void *) signal_catch);
113 signal(SIGUSR2, (void *) signal_catch);
115 while (true) {
116 if (button_pressed || slider_pressed || (XPending(display) > 0)) {
117 XNextEvent(display, &event);
118 switch (event.type) {
119 case Expose:
120 redraw_window();
121 break;
122 case ButtonPress:
123 button_press_event(&event.xbutton);
124 idle_loop = 0;
125 break;
126 case ButtonRelease:
127 button_release_event(&event.xbutton);
128 idle_loop = 0;
129 break;
130 case MotionNotify:
131 /* process cursor change, or drag events */
132 motion_event(&event.xmotion);
133 idle_loop = 0;
134 break;
135 case LeaveNotify:
136 /* go back to standard cursor */
137 if ((!button_pressed) && (!slider_pressed))
138 set_cursor(NORMAL_CURSOR);
139 break;
140 case DestroyNotify:
141 XCloseDisplay(display);
142 return EXIT_SUCCESS;
143 default:
144 break;
146 } else {
147 usleep(100000);
148 scroll_text(3, 4, 57, false);
149 /* rescroll message after some delay */
150 if (idle_loop++ > 256) {
151 scroll_text(3, 4, 57, true);
152 idle_loop = 0;
154 /* get rid of OSD after a few seconds of idle */
155 if ((idle_loop > 15) && osd_mapped() && !button_pressed) {
156 unmap_osd();
157 idle_loop = 0;
159 if (mixer_is_changed())
160 ui_update();
163 return EXIT_SUCCESS;
166 static void signal_catch(int sig)
168 switch (sig) {
169 case SIGUSR1:
170 mixer_set_volume_rel(config.scrollstep);
171 if (!osd_mapped())
172 map_osd();
173 if (osd_mapped())
174 update_osd(mixer_get_volume(), false);
175 ui_update();
176 idle_loop = 0;
177 break;
178 case SIGUSR2:
179 mixer_set_volume_rel(-config.scrollstep);
180 if (!osd_mapped())
181 map_osd();
182 if (osd_mapped())
183 update_osd(mixer_get_volume(), false);
184 ui_update();
185 idle_loop = 0;
186 break;
190 static void button_press_event(XButtonEvent *event)
192 double button_press_time = get_current_time();
193 int x = event->x;
194 int y = event->y;
195 bool double_click = false;
197 /* handle wheel scrolling to adjust volume */
198 if (config.mousewheel) {
199 if (event->button == config.wheel_button_up) {
200 mixer_set_volume_rel(config.scrollstep);
201 if (!osd_mapped())
202 map_osd();
203 if (osd_mapped())
204 update_osd(mixer_get_volume(), false);
205 ui_update();
206 return;
208 if (event->button == config.wheel_button_down) {
209 mixer_set_volume_rel(-config.scrollstep);
210 if (!osd_mapped())
211 map_osd();
212 if (osd_mapped())
213 update_osd(mixer_get_volume(), false);
214 ui_update();
215 return;
219 if ((button_press_time - prev_button_press_time) <= 0.5) {
220 double_click = true;
221 prev_button_press_time = 0.0;
222 } else
223 prev_button_press_time = button_press_time;
225 switch (check_region(x, y)) {
226 case 1: /* on knob */
227 button_pressed = true;
228 slider_pressed = false;
229 mouse_drag_home_x = x;
230 mouse_drag_home_y = y;
231 if (double_click) {
232 mixer_toggle_mute();
233 ui_update();
235 break;
236 case 2: /* on slider */
237 button_pressed = false;
238 slider_pressed = true;
239 mouse_drag_home_x = x;
240 mouse_drag_home_y = y;
241 if (double_click) {
242 mixer_set_balance(0.0);
243 ui_update();
245 break;
246 case 3: /* previous channel */
247 mixer_set_channel_rel(-1);
248 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
249 scroll_text(3, 4, 57, true);
250 unmap_osd();
251 map_osd();
252 ui_update();
253 break;
254 case 4: /* next channel */
255 mixer_set_channel_rel(1);
256 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
257 scroll_text(3, 4, 57, true);
258 unmap_osd();
259 map_osd();
260 ui_update();
261 break;
262 case 5: /* toggle mute */
263 mixer_toggle_mute();
264 ui_update();
265 break;
266 case 6: /* toggle rec */
267 mixer_toggle_rec();
268 ui_update();
269 break;
270 case 10:
271 scroll_text(3, 4, 57, true);
272 break;
273 default:
274 printf("unknown region pressed\n");
275 break;
279 static void button_release_event(XButtonEvent *event)
281 int x = event->x;
282 int y = event->y;
283 int region;
285 region = check_region(x, y);
287 if (region == 1)
288 set_cursor(HAND_CURSOR);
290 button_pressed = false;
291 slider_pressed = false;
294 static void motion_event(XMotionEvent *event)
296 int x = event->x;
297 int y = event->y;
298 int region;
300 if ((x == mouse_drag_home_x) && (y == mouse_drag_home_y))
301 return;
303 region = check_region(x, y);
305 if (button_pressed) {
306 if (y != mouse_drag_home_y) {
307 float delta;
309 set_cursor(NULL_CURSOR);
311 delta = (float)(mouse_drag_home_y - y) / display_height;
312 knob_turn(delta);
313 if (!osd_mapped())
314 map_osd();
315 if (osd_mapped())
316 update_osd(mixer_get_volume(), false);
318 XWarpPointer(display, None, event->window, x, y, 0, 0,
319 mouse_drag_home_x, mouse_drag_home_y);
320 return;
323 if (slider_pressed) {
324 if (x != mouse_drag_home_x) {
325 float delta;
327 set_cursor(NULL_CURSOR);
329 delta = (float)(x - mouse_drag_home_x) / display_width;
330 slider_move(delta);
332 XWarpPointer(display, None, event->window, x, y, 0, 0,
333 mouse_drag_home_x, mouse_drag_home_y);
334 return;
337 if (region == 1)
338 set_cursor(HAND_CURSOR);
339 else if (region == 2)
340 set_cursor(BAR_CURSOR);
341 else
342 set_cursor(NORMAL_CURSOR);