~ubuntu-branches/ubuntu/precise/libcanberra/precise

« back to all changes in this revision

Viewing changes to .pc/10_no_nonnative_crash.patch/src/canberra-gtk-module.c

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2011-10-17 12:49:05 UTC
  • Revision ID: package-import@ubuntu.com-20111017124905-yhrfb9tsjooqm0hf
Tags: 0.28-0ubuntu11
* Use 3.0 (quilt)
* debian/rules: Move autoreconf before debhelper
* debian/patches/02_nodisplay_autostart.patch:
  - Dropped, we shouldn't hide turning the login sound on or off
    from users (LP: #840858)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-*- Mode: C; c-basic-offset: 8 -*-*/
 
2
 
 
3
/***
 
4
  This file is part of libcanberra.
 
5
 
 
6
  Copyright 2008 Lennart Poettering
 
7
 
 
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.
 
12
 
 
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.
 
17
 
 
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/>.
 
21
***/
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
#include <config.h>
 
25
#endif
 
26
 
 
27
#include <gtk/gtk.h>
 
28
#include <gdk/gdkx.h>
 
29
#include <X11/Xatom.h>
 
30
 
 
31
#include "canberra-gtk.h"
 
32
 
 
33
typedef struct {
 
34
        guint signal_id;
 
35
        gboolean arg1_is_set;
 
36
        GObject *object;
 
37
        GValue arg1;
 
38
        GdkEvent *event;
 
39
} SoundEventData;
 
40
 
 
41
/*
 
42
   We generate these sounds:
 
43
 
 
44
   dialog-error
 
45
   dialog-warning
 
46
   dialog-information
 
47
   dialog-question
 
48
   window-new
 
49
   window-close
 
50
   window-minimized
 
51
   window-unminimized
 
52
   window-maximized
 
53
   window-unmaximized
 
54
   notebook-tab-changed
 
55
   dialog-ok
 
56
   dialog-cancel
 
57
   item-selected
 
58
   link-pressed
 
59
   link-released
 
60
   button-pressed
 
61
   button-released
 
62
   menu-click
 
63
   button-toggle-on
 
64
   button-toggle-off
 
65
   menu-popup
 
66
   menu-popdown
 
67
   menu-replace
 
68
   tooltip-popup
 
69
   tooltip-popdown
 
70
   drag-start
 
71
   drag-accept
 
72
   drag-fail
 
73
   expander-toggle-on
 
74
   expander-toggle-off
 
75
 
 
76
   TODO:
 
77
   scroll-xxx
 
78
   window-switch
 
79
   window-resize-xxx
 
80
   window-move-xxx
 
81
 
 
82
*/
 
83
 
 
84
static gboolean disabled = FALSE;
 
85
 
 
86
static GQueue sound_event_queue = G_QUEUE_INIT;
 
87
 
 
88
static guint idle_id = 0;
 
89
 
 
90
static guint
 
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;
 
107
 
 
108
static GQuark
 
109
        disable_sound_quark,
 
110
        was_iconized_quark,
 
111
        is_xembed_quark;
 
112
 
 
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[]);
 
116
 
 
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
 
124
        };
 
125
 
 
126
        if (mt >= G_N_ELEMENTS(message_type_table))
 
127
                return NULL;
 
128
 
 
129
        return message_type_table[mt];
 
130
}
 
131
 
 
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,
 
145
        };
 
146
 
 
147
        if (response >= 0)
 
148
                return NULL;
 
149
 
 
150
        if ((unsigned) -response >= G_N_ELEMENTS(response_table))
 
151
                return NULL;
 
152
 
 
153
        return response_table[-response];
 
154
}
 
155
 
 
156
static gboolean is_child_of_combo_box(GtkWidget *w) {
 
157
 
 
158
        while (w) {
 
159
 
 
160
                if (GTK_IS_COMBO_BOX(w))
 
161
                        return TRUE;
 
162
 
 
163
                w = gtk_widget_get_parent(w);
 
164
        }
 
165
 
 
166
        return FALSE;
 
167
}
 
168
 
 
169
static GtkDialog* find_parent_dialog(GtkWidget *w) {
 
170
 
 
171
        while (w) {
 
172
 
 
173
                if (GTK_IS_DIALOG(w))
 
174
                        return GTK_DIALOG(w);
 
175
 
 
176
                w = gtk_widget_get_parent(w);
 
177
        }
 
178
 
 
179
        return NULL;
 
180
}
 
