wmix: added error catch for XGrabKey on multimedia keys
[dockapps.git] / wmix / wmix.c
blobae8bce503ecaaa14e6b937b5bfef0bd6654312ba
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/mmkeys.h"
41 #include "include/config.h"
44 static Display *display;
45 static bool button_pressed = false;
46 static bool slider_pressed = false;
47 static double prev_button_press_time = 0.0;
49 static float display_height;
50 static float display_width;
51 static int mouse_drag_home_x;
52 static int mouse_drag_home_y;
53 static int idle_loop;
55 /* local stuff */
56 static void signal_catch(int sig);
57 static void button_press_event(XButtonEvent *event);
58 static void button_release_event(XButtonEvent *event);
59 static int key_press_event(XKeyEvent *event);
60 static void motion_event(XMotionEvent *event);
63 int main(int argc, char **argv)
65 XEvent event;
67 config_init();
68 parse_cli_options(argc, argv);
69 config_read();
71 mixer_init(config.mixer_device, config.verbose, (const char **)config.exclude_channel);
72 mixer_set_channel(0);
74 display = XOpenDisplay(config.display_name);
75 if (display == NULL) {
76 const char *name;
78 if (config.display_name) {
79 name = config.display_name;
80 } else {
81 name = getenv("DISPLAY");
82 if (name == NULL) {
83 fprintf(stderr, "wmix:error: Unable to open display, variable $DISPLAY not set\n");
84 return EXIT_FAILURE;
87 fprintf(stderr, "wmix:error: Unable to open display \"%s\"\n", name);
88 return EXIT_FAILURE;
90 display_width = (float)DisplayWidth(display, DefaultScreen(display)) / 4.0;
91 display_height = (float)DisplayHeight(display, DefaultScreen(display)) / 2.0;
93 dockapp_init(display);
94 new_window("wmix", 64, 64);
95 new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60);
96 mmkey_install(display);
98 config_release();
100 blit_string("wmix 3.0");
101 scroll_text(3, 4, 57, true);
102 ui_update();
104 /* add click regions */
105 add_region(1, 37, 36, 25, 25); /* knob */
106 add_region(2, 4, 42, 27, 15); /* balancer */
107 add_region(3, 2, 26, 7, 10); /* previous channel */
108 add_region(4, 10, 26, 7, 10); /* next channel */
109 add_region(5, 39, 14, 20, 7); /* mute toggle */
110 add_region(6, 4, 14, 13, 7); /* rec toggle */
111 add_region(10, 3, 4, 56, 7); /* re-scroll current channel name */
113 /* setup up/down signal handler */
114 create_pid_file();
115 signal(SIGUSR1, (void *) signal_catch);
116 signal(SIGUSR2, (void *) signal_catch);
118 while (true) {
119 if (button_pressed || slider_pressed || (XPending(display) > 0)) {
120 XNextEvent(display, &event);
121 switch (event.type) {
122 case KeyPress:
123 if (key_press_event(&event.xkey))
124 idle_loop = 0;
125 break;
126 case Expose:
127 redraw_window();
128 break;
129 case ButtonPress:
130 button_press_event(&event.xbutton);
131 idle_loop = 0;
132 break;
133 case ButtonRelease:
134 button_release_event(&event.xbutton);
135 idle_loop = 0;
136 break;
137 case MotionNotify:
138 /* process cursor change, or drag events */
139 motion_event(&event.xmotion);
140 idle_loop = 0;
141 break;
142 case LeaveNotify:
143 /* go back to standard cursor */
144 if ((!button_pressed) && (!slider_pressed))
145 set_cursor(NORMAL_CURSOR);
146 break;
147 case DestroyNotify:
148 XCloseDisplay(display);
149 return EXIT_SUCCESS;
150 default:
151 break;
153 } else {
154 usleep(100000);
155 scroll_text(3, 4, 57, false);
156 /* rescroll message after some delay */
157 if (idle_loop++ > 256) {
158 scroll_text(3, 4, 57, true);
159 idle_loop = 0;
161 /* get rid of OSD after a few seconds of idle */
162 if ((idle_loop > 15) && osd_mapped() && !button_pressed) {
163 unmap_osd();
164 idle_loop = 0;
166 if (mixer_is_changed())
167 ui_update();
170 return EXIT_SUCCESS;
173 static void signal_catch(int sig)
175 switch (sig) {
176 case SIGUSR1:
177 mixer_set_volume_rel(config.scrollstep);
178 if (!osd_mapped())
179 map_osd();
180 if (osd_mapped())
181 update_osd(mixer_get_volume(), false);
182 ui_update();
183 idle_loop = 0;
184 break;
185 case SIGUSR2:
186 mixer_set_volume_rel(-config.scrollstep);
187 if (!osd_mapped())
188 map_osd();
189 if (osd_mapped())
190 update_osd(mixer_get_volume(), false);
191 ui_update();
192 idle_loop = 0;
193 break;
197 static void button_press_event(XButtonEvent *event)
199 double button_press_time = get_current_time();
200 int x = event->x;
201 int y = event->y;
202 bool double_click = false;
204 /* handle wheel scrolling to adjust volume */
205 if (config.mousewheel) {
206 if (event->button == config.wheel_button_up) {
207 mixer_set_volume_rel(config.scrollstep);
208 if (!osd_mapped())
209 map_osd();
210 if (osd_mapped())
211 update_osd(mixer_get_volume(), false);
212 ui_update();
213 return;
215 if (event->button == config.wheel_button_down) {
216 mixer_set_volume_rel(-config.scrollstep);
217 if (!osd_mapped())
218 map_osd();
219 if (osd_mapped())
220 update_osd(mixer_get_volume(), false);
221 ui_update();
222 return;
226 if ((button_press_time - prev_button_press_time) <= 0.5) {
227 double_click = true;
228 prev_button_press_time = 0.0;
229 } else
230 prev_button_press_time = button_press_time;
232 switch (check_region(x, y)) {
233 case 1: /* on knob */
234 button_pressed = true;
235 slider_pressed = false;
236 mouse_drag_home_x = x;
237 mouse_drag_home_y = y;
238 if (double_click) {
239 mixer_toggle_mute();
240 ui_update();
242 break;
243 case 2: /* on slider */
244 button_pressed = false;
245 slider_pressed = true;
246 mouse_drag_home_x = x;
247 mouse_drag_home_y = y;
248 if (double_click) {
249 mixer_set_balance(0.0);
250 ui_update();
252 break;
253 case 3: /* previous channel */
254 mixer_set_channel_rel(-1);
255 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
256 scroll_text(3, 4, 57, true);
257 unmap_osd();
258 map_osd();
259 ui_update();
260 break;
261 case 4: /* next channel */
262 mixer_set_channel_rel(1);
263 blit_string(config.scrolltext ? mixer_get_channel_name() : mixer_get_short_name());
264 scroll_text(3, 4, 57, true);
265 unmap_osd();
266 map_osd();
267 ui_update();
268 break;
269 case 5: /* toggle mute */
270 mixer_toggle_mute();
271 ui_update();
272 break;
273 case 6: /* toggle rec */
274 mixer_toggle_rec();
275 ui_update();
276 break;
277 case 10:
278 scroll_text(3, 4, 57, true);
279 break;
280 default:
281 printf("unknown region pressed\n");
282 break;
286 static int key_press_event(XKeyEvent *event)
288 if (event->keycode == mmkeys.raise_volume) {
289 mixer_set_volume_rel(config.scrollstep);
290 if (!osd_mapped())
291 map_osd();
292 if (osd_mapped())
293 update_osd(mixer_get_volume(), false);
294 ui_update();
295 return 1;
297 if (event->keycode == mmkeys.lower_volume) {
298 mixer_set_volume_rel(-config.scrollstep);
299 if (!osd_mapped())
300 map_osd();
301 if (osd_mapped())
302 update_osd(mixer_get_volume(), false);
303 ui_update();
304 return 1;
306 if (event->keycode == mmkeys.mute) {
307 mixer_toggle_mute();
308 ui_update();
309 return 1;
312 /* Ignore other keys */
313 return 0;
316 static void button_release_event(XButtonEvent *event)
318 int x = event->x;
319 int y = event->y;
320 int region;
322 region = check_region(x, y);
324 if (region == 1)
325 set_cursor(HAND_CURSOR);
327 button_pressed = false;
328 slider_pressed = false;
331 static void motion_event(XMotionEvent *event)
333 int x = event->x;
334 int y = event->y;
335 int region;
337 if ((x == mouse_drag_home_x) && (y == mouse_drag_home_y))
338 return;
340 region = check_region(x, y);
342 if (button_pressed) {
343 if (y != mouse_drag_home_y) {
344 float delta;
346 set_cursor(NULL_CURSOR);
348 delta = (float)(mouse_drag_home_y - y) / display_height;
349 knob_turn(delta);
350 if (!osd_mapped())
351 map_osd();
352 if (osd_mapped())
353 update_osd(mixer_get_volume(), false);
355 XWarpPointer(display, None, event->window, x, y, 0, 0,
356 mouse_drag_home_x, mouse_drag_home_y);
357 return;
360 if (slider_pressed) {
361 if (x != mouse_drag_home_x) {
362 float delta;
364 set_cursor(NULL_CURSOR);
366 delta = (float)(x - mouse_drag_home_x) / display_width;
367 slider_move(delta);
369 XWarpPointer(display, None, event->window, x, y, 0, 0,
370 mouse_drag_home_x, mouse_drag_home_y);
371 return;
374 if (region == 1)
375 set_cursor(HAND_CURSOR);
376 else if (region == 2)
377 set_cursor(BAR_CURSOR);
378 else
379 set_cursor(NORMAL_CURSOR);