comparison src/hotkey/plugin.c @ 2330:0be42d832217

Splitted core/gui/grabbing sources; Fixed memory leak when closing configuration dialog.
author Sascha Hlusiak <contact@saschahlusiak.de>
date Sun, 20 Jan 2008 14:52:44 +0100
parents 515f9c741a5c
children ad45d65e9ae7
comparison
equal deleted inserted replaced
2316:62391135da44 2330:0be42d832217
1 /* -*- Mode: C; indent-tabs: t; c-basic-offset: 9; tab-width: 9 -*- */
2 /* 1 /*
3 * This file is part of audacious-hotkey plugin for audacious 2 * This file is part of audacious-hotkey plugin for audacious
4 * 3 *
5 * Copyright (c) 2007 - 2008 Sascha Hlusiak <contact@saschahlusiak.de> 4 * Copyright (c) 2007 - 2008 Sascha Hlusiak <contact@saschahlusiak.de>
6 * Name: plugin.c 5 * Name: plugin.c
35 */ 34 */
36 35
37 #include <config.h> 36 #include <config.h>
38 37
39 #include <stdio.h> 38 #include <stdio.h>
40 #include <stdlib.h>
41 #include <X11/Xlib.h>
42 #include <X11/keysym.h>
43 #include <X11/XF86keysym.h> 39 #include <X11/XF86keysym.h>
44 40
45 #include <gtk/gtk.h>
46 #include <gdk/gdkx.h> 41 #include <gdk/gdkx.h>
47 #include <gdk/gdkkeysyms.h>
48 #include <audacious/plugin.h> 42 #include <audacious/plugin.h>
49 #include <audacious/auddrct.h> 43 #include <audacious/auddrct.h>
50 #include <audacious/configdb.h> 44 #include <audacious/configdb.h>
51 45
52 #include <audacious/i18n.h> 46 #include <audacious/i18n.h>
53 47
54 /* for audacious_info_dialog () */ 48 #include "plugin.h"
55 #include <audacious/util.h> 49 #include "gui.h"
50 #include "grab.h"
56 51
57 52
58 /* func defs */ 53 /* func defs */
59 void x_display_init (void);
60 static void get_offending_modifiers (Display * dpy);
61 static void init (void); 54 static void init (void);
62 static void grab_keys ();
63 static void ungrab_keys ();
64 static gboolean handle_keyevent(int keycode, int state, int type);
65 static gboolean setup_filter();
66 static void release_filter();
67
68 static void load_config (void);
69 static void save_config (void);
70 static void configure (void);
71 static void clear_keyboard (GtkWidget *widget, gpointer data);
72
73 void cancel_callback (GtkWidget *widget, gpointer data);
74 void ok_callback (GtkWidget *widget, gpointer data);
75 static void about (void);
76 static void cleanup (void); 55 static void cleanup (void);
77 56
78 #define TYPE_KEY 0 57
79 #define TYPE_MOUSE 1 58 /* global vars */
80 59 static PluginConfig plugin_cfg;
81
82 typedef struct {
83 gint key, mask;
84 gint type;
85 } HotkeyConfiguration;
86
87 typedef struct {
88 gint vol_increment;
89 gint vol_decrement;
90
91 /* keyboard */
92 HotkeyConfiguration mute;
93 HotkeyConfiguration vol_down;
94 HotkeyConfiguration vol_up;
95 HotkeyConfiguration play;
96 HotkeyConfiguration stop;
97 HotkeyConfiguration pause;
98 HotkeyConfiguration prev_track;
99 HotkeyConfiguration next_track;
100 HotkeyConfiguration jump_to_file;
101 HotkeyConfiguration toggle_win;
102 HotkeyConfiguration forward;
103 HotkeyConfiguration backward;
104 HotkeyConfiguration show_aosd;
105 } PluginConfig;
106
107 PluginConfig plugin_cfg;
108
109 static Display *xdisplay = NULL;
110 static Window x_root_window = 0;
111 static gint grabbed = 0;
112 static gboolean loaded = FALSE; 60 static gboolean loaded = FALSE;
113 static unsigned int numlock_mask = 0; 61
114 static unsigned int scrolllock_mask = 0; 62
115 static unsigned int capslock_mask = 0;
116
117
118
119 typedef struct {
120 GtkWidget *keytext;
121 HotkeyConfiguration hotkey;
122 } KeyControls;
123
124 typedef struct {
125 KeyControls play;
126 KeyControls stop;
127 KeyControls pause;
128 KeyControls prev_track;
129 KeyControls next_track;
130 KeyControls vol_up;
131 KeyControls vol_down;
132 KeyControls mute;
133 KeyControls jump_to_file;
134 KeyControls forward;
135 KeyControls backward;
136 KeyControls toggle_win;
137 KeyControls show_aosd;
138 } ConfigurationControls;
139 63
140 static GeneralPlugin audacioushotkey = 64 static GeneralPlugin audacioushotkey =
141 { 65 {
142 .description = "Global Hotkey", 66 .description = "Global Hotkey",
143 .init = init, 67 .init = init,
144 .about = about, 68 .about = show_about,
145 .configure = configure, 69 .configure = show_configure,
146 .cleanup = cleanup 70 .cleanup = cleanup
147 }; 71 };
148 72
149 GeneralPlugin *hotkey_gplist[] = { &audacioushotkey, NULL }; 73 GeneralPlugin *hotkey_gplist[] = { &audacioushotkey, NULL };
150 SIMPLE_GENERAL_PLUGIN(hotkey, hotkey_gplist); 74 SIMPLE_GENERAL_PLUGIN(hotkey, hotkey_gplist);
151 75
76
77
78 PluginConfig* get_config(void)
79 {
80 return &plugin_cfg;
81 }
152 82
153 83
154 /* 84 /*
155 * plugin activated 85 * plugin activated
156 */ 86 */
157 static void init (void) 87 static void init (void)
158 { 88 {
159 x_display_init ( );
160 setup_filter(); 89 setup_filter();
161 load_config ( ); 90 load_config ( );
162 grab_keys (); 91 grab_keys ( );
163 92
164 loaded = TRUE; 93 loaded = TRUE;
165 } 94 }
166 95
167 /* check X display */
168 void x_display_init (void)
169 {
170 if (xdisplay != NULL) return;
171 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
172 x_root_window = GDK_WINDOW_XID(gdk_get_default_root_window());
173 get_offending_modifiers(xdisplay);
174 }
175
176 /* Taken from xbindkeys */
177 static void get_offending_modifiers (Display * dpy)
178 {
179 int i;
180 XModifierKeymap *modmap;
181 KeyCode nlock, slock;
182 static int mask_table[8] = {
183 ShiftMask, LockMask, ControlMask, Mod1Mask,
184 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
185 };
186
187 nlock = XKeysymToKeycode (dpy, XK_Num_Lock);
188 slock = XKeysymToKeycode (dpy, XK_Scroll_Lock);
189
190 /*
191 * Find out the masks for the NumLock and ScrollLock modifiers,
192 * so that we can bind the grabs for when they are enabled too.
193 */
194 modmap = XGetModifierMapping (dpy);
195
196 if (modmap != NULL && modmap->max_keypermod > 0)
197 {
198 for (i = 0; i < 8 * modmap->max_keypermod; i++)
199 {
200 if (modmap->modifiermap[i] == nlock && nlock != 0)
201 numlock_mask = mask_table[i / modmap->max_keypermod];
202 else if (modmap->modifiermap[i] == slock && slock != 0)
203 scrolllock_mask = mask_table[i / modmap->max_keypermod];
204 }
205 }
206
207 capslock_mask = LockMask;
208
209 if (modmap)
210 XFreeModifiermap (modmap);
211 }
212
213 /* handle keys */ 96 /* handle keys */
214 static gboolean handle_keyevent (int keycode, int state, int type) 97 gboolean handle_keyevent (int keycode, int state, int type)
215 { 98 {
216 gint current_volume, old_volume; 99 gint current_volume, old_volume;
217 static gint volume_static = 0; 100 static gint volume_static = 0;
218 gboolean play, mute; 101 gboolean play, mute;
219 102
230 } else { 113 } else {
231 /* volume is mute */ 114 /* volume is mute */
232 mute = TRUE; 115 mute = TRUE;
233 } 116 }
234 117
235 state &= ~(scrolllock_mask | numlock_mask | capslock_mask);
236
237 /* mute the playback */ 118 /* mute the playback */
238 if ((keycode == plugin_cfg.mute.key) && (state == plugin_cfg.mute.mask) && (type == plugin_cfg.mute.type)) 119 if ((keycode == plugin_cfg.mute.key) && (state == plugin_cfg.mute.mask) && (type == plugin_cfg.mute.type))
239 { 120 {
240 if (!mute) 121 if (!mute)
241 { 122 {
388 } 269 }
389 270
390 return FALSE; 271 return FALSE;
391 } 272 }
392 273
393 static GdkFilterReturn
394 gdk_filter(GdkXEvent *xevent,
395 GdkEvent *event,
396 gpointer data)
397 {
398 switch (((XEvent*)xevent)->type)
399 {
400 case KeyPress:
401 {
402 XKeyEvent *keyevent = (XKeyEvent*)xevent;
403 if (handle_keyevent(keyevent->keycode, keyevent->state, TYPE_KEY))
404 return GDK_FILTER_REMOVE;
405 break;
406 }
407 case ButtonPress:
408 {
409 XButtonEvent *buttonevent = (XButtonEvent*)xevent;
410 if (handle_keyevent(buttonevent->button, buttonevent->state, TYPE_MOUSE))
411 return GDK_FILTER_REMOVE;
412 break;
413 }
414 default:
415 return -1;
416 }
417
418 return GDK_FILTER_CONTINUE;
419 }
420
421 static gboolean
422 setup_filter()
423 {
424 gdk_window_add_filter(gdk_get_default_root_window(),
425 gdk_filter,
426 NULL);
427
428 return TRUE;
429 }
430
431 static void release_filter()
432 {
433 gdk_window_remove_filter(gdk_get_default_root_window(),
434 gdk_filter,
435 NULL);
436 }
437
438 /* load plugin configuration */ 274 /* load plugin configuration */
439 static void load_config (void) 275 void load_config (void)
440 { 276 {
441 ConfigDb *cfdb; 277 ConfigDb *cfdb;
442 278
443 if (xdisplay == NULL) x_display_init();
444
445 /* default volume level */ 279 /* default volume level */
446 plugin_cfg.vol_increment = 4; 280 plugin_cfg.vol_increment = 4;
447 plugin_cfg.vol_decrement = 4; 281 plugin_cfg.vol_decrement = 4;
448 282
449 #define load_key(hotkey,default) \ 283 #define load_key(hotkey,default) \
450 plugin_cfg.hotkey.key = (default)?(XKeysymToKeycode(xdisplay, (default))):0; \ 284 plugin_cfg.hotkey.key = (default)?(XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (default))):0; \
451 plugin_cfg.hotkey.mask = 0; \ 285 plugin_cfg.hotkey.mask = 0; \
452 plugin_cfg.hotkey.type = TYPE_KEY; \ 286 plugin_cfg.hotkey.type = TYPE_KEY; \
453 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey, &plugin_cfg.hotkey.key); \ 287 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey, &plugin_cfg.hotkey.key); \
454 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_mask", &plugin_cfg.hotkey.mask); \ 288 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_mask", &plugin_cfg.hotkey.mask); \
455 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_type", &plugin_cfg.hotkey.type); 289 aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_type", &plugin_cfg.hotkey.type);
474 308
475 aud_cfg_db_close (cfdb); 309 aud_cfg_db_close (cfdb);
476 } 310 }
477 311
478 /* save plugin configuration */ 312 /* save plugin configuration */
479 static void save_config (void) 313 void save_config (void)
480 { 314 {
481 ConfigDb *cfdb; 315 ConfigDb *cfdb;
482 316
483 #define save_key(hotkey) \ 317 #define save_key(hotkey) \
484 aud_cfg_db_set_int (cfdb, "globalHotkey", #hotkey, plugin_cfg.hotkey.key); \ 318 aud_cfg_db_set_int (cfdb, "globalHotkey", #hotkey, plugin_cfg.hotkey.key); \
503 save_key(show_aosd); 337 save_key(show_aosd);
504 338
505 aud_cfg_db_close (cfdb); 339 aud_cfg_db_close (cfdb);
506 } 340 }
507 341
508 static int x11_error_handler (Display *dpy, XErrorEvent *error)
509 {
510 return 0;
511 }
512
513 /* grab required keys */
514 static void grab_key(HotkeyConfiguration hotkey)
515 {
516 unsigned int modifier = hotkey.mask & ~(numlock_mask | capslock_mask | scrolllock_mask);
517
518 if (hotkey.key == 0) return;
519
520 if (hotkey.type == TYPE_KEY)
521 {
522 XGrabKey (xdisplay, hotkey.key, modifier, x_root_window,
523 False, GrabModeAsync, GrabModeAsync);
524
525 if (modifier == AnyModifier)
526 return;
527
528 if (numlock_mask)
529 XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask,
530 x_root_window,
531 False, GrabModeAsync, GrabModeAsync);
532
533 if (capslock_mask)
534 XGrabKey (xdisplay, hotkey.key, modifier | capslock_mask,
535 x_root_window,
536 False, GrabModeAsync, GrabModeAsync);
537
538 if (scrolllock_mask)
539 XGrabKey (xdisplay, hotkey.key, modifier | scrolllock_mask,
540 x_root_window,
541 False, GrabModeAsync, GrabModeAsync);
542
543 if (numlock_mask && capslock_mask)
544 XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask,
545 x_root_window,
546 False, GrabModeAsync, GrabModeAsync);
547
548 if (numlock_mask && scrolllock_mask)
549 XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask,
550 x_root_window,
551 False, GrabModeAsync, GrabModeAsync);
552
553 if (capslock_mask && scrolllock_mask)
554 XGrabKey (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask,
555 x_root_window,
556 False, GrabModeAsync, GrabModeAsync);
557
558 if (numlock_mask && capslock_mask && scrolllock_mask)
559 XGrabKey (xdisplay, hotkey.key,
560 modifier | numlock_mask | capslock_mask | scrolllock_mask,
561 x_root_window, False, GrabModeAsync,
562 GrabModeAsync);
563 }
564 if (hotkey.type == TYPE_MOUSE)
565 {
566 XGrabButton (xdisplay, hotkey.key, modifier, x_root_window,
567 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
568
569 if (modifier == AnyModifier)
570 return;
571
572 if (numlock_mask)
573 XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask,
574 x_root_window,
575 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
576
577 if (capslock_mask)
578 XGrabButton (xdisplay, hotkey.key, modifier | capslock_mask,
579 x_root_window,
580 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
581
582 if (scrolllock_mask)
583 XGrabButton (xdisplay, hotkey.key, modifier | scrolllock_mask,
584 x_root_window,
585 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
586
587 if (numlock_mask && capslock_mask)
588 XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask,
589 x_root_window,
590 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
591
592 if (numlock_mask && scrolllock_mask)
593 XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask,
594 x_root_window,
595 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
596
597 if (capslock_mask && scrolllock_mask)
598 XGrabButton (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask,
599 x_root_window,
600 False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
601
602 if (numlock_mask && capslock_mask && scrolllock_mask)
603 XGrabButton (xdisplay, hotkey.key,
604 modifier | numlock_mask | capslock_mask | scrolllock_mask,
605 x_root_window, False, ButtonPressMask, GrabModeAsync,
606 GrabModeAsync, None, None);
607 }
608 }
609
610 static void grab_keys ()
611 {
612 if (grabbed) return;
613 if (xdisplay == NULL) x_display_init();
614
615 XErrorHandler old_handler = 0;
616
617 XSync(xdisplay, False);
618 old_handler = XSetErrorHandler (x11_error_handler);
619
620 grab_key(plugin_cfg.mute);
621 grab_key(plugin_cfg.vol_up);
622 grab_key(plugin_cfg.vol_down);
623 grab_key(plugin_cfg.play);
624 grab_key(plugin_cfg.pause);
625 grab_key(plugin_cfg.stop);
626 grab_key(plugin_cfg.prev_track);
627 grab_key(plugin_cfg.next_track);
628 grab_key(plugin_cfg.jump_to_file);
629 grab_key(plugin_cfg.forward);
630 grab_key(plugin_cfg.backward);
631 grab_key(plugin_cfg.toggle_win);
632 grab_key(plugin_cfg.show_aosd);
633
634 XSync(xdisplay, False);
635 XSetErrorHandler (old_handler);
636
637 grabbed = 1;
638 }
639 /*
640 * plugin init end
641 */
642
643 static void set_keytext (GtkWidget *entry, gint key, gint mask, gint type)
644 {
645 gchar *text = NULL;
646
647 if (key == 0 && mask == 0)
648 {
649 text = g_strdup(_("(none)"));
650 } else {
651 static char *modifier_string[] = { "Control", "Shift", "Alt", "Mod2", "Mod3", "Super", "Mod5" };
652 static unsigned int modifiers[] = { ControlMask, ShiftMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask };
653 gchar *strings[9];
654 gchar *keytext = NULL;
655 int i, j;
656 if (type == TYPE_KEY)
657 {
658 KeySym keysym;
659 keysym = XKeycodeToKeysym(xdisplay, key, 0);
660 if (keysym == 0 || keysym == NoSymbol)
661 {
662 keytext = g_strdup_printf("#%d", key);
663 } else {
664 keytext = g_strdup(XKeysymToString(keysym));
665 }
666 }
667 if (type == TYPE_MOUSE)
668 {
669 keytext = g_strdup_printf("Button%d", key);
670 }
671
672 for (i = 0, j=0; j<7; j++)
673 {
674 if (mask & modifiers[j])
675 strings[i++] = modifier_string[j];
676 }
677 if (key != 0) strings[i++] = keytext;
678 strings[i] = NULL;
679
680 text = g_strjoinv(" + ", strings);
681 g_free(keytext);
682 }
683
684 gtk_entry_set_text(GTK_ENTRY(entry), text);
685 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
686 if (text) g_free(text);
687 }
688
689 static gboolean
690 on_entry_key_press_event(GtkWidget * widget,
691 GdkEventKey * event,
692 gpointer user_data)
693 {
694 KeyControls *controls = (KeyControls*) user_data;
695 int is_mod;
696 int mod;
697
698 if (event->keyval == GDK_Tab) return FALSE;
699
700 mod = 0;
701 is_mod = 0;
702
703 if ((event->state & GDK_CONTROL_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R))))
704 mod |= ControlMask;
705
706 if ((event->state & GDK_MOD1_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R))))
707 mod |= Mod1Mask;
708
709 if ((event->state & GDK_SHIFT_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))))
710 mod |= ShiftMask;
711
712 if ((event->state & GDK_MOD5_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_ISO_Level3_Shift))))
713 mod |= Mod5Mask;
714
715 if ((event->state & GDK_MOD4_MASK) | (!is_mod && (is_mod = (event->keyval == GDK_Super_L || event->keyval == GDK_Super_R))))
716 mod |= Mod4Mask;
717
718 if (!is_mod) {
719 controls->hotkey.key = event->hardware_keycode;
720 controls->hotkey.mask = mod;
721 controls->hotkey.type = TYPE_KEY;
722 } else controls->hotkey.key = 0;
723
724 set_keytext(controls->keytext, is_mod ? 0 : event->hardware_keycode, mod, TYPE_KEY);
725 return TRUE;
726 }
727
728 static gboolean
729 on_entry_key_release_event(GtkWidget * widget,
730 GdkEventKey * event,
731 gpointer user_data)
732 {
733 KeyControls *controls = (KeyControls*) user_data;
734 if (controls->hotkey.key == 0) {
735 controls->hotkey.mask = 0;
736 return TRUE;
737 }
738 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
739 return TRUE;
740 }
741
742 static gboolean
743 on_entry_button_press_event(GtkWidget * widget,
744 GdkEventButton * event,
745 gpointer user_data)
746 {
747 KeyControls *controls = (KeyControls*) user_data;
748 int mod;
749
750 if (!gtk_widget_is_focus(widget)) return FALSE;
751
752 mod = 0;
753 if (event->state & GDK_CONTROL_MASK)
754 mod |= ControlMask;
755
756 if (event->state & GDK_MOD1_MASK)
757 mod |= Mod1Mask;
758
759 if (event->state & GDK_SHIFT_MASK)
760 mod |= ShiftMask;
761
762 if (event->state & GDK_MOD5_MASK)
763 mod |= Mod5Mask;
764
765 if (event->state & GDK_MOD4_MASK)
766 mod |= Mod4Mask;
767
768 if ((event->button <= 3) && (mod == 0))
769 {
770 GtkWidget* dialog;
771 GtkResponseType response;
772 dialog = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(widget)),
773 GTK_DIALOG_MODAL,
774 GTK_MESSAGE_WARNING,
775 GTK_BUTTONS_YES_NO,
776 _("It is not recommended to bind the primary mouse buttons without modificators.\n\n"
777 "Do you want to continue?"));
778 gtk_window_set_title(GTK_WINDOW(dialog), _("Binding mouse buttons"));
779 response = gtk_dialog_run(GTK_DIALOG(dialog));
780 gtk_widget_destroy (dialog);
781 if (response != GTK_RESPONSE_YES) return TRUE;
782 }
783
784 controls->hotkey.key = event->button;
785 controls->hotkey.mask = mod;
786 controls->hotkey.type = TYPE_MOUSE;
787 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
788 return TRUE;
789 }
790
791 static gboolean
792 on_entry_scroll_event(GtkWidget * widget,
793 GdkEventScroll * event,
794 gpointer user_data)
795 {
796 KeyControls *controls = (KeyControls*) user_data;
797 int mod;
798
799 if (!gtk_widget_is_focus(widget)) return FALSE;
800
801 mod = 0;
802 if (event->state & GDK_CONTROL_MASK)
803 mod |= ControlMask;
804
805 if (event->state & GDK_MOD1_MASK)
806 mod |= Mod1Mask;
807
808 if (event->state & GDK_SHIFT_MASK)
809 mod |= ShiftMask;
810
811 if (event->state & GDK_MOD5_MASK)
812 mod |= Mod5Mask;
813
814 if (event->state & GDK_MOD4_MASK)
815 mod |= Mod4Mask;
816
817 if (event->direction == GDK_SCROLL_UP)
818 controls->hotkey.key = 4;
819 else if (event->direction == GDK_SCROLL_DOWN)
820 controls->hotkey.key = 5;
821 else if (event->direction == GDK_SCROLL_LEFT)
822 controls->hotkey.key = 6;
823 else if (event->direction == GDK_SCROLL_RIGHT)
824 controls->hotkey.key = 7;
825 else return FALSE;
826
827 controls->hotkey.mask = mod;
828 controls->hotkey.type = TYPE_MOUSE;
829 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
830 return TRUE;
831 }
832
833 static void add_event_controls(GtkWidget *table,
834 KeyControls *controls,
835 int row,
836 char* descr,
837 char* tooltip,
838 HotkeyConfiguration hotkey)
839 {
840 GtkWidget *label;
841 GtkWidget *button;
842
843 controls->hotkey.key = hotkey.key;
844 controls->hotkey.mask = hotkey.mask;
845 controls->hotkey.type = hotkey.type;
846 if (controls->hotkey.key == 0)
847 controls->hotkey.mask = 0;
848
849 label = gtk_label_new (_(descr));
850 gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1,
851 (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
852 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
853 gtk_misc_set_padding (GTK_MISC (label), 3, 3);
854
855 controls->keytext = gtk_entry_new ();
856 gtk_table_attach (GTK_TABLE (table), controls->keytext, 1, 2, row, row+1,
857 (GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
858 gtk_entry_set_editable (GTK_ENTRY (controls->keytext), FALSE);
859
860 set_keytext(controls->keytext, hotkey.key, hotkey.mask, hotkey.type);
861 g_signal_connect((gpointer)controls->keytext, "key_press_event",
862 G_CALLBACK(on_entry_key_press_event), controls);
863 g_signal_connect((gpointer)controls->keytext, "key_release_event",
864 G_CALLBACK(on_entry_key_release_event), controls);
865 g_signal_connect((gpointer)controls->keytext, "button_press_event",
866 G_CALLBACK(on_entry_button_press_event), controls);
867 g_signal_connect((gpointer)controls->keytext, "scroll_event",
868 G_CALLBACK(on_entry_scroll_event), controls);
869
870 button = gtk_button_new_with_label (_("None"));
871 gtk_table_attach (GTK_TABLE (table), button, 2, 3, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
872 g_signal_connect (G_OBJECT (button), "clicked",
873 G_CALLBACK (clear_keyboard), controls);
874
875 if (tooltip != NULL) {
876 GtkTooltips *tip = gtk_tooltips_new();
877 gtk_tooltips_set_tip(tip, controls->keytext, tooltip, NULL);
878 gtk_tooltips_set_tip(tip, button, tooltip, NULL);
879 gtk_tooltips_set_tip(tip, label, tooltip, NULL);
880 }
881 }
882
883 /* configuration window */
884 static void configure (void)
885 {
886 ConfigurationControls *controls;
887 GtkWidget *window;
888 GtkWidget *main_vbox, *vbox;
889 GtkWidget *hbox;
890 GtkWidget *alignment;
891 GtkWidget *frame;
892 GtkWidget *label;
893 GtkWidget *image;
894 GtkWidget *table;
895 GtkWidget *button_box, *button;
896
897 if (!xdisplay) x_display_init();
898
899 load_config ( );
900
901 ungrab_keys();
902
903 controls = (ConfigurationControls*)g_malloc(sizeof(ConfigurationControls));
904 if (!controls)
905 {
906 printf ("Faild to allocate memory for ConfigurationControls structure!\n"
907 "Aborting!");
908 return;
909 }
910
911 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
912 gtk_window_set_title (GTK_WINDOW (window), _("Global Hotkey Plugin Configuration"));
913 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER_ALWAYS);
914 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
915 gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
916 gtk_container_set_border_width (GTK_CONTAINER (window), 5);
917
918 main_vbox = gtk_vbox_new (FALSE, 4);
919 gtk_container_add (GTK_CONTAINER (window), main_vbox);
920
921 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
922 gtk_box_pack_start (GTK_BOX (main_vbox), alignment, FALSE, TRUE, 0);
923 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 4, 0, 0, 0);
924 hbox = gtk_hbox_new (FALSE, 2);
925 gtk_container_add (GTK_CONTAINER (alignment), hbox);
926 image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
927 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
928 label = gtk_label_new (_("Press a key combination inside a text field."));
929 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
930 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
931
932 label = gtk_label_new (NULL);
933 gtk_label_set_markup (GTK_LABEL (label), _("<b>Playback:</b>"));
934 frame = gtk_frame_new (NULL);
935 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
936 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
937 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
938 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
939 gtk_container_add (GTK_CONTAINER (frame), alignment);
940 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
941 vbox = gtk_vbox_new (FALSE, 2);
942 gtk_container_add (GTK_CONTAINER (alignment), vbox);
943 label = gtk_label_new (NULL);
944 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
945 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
946 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
947 gtk_label_set_markup (GTK_LABEL (label),
948 _("<i>Configure keys which controls Audacious playback.</i>"));
949 table = gtk_table_new (4, 3, FALSE);
950 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
951 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
952 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
953
954 /* prev track */
955 add_event_controls(table, &controls->prev_track, 0, _("Previous Track:"), NULL,
956 plugin_cfg.prev_track);
957
958 add_event_controls(table, &controls->play, 1, _("Play:"), NULL,
959 plugin_cfg.play);
960
961 add_event_controls(table, &controls->pause, 2, _("Pause/Resume:"), NULL,
962 plugin_cfg.pause);
963
964 add_event_controls(table, &controls->stop, 3, _("Stop:"), NULL,
965 plugin_cfg.stop);
966
967 add_event_controls(table, &controls->next_track, 4, _("Next Track:"), NULL,
968 plugin_cfg.next_track);
969
970 add_event_controls(table, &controls->forward, 5, _("Forward 5 sec.:"), NULL,
971 plugin_cfg.forward);
972
973 add_event_controls(table, &controls->backward, 6, _("Rewind 5 sec.:"), NULL,
974 plugin_cfg.backward);
975
976
977 label = gtk_label_new (NULL);
978 gtk_label_set_markup (GTK_LABEL (label), _("<b>Volume Control:</b>"));
979 frame = gtk_frame_new (NULL);
980 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
981 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
982 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
983 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
984 gtk_container_add (GTK_CONTAINER (frame), alignment);
985 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
986 vbox = gtk_vbox_new (FALSE, 2);
987 gtk_container_add (GTK_CONTAINER (alignment), vbox);
988 label = gtk_label_new (NULL);
989 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
990 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
991 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
992 gtk_label_set_markup (GTK_LABEL (label),
993 _("<i>Configure keys which controls music volume.</i>"));
994 table = gtk_table_new (3, 3, FALSE);
995 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
996 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
997 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
998
999 add_event_controls(table, &controls->mute, 0, _("Mute:"),NULL,
1000 plugin_cfg.mute);
1001
1002 add_event_controls(table, &controls->vol_up, 1, _("Volume Up:"), NULL,
1003 plugin_cfg.vol_up);
1004
1005 add_event_controls(table, &controls->vol_down, 2, _("Volume Down:"), NULL,
1006 plugin_cfg.vol_down);
1007
1008
1009 label = gtk_label_new (NULL);
1010 gtk_label_set_markup (GTK_LABEL (label), _("<b>Player:</b>"));
1011 frame = gtk_frame_new (NULL);
1012 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
1013 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
1014 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
1015 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
1016 gtk_container_add (GTK_CONTAINER (frame), alignment);
1017 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
1018 vbox = gtk_vbox_new (FALSE, 2);
1019 gtk_container_add (GTK_CONTAINER (alignment), vbox);
1020 label = gtk_label_new (NULL);
1021 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
1022 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
1023 gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
1024 gtk_label_set_markup (GTK_LABEL (label),
1025 _("<i>Configure keys which control the player.</i>"));
1026 table = gtk_table_new (3, 3, FALSE);
1027 gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
1028 gtk_table_set_col_spacings (GTK_TABLE (table), 2);
1029 gtk_table_set_row_spacings (GTK_TABLE (table), 2);
1030
1031 add_event_controls(table, &controls->jump_to_file, 0, _("Jump to File:"), NULL,
1032 plugin_cfg.jump_to_file);
1033
1034 add_event_controls(table, &controls->toggle_win, 1, _("Toggle Player Windows:"), NULL,
1035 plugin_cfg.toggle_win);
1036
1037 add_event_controls(table, &controls->show_aosd, 2, _("Show On-Screen-Display:"),
1038 _("For this, the Audacious OSD plugin must be activated."),
1039 plugin_cfg.show_aosd);
1040
1041 button_box = gtk_hbutton_box_new ( );
1042 gtk_box_pack_start (GTK_BOX (main_vbox), button_box, FALSE, TRUE, 6);
1043 gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
1044 gtk_box_set_spacing (GTK_BOX (button_box), 4);
1045
1046 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1047 gtk_container_add (GTK_CONTAINER (button_box), button);
1048 g_signal_connect (G_OBJECT (button), "clicked",
1049 G_CALLBACK (cancel_callback), controls);
1050
1051 button = gtk_button_new_from_stock (GTK_STOCK_OK);
1052 gtk_container_add (GTK_CONTAINER (button_box), button);
1053 g_signal_connect (G_OBJECT (button), "clicked",
1054 G_CALLBACK (ok_callback), controls);
1055
1056 gtk_widget_show_all (GTK_WIDGET (window));
1057 }
1058 /* configuration window end */
1059
1060 static void about (void)
1061 {
1062 static GtkWidget *dialog;
1063
1064 dialog = audacious_info_dialog (_("About Global Hotkey Plugin"),
1065 _("Global Hotkey Plugin\n"
1066 "Control the player with global key combinations or multimedia keys.\n\n"
1067 "Copyright (C) 2007-2008 Sascha Hlusiak <contact@saschahlusiak.de>\n\n"
1068 "Contributers include:\n"
1069 "Copyright (C) 2006-2007 Vladimir Paskov <vlado.paskov@gmail.com>\n"
1070 "Copyright (C) 2000-2002 Ville Syrjälä <syrjala@sci.fi>\n"
1071 " Bryn Davies <curious@ihug.com.au>\n"
1072 " Jonathan A. Davis <davis@jdhouse.org>\n"
1073 " Jeremy Tan <nsx@nsx.homeip.net>\n\n"
1074 ),
1075 _("OK"), TRUE, NULL, NULL);
1076
1077 gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
1078 GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog);
1079 }
1080
1081 /* Clear keys */
1082 static void clear_keyboard (GtkWidget *widget, gpointer data)
1083 {
1084 KeyControls *spins = (KeyControls*)data;
1085 spins->hotkey.key = 0;
1086 spins->hotkey.mask = 0;
1087 spins->hotkey.type = TYPE_KEY;
1088 set_keytext(spins->keytext, 0, 0, TYPE_KEY);
1089 }
1090
1091 void cancel_callback (GtkWidget *widget, gpointer data)
1092 {
1093 if (loaded)
1094 {
1095 grab_keys ();
1096 }
1097 if (data) g_free(data);
1098
1099 gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
1100 }
1101
1102 void ok_callback (GtkWidget *widget, gpointer data)
1103 {
1104 ConfigurationControls *controls= (ConfigurationControls*)data;
1105
1106 plugin_cfg.play = controls->play.hotkey;
1107 plugin_cfg.pause = controls->pause.hotkey;
1108 plugin_cfg.stop= controls->stop.hotkey;
1109 plugin_cfg.prev_track= controls->prev_track.hotkey;
1110 plugin_cfg.next_track = controls->next_track.hotkey;
1111 plugin_cfg.forward = controls->forward.hotkey;
1112 plugin_cfg.backward = controls->backward.hotkey;
1113 plugin_cfg.vol_up= controls->vol_up.hotkey;
1114 plugin_cfg.vol_down = controls->vol_down.hotkey;
1115 plugin_cfg.mute = controls->mute.hotkey;
1116 plugin_cfg.jump_to_file= controls->jump_to_file.hotkey;
1117 plugin_cfg.toggle_win = controls->toggle_win.hotkey;
1118 plugin_cfg.show_aosd = controls->show_aosd.hotkey;
1119
1120 save_config ( );
1121
1122 if (loaded)
1123 {
1124 grab_keys ();
1125 }
1126
1127 if (data) g_free(data);
1128
1129 gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
1130 }
1131
1132 /*
1133 * plugin cleanup
1134 */
1135 static void cleanup (void) 342 static void cleanup (void)
1136 { 343 {
1137 if (!loaded) return; 344 if (!loaded) return;
1138 ungrab_keys (); 345 ungrab_keys ();
1139 release_filter(); 346 release_filter();
1140 loaded = FALSE; 347 loaded = FALSE;
1141 } 348 }
1142 349
1143 /* grab required keys */ 350 gboolean is_loaded (void)
1144 static void ungrab_key(HotkeyConfiguration hotkey) 351 {
1145 { 352 return loaded;
1146 unsigned int modifier = hotkey.mask & ~(numlock_mask | capslock_mask | scrolllock_mask); 353 }
1147
1148 if (hotkey.key == 0) return;
1149
1150 if (hotkey.type == TYPE_KEY)
1151 {
1152 XUngrabKey (xdisplay, hotkey.key, modifier, x_root_window);
1153
1154 if (modifier == AnyModifier)
1155 return;
1156
1157 if (numlock_mask)
1158 XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask, x_root_window);
1159
1160 if (capslock_mask)
1161 XUngrabKey (xdisplay, hotkey.key, modifier | capslock_mask, x_root_window);
1162
1163 if (scrolllock_mask)
1164 XUngrabKey (xdisplay, hotkey.key, modifier | scrolllock_mask, x_root_window);
1165
1166 if (numlock_mask && capslock_mask)
1167 XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask, x_root_window);
1168
1169 if (numlock_mask && scrolllock_mask)
1170 XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask, x_root_window);
1171
1172 if (capslock_mask && scrolllock_mask)
1173 XUngrabKey (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask, x_root_window);
1174
1175 if (numlock_mask && capslock_mask && scrolllock_mask)
1176 XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
1177 }
1178 if (hotkey.type == TYPE_MOUSE)
1179 {
1180 XUngrabButton (xdisplay, hotkey.key, modifier, x_root_window);
1181
1182 if (modifier == AnyModifier)
1183 return;
1184
1185 if (numlock_mask)
1186 XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask, x_root_window);
1187
1188 if (capslock_mask)
1189 XUngrabButton (xdisplay, hotkey.key, modifier | capslock_mask, x_root_window);
1190
1191 if (scrolllock_mask)
1192 XUngrabButton (xdisplay, hotkey.key, modifier | scrolllock_mask, x_root_window);
1193
1194 if (numlock_mask && capslock_mask)
1195 XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask, x_root_window);
1196
1197 if (numlock_mask && scrolllock_mask)
1198 XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask, x_root_window);
1199
1200 if (capslock_mask && scrolllock_mask)
1201 XUngrabButton (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask, x_root_window);
1202
1203 if (numlock_mask && capslock_mask && scrolllock_mask)
1204 XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
1205 }
1206 }
1207
1208 static void ungrab_keys ()
1209 {
1210 XErrorHandler old_handler = 0;
1211
1212 if (!grabbed) return;
1213 if (!xdisplay) return;
1214
1215 XSync(xdisplay, False);
1216 old_handler = XSetErrorHandler (x11_error_handler);
1217
1218 ungrab_key(plugin_cfg.mute);
1219 ungrab_key(plugin_cfg.vol_up);
1220 ungrab_key(plugin_cfg.vol_down);
1221 ungrab_key(plugin_cfg.play);
1222 ungrab_key(plugin_cfg.pause);
1223 ungrab_key(plugin_cfg.stop);
1224 ungrab_key(plugin_cfg.prev_track);
1225 ungrab_key(plugin_cfg.next_track);
1226 ungrab_key(plugin_cfg.jump_to_file);
1227 ungrab_key(plugin_cfg.forward);
1228 ungrab_key(plugin_cfg.backward);
1229 ungrab_key(plugin_cfg.toggle_win);
1230 ungrab_key(plugin_cfg.show_aosd);
1231
1232 XSync(xdisplay, False);
1233 XSetErrorHandler (old_handler);
1234
1235 grabbed = 0;
1236 }