181
 
 
182
static void free_sound_event(SoundEventData *d) {
 
183
 
 
184
        g_object_unref(d->object);
 
185
 
 
186
        if (d->arg1_is_set)
 
187
                g_value_unset(&d->arg1);
 
188
 
 
189
        if (d->event)
 
190
                gdk_event_free(d->event);
 
191
 
 
192
        g_slice_free(SoundEventData, d);
 
193
}
 
194
 
 
195
static gboolean is_menu_hint(GdkWindowTypeHint hint) {
 
196
        return
 
197
                hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU ||
 
198
                hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
 
199
                hint == GDK_WINDOW_TYPE_HINT_MENU;
 
200
}
 
201
 
 
202
static SoundEventData* filter_sound_event(SoundEventData *d) {
 
203
        GList *i, *n;
 
204
 
 
205
        do {
 
206
 
 
207
                for (i = sound_event_queue.head; i; i = n) {
 
208
                        SoundEventData *j;
 
209
 
 
210
                        j = i->data;
 
211
                        n = i->next;
 
212
 
 
213
                        if (d->object == j->object) {
 
214
 
 
215
                                /* Let's drop a show event immediately followed by a
 
216
                                 * hide event */
 
217
 
 
218
                                if (d->signal_id == signal_id_widget_show &&
 
219
                                    j->signal_id == signal_id_widget_hide) {
 
220
 
 
221
                                        free_sound_event(d);
 
222
                                        free_sound_event(j);
 
223
                                        g_queue_delete_link(&sound_event_queue, i);
 
224
 
 
225
                                        return NULL;
 
226
                                }
 
227
 
 
228
                                /* Let's drop widget hide events in favour of dialog
 
229
                                 * response.
 
230
                                 *
 
231
                                 * Let's drop widget window state events in favour of
 
232
                                 * widget hide/show.
 
233
                                 *
 
234
                                 * Let's drop double events */
 
235
 
 
236
                                if ((d->signal_id == signal_id_widget_hide &&
 
237
                                     j->signal_id == signal_id_dialog_response) ||
 
238
 
 
239
                                    (d->signal_id == signal_id_widget_window_state_event &&
 
240
                                     j->signal_id == signal_id_widget_hide) ||
 
241
 
 
242
                                    (d->signal_id == signal_id_widget_window_state_event &&
 
243
                                     j->signal_id == signal_id_widget_show)) {
 
244
 
 
245
                                        free_sound_event(d);
 
246
                                        d = j;
 
247
                                        g_queue_delete_link(&sound_event_queue, i);
 
248
                                        break;
 
249
                                }
 
250
 
 
251
                                if ((d->signal_id == signal_id_dialog_response &&
 
252
                                     j->signal_id == signal_id_widget_hide) ||
 
253
 
 
254
                                    (d->signal_id == signal_id_widget_show &&
 
255
                                     j->signal_id == signal_id_widget_window_state_event) ||
 
256
 
 
257
                                    (d->signal_id == signal_id_widget_hide &&
 
258
                                     j->signal_id == signal_id_widget_window_state_event) ||
 
259
 
 
260
                                    (d->signal_id == j->signal_id)) {
 
261
 
 
262
                                        free_sound_event(j);
 
263
                                        g_queue_delete_link(&sound_event_queue, i);
 
264
                                }
 
265
 
 
266
                        } else if (GTK_IS_WINDOW(d->object) && GTK_IS_WINDOW(j->object)) {
 
267
 
 
268
                                GdkWindowTypeHint dhint, jhint;
 
269
 
 
270
                                dhint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
 
271
                                jhint = gtk_window_get_type_hint(GTK_WINDOW(j->object));
 
272
 
 
273
                                if (is_menu_hint(dhint) && is_menu_hint(jhint)) {
 
274
 
 
275
                                        if (d->signal_id == signal_id_widget_hide &&
 
276
                                            j->signal_id == signal_id_widget_show) {
 
277
                                                free_sound_event(d);
 
278
                                                d = j;
 
279
                                                g_queue_delete_link(&sound_event_queue, i);
 
280
                                                break;
 
281
                                        }
 
282
 
 
283
                                        if (d->signal_id == signal_id_widget_show &&
 
284
                                            j->signal_id == signal_id_widget_hide) {
 
285
 
 
286
                                                free_sound_event(j);
 
287
                                                g_queue_delete_link(&sound_event_queue, i);
 
288
                                        }
 
289
                                }
 
290
                        }
 
291
                }
 
292
 
 
293
                /* If we exited the iteration early, let's retry. */
 
294
 
 
295
        } while (i);
 
296
 
 
297
        /* FIXME: Filter menu hide on menu show */
 
298
 
 
299
        return d;
 
300
}
 
