wmcalc: Add freedesktop.org desktop entry file.
[dockapps.git] / wmix / wmix.c
bloba2020c33f57affa2773ca175da5d78d3888dcd17
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>
33 #include <X11/extensions/Xrandr.h>
35 #include "include/common.h"
36 #include "include/mixer.h"
37 #include "include/misc.h"
38 #include "include/ui_x.h"
39 #include "include/mmkeys.h"
40 #include "include/config.h"
41 #include "include/mixer-oss.h"
42 #include "include/mixer-alsa.h"
45 static Display *display;
46 static bool button_pressed = false;
47 static bool slider_pressed = false;
48 static double prev_button_press_time = 0.0;
50 static float display_height;
51 static float display_width;
52 static int mouse_drag_home_x;
53 static int mouse_drag_home_y;
54 static int idle_loop;
56 /* local stuff */
57 static void signal_catch(int sig);
58 static void button_press_event(XButtonEvent *event);
59 static void button_release_event(XButtonEvent *event);
60 static int key_press_event(XKeyEvent *event);
61 static void motion_event(XMotionEvent *event);
62 static void choose_api(int api);
65 int main(int argc, char **argv)
67 XEvent event;
68 int rr_event_base, rr_error_base;
69 Bool have_randr;
71 config_init();
72 parse_cli_options(argc, argv);
73 config_read();
74 config_set_defaults();
75 choose_api(config.api);
77 mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
78 mixer_set_channel(0);
80 display = XOpenDisplay(config.display_name);
81 if (display == NULL) {
82 const char *name;
84 if (config.display_name) {
85 name = config.display_name;
86 } else {
87 name = getenv("DISPLAY");
88 if (name == NULL) {
89 fprintf(stderr, "wmix:error: Unable to open display, variable $DISPLAY not set\n");
90 return EXIT_FAILURE;
93 fprintf(stderr, "wmix:error: Unable to open display \"%s\"\n", name);
94 return EXIT_FAILURE;
97 have_randr = XRRQueryExtension(display, &rr_event_base, &rr_error_base);
98 if (have_randr) {
99 int rr_mask = RRScreenChangeNotifyMask;
100 XRRSelectInput(display,
101 RootWindow(display, DefaultScreen(display)),
102 rr_mask);
105 display_width = (float)DisplayWidth(display, DefaultScreen(display)) / 4.0;
106 display_height = (float)DisplayHeight(display, DefaultScreen(display)) / 2.0;
108 dockapp_init(display, have_randr);
109 new_window("wmix", 64, 64);
110 new_osd(60);
112 if (config.mmkeys)
113 mmkey_install(display);
115 config_release();
117 blit_string("wmix " VERSION);
118 scroll_text(3, 4, 57, true);
119 ui_update();
121 /* add click regions */
122 add_region(1, 37, 36, 25, 25); /* knob */
123 add_region(2, 4, 42, 27, 15); /* balancer */
124 add_region(3, 2, 26, 7, 10); /* previous channel */
125 add_region(4, 10, 26, 7, 10); /* next channel */
126 add_region(5, 39, 14, 20, 7); /* mute toggle */
127 add_region(6, 4, 14, 13, 7); /* rec toggle */
128 add_region(10, 3, 4, 56, 7); /* re-scroll current channel name */
130 /* setup up/down signal handler */
131 create_pid_file();
132 signal(SIGUSR1, (void *) signal_catch);
133 signal(SIGUSR2, (void *) signal_catch);
135 while (true) {
136 if (button_pressed || slider_pressed || (XPending(display) > 0)) {
137 XNextEvent(display, &event);
138 switch (event.type) {
139 case KeyPress:
140 if (key_press_event(&event.xkey))
141 idle_loop = 0;
142 break;
143 case Expose:
144 redraw_window();
145 break;
146 case ButtonPress:
147 button_press_event(&event.xbutton);
148 idle_loop = 0;
149 break;
150 case ButtonRelease:
151 button_release_event(&event.xbutton);
152 idle_loop = 0;
153 break;
154 case MotionNotify:
155 /* process cursor change, or drag events */
156 motion_event(&event.xmotion);
157 idle_loop = 0;
158 break;
159 case LeaveNotify:
160 /* go back to standard cursor */
161 if ((!button_pressed) && (!slider_pressed))
162 set_cursor(NORMAL_CURSOR);
163 break;
164 case DestroyNotify:
165 XCloseDisplay(display);
166 return EXIT_SUCCESS;
167 default:
168 if (have_randr) {
169 if (event.type == rr_event_base + RRScreenChangeNotify) {
170 XRRUpdateConfiguration(&event);
171 ui_rrnotify();
174 break;
176 } else {
177 usleep(100000);
178 if (mixer_tick)
179 mixer_tick();
180 scroll_text(3, 4, 57, false);
181 /* rescroll message after some delay */
182 if (idle_loop++ > 256) {
183 scroll_text(3, 4, 57, true);
184 idle_loop = 0;
186 /* get rid of OSD after a few seconds of idle */
187 if ((idle_loop > 15) && osd_mapped() && !button_pressed) {
188 unmap_osd();
189 idle_loop = 0;
191 if (mixer_is_changed())
192 ui_update();
195 return EXIT_SUCCESS;
198 static void signal_catch(int sig)
200 switch (sig) {
201 case SIGUSR1:
202 mixer_set_volume_rel(config.scrollstep);
203 if (!osd_mapped())
204 map_osd();
205 if (osd_mapped())
206 update_osd(mixer_get_volume(), false);
207 ui_update();
208 idle_loop = 0;
209 break;
210 case SIGUSR2:
211 mixer_set_volume_rel(-config.scrollstep);
212 if (!osd_mapped())
213 map_osd();
214 if (osd_mapped())
215 update_osd(mixer_get_volume(), false);
216 ui_update();
217 idle_loop = 0;
218 break;
222 static void choose_api(int api)
224 if (api == 0) {
225 mixer_init = &mixer_alsa_init;
226 mixer_is_changed = &mixer_alsa_is_changed;
227 mixer_get_channel_count = mixer_alsa_get_channel_count;
228 mixer_get_channel = mixer_alsa_get_channel;
229 mixer_get_channel_name = mixer_alsa_get_channel_name;
230 mixer_get_short_name = mixer_alsa_get_short_name;
231 mixer_set_channel = mixer_alsa_set_channel;
232 mixer_set_channel_rel = mixer_alsa_set_channel_rel;
233 mixer_get_volume = mixer_alsa_get_volume;
234 mixer_set_volume = mixer_alsa_set_volume;
235 mixer_set_volume_rel = mixer_alsa_set_volume_rel;
236 mixer_get_balance = mixer_alsa_get_balance;
237 mixer_set_balance = mixer_alsa_set_balance;
238 mixer_set_balance_rel = mixer_alsa_set_balance_rel;
239 mixer_toggle_mute = mixer_alsa_toggle_mute;
240 mixer_toggle_rec = mixer_alsa_toggle_rec;
241 mixer_is_muted = mixer_alsa_is_muted;
242 mixer_is_stereo = mixer_alsa_is_stereo;
243 mixer_is_rec = mixer_alsa_is_rec;
244 mixer_can_rec = mixer_alsa_can_rec;
245 mixer_tick = mixer_alsa_tick;
246 } else if (api == 1) {
247 mixer_init = &mixer_oss_init;
248 mixer_is_changed = &mixer_oss_is_changed;
249 mixer_get_channel_count = mixer_oss_get_channel_count;
250 mixer_get_channel = mixer_oss_get_channel;
251 mixer_get_channel_name = mixer_oss_get_channel_name;
252 mixer_get_short_name = mixer_oss_get_short_name;
253 mixer_set_channel = mixer_oss_set_channel;
254 mixer_set_channel_rel = mixer_oss_set_channel_rel;
255 mixer_get_volume = mixer_oss_get_volume;
256 mixer_set_volume = mixer_oss_set_volume;
257 mixer_set_volume_rel = mixer_oss_set_volume_rel;
258 mixer_get_balance = mixer_oss_get_balance;
259 mixer_set_balance = mixer_oss_set_balance;
260 mixer_set_balance_rel = mixer_oss_set_balance_rel;
261 mixer_toggle_mute = mixer_oss_toggle_mute;
262 mixer_toggle_rec = mixer_oss_toggle_rec;
263 mixer_is_muted = mixer_oss_is_muted;
264 mixer_is_stereo = mixer_oss_is_stereo;
265 mixer_is_rec = mixer_oss_is_rec;
266 mixer_can_rec = mixer_oss_can_rec;
267 mixer_tick = NULL;
271 static void button_press_event(XButtonEvent *event)
273 double button_press_time = get_current_time();
274 int x = event->x;
275 int y = event->y;
276 bool double_click = false;
278 /* handle wheel scrolling to adjust volume */
279 if (config.mousewheel) {
280 if (event->button == config.wheel_button_up) {
281 mixer_set_volume_rel(config.scrollstep);
282 if (!osd_mapped())
283 map_osd();
284 if (osd_mapped())
285 update_osd(mixer_get_volume(), false);
286 ui_update();
287 return;
289 if (event->button == config.wheel_button_down) {
290 mixer_set_volume_rel(-config.scrollstep);
291 if (!osd_mapped())
292 map_osd();
293 if (osd_mapped())
294 update_osd(mixer_get_volume(), false);
295 ui_update();
296 return;
300 if ((button_press_time - prev_button_press_time) <= 0.5) {
301 double_click = true;
302 prev_button_press_time = 0.0;
303 } else
304 prev_button_press_time = button_press_time;
306 switch (check_region(x, y)) {
307 case 1: /* on knob */
308 button_pressed = true;
309 slider_pressed = false;
310 mouse_drag_home_x = x;
311 mouse_drag_home_y = y;
312 if (double_click) {
313 mixer_toggle_mute();
314 ui_update();
316 break;
317 case 2: /* on slider */
318 button_pressed = false;
319 slider_pressed = true;
320 mouse_drag_home_x = x;
321 mouse_drag_home_y = y;
322 if (double_click) {
323 mixer_set_balance(0.0);
324 ui_update();
326 break;
327 case 3: /* previous channel */
328 mixer_set_channel_rel(-1);
329 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
330 scroll_text(3, 4, 57, true);
331 unmap_osd();
332 map_osd();
333 ui_update();
334 break;
335 case 4: /* next channel */
336 mixer_set_channel_rel(1);
337 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
338 scroll_text(3, 4, 57, true);
339 unmap_osd();
340 map_osd();
341 ui_update();
342 break;
343 case 5: /* toggle mute */
344 mixer_toggle_mute();
345 ui_update();
346 break;
347 case 6: /* toggle rec */
348 mixer_toggle_rec();
349 ui_update();
350 break;
351 case 10:
352 scroll_text(3, 4, 57, true);
353 break;
354 default:
355 printf("unknown region pressed\n");
356 break;
360 static int key_press_event(XKeyEvent *event)
362 if (event->keycode == mmkeys.raise_volume) {
363 mixer_set_volume_rel(config.scrollstep);
364 if (!osd_mapped())
365 map_osd();
366 if (osd_mapped())
367 update_osd(mixer_get_volume(), false);
368 ui_update();
369 return 1;
371 if (event->keycode == mmkeys.lower_volume) {
372 mixer_set_volume_rel(-config.scrollstep);
373 if (!osd_mapped())
374 map_osd();
375 if (osd_mapped())
376 update_osd(mixer_get_volume(), false);
377 ui_update();
378 return 1;
380 if (event->keycode == mmkeys.mute) {
381 mixer_toggle_mute();
382 ui_update();
383 return 1;
386 /* Ignore other keys */
387 return 0;
390 static void button_release_event(XButtonEvent *event)
392 int x = event->x;
393 int y = event->y;
394 int region;
396 region = check_region(x, y);
398 if (region == 1)
399 set_cursor(HAND_CURSOR);
401 button_pressed = false;
402 slider_pressed = false;
405 static void motion_event(XMotionEvent *event)
407 int x = event->x;
408 int y = event->y;
409 int region;
411 if ((x == mouse_drag_home_x) && (y == mouse_drag_home_y))
412 return;
414 region = check_region(x, y);
416 if (button_pressed) {
417 if (y != mouse_drag_home_y) {
418 float delta;
420 set_cursor(NULL_CURSOR);
422 delta = (float)(mouse_drag_home_y - y) / display_height;
423 knob_turn(delta);
424 if (!osd_mapped())
425 map_osd();
426 if (osd_mapped())
427 update_osd(mixer_get_volume(), false);
429 XWarpPointer(display, None, event->window, x, y, 0, 0,
430 mouse_drag_home_x, mouse_drag_home_y);
431 return;
434 if (slider_pressed) {
435 if (x != mouse_drag_home_x) {
436 float delta;
438 set_cursor(NULL_CURSOR);
440 delta = (float)(x - mouse_drag_home_x) / display_width;
441 slider_move(delta);
443 XWarpPointer(display, None, event->window, x, y, 0, 0,
444 mouse_drag_home_x, mouse_drag_home_y);
445 return;
448 if (region == 1)
449 set_cursor(HAND_CURSOR);
450 else if (region == 2)
451 set_cursor(BAR_CURSOR);
452 else
453 set_cursor(NORMAL_CURSOR);