2 * Copyright (C) 2004-2007 Qball Cow <qball@sarine.nl>
3 * Borrowed from Lee Willis <lee@leewillis.co.uk> that
4 * Borrowed heavily from code by Jan Arne Petersen <jpetersen@uni-bonn.de>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include "eggcellrendererkeys.h"
28 static void mmkeys_class_init (MmKeysClass
*klass
);
29 static void mmkeys_init (MmKeys
*object
);
30 static void mmkeys_finalize (GObject
*object
);
32 static int grab_mmkey (int key_code
, unsigned int mask
, GdkWindow
*root
);
34 static GdkFilterReturn
filter_mmkeys (GdkXEvent
*xevent
,
38 #define FG_DEFAULT None
39 #define FG_ERROR "red"
41 /* Members of the treestore */
70 char *keynames
[LAST_SIGNAL
] = {
71 N_("PlayPause"), /** MM_PLAYPAUSE */
72 N_("Next"), /** MM_NEXT*/
73 N_("Previous"), /** MM_PREV */
74 N_("Stop"), /** MM_STOP */
75 N_("Fast Forward"), /** MM_FASTFORWARD */
76 N_("Fast Backward"), /** MM_FASTBACKWARD */
77 N_("Repeat"), /** MM_REPEAT */
78 N_("Random"), /** MM_RANDOM */
79 N_("Raise window"), /** MM_RAISE */
80 N_("Hide window"), /** MM_HIDE */
81 N_("Toggle window"), /** MM_TOGGLE_HIDDEN */
82 N_("Volume Up"), /** MM_VOLUME_UP */
83 N_("Volume Down"), /** MM_VOLUME_DOWN */
84 N_("Show song"), /** MM_SHOW_NOTIFICATION */
87 static GObjectClass
*parent_class
;
88 static guint signals
[LAST_SIGNAL
];
89 static int keycodes
[LAST_SIGNAL
];
90 static unsigned int masks
[LAST_SIGNAL
];
91 static int keyerror
[LAST_SIGNAL
];
93 static GType type
= 0;
95 static GType
mmkeys_get_type (void)
98 static const GTypeInfo info
= {
100 NULL
, /* base_init */
101 NULL
, /* base_finalize */
102 (GClassInitFunc
) mmkeys_class_init
,
103 NULL
, /* class_finalize */
104 NULL
, /* class_data */
107 (GInstanceInitFunc
) mmkeys_init
,
110 type
= g_type_register_static (G_TYPE_OBJECT
, "MmKeys",&info
, 0);
116 static void mmkeys_class_init (MmKeysClass
*klass
)
118 GObjectClass
*object_class
;
120 parent_class
= g_type_class_peek_parent (klass
);
121 object_class
= (GObjectClass
*) klass
;
123 object_class
->finalize
= mmkeys_finalize
;
125 signals
[MM_PLAYPAUSE
] =
126 g_signal_new ("mm_playpause",
127 G_TYPE_FROM_CLASS (klass
),
131 g_cclosure_marshal_VOID__INT
,
132 G_TYPE_NONE
, 1, G_TYPE_INT
);
134 g_signal_new ("mm_prev",
135 G_TYPE_FROM_CLASS (klass
),
139 g_cclosure_marshal_VOID__INT
,
140 G_TYPE_NONE
, 1, G_TYPE_INT
);
142 g_signal_new ("mm_next",
143 G_TYPE_FROM_CLASS (klass
),
147 g_cclosure_marshal_VOID__INT
,
148 G_TYPE_NONE
, 1, G_TYPE_INT
);
150 g_signal_new ("mm_stop",
151 G_TYPE_FROM_CLASS (klass
),
155 g_cclosure_marshal_VOID__INT
,
156 G_TYPE_NONE
, 1, G_TYPE_INT
);
157 signals
[MM_FASTFORWARD
] =
158 g_signal_new ("mm_fastforward",
159 G_TYPE_FROM_CLASS (klass
),
163 g_cclosure_marshal_VOID__INT
,
164 G_TYPE_NONE
, 1, G_TYPE_INT
);
165 signals
[MM_FASTBACKWARD
]=
166 g_signal_new ("mm_fastbackward",
167 G_TYPE_FROM_CLASS (klass
),
171 g_cclosure_marshal_VOID__INT
,
172 G_TYPE_NONE
, 1, G_TYPE_INT
);
174 g_signal_new ("mm_repeat",
175 G_TYPE_FROM_CLASS (klass
),
179 g_cclosure_marshal_VOID__INT
,
180 G_TYPE_NONE
, 1, G_TYPE_INT
);
182 g_signal_new ("mm_random",
183 G_TYPE_FROM_CLASS (klass
),
187 g_cclosure_marshal_VOID__INT
,
188 G_TYPE_NONE
, 1, G_TYPE_INT
);
190 g_signal_new ("mm_raise",
191 G_TYPE_FROM_CLASS (klass
),
195 g_cclosure_marshal_VOID__INT
,
196 G_TYPE_NONE
, 1, G_TYPE_INT
);
198 g_signal_new ("mm_hide",
199 G_TYPE_FROM_CLASS (klass
),
203 g_cclosure_marshal_VOID__INT
,
204 G_TYPE_NONE
, 1, G_TYPE_INT
);
205 signals
[MM_TOGGLE_HIDDEN
]=
206 g_signal_new ("mm_toggle_hidden",
207 G_TYPE_FROM_CLASS (klass
),
211 g_cclosure_marshal_VOID__INT
,
212 G_TYPE_NONE
, 1, G_TYPE_INT
);
213 signals
[MM_VOLUME_UP
] =
214 g_signal_new ("mm_volume_up",
215 G_TYPE_FROM_CLASS (klass
),
219 g_cclosure_marshal_VOID__INT
,
220 G_TYPE_NONE
, 1, G_TYPE_INT
);
222 signals
[MM_VOLUME_DOWN
] =
223 g_signal_new ("mm_volume_down",
224 G_TYPE_FROM_CLASS (klass
),
228 g_cclosure_marshal_VOID__INT
,
229 G_TYPE_NONE
, 1, G_TYPE_INT
);
230 signals
[MM_SHOW_NOTIFICATION
] =
231 g_signal_new ("mm_show_notification",
232 G_TYPE_FROM_CLASS (klass
),
236 g_cclosure_marshal_VOID__INT
,
237 G_TYPE_NONE
, 1, G_TYPE_INT
);
240 static void mmkeys_finalize (GObject
*object
)
242 parent_class
->finalize (G_OBJECT(object
));
245 static void mmkeys_init (MmKeys
*object
)
252 int anyKeybindsFailed
= FALSE
;
253 int anyDuplicatesFound
= FALSE
;
255 display
= gdk_display_get_default ();
259 keycode
= XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay
);
260 keycodes
[MM_PLAYPAUSE
] = cfg_get_single_value_as_int_with_default(config
, "Keybindings", keynames
[MM_PLAYPAUSE
], keycode
);
261 masks
[MM_PLAYPAUSE
] = cfg_get_single_value_as_int_with_default(config
, "Keymasks", keynames
[MM_PLAYPAUSE
], 0);
265 keycode
= XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev
);
266 keycodes
[MM_PREV
] = cfg_get_single_value_as_int_with_default(config
, "Keybindings", keynames
[MM_PREV
], keycode
);
267 masks
[MM_PREV
] = cfg_get_single_value_as_int_with_default(config
, "Keymasks", keynames
[MM_PREV
], 0);
271 keycode
= XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext
);
272 keycodes
[MM_NEXT
] = cfg_get_single_value_as_int_with_default(config
, "Keybindings", keynames
[MM_NEXT
], keycode
);
273 masks
[MM_NEXT
] = cfg_get_single_value_as_int_with_default(config
, "Keymasks", keynames
[MM_NEXT
], 0);
277 keycode
= XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop
);
278 keycodes
[MM_STOP
] = cfg_get_single_value_as_int_with_default(config
, "Keybindings", keynames
[MM_STOP
], keycode
);
279 masks
[MM_STOP
] = cfg_get_single_value_as_int_with_default(config
, "Keymasks", keynames
[MM_STOP
], 0);
281 for(i
=0;i
<LAST_SIGNAL
;i
++)
283 keycodes
[i
] = cfg_get_single_value_as_int_with_default(config
, "Keybindings", keynames
[i
], 0);
284 masks
[i
] = cfg_get_single_value_as_int_with_default(config
, "Keymasks", keynames
[i
], 0);
286 /* Detect duplicates */
289 if (keycodes
[i
] != 0 &&
290 keycodes
[i
] == keycodes
[j
] &&
291 masks
[i
] == masks
[j
])
293 anyDuplicatesFound
= TRUE
;
297 cfg_set_single_value_as_int(config
, "Keybindings", keynames
[i
], 0);
298 cfg_set_single_value_as_int(config
, "Keymasks", keynames
[i
], 0);
303 for (i
= 0; i
< gdk_display_get_n_screens (display
); i
++) {
304 screen
= gdk_display_get_screen (display
, i
);
305 if (screen
!= NULL
) {
306 root
= gdk_screen_get_root_window (screen
);
308 for (j
= 0; j
< LAST_SIGNAL
;j
++) {
310 if( !grab_mmkey (keycodes
[j
], masks
[j
], root
) )
313 anyKeybindsFailed
= TRUE
;
317 gdk_window_add_filter (root
, filter_mmkeys
, object
);
321 if (anyKeybindsFailed
)
323 GString
*message
= g_string_new (_("Could not grab the following multimedia keys:\n\n"));
324 for (i
=0;i
<LAST_SIGNAL
;i
++)
326 if (keyerror
[i
] && keycodes
[i
] != 0)
328 gchar
*rawkeysym
= egg_virtual_accelerator_name (
329 XKeycodeToKeysym(GDK_DISPLAY(), keycodes
[i
], 0),
330 keycodes
[i
], masks
[i
]);
331 gchar
*keysym
= g_markup_escape_text(rawkeysym
, -1);
333 g_string_append_printf( message
,
335 _(keynames
[i
]), keysym
);
339 g_string_append( message
,
340 _("\nEnsure that your window manager (or other applications) have not already bound this key for some other function, then restart gmpc." ));
341 show_error_message (message
->str
, TRUE
);
342 g_string_free (message
, TRUE
);
345 if (anyDuplicatesFound
)
347 show_error_message(_("Duplicate mapping(s) detected\n\n"
348 "Some duplicate multimedia key mappings were detected, and disabled. Please revisit the preferences and ensure your settings are now correct."), TRUE
);
352 MmKeys
* mmkeys_new (void)
354 return MMKEYS (g_object_new (TYPE_MMKEYS
, NULL
));
358 static int grab_mmkey (int key_code
, unsigned int mask
, GdkWindow
*root
)
360 gdk_error_trap_push ();
362 XGrabKey (GDK_DISPLAY (), key_code
,
364 GDK_WINDOW_XID (root
), True
,
365 GrabModeAsync
, GrabModeAsync
);
366 XGrabKey (GDK_DISPLAY (), key_code
,
368 GDK_WINDOW_XID (root
), True
,
369 GrabModeAsync
, GrabModeAsync
);
370 XGrabKey (GDK_DISPLAY (), key_code
,
372 GDK_WINDOW_XID (root
), True
,
373 GrabModeAsync
, GrabModeAsync
);
374 XGrabKey (GDK_DISPLAY (), key_code
,
376 GDK_WINDOW_XID (root
), True
,
377 GrabModeAsync
, GrabModeAsync
);
378 XGrabKey (GDK_DISPLAY (), key_code
,
379 Mod2Mask
| Mod5Mask
| mask
,
380 GDK_WINDOW_XID (root
), True
,
381 GrabModeAsync
, GrabModeAsync
);
382 XGrabKey (GDK_DISPLAY (), key_code
,
383 Mod2Mask
| LockMask
| mask
,
384 GDK_WINDOW_XID (root
), True
,
385 GrabModeAsync
, GrabModeAsync
);
386 XGrabKey (GDK_DISPLAY (), key_code
,
387 Mod5Mask
| LockMask
| mask
,
388 GDK_WINDOW_XID (root
), True
,
389 GrabModeAsync
, GrabModeAsync
);
390 XGrabKey (GDK_DISPLAY (), key_code
,
391 Mod2Mask
| Mod5Mask
| LockMask
| mask
,
392 GDK_WINDOW_XID (root
), True
,
393 GrabModeAsync
, GrabModeAsync
);
396 if (gdk_error_trap_pop ()) {
397 debug_printf (DEBUG_INFO
, "Error grabbing key %d+%d, %p\n", key_code
, mask
, root
);
403 static GdkFilterReturn
filter_mmkeys (GdkXEvent
*xevent
, GdkEvent
*event
, gpointer data
)
408 unsigned int keystate
;
410 xev
= (XEvent
*) xevent
;
411 if (xev
->type
!= KeyPress
) {
412 return GDK_FILTER_CONTINUE
;
415 key
= (XKeyEvent
*) xevent
;
416 keystate
= key
->state
& ~(Mod2Mask
| Mod5Mask
| LockMask
);
417 for(i
=0; i
< LAST_SIGNAL
;i
++)
419 if(keycodes
[i
] == key
->keycode
&& masks
[i
] == keystate
)
421 g_signal_emit (data
, signals
[i
], 0, 0);
422 debug_printf(DEBUG_INFO
, "%s pressed", keynames
[i
]);
423 return GDK_FILTER_REMOVE
;
427 return GDK_FILTER_CONTINUE
;
431 static void ungrab_mmkey (int key_code
, int mask
, GdkWindow
*root
)
433 gdk_error_trap_push ();
435 XUngrabKey (GDK_DISPLAY (), key_code
,
437 GDK_WINDOW_XID (root
));
438 XUngrabKey (GDK_DISPLAY (), key_code
,
440 GDK_WINDOW_XID (root
));
441 XUngrabKey (GDK_DISPLAY (), key_code
,
443 GDK_WINDOW_XID (root
));
444 XUngrabKey (GDK_DISPLAY (), key_code
,
446 GDK_WINDOW_XID (root
));
447 XUngrabKey (GDK_DISPLAY (), key_code
,
448 Mod2Mask
| Mod5Mask
| mask
,
449 GDK_WINDOW_XID (root
));
450 XUngrabKey (GDK_DISPLAY (), key_code
,
451 Mod2Mask
| LockMask
| mask
,
452 GDK_WINDOW_XID (root
));
453 XUngrabKey (GDK_DISPLAY (), key_code
,
454 Mod5Mask
| LockMask
| mask
,
455 GDK_WINDOW_XID (root
));
456 XUngrabKey (GDK_DISPLAY (), key_code
,
457 Mod2Mask
| Mod5Mask
| LockMask
| mask
,
458 GDK_WINDOW_XID (root
));
461 if (gdk_error_trap_pop ()) {
462 debug_printf (DEBUG_INFO
, "Error ungrabbing key %d+%d, %p\n", key_code
, mask
, root
);
468 void grab_key(int key
, int keycode
, unsigned int mask
)
474 display
= gdk_display_get_default ();
477 if(keycodes
[key
] > 0)
479 for (i
= 0; i
< gdk_display_get_n_screens (display
); i
++) {
480 screen
= gdk_display_get_screen (display
, i
);
481 if (screen
!= NULL
) {
482 root
= gdk_screen_get_root_window (screen
);
483 ungrab_mmkey (keycodes
[key
], masks
[key
], root
);
489 keyerror
[key
] = FALSE
;
492 keycodes
[key
] = keycode
;
495 for (i
= 0; i
< gdk_display_get_n_screens (display
); i
++) {
496 screen
= gdk_display_get_screen (display
, i
);
497 if (screen
!= NULL
) {
498 root
= gdk_screen_get_root_window (screen
);
499 if (!grab_mmkey (keycodes
[key
], masks
[key
], root
))
502 keyerror
[key
] = TRUE
;
507 cfg_set_single_value_as_int(config
,"Keybindings", keynames
[key
], keycodes
[key
]);
508 cfg_set_single_value_as_int(config
,"Keymasks", keynames
[key
], masks
[key
]);
512 * Multimedia key plugin for config panel
514 void mmkeys_pref_destroy(GtkWidget
*container
);
515 void mmkeys_pref_construct(GtkWidget
*container
);
516 GladeXML
*mmkeys_pref_xml
= NULL
;
518 gmpcPrefPlugin mmkeys_gpp
= {
519 .construct
= mmkeys_pref_construct
,
520 .destroy
= mmkeys_pref_destroy
523 gmpcPlugin mmkeys_plug
=
525 . name
= N_("Multimedia Keys"),
527 .plugin_type
= GMPC_INTERNALL
,
528 .pref
= &mmkeys_gpp
/* preferences */
531 static void accel_cleared_callback(GtkCellRendererText
*cell
, const char *path_string
, gpointer data
)
533 GtkTreeModel
*model
= (GtkTreeModel
*)data
;
534 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
538 gtk_tree_model_get_iter (model
, &iter
, path
);
540 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
544 MM_STORE_FOREGROUND
, FG_DEFAULT
,
546 gtk_tree_path_free (path
);
547 gtk_tree_model_get(model
, &iter
, 1, &key
, -1);
552 accel_edited_callback (GtkCellRendererText
*cell
,
553 const char *path_string
,
555 GdkModifierType mask
,
556 guint hardware_keycode
,
559 GtkTreeModel
*model
= (GtkTreeModel
*)data
;
560 GtkTreePath
*path
= gtk_tree_path_new_from_string (path_string
);
565 gtk_tree_model_get_iter (model
, &iter
, path
);
567 debug_printf(DEBUG_INFO
, "EggCellRenderKeys grabbed %d %u", mask
, hardware_keycode
);
568 if(hardware_keycode
== 22)
570 hardware_keycode
= 0;
573 gtk_tree_model_get(model
, &iter
, 1, &key
, -1);
575 /* Check for duplicates */
576 for (i
=0;i
<LAST_SIGNAL
;i
++) {
579 if (hardware_keycode
!= 0 &&
580 keycodes
[i
] == hardware_keycode
&&
584 gchar
*rawkeysym
= egg_virtual_accelerator_name (
585 XKeycodeToKeysym(GDK_DISPLAY(), hardware_keycode
, 0),
586 hardware_keycode
, mask
);
587 gchar
*keysym
= g_markup_escape_text(rawkeysym
, -1);
589 message
= g_strdup_printf( _("<b>Duplicate mapping detected</b>\n\n"
590 "%s is already mapped to %s"),
591 keysym
, _(keynames
[i
]) );
593 show_error_message (message
, TRUE
);
596 /* Clear the duplicate entry */
597 accel_cleared_callback(cell
, path_string
, data
);
602 grab_key(key
, hardware_keycode
, mask
);
603 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
604 MM_STORE_KEYCODE
, hardware_keycode
,
606 MM_STORE_KEYVAL
, keyval
,
607 MM_STORE_FOREGROUND
, keyerror
[key
] ? FG_ERROR
: FG_DEFAULT
,
609 gtk_tree_path_free (path
);
613 gchar
*rawkeysym
= egg_virtual_accelerator_name (
614 XKeycodeToKeysym(GDK_DISPLAY(), keycodes
[key
], 0),
615 keycodes
[key
], masks
[key
]);
616 gchar
*keysym
= g_markup_escape_text(rawkeysym
, -1);
618 message
= g_strdup_printf(
619 _("Could not grab multimedia key:\n\n"
621 "Ensure that your window manager (or other applications) have not already bound this key for some other function, then restart gmpc."),
622 _(keynames
[key
]), keysym
);
624 show_error_message (message
, TRUE
);
636 void mmkeys_pref_destroy(GtkWidget
*container
)
640 GtkWidget
*vbox
= glade_xml_get_widget(mmkeys_pref_xml
, "mmkeys-vbox");
641 gtk_container_remove(GTK_CONTAINER(container
),vbox
);
642 g_object_unref(mmkeys_pref_xml
);
643 mmkeys_pref_xml
= NULL
;
647 void mmkeys_pref_construct(GtkWidget
*container
)
649 gchar
*path
= gmpc_get_full_glade_path("gmpc.glade");
650 mmkeys_pref_xml
= glade_xml_new(path
, "mmkeys-vbox",NULL
);
655 GtkWidget
*vbox
= glade_xml_get_widget(mmkeys_pref_xml
, "mmkeys-vbox");
656 GtkTreeViewColumn
*column
= NULL
;
657 GtkListStore
*store
= gtk_list_store_new(MM_STORE_COUNT
,
658 G_TYPE_STRING
, /* MM_STORE_KEYNAME */
659 G_TYPE_INT
, /* MM_STORE_INDEX */
660 G_TYPE_UINT
, /* MM_STORE_KEYCODE */
661 G_TYPE_UINT
, /* MM_STORE_MASK */
662 G_TYPE_UINT
, /* MM_STORE_KEYVAL */
663 G_TYPE_STRING
/* MM_STORE_FOREGROUND */
665 GtkCellRenderer
*rend
=gtk_cell_renderer_text_new();
666 gtk_tree_view_set_model(GTK_TREE_VIEW (glade_xml_get_widget(mmkeys_pref_xml
, "mmkeys-tree")), GTK_TREE_MODEL(store
));
668 column
= gtk_tree_view_column_new();
669 gtk_tree_view_column_pack_start(column
, rend
, TRUE
);
670 gtk_tree_view_column_add_attribute(column
, rend
,
671 "text", MM_STORE_KEYNAME
);
672 gtk_tree_view_column_set_title(column
, _("Action"));
673 gtk_tree_view_append_column(GTK_TREE_VIEW (glade_xml_get_widget(mmkeys_pref_xml
, "mmkeys-tree")), column
);
675 rend
= egg_cell_renderer_keys_new ();
676 column
= gtk_tree_view_column_new ();
678 /* g_object_set (G_OBJECT (rend), "accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X);*/
679 egg_cell_renderer_keys_set_accel_mode(EGG_CELL_RENDERER_KEYS(rend
), EGG_CELL_RENDERER_KEYS_MODE_GTK
);
680 g_object_set (G_OBJECT (rend
), "editable", TRUE
, NULL
);
682 g_signal_connect (G_OBJECT (rend
),
684 G_CALLBACK (accel_edited_callback
),
687 g_signal_connect (G_OBJECT (rend
),
689 G_CALLBACK (accel_cleared_callback
),
691 gtk_tree_view_column_pack_start (column
, rend
,
693 gtk_tree_view_column_set_title(column
, _("Shortcut"));
694 gtk_tree_view_column_set_attributes (column
, rend
,
695 "keycode", MM_STORE_KEYCODE
,
696 "accel_mask", MM_STORE_MASK
,
697 "accel_key", MM_STORE_KEYVAL
,
698 "foreground", MM_STORE_FOREGROUND
,
700 gtk_tree_view_append_column (GTK_TREE_VIEW (glade_xml_get_widget(mmkeys_pref_xml
, "mmkeys-tree")), column
);
702 gtk_container_add(GTK_CONTAINER(container
),vbox
);
703 for(i
=0;i
< LAST_SIGNAL
;i
++)
706 gtk_list_store_append(store
, &iter
);
707 gtk_list_store_set(store
, &iter
,
708 MM_STORE_KEYNAME
, _(keynames
[i
]),
710 MM_STORE_KEYCODE
, keycodes
[i
],
711 MM_STORE_MASK
, masks
[i
],
712 MM_STORE_KEYVAL
, XKeycodeToKeysym(GDK_DISPLAY(), keycodes
[i
], 0),
713 MM_STORE_FOREGROUND
, keyerror
[i
] ? FG_ERROR
: FG_DEFAULT
,