301
 
 
302
static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) {
 
303
        Atom type_return;
 
304
        gint format_return;
 
305
        gulong nitems_return;
 
306
        gulong bytes_after_return;
 
307
        guchar *data = NULL;
 
308
        gint ret = -1;
 
309
 
 
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,
 
314
                               &data) != Success)
 
315
                return -1;
 
316
 
 
317
        if (type_return == XA_CARDINAL && format_return == 32 && data) {
 
318
                guint32 desktop = *(guint32*) data;
 
319
 
 
320
                if (desktop != 0xFFFFFFFF)
 
321
                        ret = (gint) desktop;
 
322
        }
 
323
 
 
324
        if (type_return != None && data != NULL)
 
325
                XFree(data);
 
326
 
 
327
        return ret;
 
328
}
 
329
 
 
330
static gint display_get_desktop(GdkDisplay *d) {
 
331
        Atom type_return;
 
332
        gint format_return;
 
333
        gulong nitems_return;
 
334
        gulong bytes_after_return;
 
335
        guchar *data = NULL;
 
336
        gint ret = -1;
 
337
 
 
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,
 
342
                               &data) != Success)
 
343
                return -1;
 
344
 
 
345
        if (type_return == XA_CARDINAL && format_return == 32 && data) {
 
346
 
 
347
                guint32 desktop = *(guint32*) data;
 
348
 
 
349
                if (desktop != 0xFFFFFFFF)
 
350
                        ret = (gint) desktop;
 
351
        }
 
352
 
 
353
        if (type_return != None && data != NULL)
 
354
                XFree(data);
 
355
 
 
356
        return ret;
 
357
}
 
358
 
 
359
static gboolean window_is_xembed(GdkDisplay *d, GdkWindow *w) {
 
360
        Atom type_return;
 
361
        gint format_return;
 
362
        gulong nitems_return;
 
363
        gulong bytes_after_return;
 
364
        guchar *data = NULL;
 
365
        gboolean ret = FALSE;
 
366
        Atom xembed;
 
367
 
 
368
        /* Gnome Panel applets are XEMBED windows. We need to make sure we
 
369
         * ignore them */
 
370
 
 
371
        xembed = gdk_x11_get_xatom_by_name_for_display(d, "_XEMBED_INFO");
 
372
 
 
373
        if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w),
 
374
                               xembed,
 
375
                               0, 2, False, xembed, &type_return,
 
376
                               &format_return, &nitems_return, &bytes_after_return,
 
377
                               &data) != Success) {
 
378
                return FALSE;
 
379
        }
 
380
 
 
381
        if (type_return == xembed && format_return == 32 && data)
 
382
                ret = TRUE;
 
383
 
 
384
        if (type_return != None && data != NULL)
 
385
                XFree(data);
 
386
 
 
387
        return ret;
 
388
}
 
389
 
 
390
static void dispatch_sound_event(SoundEventData *d) {
 
391
        int ret = CA_SUCCESS;
 
392
        static gboolean menu_is_popped_up = FALSE;
 
393
 
 
394
        if (g_object_get_qdata(d->object, disable_sound_quark))
 
395
                return;
 
396
 
 
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) {
 
401
                GdkWindow *window;
 
402
 
 
403
                g_object_unref(G_OBJECT(d->event->any.window));
 
404
 
 
405
                if ((window = gtk_widget_get_window(GTK_WIDGET(d->object))))
 
406
                        d->event->any.window = GDK_WINDOW(g_object_ref(G_OBJECT(window)));
 
407
                else
 
408
                        d->event->any.window = NULL;
 
409
        }
 
410
 
 
411
        if (d->signal_id == signal_id_widget_show) {
 
412
                GdkWindowTypeHint hint;
 
413
 
 
414
                /* Show/hide signals for non-windows have already been filtered out
 
415
                 * by the emission hook! */
 
416
 
 
417
                hint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
 
418
 
 
419
                if (is_menu_hint(hint)) {
 
420
 
 
421
                        if (!menu_is_popped_up) {
 
422
 
 
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",
 
427
                                                             NULL);
 
428
                        } else {
 
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",
 
433
                                                             NULL);
 
434
                        }
 
