1
/*-*- Mode: C; c-basic-offset: 8 -*-*/
4
This file is part of libcanberra.
6
Copyright 2008 Lennart Poettering
8
libcanberra is free software; you can redistribute it and/or modify
9
it under the terms of the GNU Lesser General Public License as
10
published by the Free Software Foundation, either version 2.1 of the
11
License, or (at your option) any later version.
13
libcanberra is distributed in the hope that it will be useful, but
14
WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
Lesser General Public License for more details.
18
You should have received a copy of the GNU Lesser General Public
19
License along with libcanberra. If not, see
20
<http://www.gnu.org/licenses/>.
29
#include <X11/Xatom.h>
31
#include "canberra-gtk.h"
42
We generate these sounds:
84
static gboolean disabled = FALSE;
86
static GQueue sound_event_queue = G_QUEUE_INIT;
88
static guint idle_id = 0;
91
signal_id_dialog_response,
92
signal_id_widget_show,
93
signal_id_widget_hide,
94
signal_id_check_menu_item_toggled,
95
signal_id_menu_item_activate,
96
signal_id_toggle_button_toggled,
97
signal_id_button_pressed,
98
signal_id_button_released,
99
signal_id_widget_window_state_event,
100
signal_id_notebook_switch_page,
101
signal_id_tree_view_cursor_changed,
102
signal_id_icon_view_selection_changed,
103
signal_id_widget_drag_begin,
104
signal_id_widget_drag_failed,
105
signal_id_widget_drag_drop,
106
signal_id_expander_activate;
113
/* Make sure GCC doesn't warn us about a missing prototype for this
114
* exported function */
115
void gtk_module_init(gint *argc, gchar ***argv[]);
117
static const char *translate_message_tye(GtkMessageType mt) {
118
static const char *const message_type_table[] = {
119
[GTK_MESSAGE_INFO] = "dialog-information",
120
[GTK_MESSAGE_WARNING] = "dialog-warning",
121
[GTK_MESSAGE_QUESTION] = "dialog-question",
122
[GTK_MESSAGE_ERROR] = "dialog-error",
123
[GTK_MESSAGE_OTHER] = NULL
126
if (mt >= G_N_ELEMENTS(message_type_table))
129
return message_type_table[mt];
132
static const char *translate_response(int response) {
133
static const char *const response_table[] = {
134
[-GTK_RESPONSE_NONE] = NULL,
135
[-GTK_RESPONSE_REJECT] = "dialog-cancel",
136
[-GTK_RESPONSE_DELETE_EVENT] = "dialog-cancel",
137
[-GTK_RESPONSE_ACCEPT] = "dialog-ok",
138
[-GTK_RESPONSE_OK] = "dialog-ok",
139
[-GTK_RESPONSE_CANCEL] = "dialog-cancel",
140
[-GTK_RESPONSE_CLOSE] = "dialog-ok",
141
[-GTK_RESPONSE_YES] = "dialog-ok",
142
[-GTK_RESPONSE_NO] = "dialog-cancel",
143
[-GTK_RESPONSE_APPLY] = "dialog-ok",
144
[-GTK_RESPONSE_HELP] = NULL,
150
if ((unsigned) -response >= G_N_ELEMENTS(response_table))
153
return response_table[-response];
156
static gboolean is_child_of_combo_box(GtkWidget *w) {
160
if (GTK_IS_COMBO_BOX(w))
163
w = gtk_widget_get_parent(w);
169
static GtkDialog* find_parent_dialog(GtkWidget *w) {
173
if (GTK_IS_DIALOG(w))
174
return GTK_DIALOG(w);
176
w = gtk_widget_get_parent(w);
182
static void free_sound_event(SoundEventData *d) {
184
g_object_unref(d->object);
187
g_value_unset(&d->arg1);
190
gdk_event_free(d->event);
192
g_slice_free(SoundEventData, d);
195
static gboolean is_menu_hint(GdkWindowTypeHint hint) {
197
hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU ||
198
hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
199
hint == GDK_WINDOW_TYPE_HINT_MENU;
202
static SoundEventData* filter_sound_event(SoundEventData *d) {
207
for (i = sound_event_queue.head; i; i = n) {
213
if (d->object == j->object) {
215
/* Let's drop a show event immediately followed by a
218
if (d->signal_id == signal_id_widget_show &&
219
j->signal_id == signal_id_widget_hide) {
223
g_queue_delete_link(&sound_event_queue, i);
228
/* Let's drop widget hide events in favour of dialog
231
* Let's drop widget window state events in favour of
234
* Let's drop double events */
236
if ((d->signal_id == signal_id_widget_hide &&
237
j->signal_id == signal_id_dialog_response) ||
239
(d->signal_id == signal_id_widget_window_state_event &&
240
j->signal_id == signal_id_widget_hide) ||
242
(d->signal_id == signal_id_widget_window_state_event &&
243
j->signal_id == signal_id_widget_show)) {
247
g_queue_delete_link(&sound_event_queue, i);
251
if ((d->signal_id == signal_id_dialog_response &&
252
j->signal_id == signal_id_widget_hide) ||
254
(d->signal_id == signal_id_widget_show &&
255
j->signal_id == signal_id_widget_window_state_event) ||
257
(d->signal_id == signal_id_widget_hide &&
258
j->signal_id == signal_id_widget_window_state_event) ||
260
(d->signal_id == j->signal_id)) {
263
g_queue_delete_link(&sound_event_queue, i);
266
} else if (GTK_IS_WINDOW(d->object) && GTK_IS_WINDOW(j->object)) {
268
GdkWindowTypeHint dhint, jhint;
270
dhint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
271
jhint = gtk_window_get_type_hint(GTK_WINDOW(j->object));
273
if (is_menu_hint(dhint) && is_menu_hint(jhint)) {
275
if (d->signal_id == signal_id_widget_hide &&
276
j->signal_id == signal_id_widget_show) {
279
g_queue_delete_link(&sound_event_queue, i);
283
if (d->signal_id == signal_id_widget_show &&
284
j->signal_id == signal_id_widget_hide) {
287
g_queue_delete_link(&sound_event_queue, i);
293
/* If we exited the iteration early, let's retry. */
297
/* FIXME: Filter menu hide on menu show */
302
static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) {
305
gulong nitems_return;
306
gulong bytes_after_return;
310
if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w),
311
gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"),
312
0, G_MAXLONG, False, XA_CARDINAL, &type_return,
313
&format_return, &nitems_return, &bytes_after_return,
317
if (type_return == XA_CARDINAL && format_return == 32 && data) {
318
guint32 desktop = *(guint32*) data;
320
if (desktop != 0xFFFFFFFF)
321
ret = (gint) desktop;
324
if (type_return != None && data != NULL)
330
static gint display_get_desktop(GdkDisplay *d) {
333
gulong nitems_return;
334
gulong bytes_after_return;
338
if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), DefaultRootWindow(GDK_DISPLAY_XDISPLAY(d)),
339
gdk_x11_get_xatom_by_name_for_display(d, "_NET_CURRENT_DESKTOP"),
340
0, G_MAXLONG, False, XA_CARDINAL, &type_return,
341
&format_return, &nitems_return, &bytes_after_return,
345
if (type_return == XA_CARDINAL && format_return == 32 && data) {
347
guint32 desktop = *(guint32*) data;
349
if (desktop != 0xFFFFFFFF)
350
ret = (gint) desktop;
353
if (type_return != None && data != NULL)
359
static gboolean window_is_xembed(GdkDisplay *d, GdkWindow *w) {
362
gulong nitems_return;
363
gulong bytes_after_return;
365
gboolean ret = FALSE;
368
/* Gnome Panel applets are XEMBED windows. We need to make sure we
371
xembed = gdk_x11_get_xatom_by_name_for_display(d, "_XEMBED_INFO");
373
if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w),
375
0, 2, False, xembed, &type_return,
376
&format_return, &nitems_return, &bytes_after_return,
381
if (type_return == xembed && format_return == 32 && data)
384
if (type_return != None && data != NULL)
390
static void dispatch_sound_event(SoundEventData *d) {
391
int ret = CA_SUCCESS;
392
static gboolean menu_is_popped_up = FALSE;
394
if (g_object_get_qdata(d->object, disable_sound_quark))
397
/* The GdkWindow of the the widget might have changed while this
398
* event was queued for us. Make sure to update it from the
399
* current one if necessary. */
400
if (d->event && d->event->any.window) {
403
g_object_unref(G_OBJECT(d->event->any.window));
405
if ((window = gtk_widget_get_window(GTK_WIDGET(d->object))))
406
d->event->any.window = GDK_WINDOW(g_object_ref(G_OBJECT(window)));
408
d->event->any.window = NULL;
411
if (d->signal_id == signal_id_widget_show) {
412
GdkWindowTypeHint hint;
414
/* Show/hide signals for non-windows have already been filtered out
415
* by the emission hook! */
417
hint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
419
if (is_menu_hint(hint)) {
421
if (!menu_is_popped_up) {
423
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
424
CA_PROP_EVENT_ID, "menu-popup",
425
CA_PROP_EVENT_DESCRIPTION, "Menu popped up",
426
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
429
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
430
CA_PROP_EVENT_ID, "menu-replace",
431
CA_PROP_EVENT_DESCRIPTION, "Menu replaced",
432
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
436
menu_is_popped_up = TRUE;
438
} else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) {
440
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
441
CA_PROP_EVENT_ID, "tooltip-popup",
442
CA_PROP_EVENT_DESCRIPTION, "Tooltip popped up",
443
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
446
} else if (hint == GDK_WINDOW_TYPE_HINT_NORMAL ||
447
hint == GDK_WINDOW_TYPE_HINT_DIALOG) {
449
gboolean played_sound = FALSE;
453
gtk_widget_get_realized(GTK_WIDGET(d->object)) &&
455
gtk_widget_get_display(GTK_WIDGET(d->object)),
456
gtk_widget_get_window(GTK_WIDGET(d->object)));
458
g_object_set_qdata(d->object, is_xembed_quark, GINT_TO_POINTER(is_xembed));
460
if (GTK_IS_MESSAGE_DIALOG(d->object)) {
464
g_object_get(d->object, "message_type", &mt, NULL);
466
if ((id = translate_message_tye(mt))) {
468
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
469
CA_PROP_EVENT_ID, id,
470
CA_PROP_EVENT_DESCRIPTION, "Message dialog shown",
471
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
480
gtk_window_get_decorated(GTK_WINDOW(d->object))) {
482
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
483
CA_PROP_EVENT_ID, "window-new",
484
CA_PROP_EVENT_DESCRIPTION, "Window shown",
485
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
492
if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) {
497
response = g_value_get_int(&d->arg1);
499
if ((id = translate_response(response))) {
501
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
502
CA_PROP_EVENT_ID, id,
503
CA_PROP_EVENT_DESCRIPTION, "Dialog closed",
504
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
507
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
508
CA_PROP_EVENT_ID, "window-close",
509
CA_PROP_EVENT_DESCRIPTION, "Window closed",
510
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
514
} else if (d->signal_id == signal_id_widget_hide) {
515
GdkWindowTypeHint hint;
517
hint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
519
if (is_menu_hint(hint)) {
521
if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) {
523
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
524
CA_PROP_EVENT_ID, "menu-popdown",
525
CA_PROP_EVENT_DESCRIPTION, "Menu popped down",
526
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
530
menu_is_popped_up = FALSE;
532
} else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) {
534
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
535
CA_PROP_EVENT_ID, "tooltip-popdown",
536
CA_PROP_EVENT_DESCRIPTION, "Tooltip popped down",
537
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
540
} else if ((hint == GDK_WINDOW_TYPE_HINT_NORMAL ||
541
hint == GDK_WINDOW_TYPE_HINT_DIALOG)) {
545
is_xembed = !!g_object_get_qdata(d->object, is_xembed_quark);
548
gtk_window_get_decorated(GTK_WINDOW(d->object)))
549
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
550
CA_PROP_EVENT_ID, "window-close",
551
CA_PROP_EVENT_DESCRIPTION, "Window closed",
552
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
557
if (GTK_IS_WINDOW(d->object) && d->signal_id == signal_id_widget_window_state_event) {
558
GdkEventWindowState *e;
559
gint w_desktop = -1, c_desktop = -1;
561
e = (GdkEventWindowState*) d->event;
563
/* Unfortunately GDK_WINDOW_STATE_ICONIFIED is used both for
564
* proper minimizing and when a window becomes invisible
565
* because the desktop was switched. To handle this we check
566
* if the window becoming invisible is actually on the current
567
* desktop, and only if that's the case we assume it is being
568
* minimized. We then store this information, so that we know
569
* later on when the window is unminimized again. */
571
if (gtk_widget_get_realized(GTK_WIDGET(d->object))) {
574
display = gtk_widget_get_display(GTK_WIDGET(d->object));
575
w_desktop = window_get_desktop(display, gtk_widget_get_window(GTK_WIDGET(d->object)));
576
c_desktop = display_get_desktop(display);
579
if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) &&
580
(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) &&
581
(w_desktop == c_desktop || w_desktop < 0)) {
583
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
584
CA_PROP_EVENT_ID, "window-minimized",
585
CA_PROP_EVENT_DESCRIPTION, "Window minimized",
586
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
589
g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(1));
591
} else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) &&
592
(e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) {
594
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
595
CA_PROP_EVENT_ID, "window-maximized",
596
CA_PROP_EVENT_DESCRIPTION, "Window maximized",
597
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
600
g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0));
602
} else if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) &&
603
!(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) &&
604
g_object_get_qdata(d->object, was_iconized_quark)) {
606
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
607
CA_PROP_EVENT_ID, "window-unminimized",
608
CA_PROP_EVENT_DESCRIPTION, "Window unminimized",
609
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
612
g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0));
614
} else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) &&
615
!(e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) {
617
ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0,
618
CA_PROP_EVENT_ID, "window-unmaximized",
619
CA_PROP_EVENT_DESCRIPTION, "Window unmaximized",
620
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
625
if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) {
627
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d->object)))
628
ret = ca_gtk_play_for_event(d->event, 0,
629
CA_PROP_EVENT_ID, "button-toggle-on",
630
CA_PROP_EVENT_DESCRIPTION, "Check menu item checked",
631
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
634
ret = ca_gtk_play_for_event(d->event, 0,
635
CA_PROP_EVENT_ID, "button-toggle-off",
636
CA_PROP_EVENT_DESCRIPTION, "Check menu item unchecked",
637
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
640
} else if (GTK_IS_MENU_ITEM(d->object) && d->signal_id == signal_id_menu_item_activate) {
642
if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(d->object)))
643
ret = ca_gtk_play_for_event(d->event, 0,
644
CA_PROP_EVENT_ID, "menu-click",
645
CA_PROP_EVENT_DESCRIPTION, "Menu item clicked",
646
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
650
if (GTK_IS_TOGGLE_BUTTON(d->object)) {
652
if (d->signal_id == signal_id_toggle_button_toggled) {
654
if (!is_child_of_combo_box(GTK_WIDGET(d->object))) {
656
/* We don't want to play this sound if this is a toggle
657
* button belonging to combo box. */
659
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->object)))
660
ret = ca_gtk_play_for_event(d->event, 0,
661
CA_PROP_EVENT_ID, "button-toggle-on",
662
CA_PROP_EVENT_DESCRIPTION, "Toggle button checked",
663
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
666
ret = ca_gtk_play_for_event(d->event, 0,
667
CA_PROP_EVENT_ID, "button-toggle-off",
668
CA_PROP_EVENT_DESCRIPTION, "Toggle button unchecked",
669
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
674
} else if (GTK_IS_LINK_BUTTON(d->object)) {
676
if (d->signal_id == signal_id_button_pressed) {
677
ret = ca_gtk_play_for_event(d->event, 0,
678
CA_PROP_EVENT_ID, "link-pressed",
679
CA_PROP_EVENT_DESCRIPTION, "Link pressed",
680
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
683
} else if (d->signal_id == signal_id_button_released) {
685
ret = ca_gtk_play_for_event(d->event, 0,
686
CA_PROP_EVENT_ID, "link-released",
687
CA_PROP_EVENT_DESCRIPTION, "Link released",
688
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
692
} else if (GTK_IS_BUTTON(d->object) && !GTK_IS_TOGGLE_BUTTON(d->object)) {
694
if (d->signal_id == signal_id_button_pressed) {
695
ret = ca_gtk_play_for_event(d->event, 0,
696
CA_PROP_EVENT_ID, "button-pressed",
697
CA_PROP_EVENT_DESCRIPTION, "Button pressed",
698
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
701
} else if (d->signal_id == signal_id_button_released) {
703
gboolean dont_play = FALSE;
705
if ((dialog = find_parent_dialog(GTK_WIDGET(d->object)))) {
708
/* Don't play the click sound if this is a response widget
709
* we will generate a dialog-xxx event sound anyway. */
711
response = gtk_dialog_get_response_for_widget(dialog, GTK_WIDGET(d->object));
712
dont_play = !!translate_response(response);
716
ret = ca_gtk_play_for_event(d->event, 0,
717
CA_PROP_EVENT_ID, "button-released",
718
CA_PROP_EVENT_DESCRIPTION, "Button released",
719
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
724
if (GTK_IS_NOTEBOOK(d->object) && d->signal_id == signal_id_notebook_switch_page) {
725
ret = ca_gtk_play_for_event(d->event, 0,
726
CA_PROP_EVENT_ID, "notebook-tab-changed",
727
CA_PROP_EVENT_DESCRIPTION, "Tab changed",
728
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
733
if (GTK_IS_TREE_VIEW(d->object) && d->signal_id == signal_id_tree_view_cursor_changed) {
734
ret = ca_gtk_play_for_event(d->event, 0,
735
CA_PROP_EVENT_ID, "item-selected",
736
CA_PROP_EVENT_DESCRIPTION, "Item selected",
737
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
742
if (GTK_IS_ICON_VIEW(d->object) && d->signal_id == signal_id_icon_view_selection_changed) {
743
ret = ca_gtk_play_for_event(d->event, 0,
744
CA_PROP_EVENT_ID, "item-selected",
745
CA_PROP_EVENT_DESCRIPTION, "Item selected",
746
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
751
if (GTK_IS_EXPANDER(d->object) && d->signal_id == signal_id_expander_activate) {
753
if (gtk_expander_get_expanded(GTK_EXPANDER(d->object)))
754
ret = ca_gtk_play_for_event(d->event, 0,
755
CA_PROP_EVENT_ID, "expander-toggle-on",
756
CA_PROP_EVENT_DESCRIPTION, "Expander expanded",
757
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
760
ret = ca_gtk_play_for_event(d->event, 0,
761
CA_PROP_EVENT_ID, "expander-toggle-off",
762
CA_PROP_EVENT_DESCRIPTION, "Expander unexpanded",
763
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
769
if (GTK_IS_WIDGET(d->object)) {
771
if (d->signal_id == signal_id_widget_drag_begin) {
773
ret = ca_gtk_play_for_event(d->event, 0,
774
CA_PROP_EVENT_ID, "drag-start",
775
CA_PROP_EVENT_DESCRIPTION, "Drag started",
776
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
780
} else if (d->signal_id == signal_id_widget_drag_drop) {
782
ret = ca_gtk_play_for_event(d->event, 0,
783
CA_PROP_EVENT_ID, "drag-accept",
784
CA_PROP_EVENT_DESCRIPTION, "Drag accepted",
785
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
789
} else if (d->signal_id == signal_id_widget_drag_failed) {
791
ret = ca_gtk_play_for_event(d->event, 0,
792
CA_PROP_EVENT_ID, "drag-fail",
793
CA_PROP_EVENT_DESCRIPTION, "Drag failed",
794
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
803
/* if (ret != CA_SUCCESS) */
804
/* g_warning("Failed to play event sound: %s", ca_strerror(ret)); */
807
static void dispatch_queue(void) {
810
while ((d = g_queue_pop_head(&sound_event_queue))) {
812
if (!(d = filter_sound_event(d)))
815
dispatch_sound_event(d);
820
static gboolean idle_cb(void *userdata) {
828
static void connect_settings(void);
830
static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_values, const GValue *param_values, gpointer data) {
831
static SoundEventData *d = NULL;
840
object = g_value_get_object(¶m_values[0]);
842
/* g_message("signal '%s' on object of type '%s' with name '%s'", */
843
/* g_signal_name(hint->signal_id), */
844
/* G_OBJECT_TYPE_NAME(object), */
845
/* gtk_widget_get_name(GTK_WIDGET(object))); */
847
/* if (GTK_IS_WINDOW(object)) */
848
/* g_message("window role='%s' title='%s' type='%u'", */
849
/* gtk_window_get_role(GTK_WINDOW(object)), */
850
/* gtk_window_get_title(GTK_WINDOW(object)), */
851
/* gtk_window_get_type_hint(GTK_WINDOW(object))); */
853
/* Filter a few very often occuring signals as quickly as possible */
854
if ((hint->signal_id == signal_id_widget_hide ||
855
hint->signal_id == signal_id_widget_show ||
856
hint->signal_id == signal_id_widget_window_state_event) &&
857
!GTK_IS_WINDOW(object))
860
if (hint->signal_id != signal_id_widget_hide &&
861
hint->signal_id != signal_id_dialog_response &&
862
!gtk_widget_is_drawable(GTK_WIDGET (object)))
865
d = g_slice_new0(SoundEventData);
867
d->object = g_object_ref(object);
869
d->signal_id = hint->signal_id;
871
if (d->signal_id == signal_id_widget_window_state_event) {
872
d->event = gdk_event_copy(g_value_peek_pointer(¶m_values[1]));
873
} else if ((e = gtk_get_current_event()))
874
d->event = gdk_event_copy(e);
876
if (n_param_values > 1) {
877
g_value_init(&d->arg1, G_VALUE_TYPE(¶m_values[1]));
878
g_value_copy(¶m_values[1], &d->arg1);
879
d->arg1_is_set = TRUE;
882
g_queue_push_tail(&sound_event_queue, d);
885
idle_id = gdk_threads_add_idle_full(GDK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, NULL, NULL);
890
static void install_hook(GType type, const char *sig, guint *sn) {
891
GTypeClass *type_class;
893
type_class = g_type_class_ref(type);
895
*sn = g_signal_lookup(sig, type);
896
g_signal_add_emission_hook(*sn, 0, emission_hook_cb, NULL, NULL);
898
g_type_class_unref(type_class);
901
static void read_enable_input_feedback_sounds(GtkSettings *s) {
902
gboolean enabled = !disabled;
904
if (g_getenv("CANBERRA_FORCE_INPUT_FEEDBACK_SOUNDS"))
907
g_object_get(G_OBJECT(s), "gtk-enable-input-feedback-sounds", &enabled, NULL);
912
static void enable_input_feedback_sounds_changed(GtkSettings *s, GParamSpec *arg1, gpointer userdata) {
913
read_enable_input_feedback_sounds(s);
916
static void connect_settings(void) {
918
static gboolean connected = FALSE;
923
if (!(s = gtk_settings_get_default()))
926
if (g_object_class_find_property(G_OBJECT_GET_CLASS(s), "gtk-enable-input-feedback-sounds")) {
927
g_signal_connect(G_OBJECT(s), "notify::gtk-enable-input-feedback-sounds", G_CALLBACK(enable_input_feedback_sounds_changed), NULL);
928
read_enable_input_feedback_sounds(s);
930
g_debug("This Gtk+ version doesn't have the GtkSettings::gtk-enable-input-feedback-sounds property.");
935
#if GTK_CHECK_VERSION(3,0,0)
936
#warning "We really need a quit handler in Gtk 3.0, https://bugzilla.gnome.org/show_bug.cgi?id=639770"
938
static gboolean quit_handler(gpointer data) {
944
G_MODULE_EXPORT void gtk_module_init(gint *argc, gchar ***argv[]) {
946
/* This is the same quark libgnomeui uses! */
947
disable_sound_quark = g_quark_from_string("gnome_disable_sound_events");
948
was_iconized_quark = g_quark_from_string("canberra_was_iconized");
949
is_xembed_quark = g_quark_from_string("canberra_is_xembed");
951
/* Hook up the gtk setting */
954
install_hook(GTK_TYPE_WINDOW, "show", &signal_id_widget_show);
955
install_hook(GTK_TYPE_WINDOW, "hide", &signal_id_widget_hide);
956
install_hook(GTK_TYPE_DIALOG, "response", &signal_id_dialog_response);
957
install_hook(GTK_TYPE_MENU_ITEM, "activate", &signal_id_menu_item_activate);
958
install_hook(GTK_TYPE_CHECK_MENU_ITEM, "toggled", &signal_id_check_menu_item_toggled);
959
install_hook(GTK_TYPE_TOGGLE_BUTTON, "toggled", &signal_id_toggle_button_toggled);
960
install_hook(GTK_TYPE_BUTTON, "pressed", &signal_id_button_pressed);
961
install_hook(GTK_TYPE_BUTTON, "released", &signal_id_button_released);
962
install_hook(GTK_TYPE_WIDGET, "window-state-event", &signal_id_widget_window_state_event);
963
install_hook(GTK_TYPE_NOTEBOOK, "switch-page", &signal_id_notebook_switch_page);
964
install_hook(GTK_TYPE_TREE_VIEW, "cursor-changed", &signal_id_tree_view_cursor_changed);
965
install_hook(GTK_TYPE_ICON_VIEW, "selection-changed", &signal_id_icon_view_selection_changed);
966
install_hook(GTK_TYPE_WIDGET, "drag-begin", &signal_id_widget_drag_begin);
967
install_hook(GTK_TYPE_WIDGET, "drag-drop", &signal_id_widget_drag_drop);
968
install_hook(GTK_TYPE_WIDGET, "drag-failed", &signal_id_widget_drag_failed);
969
install_hook(GTK_TYPE_EXPANDER, "activate", &signal_id_expander_activate);
971
#if !GTK_CHECK_VERSION(3,0,0)
972
gtk_quit_add(1, quit_handler, NULL);
976
G_MODULE_EXPORT gchar* g_module_check_init(GModule *module);
978
G_MODULE_EXPORT gchar* g_module_check_init(GModule *module) {
979
g_module_make_resident(module);