Make AddMouseRegion's index unsigned
[dockapps.git] / wmix / wmix.c
blobc42c978b6ed236a1a734d7639726836be3e89947
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"
44 void (*mixer_init)(const char *mixer_device,
45 bool verbose,
46 const char *exclude[]);
47 bool (*mixer_is_changed)(void);
48 int (*mixer_get_channel_count)(void);
49 int (*mixer_get_channel)(void);
50 const char *(*mixer_get_channel_name)(void);
51 const char *(*mixer_get_short_name)(void);
52 void (*mixer_set_channel)(int channel);
53 void (*mixer_set_channel_rel)(int delta_channel);
54 float (*mixer_get_volume)(void);
55 void (*mixer_set_volume)(float volume);
56 void (*mixer_set_volume_rel)(float delta_volume);
57 float (*mixer_get_balance)(void);
58 void (*mixer_set_balance)(float balance);
59 void (*mixer_set_balance_rel)(float delta_balance);
60 void (*mixer_toggle_mute)(void);
61 void (*mixer_toggle_rec)(void);
62 bool (*mixer_is_muted)(void);
63 bool (*mixer_is_stereo)(void);
64 bool (*mixer_is_rec)(void);
65 bool (*mixer_can_rec)(void);
66 bool (*is_exclude)(const char *short_name,
67 const char *exclude[]);
68 void (*mixer_tick)(void);
70 static Display *display;
71 static bool button_pressed = false;
72 static bool slider_pressed = false;
73 static double prev_button_press_time = 0.0;
75 static float display_height;
76 static float display_width;
77 static int mouse_drag_home_x;
78 static int mouse_drag_home_y;
79 static int idle_loop;
81 /* local stuff */
82 static void signal_catch(int sig);
83 static void button_press_event(XButtonEvent *event);
84 static void button_release_event(XButtonEvent *event);
85 static int key_press_event(XKeyEvent *event);
86 static void motion_event(XMotionEvent *event);
87 static void choose_api(int api);
90 int main(int argc, char **argv)
92 XEvent event;
93 int rr_event_base, rr_error_base;
94 Bool have_randr;
96 config_init();
97 parse_cli_options(argc, argv);
98 config_read();
99 config_set_defaults();
100 choose_api(config.api);
102 mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
103 mixer_set_channel(0);
105 display = XOpenDisplay(config.display_name);
106 if (display == NULL) {
107 const char *name;
109 if (config.display_name) {
110 name = config.display_name;
111 } else {
112 name = getenv("DISPLAY");
113 if (name == NULL) {
114 fprintf(stderr, "wmix:error: Unable to open display, variable $DISPLAY not set\n");
115 return EXIT_FAILURE;
118 fprintf(stderr, "wmix:error: Unable to open display \"%s\"\n", name);
119 return EXIT_FAILURE;
122 have_randr = XRRQueryExtension(display, &rr_event_base, &rr_error_base);
123 if (have_randr) {
124 int rr_mask = RRScreenChangeNotifyMask;
125 XRRSelectInput(display,
126 RootWindow(display, DefaultScreen(display)),
127 rr_mask);
130 display_width = (float)DisplayWidth(display, DefaultScreen(display)) / 4.0;
131 display_height = (float)DisplayHeight(display, DefaultScreen(display)) / 2.0;
133 dockapp_init(display, have_randr);
134 new_window("wmix", 64, 64, argc, argv);
135 new_osd(60);
137 if (config.mmkeys)
138 mmkey_install(display);
140 config_release();
142 blit_string("wmix " VERSION);
143 scroll_text(3, 4, 57, true);
144 ui_update();
146 /* add click regions */
147 add_region(1, 37, 36, 25, 25); /* knob */
148 add_region(2, 4, 42, 27, 15); /* balancer */
149 add_region(3, 2, 26, 7, 10); /* previous channel */
150 add_region(4, 10, 26, 7, 10); /* next channel */
151 add_region(5, 39, 14, 20, 7); /* mute toggle */
152 add_region(6, 4, 14, 13, 7); /* rec toggle */
153 add_region(10, 3, 4, 56, 7); /* re-scroll current channel name */
155 /* setup up/down signal handler */
156 create_pid_file();
157 signal(SIGUSR1, (void *) signal_catch);
158 signal(SIGUSR2, (void *) signal_catch);
160 while (true) {
161 if (button_pressed || slider_pressed || (XPending(display) > 0)) {
162 XNextEvent(display, &event);
163 switch (event.type) {
164 case KeyPress:
165 if (key_press_event(&event.xkey))
166 idle_loop = 0;
167 break;
168 case Expose:
169 redraw_window();
170 break;
171 case ButtonPress:
172 button_press_event(&event.xbutton);
173 idle_loop = 0;
174 break;
175 case ButtonRelease:
176 button_release_event(&event.xbutton);
177 idle_loop = 0;
178 break;
179 case MotionNotify:
180 /* process cursor change, or drag events */
181 motion_event(&event.xmotion);
182 idle_loop = 0;
183 break;
184 case LeaveNotify:
185 /* go back to standard cursor */
186 if ((!button_pressed) && (!slider_pressed))
187 set_cursor(NORMAL_CURSOR);
188 break;
189 case DestroyNotify:
190 XCloseDisplay(display);
191 return EXIT_SUCCESS;
192 default:
193 if (have_randr) {
194 if (event.type == rr_event_base + RRScreenChangeNotify) {
195 XRRUpdateConfiguration(&event);
196 ui_rrnotify();
199 break;
201 } else {
202 usleep(100000);
203 if (mixer_tick)
204 mixer_tick();
205 scroll_text(3, 4, 57, false);
206 /* rescroll message after some delay */
207 if (idle_loop++ > 256) {
208 scroll_text(3, 4, 57, true);
209 idle_loop = 0;
211 /* get rid of OSD after a few seconds of idle */
212 if ((idle_loop > 15) && osd_mapped() && !button_pressed) {
213 unmap_osd();
214 idle_loop = 0;
216 if (mixer_is_changed())
217 ui_update();
220 return EXIT_SUCCESS;
223 static void signal_catch(int sig)
225 switch (sig) {
226 case SIGUSR1:
227 mixer_set_volume_rel(config.scrollstep);
228 if (!osd_mapped())
229 map_osd();
230 if (osd_mapped())
231 update_osd(mixer_get_volume(), false);
232 ui_update();
233 idle_loop = 0;
234 break;
235 case SIGUSR2:
236 mixer_set_volume_rel(-config.scrollstep);
237 if (!osd_mapped())
238 map_osd();
239 if (osd_mapped())
240 update_osd(mixer_get_volume(), false);
241 ui_update();
242 idle_loop = 0;
243 break;
247 static void choose_api(int api)
249 if (api == 0) {
250 mixer_init = &mixer_alsa_init;
251 mixer_is_changed = &mixer_alsa_is_changed;
252 mixer_get_channel_count = mixer_alsa_get_channel_count;
253 mixer_get_channel = mixer_alsa_get_channel;
254 mixer_get_channel_name = mixer_alsa_get_channel_name;
255 mixer_get_short_name = mixer_alsa_get_short_name;
256 mixer_set_channel = mixer_alsa_set_channel;
257 mixer_set_channel_rel = mixer_alsa_set_channel_rel;
258 mixer_get_volume = mixer_alsa_get_volume;
259 mixer_set_volume = mixer_alsa_set_volume;
260 mixer_set_volume_rel = mixer_alsa_set_volume_rel;
261 mixer_get_balance = mixer_alsa_get_balance;
262 mixer_set_balance = mixer_alsa_set_balance;
263 mixer_set_balance_rel = mixer_alsa_set_balance_rel;
264 mixer_toggle_mute = mixer_alsa_toggle_mute;
265 mixer_toggle_rec = mixer_alsa_toggle_rec;
266 mixer_is_muted = mixer_alsa_is_muted;
267 mixer_is_stereo = mixer_alsa_is_stereo;
268 mixer_is_rec = mixer_alsa_is_rec;
269 mixer_can_rec = mixer_alsa_can_rec;
270 mixer_tick = mixer_alsa_tick;
271 } else if (api == 1) {
272 mixer_init = &mixer_oss_init;
273 mixer_is_changed = &mixer_oss_is_changed;
274 mixer_get_channel_count = mixer_oss_get_channel_count;
275 mixer_get_channel = mixer_oss_get_channel;
276 mixer_get_channel_name = mixer_oss_get_channel_name;
277 mixer_get_short_name = mixer_oss_get_short_name;
278 mixer_set_channel = mixer_oss_set_channel;
279 mixer_set_channel_rel = mixer_oss_set_channel_rel;
280 mixer_get_volume = mixer_oss_get_volume;
281 mixer_set_volume = mixer_oss_set_volume;
282 mixer_set_volume_rel = mixer_oss_set_volume_rel;
283 mixer_get_balance = mixer_oss_get_balance;
284 mixer_set_balance = mixer_oss_set_balance;
285 mixer_set_balance_rel = mixer_oss_set_balance_rel;
286 mixer_toggle_mute = mixer_oss_toggle_mute;
287 mixer_toggle_rec = mixer_oss_toggle_rec;
288 mixer_is_muted = mixer_oss_is_muted;
289 mixer_is_stereo = mixer_oss_is_stereo;
290 mixer_is_rec = mixer_oss_is_rec;
291 mixer_can_rec = mixer_oss_can_rec;
292 mixer_tick = NULL;
296 static void button_press_event(XButtonEvent *event)
298 double button_press_time = get_current_time();
299 int x = event->x;
300 int y = event->y;
301 bool double_click = false;
303 /* handle wheel scrolling to adjust volume */
304 if (config.mousewheel) {
305 if (event->button == config.wheel_button_up) {
306 mixer_set_volume_rel(config.scrollstep);
307 if (!osd_mapped())
308 map_osd();
309 if (osd_mapped())
310 update_osd(mixer_get_volume(), false);
311 ui_update();
312 return;
314 if (event->button == config.wheel_button_down) {
315 mixer_set_volume_rel(-config.scrollstep);
316 if (!osd_mapped())
317 map_osd();
318 if (osd_mapped())
319 update_osd(mixer_get_volume(), false);
320 ui_update();
321 return;
325 if ((button_press_time - prev_button_press_time) <= 0.5) {
326 double_click = true;
327 prev_button_press_time = 0.0;
328 } else
329 prev_button_press_time = button_press_time;
331 switch (check_region(x, y)) {
332 case 1: /* on knob */
333 button_pressed = true;
334 slider_pressed = false;
335 mouse_drag_home_x = x;
336 mouse_drag_home_y = y;
337 if (double_click) {
338 mixer_toggle_mute();
339 ui_update();
341 break;
342 case 2: /* on slider */
343 button_pressed = false;
344 slider_pressed = true;
345 mouse_drag_home_x = x;
346 mouse_drag_home_y = y;
347 if (double_click) {
348 mixer_set_balance(0.0);
349 ui_update();
351 break;
352 case 3: /* previous channel */
353 mixer_set_channel_rel(-1);
354 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
355 scroll_text(3, 4, 57, true);
356 unmap_osd();
357 map_osd();
358 ui_update();
359 break;
360 case 4: /* next channel */
361 mixer_set_channel_rel(1);
362 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
363 scroll_text(3, 4, 57, true);
364 unmap_osd();
365 map_osd();
366 ui_update();
367 break;
368 case 5: /* toggle mute */
369 mixer_toggle_mute();
370 ui_update();
371 break;
372 case 6: /* toggle rec */
373 mixer_toggle_rec();
374 ui_update();
375 break;
376 case 10:
377 scroll_text(3, 4, 57, true);
378 break;
379 default:
380 printf("unknown region pressed\n");
381 break;
385 static int key_press_event(XKeyEvent *event)
387 if (event->keycode == mmkeys.raise_volume) {
388 mixer_set_volume_rel(config.scrollstep);
389 if (!osd_mapped())
390 map_osd();
391 if (osd_mapped())
392 update_osd(mixer_get_volume(), false);
393 ui_update();
394 return 1;
396 if (event->keycode == mmkeys.lower_volume) {
397 mixer_set_volume_rel(-config.scrollstep);
398 if (!osd_mapped())
399 map_osd();
400 if (osd_mapped())
401 update_osd(mixer_get_volume(), false);
402 ui_update();
403 return 1;
405 if (event->keycode == mmkeys.mute) {
406 mixer_toggle_mute();
407 ui_update();
408 return 1;
411 /* Ignore other keys */
412 return 0;
415 static void button_release_event(XButtonEvent *event)
417 int x = event->x;
418 int y = event->y;
419 int region;
421 region = check_region(x, y);
423 if (region == 1)
424 set_cursor(HAND_CURSOR);
426 button_pressed = false;
427 slider_pressed = false;
430 static void motion_event(XMotionEvent *event)
432 int x = event->x;
433 int y = event->y;
434 int region;
436 if ((x == mouse_drag_home_x) && (y == mouse_drag_home_y))
437 return;
439 region = check_region(x, y);
441 if (button_pressed) {
442 if (y != mouse_drag_home_y) {
443 float delta;
445 set_cursor(NULL_CURSOR);
447 delta = (float)(mouse_drag_home_y - y) / display_height;
448 knob_turn(delta);
449 if (!osd_mapped())
450 map_osd();
451 if (osd_mapped())
452 update_osd(mixer_get_volume(), false);
454 XWarpPointer(display, None, event->window, x, y, 0, 0,
455 mouse_drag_home_x, mouse_drag_home_y);
456 return;
459 if (slider_pressed) {
460 if (x != mouse_drag_home_x) {
461 float delta;
463 set_cursor(NULL_CURSOR);
465 delta = (float)(x - mouse_drag_home_x) / display_width;
466 slider_move(delta);
468 XWarpPointer(display, None, event->window, x, y, 0, 0,
469 mouse_drag_home_x, mouse_drag_home_y);
470 return;
473 if (region == 1)
474 set_cursor(HAND_CURSOR);
475 else if (region == 2)
476 set_cursor(BAR_CURSOR);
477 else
478 set_cursor(NORMAL_CURSOR);