435
 
 
436
                        menu_is_popped_up = TRUE;
 
437
 
 
438
                } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) {
 
439
 
 
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",
 
444
                                                     NULL);
 
445
 
 
446
                } else if (hint == GDK_WINDOW_TYPE_HINT_NORMAL ||
 
447
                           hint == GDK_WINDOW_TYPE_HINT_DIALOG) {
 
448
 
 
449
                        gboolean played_sound = FALSE;
 
450
                        gboolean is_xembed;
 
451
 
 
452
                        is_xembed =
 
453
                                gtk_widget_get_realized(GTK_WIDGET(d->object)) &&
 
454
                                window_is_xembed(
 
455
                                                gtk_widget_get_display(GTK_WIDGET(d->object)),
 
456
                                                gtk_widget_get_window(GTK_WIDGET(d->object)));
 
457
 
 
458
                        g_object_set_qdata(d->object, is_xembed_quark, GINT_TO_POINTER(is_xembed));
 
459
 
 
460
                        if (GTK_IS_MESSAGE_DIALOG(d->object)) {
 
461
                                GtkMessageType mt;
 
462
                                const char *id;
 
463
 
 
464
                                g_object_get(d->object, "message_type", &mt, NULL);
 
465
 
 
466
                                if ((id = translate_message_tye(mt))) {
 
467
 
 
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",
 
472
                                                                     NULL);
 
473
                                        played_sound = TRUE;
 
474
                                }
 
475
 
 
476
                        }
 
477
 
 
478
                        if (!played_sound &&
 
479
                            !is_xembed &&
 
480
                            gtk_window_get_decorated(GTK_WINDOW(d->object))) {
 
481
 
 
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",
 
486
                                                             NULL);
 
487
 
 
488
                        }
 
489
                }
 
490
        }
 
491
 
 
492
        if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) {
 
493
 
 
494
                int response;
 
495
                const char *id;
 
496
 
 
497
                response = g_value_get_int(&d->arg1);
 
498
 
 
499
                if ((id = translate_response(response))) {
 
500
 
 
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",
 
505
                                                     NULL);
 
506
                } else {
 
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",
 
511
                                                     NULL);
 
512
                }
 
513
 
 
514
        } else if (d->signal_id == signal_id_widget_hide) {
 
515
                GdkWindowTypeHint hint;
 
516
 
 
517
                hint = gtk_window_get_type_hint(GTK_WINDOW(d->object));
 
518
 
 
519
                if (is_menu_hint(hint)) {
 
520
 
 
521
                        if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) {
 
522
 
 
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",
 
527
                                                             NULL);
 
528
                        }
 
529
 
 
530
                        menu_is_popped_up = FALSE;
 
531
 
 
532
                } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) {
 
533
 
 
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",
 
538
                                                     NULL);
 
539
 
 
540
                } else if ((hint == GDK_WINDOW_TYPE_HINT_NORMAL ||
 
541
                            hint == GDK_WINDOW_TYPE_HINT_DIALOG)) {
 
542
 
 
543
                        gboolean is_xembed;
 
544
 
 
545
                        is_xembed = !!g_object_get_qdata(d->object, is_xembed_quark);
 
546
 
 
547
                        if (!is_xembed &&
 
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",
 
553
                                                             NULL);
 
554
                }
 
555
        }
 
556
 
 
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;
 
560
 
 
561
                e = (GdkEventWindowState*) d->event;
 
562
 
 
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. */
 
570
 
 
571
                if (gtk_widget_get_realized(GTK_WIDGET(d->object))) {
 
572
                        GdkDisplay *display;
 
573
 
 
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);
 
577
                }
 
578
 
 
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)) {
 
582
 
 
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",
 
587
                                                     NULL);
 
588
 
 
589
                        g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(1));
 
590
 
 
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))) {
 
593
 
 
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",
 
598
                                                     NULL);
 
599
 
 
600
                        g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0));
 
601
 
 
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)) {
 
605
 
 
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",
 
610
                                                     NULL);
 
611
 
 
612
                        g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0));
 
613
 
 
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))) {
 
616
 
 
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",
 
621
                                                     NULL);
 
622
                }
 
623
        }
 
624
 
 
625
        if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) {
 
626
 
 
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",
 
632
                                                    NULL);
 
633
                else
 
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",
 
