comparison src/hotkey/plugin.c @ 1543:ec4e8ec829b1

Include global hotkey plugin in build system (src/hotkey)
author Sascha Hlusiak <contact@saschahlusiak.de>
date Fri, 31 Aug 2007 22:48:53 +0200
parents
children a9af4f1aede9
comparison
equal deleted inserted replaced
1542:3e8d69cd4bf1 1543:ec4e8ec829b1
1 /* -*- Mode: C; indent-tabs: t; c-basic-offset: 9; tab-width: 9 -*- */
2 /*
3 * This file is part of audacious-hotkey plugin for audacious
4 *
5 * Copyright (c) 2007 Sascha Hlusiak <contact@saschahlusiak.de>
6 * Name: plugin.c
7 * Description: plugin.c
8 *
9 * Part of this code is from itouch-ctrl plugin.
10 * Authors of itouch-ctrl are listed below:
11 *
12 * Copyright (c) 2006 - 2007 Vladimir Paskov <vlado.paskov@gmail.com>
13 *
14 * Part of this code are from xmms-itouch plugin.
15 * Authors of xmms-itouch are listed below:
16 *
17 * Copyright (C) 2000-2002 Ville Syrjälä <syrjala@sci.fi>
18 * Bryn Davies <curious@ihug.com.au>
19 * Jonathan A. Davis <davis@jdhouse.org>
20 * Jeremy Tan <nsx@nsx.homeip.net>
21 *
22 * audacious-hotkey is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * audacious-hotkey is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with audacious-hotkey; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 */
36
37 #include <config.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <X11/Xlib.h>
42 #include <X11/keysym.h>
43 #include <X11/XF86keysym.h>
44
45 #include <gtk/gtk.h>
46 #include <gdk/gdkx.h>
47 #include <gdk/gdkkeysyms.h>
48 #include <audacious/plugin.h>
49 #include <audacious/auddrct.h>
50 #include <audacious/configdb.h>
51
52 #ifdef ENABLE_NLS
53 #ifdef HAVE_AUDACIOUS_I18N_H
54 #include <audacious/i18n.h>
55 #else
56 #ifdef HAVE_DCGETTEXT
57 #include <libintl.h>
58 #define _(String) dgettext(PACKAGE, String)
59 #else
60 #define _(String) (String)
61 #endif
62 #endif
63 #else
64 #define _(String) (String)
65 #endif
66
67 /* for xmms_show_message () */
68 #include <audacious/util.h>
69
70
71 /* func defs */
72 void x_display_init (void);
73 static void get_offending_modifiers (Display * dpy);
74 static void init (void);
75 static void grab_keys ();
76 static void ungrab_keys ();
77 static gboolean handle_keyevent(int keycode, int state);
78 static gboolean setup_filter();
79 static void release_filter();
80
81 static void load_config (void);
82 static void save_config (void);
83 static void configure (void);
84 static void clear_keyboard (GtkWidget *widget, gpointer data);
85
86 void cancel_callback (GtkWidget *widget, gpointer data);
87 void ok_callback (GtkWidget *widget, gpointer data);
88 static void about (void);
89 static void cleanup (void);
90
91 typedef struct {
92 gint vol_increment;
93 gint vol_decrement;
94
95 /* keyboard */
96 gint mute, mute_mask;
97 gint vol_down, vol_down_mask;
98 gint vol_up, vol_up_mask;
99 gint play, play_mask;
100 gint stop, stop_mask;
101 gint pause, pause_mask;
102 gint prev_track, prev_track_mask;
103 gint next_track, next_track_mask;
104 gint jump_to_file, jump_to_file_mask;
105 gint toggle_win, toggle_win_mask;
106
107 gint forward, forward_mask;
108 gint backward, backward_mask;
109 } PluginConfig;
110
111 PluginConfig plugin_cfg;
112
113 static Display *xdisplay = NULL;
114 static Window x_root_window = 0;
115 static gint grabbed = 0;
116 static gboolean loaded = FALSE;
117 static unsigned int numlock_mask = 0;
118 static unsigned int scrolllock_mask = 0;
119 static unsigned int capslock_mask = 0;
120
121
122
123 typedef struct {
124 GtkWidget *keytext;
125 gint key, mask;
126 } KeyControls;
127
128 typedef struct {
129 KeyControls play;
130 KeyControls stop;
131 KeyControls pause;
132 KeyControls prev;
133 KeyControls next;
134 KeyControls up;
135 KeyControls down;
136 KeyControls mute;
137 KeyControls jump_to_file;
138 KeyControls forward;
139 KeyControls backward;
140 KeyControls toggle_win;
141 } ConfigurationControls;
142
143 static GeneralPlugin audacioushotkey =
144 {
145 NULL,
146 NULL,
147 "Global Hotkey",
148 init,
149 about,
150 configure,
151 cleanup
152 };
153
154 GeneralPlugin *hotkey_gplist[] = { &audacioushotkey, NULL };
155 DECLARE_PLUGIN(hotkey, NULL, NULL, NULL, NULL, NULL, hotkey_gplist, NULL, NULL);
156
157
158
159 /*
160 * plugin activated
161 */
162 static void init (void)
163 {
164 x_display_init ( );
165 setup_filter();
166 load_config ( );
167 grab_keys ();
168
169 loaded = TRUE;
170 }
171
172 /* check X display */
173 void x_display_init (void)
174 {
175 if (xdisplay != NULL) return;
176 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
177 x_root_window = GDK_WINDOW_XID(gdk_get_default_root_window());
178 get_offending_modifiers(xdisplay);
179 }
180
181 /* Taken from xbindkeys */
182 static void get_offending_modifiers (Display * dpy)
183 {
184 int i;
185 XModifierKeymap *modmap;
186 KeyCode nlock, slock;
187 static int mask_table[8] = {
188 ShiftMask, LockMask, ControlMask, Mod1Mask,
189 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
190 };
191
192 nlock = XKeysymToKeycode (dpy, XK_Num_Lock);
193 slock = XKeysymToKeycode (dpy, XK_Scroll_Lock);
194
195 /*
196 * Find out the masks for the NumLock and ScrollLock modifiers,
197 * so that we can bind the grabs for when they are enabled too.
198 */
199 modmap = XGetModifierMapping (dpy);
200
201 if (modmap != NULL && modmap->max_keypermod > 0)
202 {
203 for (i = 0; i < 8 * modmap->max_keypermod; i++)
204 {
205 if (modmap->modifiermap[i] == nlock && nlock != 0)
206 numlock_mask = mask_table[i / modmap->max_keypermod];
207 else if (modmap->modifiermap[i] == slock && slock != 0)
208 scrolllock_mask = mask_table[i / modmap->max_keypermod];
209 }
210 }
211
212 capslock_mask = LockMask;
213
214 if (modmap)
215 XFreeModifiermap (modmap);
216 }
217
218 /* handle keys */
219 static gboolean handle_keyevent (int keycode, int state)
220 {
221 gint current_volume, old_volume;
222 static gint volume_static = 0;
223 gboolean play, mute;
224
225 /* playing or not */
226 play = audacious_drct_is_playing ();
227
228 /* get current volume */
229 audacious_drct_get_volume_main (&current_volume);
230 old_volume = current_volume;
231 if (current_volume)
232 {
233 /* volume is not mute */
234 mute = FALSE;
235 } else {
236 /* volume is mute */
237 mute = TRUE;
238 }
239
240 state &= ~(scrolllock_mask | numlock_mask | capslock_mask);
241
242 /* mute the playback */
243 if ((keycode == plugin_cfg.mute) && (state == plugin_cfg.mute_mask))
244 {
245 if (!mute)
246 {
247 volume_static = current_volume;
248 audacious_drct_set_main_volume (0);
249 mute = TRUE;
250 } else {
251 audacious_drct_set_main_volume (volume_static);
252 mute = FALSE;
253 }
254 return TRUE;
255 }
256
257 /* decreace volume */
258 if ((keycode == plugin_cfg.vol_down) && (state == plugin_cfg.vol_down_mask))
259 {
260 if (mute)
261 {
262 current_volume = old_volume;
263 old_volume = 0;
264 mute = FALSE;
265 }
266
267 if ((current_volume -= plugin_cfg.vol_decrement) < 0)
268 {
269 current_volume = 0;
270 }
271
272 if (current_volume != old_volume)
273 {
274 audacious_drct_set_main_volume (current_volume);
275 }
276
277 old_volume = current_volume;
278 return TRUE;
279 }
280
281 /* increase volume */
282 if ((keycode == plugin_cfg.vol_up) && (state == plugin_cfg.vol_up_mask))
283 {
284 if (mute)
285 {
286 current_volume = old_volume;
287 old_volume = 0;
288 mute = FALSE;
289 }
290
291 if ((current_volume += plugin_cfg.vol_increment) > 100)
292 {
293 current_volume = 100;
294 }
295
296 if (current_volume != old_volume)
297 {
298 audacious_drct_set_main_volume (current_volume);
299 }
300
301 old_volume = current_volume;
302 return TRUE;
303 }
304
305 /* play */
306 if ((keycode == plugin_cfg.play) && (state == plugin_cfg.play_mask))
307 {
308 if (!play)
309 {
310 audacious_drct_play ();
311 } else {
312 audacious_drct_pause ();
313 }
314 return TRUE;
315 }
316
317 /* pause */
318 if ((keycode == plugin_cfg.pause) && (state == plugin_cfg.pause_mask))
319 {
320 if (!play) audacious_drct_play ();
321 else audacious_drct_pause ();
322
323 return TRUE;
324 }
325
326 /* stop */
327 if ((keycode == plugin_cfg.stop) && (state == plugin_cfg.stop_mask))
328 {
329 audacious_drct_stop ();
330 return TRUE;
331 }
332
333 /* prev track */
334 if ((keycode == plugin_cfg.prev_track) && (state == plugin_cfg.prev_track_mask))
335 {
336 audacious_drct_playlist_prev ();
337 return TRUE;
338 }
339
340 /* next track */
341 if ((keycode == plugin_cfg.next_track) && (state == plugin_cfg.next_track_mask))
342 {
343 audacious_drct_playlist_next ();
344 return TRUE;
345 }
346
347 /* forward */
348 if ((keycode == plugin_cfg.forward) && (state == plugin_cfg.forward_mask))
349 {
350 gint time = audacious_drct_get_output_time();
351 time += 5000; /* Jump 5s into future */
352 audacious_drct_jump_to_time(time);
353 return TRUE;
354 }
355
356 /* backward */
357 if ((keycode == plugin_cfg.backward) && (state == plugin_cfg.backward_mask))
358 {
359 gint time = audacious_drct_get_output_time();
360 if (time > 5000) time -= 5000; /* Jump 5s back */
361 else time = 0;
362 audacious_drct_jump_to_time(time);
363 return TRUE;
364 }
365
366 /* Open Jump-To-File dialog */
367 if ((keycode == plugin_cfg.jump_to_file) && (state == plugin_cfg.jump_to_file_mask))
368 {
369 audacious_drct_show_jtf_box();
370 return TRUE;
371 }
372
373 /* Toggle Windows */
374 if ((keycode == plugin_cfg.toggle_win) && (state == plugin_cfg.toggle_win_mask))
375 {
376 static gboolean is_main, is_eq, is_pl;
377 is_main = audacious_drct_main_win_is_visible();
378 if (is_main) {
379 is_pl = audacious_drct_pl_win_is_visible();
380 is_eq = audacious_drct_eq_win_is_visible();
381 audacious_drct_main_win_toggle(FALSE);
382 audacious_drct_pl_win_toggle(FALSE);
383 audacious_drct_eq_win_toggle(FALSE);
384 } else {
385 audacious_drct_main_win_toggle(TRUE);
386 audacious_drct_pl_win_toggle(is_pl);
387 audacious_drct_eq_win_toggle(is_eq);
388 }
389 return TRUE;
390 }
391
392 return FALSE;
393 }
394
395 static GdkFilterReturn
396 gdk_filter(GdkXEvent *xevent,
397 GdkEvent *event,
398 gpointer data)
399 {
400 XKeyEvent *keyevent = (XKeyEvent*)xevent;
401
402 if (((XEvent*)keyevent)->type != KeyPress)
403 return -1;
404
405 if (handle_keyevent(keyevent->keycode, keyevent->state))
406 return GDK_FILTER_REMOVE;
407
408 return GDK_FILTER_CONTINUE;
409 }
410
411 static gboolean
412 setup_filter()
413 {
414 gdk_window_add_filter(gdk_get_default_root_window(),
415 gdk_filter,
416 NULL);
417
418 return TRUE;
419 }
420
421 static void release_filter()
422 {
423 gdk_window_remove_filter(gdk_get_default_root_window(),
424 gdk_filter,
425 NULL);
426 }
427
428 /* load plugin configuration */
429 static void load_config (void)
430 {
431 ConfigDb *cfdb;
432
433 if (xdisplay == NULL) x_display_init();
434
435 /* default volume level */
436 plugin_cfg.vol_increment = 4;
437 plugin_cfg.vol_decrement = 4;
438
439 plugin_cfg.mute = XKeysymToKeycode(xdisplay, XF86XK_AudioMute);
440 plugin_cfg.mute_mask = 0;
441 plugin_cfg.vol_down = XKeysymToKeycode(xdisplay, XF86XK_AudioLowerVolume);
442 plugin_cfg.vol_down_mask = 0;
443 plugin_cfg.vol_up = XKeysymToKeycode(xdisplay, XF86XK_AudioRaiseVolume);
444 plugin_cfg.vol_up_mask = 0;
445 plugin_cfg.play = XKeysymToKeycode(xdisplay, XF86XK_AudioPlay);
446 plugin_cfg.play_mask = 0;
447 plugin_cfg.pause = XKeysymToKeycode(xdisplay, XF86XK_AudioPause);
448 plugin_cfg.pause_mask = 0;
449 plugin_cfg.stop = XKeysymToKeycode(xdisplay, XF86XK_AudioStop);
450 plugin_cfg.stop_mask = 0;
451 plugin_cfg.prev_track = XKeysymToKeycode(xdisplay, XF86XK_AudioPrev);
452 plugin_cfg.prev_track_mask = 0;
453 plugin_cfg.next_track = XKeysymToKeycode(xdisplay, XF86XK_AudioNext);
454 plugin_cfg.next_track_mask = 0;
455 plugin_cfg.jump_to_file = XKeysymToKeycode(xdisplay, XF86XK_AudioMedia);
456 plugin_cfg.jump_to_file_mask = 0;
457 plugin_cfg.forward = 0;
458 plugin_cfg.forward_mask = 0;
459 plugin_cfg.backward = XKeysymToKeycode(xdisplay, XF86XK_AudioRewind);
460 plugin_cfg.backward_mask = 0;
461 plugin_cfg.toggle_win = 0;
462 plugin_cfg.toggle_win_mask = 0;
463
464 /* open configuration database */
465 cfdb = bmp_cfg_db_open ( );
466
467 bmp_cfg_db_get_int (cfdb, "globalHotkey", "mute", &plugin_cfg.mute);
468 bmp_cfg_db_get_int (cfdb, "globalHotkey", "mute_mask", &plugin_cfg.mute_mask);
469 bmp_cfg_db_get_int (cfdb, "globalHotkey", "vol_down", &plugin_cfg.vol_down);
470 bmp_cfg_db_get_int (cfdb, "globalHotkey", "vol_down_mask", &plugin_cfg.vol_down_mask);
471 bmp_cfg_db_get_int (cfdb, "globalHotkey", "vol_up", &plugin_cfg.vol_up);
472 bmp_cfg_db_get_int (cfdb, "globalHotkey", "vol_up_mask", &plugin_cfg.vol_up_mask);
473 bmp_cfg_db_get_int (cfdb, "globalHotkey", "play", &plugin_cfg.play);
474 bmp_cfg_db_get_int (cfdb, "globalHotkey", "play_mask", &plugin_cfg.play_mask);
475 bmp_cfg_db_get_int (cfdb, "globalHotkey", "pause", &plugin_cfg.pause);
476 bmp_cfg_db_get_int (cfdb, "globalHotkey", "pause_mask", &plugin_cfg.pause_mask);
477 bmp_cfg_db_get_int (cfdb, "globalHotkey", "stop", &plugin_cfg.stop);
478 bmp_cfg_db_get_int (cfdb, "globalHotkey", "stop_mask", &plugin_cfg.stop_mask);
479 bmp_cfg_db_get_int (cfdb, "globalHotkey", "prev_track", &plugin_cfg.prev_track);
480 bmp_cfg_db_get_int (cfdb, "globalHotkey", "prev_track_mask", &plugin_cfg.prev_track_mask);
481 bmp_cfg_db_get_int (cfdb, "globalHotkey", "next_track", &plugin_cfg.next_track);
482 bmp_cfg_db_get_int (cfdb, "globalHotkey", "next_track_mask", &plugin_cfg.next_track_mask);
483 bmp_cfg_db_get_int (cfdb, "globalHotkey", "jump_to_file", &plugin_cfg.jump_to_file);
484 bmp_cfg_db_get_int (cfdb, "globalHotkey", "jump_to_file_mask", &plugin_cfg.jump_to_file_mask);
485 bmp_cfg_db_get_int (cfdb, "globalHotkey", "forward", &plugin_cfg.forward);
486 bmp_cfg_db_get_int (cfdb, "globalHotkey", "forward_mask", &plugin_cfg.forward_mask);
487 bmp_cfg_db_get_int (cfdb, "globalHotkey", "backward", &plugin_cfg.backward);
488 bmp_cfg_db_get_int (cfdb, "globalHotkey", "backward_mask", &plugin_cfg.backward_mask);
489 bmp_cfg_db_get_int (cfdb, "globalHotkey", "toggle_win", &plugin_cfg.toggle_win);
490 bmp_cfg_db_get_int (cfdb, "globalHotkey", "toggle_win_mask", &plugin_cfg.toggle_win_mask);
491
492 bmp_cfg_db_close (cfdb);
493 }
494
495 /* save plugin configuration */
496 static void save_config (void)
497 {
498 ConfigDb *cfdb;
499
500 /* open configuration database */
501 cfdb = bmp_cfg_db_open ( );
502
503 bmp_cfg_db_set_int (cfdb, "globalHotkey", "mute", plugin_cfg.mute);
504 bmp_cfg_db_set_int (cfdb, "globalHotkey", "mute_mask", plugin_cfg.mute_mask);
505 bmp_cfg_db_set_int (cfdb, "globalHotkey", "vol_up", plugin_cfg.vol_up);
506 bmp_cfg_db_set_int (cfdb, "globalHotkey", "vol_up_mask", plugin_cfg.vol_up_mask);
507 bmp_cfg_db_set_int (cfdb, "globalHotkey", "vol_down", plugin_cfg.vol_down);
508 bmp_cfg_db_set_int (cfdb, "globalHotkey", "vol_down_mask", plugin_cfg.vol_down_mask);
509 bmp_cfg_db_set_int (cfdb, "globalHotkey", "play", plugin_cfg.play);
510 bmp_cfg_db_set_int (cfdb, "globalHotkey", "play_mask", plugin_cfg.play_mask);
511 bmp_cfg_db_set_int (cfdb, "globalHotkey", "pause", plugin_cfg.pause);
512 bmp_cfg_db_set_int (cfdb, "globalHotkey", "pause_mask", plugin_cfg.pause_mask);
513 bmp_cfg_db_set_int (cfdb, "globalHotkey", "stop", plugin_cfg.stop);
514 bmp_cfg_db_set_int (cfdb, "globalHotkey", "stop_mask", plugin_cfg.stop_mask);
515 bmp_cfg_db_set_int (cfdb, "globalHotkey", "prev_track", plugin_cfg.prev_track);
516 bmp_cfg_db_set_int (cfdb, "globalHotkey", "prev_track_mask", plugin_cfg.prev_track_mask);
517 bmp_cfg_db_set_int (cfdb, "globalHotkey", "next_track", plugin_cfg.next_track);
518 bmp_cfg_db_set_int (cfdb, "globalHotkey", "next_track_mask", plugin_cfg.next_track_mask);
519 bmp_cfg_db_set_int (cfdb, "globalHotkey", "jump_to_file", plugin_cfg.jump_to_file);
520 bmp_cfg_db_set_int (cfdb, "globalHotkey", "jump_to_file_mask", plugin_cfg.jump_to_file_mask);
521 bmp_cfg_db_set_int (cfdb, "globalHotkey", "forward", plugin_cfg.forward);
522 bmp_cfg_db_set_int (cfdb, "globalHotkey", "forward_mask", plugin_cfg.forward_mask);
523 bmp_cfg_db_set_int (cfdb, "globalHotkey", "backward", plugin_cfg.backward);
524 bmp_cfg_db_set_int (cfdb, "globalHotkey", "backward_mask", plugin_cfg.backward_mask);
525 bmp_cfg_db_set_int (cfdb, "globalHotkey", "toggle_win", plugin_cfg.toggle_win);
526 bmp_cfg_db_set_int (cfdb, "globalHotkey", "toggle_win_mask", plugin_cfg.toggle_win_mask);
527 bmp_cfg_db_close (cfdb);
528 }
529
530 static int x11_error_handler (Display *dpy, XErrorEvent *error)
531 {
532 return 0;
533 }
534
535 /* grab requied keys */
536 static void grab_key(KeyCode keycode, unsigned int modifier)
537 {
538 modifier &= ~(numlock_mask | capslock_mask | scrolllock_mask);
539
540 XGrabKey (xdisplay, keycode, modifier, x_root_window,
541 False, GrabModeAsync, GrabModeAsync);
542
543 if (modifier == AnyModifier)
544 return;
545
546 if (numlock_mask)
547 XGrabKey (xdisplay, keycode, modifier | numlock_mask,
548 x_root_window,
549 False, GrabModeAsync, GrabModeAsync);
550
551 if (capslock_mask)
552 XGrabKey (xdisplay, keycode, modifier | capslock_mask,
553 x_root_window,
554 False, GrabModeAsync, GrabModeAsync);
555
556 if (scrolllock_mask)
557 XGrabKey (xdisplay, keycode, modifier | scrolllock_mask,
558 x_root_window,
559 False, GrabModeAsync, GrabModeAsync);
560
561 if (numlock_mask && capslock_mask)
562 XGrabKey (xdisplay, keycode, modifier | numlock_mask | capslock_mask,
563 x_root_window,
564 False, GrabModeAsync, GrabModeAsync);
565
566 if (numlock_mask && scrolllock_mask)
567 XGrabKey (xdisplay, keycode, modifier | numlock_mask | scrolllock_mask,
568 x_root_window,
569 False, GrabModeAsync, GrabModeAsync);
570
571 if (capslock_mask && scrolllock_mask)
572 XGrabKey (xdisplay, keycode, modifier | capslock_mask | scrolllock_mask,
573 x_root_window,
574 False, GrabModeAsync, GrabModeAsync);
575
576 if (numlock_mask && capslock_mask && scrolllock_mask)
577 XGrabKey (xdisplay, keycode,
578 modifier | numlock_mask | capslock_mask | scrolllock_mask,
579 x_root_window, False, GrabModeAsync,
580 GrabModeAsync);
581 }
582
583
584 static void grab_keys ()
585 {
586 if (grabbed) return;
587 if (xdisplay == NULL) x_display_init();
588
589 XErrorHandler old_handler = 0;
590
591 XSync(xdisplay, False);
592 old_handler = XSetErrorHandler (x11_error_handler);
593
594 if (plugin_cfg.mute) grab_key(plugin_cfg.mute, plugin_cfg.mute_mask);
595 if (plugin_cfg.vol_up) grab_key(plugin_cfg.vol_up, plugin_cfg.vol_up_mask);
596 if (plugin_cfg.vol_down) grab_key(plugin_cfg.vol_down, plugin_cfg.vol_down_mask);
597 if (plugin_cfg.play) grab_key(plugin_cfg.play, plugin_cfg.play_mask);
598 if (plugin_cfg.pause) grab_key(plugin_cfg.pause, plugin_cfg.pause_mask);
599 if (plugin_cfg.stop) grab_key(plugin_cfg.stop, plugin_cfg.stop_mask);
600 if (plugin_cfg.prev_track) grab_key(plugin_cfg.prev_track, plugin_cfg.prev_track_mask);
601 if (plugin_cfg.next_track) grab_key(plugin_cfg.next_track, plugin_cfg.next_track_mask);
602 if (plugin_cfg.jump_to_file) grab_key(plugin_cfg.jump_to_file, plugin_cfg.jump_to_file_mask);
603 if (plugin_cfg.forward) grab_key(plugin_cfg.forward, plugin_cfg.forward_mask);
604 if (plugin_cfg.backward) grab_key(plugin_cfg.backward, plugin_cfg.backward_mask);
605 if (plugin_cfg.toggle_win) grab_key(plugin_cfg.toggle_win, plugin_cfg.toggle_win_mask);
606
607 XSync(xdisplay, False);
608 XSetErrorHandler (old_handler);
609
610 grabbed = 1;
611 }
612 /*
613 * plugin init end
614 */
615
616 static void set_keytext (GtkWidget *entry, gint key, gint mask)
617 {
618 gchar *text = NULL;
619
620 if (key == 0 && mask == 0)
621 {
622 text = g_strdup(_("(none)"));
623 } else {
624 static char *modifier_string[] = { "Control", "Shift", "Alt", "Mod2", "Mod3", "Super", "Mod5" };
625 static unsigned int modifiers[] = { ControlMask, ShiftMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask };
626 gchar *strings[9];
627 gchar *keytext = NULL;
628 int i, j;
629 KeySym keysym;
630
631 keysym = XKeycodeToKeysym(xdisplay, key, 0);
632 if (keysym == 0 || keysym == NoSymbol)
633 {
634 keytext = g_strdup_printf("#%3d", key);
635 } else {
636 keytext = g_strdup(XKeysymToString(keysym));
637 }
638
639 for (i = 0, j=0; j<7; j++)
640 {
641 if (mask & modifiers[j])
642 strings[i++] = modifier_string[j];
643 }
644 if (key != 0) strings[i++] = keytext;
645 strings[i] = NULL;
646
647 text = g_strjoinv(" + ", strings);
648 g_free(keytext);
649 }
650
651 gtk_entry_set_text(GTK_ENTRY(entry), text);
652 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
653 if (text) g_free(text);
654 }
655
656 static gboolean
657 on_entry_key_press_event(GtkWidget * widget,
658 GdkEventKey * event,
659 gpointer user_data)
660 {
661 KeyControls *controls = (KeyControls*) user_data;
662 int is_mod;
663 int mod;
664
665 if (event->keyval == GDK_Tab) return FALSE;
666 mod = 0;
667 is_mod = 0;
668
669 if ((event->state & GDK_CONTROL_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R))))
670 mod |= ControlMask;
671
672 if ((event->state & GDK_MOD1_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R))))
673 mod |= Mod1Mask;
674
675 if ((event->state & GDK_SHIFT_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))))
676 mod |= ShiftMask;
677
678 if ((event->state & GDK_MOD5_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_ISO_Level3_Shift))))
679 mod |= Mod5Mask;
680
681 if ((event->state & GDK_MOD4_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Super_L || event->keyval == GDK_Super_R))))
682 mod |= Mod4Mask;
683
684 if (!is_mod) {
685 controls->key = event->hardware_keycode;
686 controls->mask = mod;
687 } else controls->key = 0;
688
689 set_keytext(controls->keytext, is_mod ? 0 : event->hardware_keycode, mod);
690 return FALSE;
691 }
692
693 static gboolean
694 on_entry_key_release_event(GtkWidget * widget,
695 GdkEventKey * event,
696 gpointer user_data)
697 {
698 KeyControls *controls = (KeyControls*) user_data;
699 if (controls->key == 0) {
700 controls->mask = 0;
701 }
702 set_keytext(controls->keytext, controls->key, controls->mask);
703 return FALSE;
704 }
705
706
707 static void add_event_controls(GtkWidget *table, KeyControls *controls, int row, char* descr, gint key, gint mask)
708 {
709 GtkWidget *label;
710 GtkWidget *button;
711
712 controls->key = key;
713 controls->mask = mask;
714
715 label = gtk_label_new (_(descr));
716 gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
717 (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
718 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
719 gtk_misc_set_padding (GTK_MISC (label), 3, 3);
720
721 controls->keytext = gtk_entry_new ();
722 gtk_table_attach (GTK_TABLE (table), controls->keytext, 1, 2, row, row+1,
723 (GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
724 gtk_entry_set_editable (GTK_ENTRY (controls->keytext), FALSE);
725
726 set_keytext(controls->keytext, key, mask);
727 g_signal_connect((gpointer)controls->keytext, "key_press_event",
728 G_CALLBACK(on_entry_key_press_event), controls);
729 g_signal_connect((gpointer)controls->keytext, "key_release_event",
730 G_CALLBACK(on_entry_key_release_event), controls);
731
732 button = gtk_button_new_with_label (_("None"));
733 gtk_table_attach (GTK_TABLE (table), button, 2, 3, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
734 g_signal_connect (G_OBJECT (button), "clicked",
735 G_CALLBACK (clear_keyboard), controls);
736 }
737
738 /* configuration window */
739 static void configure (void)
740 {
741 ConfigurationControls *controls;
742 GtkWidget *window;
743 GtkWidget *main_vbox, *vbox;
744 GtkWidget *hbox;
745 GtkWidget *alignment;
746 GtkWidget *frame;
747 GtkWidget *label;
748 GtkWidget *image;
749 GtkWidget *table;
750 GtkWidget *button_box, *button;
751
752 if (!xdisplay) x_display_init();
753
754 #ifdef ENABLE_NLS
755 bind_textdomain_codeset(PACKAGE, "UTF-8");
756 #endif
757
758 load_config ( );
759
760 ungrab_keys();
761
762 controls = (ConfigurationControls*)g_malloc(sizeof(ConfigurationControls));
763 if (!controls)
764 {
765 printf ("Faild to allocate memory for ConfigurationControls structure!\n"
766 "Aborting!");
767 return;
768 }
769
770 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
771 gtk_window_set_title (GTK_WINDOW (window), _("Global Hotkey Plugin Configuration"));
772 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER_ALWAYS);
773 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
774 gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
775 gtk_container_set_border_width (GTK_CONTAINER (window), 5);
776
777 main_vbox = gtk_vbox_new (FALSE, 4);
778 gtk_container_add (GTK_CONTAINER (window), main_vbox);
779
780 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
781 gtk_box_pack_start (GTK_BOX (main_vbox), alignment, FALSE, TRUE, 0);
782 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 4, 0, 0, 0);
783 hbox = gtk_hbox_new (FALSE, 2);
784 gtk_container_add (GTK_CONTAINER (alignment), hbox);
785 image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
786 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
787 label = gtk_label_new (_("Press a key combination inside a text field."));
788 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
789 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
790
791 label = gtk_label_new (NULL);
792 gtk_label_set_markup (GTK_LABEL (label), _("<b>Playback:</b>"));
793 frame = gtk_frame_new (NULL);
794 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
795 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
796 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
797 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
798 gtk_container_add (GTK_CONTAINER (frame), alignment);
799 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
800 vbox = gtk_vbox_new (FALSE, 2);
801 gtk_container_add (GTK_CONTAINER (alignment), vbox);
802 label = gtk_label_new (NULL);
803 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
804 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
805 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
806 gtk_label_set_markup (GTK_LABEL (label),
807 _("<i>Configure keys which controls Audacious playback.</i>"));
808 table = gtk_table_new (4, 3, FALSE);
809 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
810 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
811 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
812
813 /* prev track */
814 add_event_controls(table, &controls->prev, 0, _("Previous Track:"),
815 plugin_cfg.prev_track, plugin_cfg.prev_track_mask);
816
817 add_event_controls(table, &controls->play, 1, _("Play/Pause:"),
818 plugin_cfg.play, plugin_cfg.play_mask);
819
820 add_event_controls(table, &controls->pause, 2, _("Pause:"),
821 plugin_cfg.pause, plugin_cfg.pause_mask);
822
823 add_event_controls(table, &controls->stop, 3, _("Stop:"),
824 plugin_cfg.stop, plugin_cfg.stop_mask);
825
826 add_event_controls(table, &controls->next, 4, _("Next Track:"),
827 plugin_cfg.next_track, plugin_cfg.next_track_mask);
828
829 add_event_controls(table, &controls->forward, 5, _("Forward 5 sec.:"),
830 plugin_cfg.forward, plugin_cfg.forward_mask);
831
832 add_event_controls(table, &controls->backward, 6, _("Rewind 5 sec.:"),
833 plugin_cfg.backward, plugin_cfg.backward_mask);
834
835
836 label = gtk_label_new (NULL);
837 gtk_label_set_markup (GTK_LABEL (label), _("<b>Volume Control:</b>"));
838 frame = gtk_frame_new (NULL);
839 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
840 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
841 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
842 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
843 gtk_container_add (GTK_CONTAINER (frame), alignment);
844 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
845 vbox = gtk_vbox_new (FALSE, 2);
846 gtk_container_add (GTK_CONTAINER (alignment), vbox);
847 label = gtk_label_new (NULL);
848 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
849 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
850 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
851 gtk_label_set_markup (GTK_LABEL (label),
852 _("<i>Configure keys which controls music volume.</i>"));
853 table = gtk_table_new (3, 3, FALSE);
854 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
855 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
856 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
857
858
859
860 add_event_controls(table, &controls->mute, 0, _("Mute:"),
861 plugin_cfg.mute, plugin_cfg.mute_mask);
862
863 add_event_controls(table, &controls->up, 1, _("Volume Up:"),
864 plugin_cfg.vol_up, plugin_cfg.vol_up_mask);
865
866 add_event_controls(table, &controls->down, 2, _("Volume Down:"),
867 plugin_cfg.vol_down, plugin_cfg.vol_down_mask);
868
869
870 label = gtk_label_new (NULL);
871 gtk_label_set_markup (GTK_LABEL (label), _("<b>Player:</b>"));
872 frame = gtk_frame_new (NULL);
873 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
874 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
875 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
876 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
877 gtk_container_add (GTK_CONTAINER (frame), alignment);
878 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
879 vbox = gtk_vbox_new (FALSE, 2);
880 gtk_container_add (GTK_CONTAINER (alignment), vbox);
881 label = gtk_label_new (NULL);
882 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
883 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
884 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
885 gtk_label_set_markup (GTK_LABEL (label),
886 _("<i>Configure keys which control the player.</i>"));
887 table = gtk_table_new (3, 2, FALSE);
888 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
889 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
890 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
891
892 add_event_controls(table, &controls->jump_to_file, 0, _("Jump to File:"),
893 plugin_cfg.jump_to_file, plugin_cfg.jump_to_file_mask);
894
895 add_event_controls(table, &controls->toggle_win, 1, _("Toggle Player Windows:"),
896 plugin_cfg.toggle_win, plugin_cfg.toggle_win_mask);
897
898
899 button_box = gtk_hbutton_box_new ( );
900 gtk_box_pack_start (GTK_BOX (main_vbox), button_box, FALSE, TRUE, 6);
901 gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
902 gtk_box_set_spacing (GTK_BOX (button_box), 4);
903
904 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
905 gtk_container_add (GTK_CONTAINER (button_box), button);
906 g_signal_connect (G_OBJECT (button), "clicked",
907 G_CALLBACK (cancel_callback), controls);
908
909 button = gtk_button_new_from_stock (GTK_STOCK_OK);
910 gtk_container_add (GTK_CONTAINER (button_box), button);
911 g_signal_connect (G_OBJECT (button), "clicked",
912 G_CALLBACK (ok_callback), controls);
913
914 gtk_widget_show_all (GTK_WIDGET (window));
915 }
916 /* configuration window end */
917
918 static void about (void)
919 {
920 static GtkWidget *dialog;
921
922 #ifdef ENABLE_NLS
923 bind_textdomain_codeset(PACKAGE, "UTF-8");
924 #endif
925
926 dialog = xmms_show_message (_("About Global Hotkey Plugin"),
927 _("Global Hotkey Plugin version " VERSION "\n\n"
928 "Copyright (C) 2007 Sascha Hlusiak <contact@saschahlusiak.de>\n\n"
929
930 "Parts of the plugin source are from itouch-ctrl plugin.\n"
931 "Authors of itouch-ctrl are listed below:\n"
932 "Copyright (C) 2006 - 2007 Vladimir Paskov <vlado.paskov@gmail.com>\n\n"
933
934 "Parts of the plugin source are from xmms-itouch plugin.\n"
935 "Authors of xmms-itouch are listed below:\n"
936 "Copyright (C) 2000-2002 Ville Syrjälä <syrjala@sci.fi>\n"
937 " Bryn Davies <curious@ihug.com.au>\n"
938 " Jonathan A. Davis <davis@jdhouse.org>\n"
939 " Jeremy Tan <nsx@nsx.homeip.net>\n\n"
940 ),
941 _("Ok"), TRUE, NULL, NULL);
942
943 gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
944 GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog);
945 }
946
947 /* Clear keys */
948 static void clear_keyboard (GtkWidget *widget, gpointer data)
949 {
950 KeyControls *spins = (KeyControls*)data;
951 spins->key = 0;
952 spins->mask = 0;
953 set_keytext(spins->keytext, 0, 0);
954 }
955
956 void cancel_callback (GtkWidget *widget, gpointer data)
957 {
958 if (loaded)
959 {
960 grab_keys ();
961 }
962 if (data) g_free(data);
963
964 gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
965 }
966
967 void ok_callback (GtkWidget *widget, gpointer data)
968 {
969 ConfigurationControls *controls= (ConfigurationControls*)data;
970
971 plugin_cfg.play = controls->play.key;
972 plugin_cfg.play_mask = controls->play.mask;
973
974 plugin_cfg.pause = controls->pause.key;
975 plugin_cfg.pause_mask = controls->pause.mask;
976
977 plugin_cfg.stop = controls->stop.key;
978 plugin_cfg.stop_mask = controls->stop.mask;
979
980 plugin_cfg.prev_track = controls->prev.key;
981 plugin_cfg.prev_track_mask = controls->prev.mask;
982
983 plugin_cfg.next_track = controls->next.key;
984 plugin_cfg.next_track_mask = controls->next.mask;
985
986 plugin_cfg.forward = controls->forward.key;
987 plugin_cfg.forward_mask = controls->forward.mask;
988
989 plugin_cfg.backward = controls->backward.key;
990 plugin_cfg.backward_mask = controls->backward.mask;
991
992 plugin_cfg.vol_up = controls->up.key;
993 plugin_cfg.vol_up_mask = controls->up.mask;
994
995 plugin_cfg.vol_down = controls->down.key;
996 plugin_cfg.vol_down_mask = controls->down.mask;
997
998 plugin_cfg.mute = controls->mute.key;
999 plugin_cfg.mute_mask = controls->mute.mask;
1000
1001 plugin_cfg.jump_to_file = controls->jump_to_file.key;
1002 plugin_cfg.jump_to_file_mask = controls->jump_to_file.mask;
1003
1004 plugin_cfg.toggle_win= controls->toggle_win.key;
1005 plugin_cfg.toggle_win_mask = controls->toggle_win.mask;
1006
1007 save_config ( );
1008
1009 if (loaded)
1010 {
1011 grab_keys ();
1012 }
1013
1014 if (data) g_free(data);
1015
1016 gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
1017 }
1018
1019 /*
1020 * plugin cleanup
1021 */
1022 static void cleanup (void)
1023 {
1024 if (!loaded) return;
1025 ungrab_keys ();
1026 release_filter();
1027 loaded = FALSE;
1028 }
1029
1030 static void ungrab_keys ()
1031 {
1032 if (!grabbed) return;
1033 if (!xdisplay) return;
1034
1035 XUngrabKey (xdisplay, AnyKey, AnyModifier, x_root_window);
1036
1037 grabbed = 0;
1038 }
1039