638
                                                    NULL);
 
639
 
 
640
        } else if (GTK_IS_MENU_ITEM(d->object) && d->signal_id == signal_id_menu_item_activate) {
 
641
 
 
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",
 
647
                                                    NULL);
 
648
        }
 
649
 
 
650
        if (GTK_IS_TOGGLE_BUTTON(d->object)) {
 
651
 
 
652
                if (d->signal_id == signal_id_toggle_button_toggled) {
 
653
 
 
654
                        if (!is_child_of_combo_box(GTK_WIDGET(d->object))) {
 
655
 
 
656
                                /* We don't want to play this sound if this is a toggle
 
657
                                 * button belonging to combo box. */
 
658
 
 
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",
 
664
                                                                    NULL);
 
665
                                else
 
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",
 
670
                                                                    NULL);
 
671
                        }
 
672
                }
 
673
 
 
674
        } else if (GTK_IS_LINK_BUTTON(d->object)) {
 
675
 
 
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",
 
681
                                                    NULL);
 
682
 
 
683
                } else if (d->signal_id == signal_id_button_released) {
 
684
 
 
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",
 
689
                                                    NULL);
 
690
                }
 
691
 
 
692
        } else if (GTK_IS_BUTTON(d->object) && !GTK_IS_TOGGLE_BUTTON(d->object)) {
 
693
 
 
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",
 
699
                                                    NULL);
 
700
 
 
701
                } else if (d->signal_id == signal_id_button_released) {
 
702
                        GtkDialog *dialog;
 
703
                        gboolean dont_play = FALSE;
 
704
 
 
705
                        if ((dialog = find_parent_dialog(GTK_WIDGET(d->object)))) {
 
706
                                int response;
 
707
 
 
708
                                /* Don't play the click sound if this is a response widget
 
709
                                 * we will generate a dialog-xxx event sound anyway. */
 
710
 
 
711
                                response = gtk_dialog_get_response_for_widget(dialog, GTK_WIDGET(d->object));
 
712
                                dont_play = !!translate_response(response);
 
713
                        }
 
714
 
 
715
                        if (!dont_play)
 
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",
 
720
                                                            NULL);
 
721
                }
 
722
        }
 
723
 
 
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",
 
729
                                            NULL);
 
730
                goto finish;
 
731
        }
 
732
 
 
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",
 
738
                                            NULL);
 
739
                goto finish;
 
740
        }
 
741
 
 
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",
 
747
                                            NULL);
 
748
                goto finish;
 
749
        }
 
750
 
 
751
        if (GTK_IS_EXPANDER(d->object) && d->signal_id == signal_id_expander_activate) {
 
752
 
 
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",
 
758
                                                    NULL);
 
759
                else
 
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",
 
764
                                                    NULL);
 
765
 
 
766
                goto finish;
 
767
        }
 
768
 
 
769
        if (GTK_IS_WIDGET(d->object)) {
 
770
 
 
771
                if (d->signal_id == signal_id_widget_drag_begin) {
 
772
 
 
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",
 
777
                                                    NULL);
 
778
                        goto finish;
 
779
 
 
780
                } else if (d->signal_id == signal_id_widget_drag_drop) {
 
781
 
 
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",
 
786
                                                    NULL);
 
787
                        goto finish;
 
788
 
 
789
                } else if (d->signal_id == signal_id_widget_drag_failed) {
 
790
 
 
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",
 
795
                                                    NULL);
 
796
                        goto finish;
 
797
                }
 
798
        }
 
799
 
 
800
finish:
 
801
 
 
802
        ;
 
803
        /* if (ret != CA_SUCCESS) */
 
804
        /*     g_warning("Failed to play event sound: %s", ca_strerror(ret)); */
 
805
}
 
806
 
 
807
static void dispatch_queue(void) {
 
808
        SoundEventData *d;
 
809
 
 
810
        while ((d = g_queue_pop_head(&sound_event_queue))) {
 
811
 
 
812
                if (!(d = filter_sound_event(d)))
 
813
                        continue;
 
814
 
 
815
                dispatch_sound_event(d);
 
816
                free_sound_event(d);
 
817
        }
 
818
}
 
819
 
 
820
static gboolean idle_cb(void *userdata) {
 
821
        idle_id = 0;
 
822
 
 
823
        dispatch_queue();
 
824
 
 
825
        return FALSE;
 
826
}
 
827
 
 
828
static void connect_settings(void);
 
829
 
 
830
static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_values, const GValue *param_values, gpointer data) {
 
831
        static SoundEventData *d = NULL;
 
832
        GdkEvent *e;
 
833
        GObject *object;
 
834
 
 
835
        connect_settings();
 
836
 
 
837
        if (disabled)
 
838
                return TRUE;
 
839
 
 
840
        object = g_value_get_object(&param_values[0]);
 
841
 
 
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))); */
 
846
 
 
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))); */
 
852
 
 
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))
 
858
                return TRUE;
 
859
 
 
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)))
 
863
                return TRUE;
 
864
 
 
865
        d = g_slice_new0(SoundEventData);
 
866
 
 
867
        d->object = g_object_ref(object);
 
868
 
 
869
        d->signal_id = hint->signal_id;
 
870
 
 
871
        if (d->signal_id == signal_id_widget_window_state_event) {
 
872
                d->event = gdk_event_copy(g_value_peek_pointer(&param_values[1]));
 
873
        } else if ((e = gtk_get_current_event()))
 
874
                d->event = gdk_event_copy(e);
 
875
 
 
876
        if (n_param_values > 1) {
 
877
                g_value_init(&d->arg1, G_VALUE_TYPE(&param_values[1]));
 
878
                g_value_copy(&param_values[1], &d->arg1);
 
879
                d->arg1_is_set = TRUE;
 
880
        }
 
881
 
 
882
        g_queue_push_tail(&sound_event_queue, d);
 
883
 
 
884
        if (idle_id == 0)
 
885
                idle_id = gdk_threads_add_idle_full(GDK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, NULL, NULL);
 
886
 
 
887
        return TRUE;
 
888
}
 
889
 
 
890
static void install_hook(GType type, const char *sig, guint *sn) {
 
891
        GTypeClass *type_class;
 
892
 
 
893
        type_class = g_type_class_ref(type);
 
894
 
 
895
        *sn = g_signal_lookup(sig, type);
 
896
        g_signal_add_emission_hook(*sn, 0, emission_hook_cb, NULL, NULL);
 
897
 
 
898
        g_type_class_unref(type_class);
 
899
}
 
900
 
 
901
static void read_enable_input_feedback_sounds(GtkSettings *s) {
 
902
        gboolean enabled = !disabled;
 
903
 
 
904
        if (g_getenv("CANBERRA_FORCE_INPUT_FEEDBACK_SOUNDS"))
 
905
                disabled = FALSE;
 
906
        else {
 
907
                g_object_get(G_OBJECT(s), "gtk-enable-input-feedback-sounds", &enabled, NULL);
 
908
                disabled = !enabled;
 
909
        }
 
910
}
 
911
 
 
912
static void enable_input_feedback_sounds_changed(GtkSettings *s, GParamSpec *arg1, gpointer userdata) {
 
913
        read_enable_input_feedback_sounds(s);
 
914
}
 
915
 
 
916
static void connect_settings(void) {
 
917
        GtkSettings *s;
 
918
        static gboolean connected = FALSE;
 
919
 
 
920
        if (connected)
 
921
                return;
 
922
 
 
923
        if (!(s = gtk_settings_get_default()))
 
924
                return;
 
925
 
 
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);
 
929
        } else
 
930
                g_debug("This Gtk+ version doesn't have the GtkSettings::gtk-enable-input-feedback-sounds property.");
 
931
 
 
932
        connected = TRUE;
 
933
}
 
934
 
 
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"
 
937
#else
 
938
static gboolean quit_handler(gpointer data) {
 
939
        dispatch_queue();
 
940
        return FALSE;
 
941
}
 
942
#endif
 
943
 
 
944
G_MODULE_EXPORT void gtk_module_init(gint *argc, gchar ***argv[]) {
 
945
 
 
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");
 
950
 
 
951
        /* Hook up the gtk setting */
 
952
        connect_settings();
 
953
 
 
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);
 
970
 
 
971
#if !GTK_CHECK_VERSION(3,0,0)
 
972
        gtk_quit_add(1, quit_handler, NULL);
 
973
#endif
 
974
}
 
975
 
 
976
G_MODULE_EXPORT gchar* g_module_check_init(GModule *module);
 
977
 
 
978
G_MODULE_EXPORT gchar* g_module_check_init(GModule *module) {
 
979
        g_module_make_resident(module);
 
980
        return NULL;
 
981
}