Merge lp:~3v1n0/ubuntu/vivid/libcanberra/fix-mir-crash into lp:ubuntu/vivid/libcanberra
- Vivid (15.04)
- fix-mir-crash
- Merge into vivid
Proposed by
Marco Trevisan (Treviño)
Status: | Merged | ||||
---|---|---|---|---|---|
Merge reported by: | Sebastien Bacher | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~3v1n0/ubuntu/vivid/libcanberra/fix-mir-crash | ||||
Merge into: | lp:ubuntu/vivid/libcanberra | ||||
Diff against target: |
1704 lines (+1628/-0) 8 files modified
.pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk-module.c (+990/-0) .pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk.c (+544/-0) .pc/applied-patches (+1/-0) debian/changelog (+7/-0) debian/patches/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch (+65/-0) debian/patches/series (+1/-0) src/canberra-gtk-module.c (+15/-0) src/canberra-gtk.c (+5/-0) |
||||
To merge this branch: | bzr merge lp:~3v1n0/ubuntu/vivid/libcanberra/fix-mir-crash | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sebastien Bacher | Approve | ||
Review via email: mp+244481@code.launchpad.net |
Commit message
gtk: don't assume all GdkDisplay's are GdkX11Display's
Fixes crash on Mir.
Description of the change
Backporting upstream commit to ubuntu, to get canberra properly work in Mir.
http://
To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote : | # |
Revision history for this message
Sebastien Bacher (seb128) wrote : | # |
ignore previous comment, I focussed on the ifdef, the useful code is in between those ;-)
review:
Approve
Revision history for this message
Sebastien Bacher (seb128) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory '.pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch' |
2 | === added directory '.pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src' |
3 | === added file '.pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk-module.c' |
4 | --- .pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk-module.c 1970-01-01 00:00:00 +0000 |
5 | +++ .pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk-module.c 2014-12-11 18:09:28 +0000 |
6 | @@ -0,0 +1,990 @@ |
7 | +/*-*- Mode: C; c-basic-offset: 8 -*-*/ |
8 | + |
9 | +/*** |
10 | + This file is part of libcanberra. |
11 | + |
12 | + Copyright 2008 Lennart Poettering |
13 | + |
14 | + libcanberra is free software; you can redistribute it and/or modify |
15 | + it under the terms of the GNU Lesser General Public License as |
16 | + published by the Free Software Foundation, either version 2.1 of the |
17 | + License, or (at your option) any later version. |
18 | + |
19 | + libcanberra is distributed in the hope that it will be useful, but |
20 | + WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 | + Lesser General Public License for more details. |
23 | + |
24 | + You should have received a copy of the GNU Lesser General Public |
25 | + License along with libcanberra. If not, see |
26 | + <http://www.gnu.org/licenses/>. |
27 | +***/ |
28 | + |
29 | +#ifdef HAVE_CONFIG_H |
30 | +#include <config.h> |
31 | +#endif |
32 | + |
33 | +#include <gtk/gtk.h> |
34 | +#include <gdk/gdkx.h> |
35 | +#include <X11/Xatom.h> |
36 | + |
37 | +#include "canberra-gtk.h" |
38 | + |
39 | +typedef struct { |
40 | + guint signal_id; |
41 | + gboolean arg1_is_set; |
42 | + GObject *object; |
43 | + GValue arg1; |
44 | + GdkEvent *event; |
45 | +} SoundEventData; |
46 | + |
47 | +/* |
48 | + We generate these sounds: |
49 | + |
50 | + dialog-error |
51 | + dialog-warning |
52 | + dialog-information |
53 | + dialog-question |
54 | + window-new |
55 | + window-close |
56 | + window-minimized |
57 | + window-unminimized |
58 | + window-maximized |
59 | + window-unmaximized |
60 | + notebook-tab-changed |
61 | + dialog-ok |
62 | + dialog-cancel |
63 | + item-selected |
64 | + link-pressed |
65 | + link-released |
66 | + button-pressed |
67 | + button-released |
68 | + menu-click |
69 | + button-toggle-on |
70 | + button-toggle-off |
71 | + menu-popup |
72 | + menu-popdown |
73 | + menu-replace |
74 | + tooltip-popup |
75 | + tooltip-popdown |
76 | + drag-start |
77 | + drag-accept |
78 | + drag-fail |
79 | + expander-toggle-on |
80 | + expander-toggle-off |
81 | + |
82 | + TODO: |
83 | + scroll-xxx |
84 | + window-switch |
85 | + window-resize-xxx |
86 | + window-move-xxx |
87 | + |
88 | +*/ |
89 | + |
90 | +static gboolean disabled = FALSE; |
91 | + |
92 | +static GQueue sound_event_queue = G_QUEUE_INIT; |
93 | + |
94 | +static guint idle_id = 0; |
95 | + |
96 | +static guint |
97 | + signal_id_dialog_response, |
98 | + signal_id_widget_show, |
99 | + signal_id_widget_hide, |
100 | + signal_id_check_menu_item_toggled, |
101 | + signal_id_menu_item_activate, |
102 | + signal_id_toggle_button_toggled, |
103 | + signal_id_button_pressed, |
104 | + signal_id_button_released, |
105 | + signal_id_widget_window_state_event, |
106 | + signal_id_notebook_switch_page, |
107 | + signal_id_tree_view_cursor_changed, |
108 | + signal_id_icon_view_selection_changed, |
109 | + signal_id_widget_drag_begin, |
110 | + signal_id_widget_drag_failed, |
111 | + signal_id_widget_drag_drop, |
112 | + signal_id_expander_activate; |
113 | + |
114 | +static GQuark |
115 | + disable_sound_quark, |
116 | + was_iconized_quark, |
117 | + is_xembed_quark; |
118 | + |
119 | +/* Make sure GCC doesn't warn us about a missing prototype for this |
120 | + * exported function */ |
121 | +void gtk_module_init(gint *argc, gchar ***argv[]); |
122 | + |
123 | +static const char *translate_message_tye(GtkMessageType mt) { |
124 | + static const char *const message_type_table[] = { |
125 | + [GTK_MESSAGE_INFO] = "dialog-information", |
126 | + [GTK_MESSAGE_WARNING] = "dialog-warning", |
127 | + [GTK_MESSAGE_QUESTION] = "dialog-question", |
128 | + [GTK_MESSAGE_ERROR] = "dialog-error", |
129 | + [GTK_MESSAGE_OTHER] = NULL |
130 | + }; |
131 | + |
132 | + if (mt >= G_N_ELEMENTS(message_type_table)) |
133 | + return NULL; |
134 | + |
135 | + return message_type_table[mt]; |
136 | +} |
137 | + |
138 | +static const char *translate_response(int response) { |
139 | + static const char *const response_table[] = { |
140 | + [-GTK_RESPONSE_NONE] = NULL, |
141 | + [-GTK_RESPONSE_REJECT] = "dialog-cancel", |
142 | + [-GTK_RESPONSE_DELETE_EVENT] = "dialog-cancel", |
143 | + [-GTK_RESPONSE_ACCEPT] = "dialog-ok", |
144 | + [-GTK_RESPONSE_OK] = "dialog-ok", |
145 | + [-GTK_RESPONSE_CANCEL] = "dialog-cancel", |
146 | + [-GTK_RESPONSE_CLOSE] = "dialog-ok", |
147 | + [-GTK_RESPONSE_YES] = "dialog-ok", |
148 | + [-GTK_RESPONSE_NO] = "dialog-cancel", |
149 | + [-GTK_RESPONSE_APPLY] = "dialog-ok", |
150 | + [-GTK_RESPONSE_HELP] = NULL, |
151 | + }; |
152 | + |
153 | + if (response >= 0) |
154 | + return NULL; |
155 | + |
156 | + if ((unsigned) -response >= G_N_ELEMENTS(response_table)) |
157 | + return NULL; |
158 | + |
159 | + return response_table[-response]; |
160 | +} |
161 | + |
162 | +static gboolean is_child_of_combo_box(GtkWidget *w) { |
163 | + |
164 | + while (w) { |
165 | + |
166 | + if (GTK_IS_COMBO_BOX(w)) |
167 | + return TRUE; |
168 | + |
169 | + w = gtk_widget_get_parent(w); |
170 | + } |
171 | + |
172 | + return FALSE; |
173 | +} |
174 | + |
175 | +static GtkDialog* find_parent_dialog(GtkWidget *w) { |
176 | + |
177 | + while (w) { |
178 | + |
179 | + if (GTK_IS_DIALOG(w)) |
180 | + return GTK_DIALOG(w); |
181 | + |
182 | + w = gtk_widget_get_parent(w); |
183 | + } |
184 | + |
185 | + return NULL; |
186 | +} |
187 | + |
188 | +static void free_sound_event(SoundEventData *d) { |
189 | + |
190 | + g_object_unref(d->object); |
191 | + |
192 | + if (d->arg1_is_set) |
193 | + g_value_unset(&d->arg1); |
194 | + |
195 | + if (d->event) |
196 | + gdk_event_free(d->event); |
197 | + |
198 | + g_slice_free(SoundEventData, d); |
199 | +} |
200 | + |
201 | +static gboolean is_menu_hint(GdkWindowTypeHint hint) { |
202 | + return |
203 | + hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU || |
204 | + hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU || |
205 | + hint == GDK_WINDOW_TYPE_HINT_MENU; |
206 | +} |
207 | + |
208 | +static SoundEventData* filter_sound_event(SoundEventData *d) { |
209 | + GList *i, *n; |
210 | + |
211 | + do { |
212 | + |
213 | + for (i = sound_event_queue.head; i; i = n) { |
214 | + SoundEventData *j; |
215 | + |
216 | + j = i->data; |
217 | + n = i->next; |
218 | + |
219 | + if (d->object == j->object) { |
220 | + |
221 | + /* Let's drop a show event immediately followed by a |
222 | + * hide event */ |
223 | + |
224 | + if (d->signal_id == signal_id_widget_show && |
225 | + j->signal_id == signal_id_widget_hide) { |
226 | + |
227 | + free_sound_event(d); |
228 | + free_sound_event(j); |
229 | + g_queue_delete_link(&sound_event_queue, i); |
230 | + |
231 | + return NULL; |
232 | + } |
233 | + |
234 | + /* Let's drop widget hide events in favour of dialog |
235 | + * response. |
236 | + * |
237 | + * Let's drop widget window state events in favour of |
238 | + * widget hide/show. |
239 | + * |
240 | + * Let's drop double events */ |
241 | + |
242 | + if ((d->signal_id == signal_id_widget_hide && |
243 | + j->signal_id == signal_id_dialog_response) || |
244 | + |
245 | + (d->signal_id == signal_id_widget_window_state_event && |
246 | + j->signal_id == signal_id_widget_hide) || |
247 | + |
248 | + (d->signal_id == signal_id_widget_window_state_event && |
249 | + j->signal_id == signal_id_widget_show)) { |
250 | + |
251 | + free_sound_event(d); |
252 | + d = j; |
253 | + g_queue_delete_link(&sound_event_queue, i); |
254 | + break; |
255 | + } |
256 | + |
257 | + if ((d->signal_id == signal_id_dialog_response && |
258 | + j->signal_id == signal_id_widget_hide) || |
259 | + |
260 | + (d->signal_id == signal_id_widget_show && |
261 | + j->signal_id == signal_id_widget_window_state_event) || |
262 | + |
263 | + (d->signal_id == signal_id_widget_hide && |
264 | + j->signal_id == signal_id_widget_window_state_event) || |
265 | + |
266 | + (d->signal_id == j->signal_id)) { |
267 | + |
268 | + free_sound_event(j); |
269 | + g_queue_delete_link(&sound_event_queue, i); |
270 | + } |
271 | + |
272 | + } else if (GTK_IS_WINDOW(d->object) && GTK_IS_WINDOW(j->object)) { |
273 | + |
274 | + GdkWindowTypeHint dhint, jhint; |
275 | + |
276 | + dhint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); |
277 | + jhint = gtk_window_get_type_hint(GTK_WINDOW(j->object)); |
278 | + |
279 | + if (is_menu_hint(dhint) && is_menu_hint(jhint)) { |
280 | + |
281 | + if (d->signal_id == signal_id_widget_hide && |
282 | + j->signal_id == signal_id_widget_show) { |
283 | + free_sound_event(d); |
284 | + d = j; |
285 | + g_queue_delete_link(&sound_event_queue, i); |
286 | + break; |
287 | + } |
288 | + |
289 | + if (d->signal_id == signal_id_widget_show && |
290 | + j->signal_id == signal_id_widget_hide) { |
291 | + |
292 | + free_sound_event(j); |
293 | + g_queue_delete_link(&sound_event_queue, i); |
294 | + } |
295 | + } |
296 | + } |
297 | + } |
298 | + |
299 | + /* If we exited the iteration early, let's retry. */ |
300 | + |
301 | + } while (i); |
302 | + |
303 | + /* FIXME: Filter menu hide on menu show */ |
304 | + |
305 | + return d; |
306 | +} |
307 | + |
308 | +static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) { |
309 | + Atom type_return; |
310 | + gint format_return; |
311 | + gulong nitems_return; |
312 | + gulong bytes_after_return; |
313 | + guchar *data = NULL; |
314 | + gint ret = -1; |
315 | + |
316 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
317 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
318 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
319 | + &format_return, &nitems_return, &bytes_after_return, |
320 | + &data) != Success) |
321 | + return -1; |
322 | + |
323 | + if (type_return == XA_CARDINAL && format_return == 32 && data) { |
324 | + guint32 desktop = *(guint32*) data; |
325 | + |
326 | + if (desktop != 0xFFFFFFFF) |
327 | + ret = (gint) desktop; |
328 | + } |
329 | + |
330 | + if (type_return != None && data != NULL) |
331 | + XFree(data); |
332 | + |
333 | + return ret; |
334 | +} |
335 | + |
336 | +static gint display_get_desktop(GdkDisplay *d) { |
337 | + Atom type_return; |
338 | + gint format_return; |
339 | + gulong nitems_return; |
340 | + gulong bytes_after_return; |
341 | + guchar *data = NULL; |
342 | + gint ret = -1; |
343 | + |
344 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), DefaultRootWindow(GDK_DISPLAY_XDISPLAY(d)), |
345 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_CURRENT_DESKTOP"), |
346 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
347 | + &format_return, &nitems_return, &bytes_after_return, |
348 | + &data) != Success) |
349 | + return -1; |
350 | + |
351 | + if (type_return == XA_CARDINAL && format_return == 32 && data) { |
352 | + |
353 | + guint32 desktop = *(guint32*) data; |
354 | + |
355 | + if (desktop != 0xFFFFFFFF) |
356 | + ret = (gint) desktop; |
357 | + } |
358 | + |
359 | + if (type_return != None && data != NULL) |
360 | + XFree(data); |
361 | + |
362 | + return ret; |
363 | +} |
364 | + |
365 | +static gboolean window_is_xembed(GdkDisplay *d, GdkWindow *w) { |
366 | + Atom type_return; |
367 | + gint format_return; |
368 | + gulong nitems_return; |
369 | + gulong bytes_after_return; |
370 | + guchar *data = NULL; |
371 | + gboolean ret = FALSE; |
372 | + Atom xembed; |
373 | + |
374 | + /* Gnome Panel applets are XEMBED windows. We need to make sure we |
375 | + * ignore them */ |
376 | + |
377 | + xembed = gdk_x11_get_xatom_by_name_for_display(d, "_XEMBED_INFO"); |
378 | + |
379 | + /* be robust against not existing XIDs (LP: #834403) */ |
380 | + gdk_error_trap_push(); |
381 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
382 | + xembed, |
383 | + 0, 2, False, xembed, &type_return, |
384 | + &format_return, &nitems_return, &bytes_after_return, |
385 | + &data) != Success) { |
386 | + return FALSE; |
387 | + } |
388 | + |
389 | +#if GTK_CHECK_VERSION(3,0,0) |
390 | + gdk_error_trap_pop_ignored(); |
391 | +#else |
392 | + gdk_flush(); |
393 | + gdk_error_trap_pop(); |
394 | +#endif |
395 | + |
396 | + if (type_return == xembed && format_return == 32 && data) |
397 | + ret = TRUE; |
398 | + |
399 | + if (type_return != None && data != NULL) |
400 | + XFree(data); |
401 | + |
402 | + return ret; |
403 | +} |
404 | + |
405 | +static void dispatch_sound_event(SoundEventData *d) { |
406 | + int ret = CA_SUCCESS; |
407 | + static gboolean menu_is_popped_up = FALSE; |
408 | + |
409 | + if (g_object_get_qdata(d->object, disable_sound_quark)) |
410 | + return; |
411 | + |
412 | + /* The GdkWindow of the the widget might have changed while this |
413 | + * event was queued for us. Make sure to update it from the |
414 | + * current one if necessary. */ |
415 | + if (d->event && d->event->any.window) { |
416 | + GdkWindow *window; |
417 | + |
418 | + g_object_unref(G_OBJECT(d->event->any.window)); |
419 | + |
420 | + if ((window = gtk_widget_get_window(GTK_WIDGET(d->object)))) |
421 | + d->event->any.window = GDK_WINDOW(g_object_ref(G_OBJECT(window))); |
422 | + else |
423 | + d->event->any.window = NULL; |
424 | + } |
425 | + |
426 | + if (d->signal_id == signal_id_widget_show) { |
427 | + GdkWindowTypeHint hint; |
428 | + |
429 | + /* Show/hide signals for non-windows have already been filtered out |
430 | + * by the emission hook! */ |
431 | + |
432 | + hint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); |
433 | + |
434 | + if (is_menu_hint(hint)) { |
435 | + |
436 | + if (!menu_is_popped_up) { |
437 | + |
438 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
439 | + CA_PROP_EVENT_ID, "menu-popup", |
440 | + CA_PROP_EVENT_DESCRIPTION, "Menu popped up", |
441 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
442 | + NULL); |
443 | + } else { |
444 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
445 | + CA_PROP_EVENT_ID, "menu-replace", |
446 | + CA_PROP_EVENT_DESCRIPTION, "Menu replaced", |
447 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
448 | + NULL); |
449 | + } |
450 | + |
451 | + menu_is_popped_up = TRUE; |
452 | + |
453 | + } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) { |
454 | + |
455 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
456 | + CA_PROP_EVENT_ID, "tooltip-popup", |
457 | + CA_PROP_EVENT_DESCRIPTION, "Tooltip popped up", |
458 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
459 | + NULL); |
460 | + |
461 | + } else if (hint == GDK_WINDOW_TYPE_HINT_NORMAL || |
462 | + hint == GDK_WINDOW_TYPE_HINT_DIALOG) { |
463 | + |
464 | + gboolean played_sound = FALSE; |
465 | + gboolean is_xembed; |
466 | + |
467 | + is_xembed = |
468 | + gtk_widget_get_realized(GTK_WIDGET(d->object)) && |
469 | + window_is_xembed( |
470 | + gtk_widget_get_display(GTK_WIDGET(d->object)), |
471 | + gtk_widget_get_window(GTK_WIDGET(d->object))); |
472 | + |
473 | + g_object_set_qdata(d->object, is_xembed_quark, GINT_TO_POINTER(is_xembed)); |
474 | + |
475 | + if (GTK_IS_MESSAGE_DIALOG(d->object)) { |
476 | + GtkMessageType mt; |
477 | + const char *id; |
478 | + |
479 | + g_object_get(d->object, "message_type", &mt, NULL); |
480 | + |
481 | + if ((id = translate_message_tye(mt))) { |
482 | + |
483 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
484 | + CA_PROP_EVENT_ID, id, |
485 | + CA_PROP_EVENT_DESCRIPTION, "Message dialog shown", |
486 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
487 | + NULL); |
488 | + played_sound = TRUE; |
489 | + } |
490 | + |
491 | + } |
492 | + |
493 | + if (!played_sound && |
494 | + !is_xembed && |
495 | + gtk_window_get_decorated(GTK_WINDOW(d->object))) { |
496 | + |
497 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
498 | + CA_PROP_EVENT_ID, "window-new", |
499 | + CA_PROP_EVENT_DESCRIPTION, "Window shown", |
500 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
501 | + NULL); |
502 | + |
503 | + } |
504 | + } |
505 | + } |
506 | + |
507 | + if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) { |
508 | + |
509 | + int response; |
510 | + const char *id; |
511 | + |
512 | + response = g_value_get_int(&d->arg1); |
513 | + |
514 | + if ((id = translate_response(response))) { |
515 | + |
516 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
517 | + CA_PROP_EVENT_ID, id, |
518 | + CA_PROP_EVENT_DESCRIPTION, "Dialog closed", |
519 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
520 | + NULL); |
521 | + } else { |
522 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
523 | + CA_PROP_EVENT_ID, "window-close", |
524 | + CA_PROP_EVENT_DESCRIPTION, "Window closed", |
525 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
526 | + NULL); |
527 | + } |
528 | + |
529 | + } else if (d->signal_id == signal_id_widget_hide) { |
530 | + GdkWindowTypeHint hint; |
531 | + |
532 | + hint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); |
533 | + |
534 | + if (is_menu_hint(hint)) { |
535 | + |
536 | + if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { |
537 | + |
538 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
539 | + CA_PROP_EVENT_ID, "menu-popdown", |
540 | + CA_PROP_EVENT_DESCRIPTION, "Menu popped down", |
541 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
542 | + NULL); |
543 | + } |
544 | + |
545 | + menu_is_popped_up = FALSE; |
546 | + |
547 | + } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) { |
548 | + |
549 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
550 | + CA_PROP_EVENT_ID, "tooltip-popdown", |
551 | + CA_PROP_EVENT_DESCRIPTION, "Tooltip popped down", |
552 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
553 | + NULL); |
554 | + |
555 | + } else if ((hint == GDK_WINDOW_TYPE_HINT_NORMAL || |
556 | + hint == GDK_WINDOW_TYPE_HINT_DIALOG)) { |
557 | + |
558 | + gboolean is_xembed; |
559 | + |
560 | + is_xembed = !!g_object_get_qdata(d->object, is_xembed_quark); |
561 | + |
562 | + if (!is_xembed && |
563 | + gtk_window_get_decorated(GTK_WINDOW(d->object))) |
564 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
565 | + CA_PROP_EVENT_ID, "window-close", |
566 | + CA_PROP_EVENT_DESCRIPTION, "Window closed", |
567 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
568 | + NULL); |
569 | + } |
570 | + } |
571 | + |
572 | + if (GTK_IS_WINDOW(d->object) && d->signal_id == signal_id_widget_window_state_event) { |
573 | + GdkEventWindowState *e; |
574 | + gint w_desktop = -1, c_desktop = -1; |
575 | + |
576 | + e = (GdkEventWindowState*) d->event; |
577 | + |
578 | + /* Unfortunately GDK_WINDOW_STATE_ICONIFIED is used both for |
579 | + * proper minimizing and when a window becomes invisible |
580 | + * because the desktop was switched. To handle this we check |
581 | + * if the window becoming invisible is actually on the current |
582 | + * desktop, and only if that's the case we assume it is being |
583 | + * minimized. We then store this information, so that we know |
584 | + * later on when the window is unminimized again. */ |
585 | + |
586 | + if (gtk_widget_get_realized(GTK_WIDGET(d->object))) { |
587 | + GdkDisplay *display; |
588 | + |
589 | + display = gtk_widget_get_display(GTK_WIDGET(d->object)); |
590 | + w_desktop = window_get_desktop(display, gtk_widget_get_window(GTK_WIDGET(d->object))); |
591 | + c_desktop = display_get_desktop(display); |
592 | + } |
593 | + |
594 | + if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && |
595 | + (e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && |
596 | + (w_desktop == c_desktop || w_desktop < 0)) { |
597 | + |
598 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
599 | + CA_PROP_EVENT_ID, "window-minimized", |
600 | + CA_PROP_EVENT_DESCRIPTION, "Window minimized", |
601 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
602 | + NULL); |
603 | + |
604 | + g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(1)); |
605 | + |
606 | + } else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) && |
607 | + (e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) { |
608 | + |
609 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
610 | + CA_PROP_EVENT_ID, "window-maximized", |
611 | + CA_PROP_EVENT_DESCRIPTION, "Window maximized", |
612 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
613 | + NULL); |
614 | + |
615 | + g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0)); |
616 | + |
617 | + } else if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && |
618 | + !(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && |
619 | + g_object_get_qdata(d->object, was_iconized_quark)) { |
620 | + |
621 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
622 | + CA_PROP_EVENT_ID, "window-unminimized", |
623 | + CA_PROP_EVENT_DESCRIPTION, "Window unminimized", |
624 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
625 | + NULL); |
626 | + |
627 | + g_object_set_qdata(d->object, was_iconized_quark, GINT_TO_POINTER(0)); |
628 | + |
629 | + } else if ((e->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN)) && |
630 | + !(e->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED|GDK_WINDOW_STATE_FULLSCREEN))) { |
631 | + |
632 | + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, |
633 | + CA_PROP_EVENT_ID, "window-unmaximized", |
634 | + CA_PROP_EVENT_DESCRIPTION, "Window unmaximized", |
635 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
636 | + NULL); |
637 | + } |
638 | + } |
639 | + |
640 | + if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) { |
641 | + |
642 | + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d->object))) |
643 | + ret = ca_gtk_play_for_event(d->event, 0, |
644 | + CA_PROP_EVENT_ID, "button-toggle-on", |
645 | + CA_PROP_EVENT_DESCRIPTION, "Check menu item checked", |
646 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
647 | + NULL); |
648 | + else |
649 | + ret = ca_gtk_play_for_event(d->event, 0, |
650 | + CA_PROP_EVENT_ID, "button-toggle-off", |
651 | + CA_PROP_EVENT_DESCRIPTION, "Check menu item unchecked", |
652 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
653 | + NULL); |
654 | + |
655 | + } else if (GTK_IS_MENU_ITEM(d->object) && d->signal_id == signal_id_menu_item_activate) { |
656 | + |
657 | + if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(d->object))) |
658 | + ret = ca_gtk_play_for_event(d->event, 0, |
659 | + CA_PROP_EVENT_ID, "menu-click", |
660 | + CA_PROP_EVENT_DESCRIPTION, "Menu item clicked", |
661 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
662 | + NULL); |
663 | + } |
664 | + |
665 | + if (GTK_IS_TOGGLE_BUTTON(d->object)) { |
666 | + |
667 | + if (d->signal_id == signal_id_toggle_button_toggled) { |
668 | + |
669 | + if (!is_child_of_combo_box(GTK_WIDGET(d->object))) { |
670 | + |
671 | + /* We don't want to play this sound if this is a toggle |
672 | + * button belonging to combo box. */ |
673 | + |
674 | + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->object))) |
675 | + ret = ca_gtk_play_for_event(d->event, 0, |
676 | + CA_PROP_EVENT_ID, "button-toggle-on", |
677 | + CA_PROP_EVENT_DESCRIPTION, "Toggle button checked", |
678 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
679 | + NULL); |
680 | + else |
681 | + ret = ca_gtk_play_for_event(d->event, 0, |
682 | + CA_PROP_EVENT_ID, "button-toggle-off", |
683 | + CA_PROP_EVENT_DESCRIPTION, "Toggle button unchecked", |
684 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
685 | + NULL); |
686 | + } |
687 | + } |
688 | + |
689 | + } else if (GTK_IS_LINK_BUTTON(d->object)) { |
690 | + |
691 | + if (d->signal_id == signal_id_button_pressed) { |
692 | + ret = ca_gtk_play_for_event(d->event, 0, |
693 | + CA_PROP_EVENT_ID, "link-pressed", |
694 | + CA_PROP_EVENT_DESCRIPTION, "Link pressed", |
695 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
696 | + NULL); |
697 | + |
698 | + } else if (d->signal_id == signal_id_button_released) { |
699 | + |
700 | + ret = ca_gtk_play_for_event(d->event, 0, |
701 | + CA_PROP_EVENT_ID, "link-released", |
702 | + CA_PROP_EVENT_DESCRIPTION, "Link released", |
703 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
704 | + NULL); |
705 | + } |
706 | + |
707 | + } else if (GTK_IS_BUTTON(d->object) && !GTK_IS_TOGGLE_BUTTON(d->object)) { |
708 | + |
709 | + if (d->signal_id == signal_id_button_pressed) { |
710 | + ret = ca_gtk_play_for_event(d->event, 0, |
711 | + CA_PROP_EVENT_ID, "button-pressed", |
712 | + CA_PROP_EVENT_DESCRIPTION, "Button pressed", |
713 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
714 | + NULL); |
715 | + |
716 | + } else if (d->signal_id == signal_id_button_released) { |
717 | + GtkDialog *dialog; |
718 | + gboolean dont_play = FALSE; |
719 | + |
720 | + if ((dialog = find_parent_dialog(GTK_WIDGET(d->object)))) { |
721 | + int response; |
722 | + |
723 | + /* Don't play the click sound if this is a response widget |
724 | + * we will generate a dialog-xxx event sound anyway. */ |
725 | + |
726 | + response = gtk_dialog_get_response_for_widget(dialog, GTK_WIDGET(d->object)); |
727 | + dont_play = !!translate_response(response); |
728 | + } |
729 | + |
730 | + if (!dont_play) |
731 | + ret = ca_gtk_play_for_event(d->event, 0, |
732 | + CA_PROP_EVENT_ID, "button-released", |
733 | + CA_PROP_EVENT_DESCRIPTION, "Button released", |
734 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
735 | + NULL); |
736 | + } |
737 | + } |
738 | + |
739 | + if (GTK_IS_NOTEBOOK(d->object) && d->signal_id == signal_id_notebook_switch_page) { |
740 | + ret = ca_gtk_play_for_event(d->event, 0, |
741 | + CA_PROP_EVENT_ID, "notebook-tab-changed", |
742 | + CA_PROP_EVENT_DESCRIPTION, "Tab changed", |
743 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
744 | + NULL); |
745 | + goto finish; |
746 | + } |
747 | + |
748 | + if (GTK_IS_TREE_VIEW(d->object) && d->signal_id == signal_id_tree_view_cursor_changed) { |
749 | + ret = ca_gtk_play_for_event(d->event, 0, |
750 | + CA_PROP_EVENT_ID, "item-selected", |
751 | + CA_PROP_EVENT_DESCRIPTION, "Item selected", |
752 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
753 | + NULL); |
754 | + goto finish; |
755 | + } |
756 | + |
757 | + if (GTK_IS_ICON_VIEW(d->object) && d->signal_id == signal_id_icon_view_selection_changed) { |
758 | + ret = ca_gtk_play_for_event(d->event, 0, |
759 | + CA_PROP_EVENT_ID, "item-selected", |
760 | + CA_PROP_EVENT_DESCRIPTION, "Item selected", |
761 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
762 | + NULL); |
763 | + goto finish; |
764 | + } |
765 | + |
766 | + if (GTK_IS_EXPANDER(d->object) && d->signal_id == signal_id_expander_activate) { |
767 | + |
768 | + if (gtk_expander_get_expanded(GTK_EXPANDER(d->object))) |
769 | + ret = ca_gtk_play_for_event(d->event, 0, |
770 | + CA_PROP_EVENT_ID, "expander-toggle-on", |
771 | + CA_PROP_EVENT_DESCRIPTION, "Expander expanded", |
772 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
773 | + NULL); |
774 | + else |
775 | + ret = ca_gtk_play_for_event(d->event, 0, |
776 | + CA_PROP_EVENT_ID, "expander-toggle-off", |
777 | + CA_PROP_EVENT_DESCRIPTION, "Expander unexpanded", |
778 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
779 | + NULL); |
780 | + |
781 | + goto finish; |
782 | + } |
783 | + |
784 | + if (GTK_IS_WIDGET(d->object)) { |
785 | + |
786 | + if (d->signal_id == signal_id_widget_drag_begin) { |
787 | + |
788 | + ret = ca_gtk_play_for_event(d->event, 0, |
789 | + CA_PROP_EVENT_ID, "drag-start", |
790 | + CA_PROP_EVENT_DESCRIPTION, "Drag started", |
791 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
792 | + NULL); |
793 | + goto finish; |
794 | + |
795 | + } else if (d->signal_id == signal_id_widget_drag_drop) { |
796 | + |
797 | + ret = ca_gtk_play_for_event(d->event, 0, |
798 | + CA_PROP_EVENT_ID, "drag-accept", |
799 | + CA_PROP_EVENT_DESCRIPTION, "Drag accepted", |
800 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
801 | + NULL); |
802 | + goto finish; |
803 | + |
804 | + } else if (d->signal_id == signal_id_widget_drag_failed) { |
805 | + |
806 | + ret = ca_gtk_play_for_event(d->event, 0, |
807 | + CA_PROP_EVENT_ID, "drag-fail", |
808 | + CA_PROP_EVENT_DESCRIPTION, "Drag failed", |
809 | + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", |
810 | + NULL); |
811 | + goto finish; |
812 | + } |
813 | + } |
814 | + |
815 | +finish: |
816 | + |
817 | + ; |
818 | + /* if (ret != CA_SUCCESS) */ |
819 | + /* g_warning("Failed to play event sound: %s", ca_strerror(ret)); */ |
820 | +} |
821 | + |
822 | +static void dispatch_queue(void) { |
823 | + SoundEventData *d; |
824 | + |
825 | + while ((d = g_queue_pop_head(&sound_event_queue))) { |
826 | + |
827 | + if (!(d = filter_sound_event(d))) |
828 | + continue; |
829 | + |
830 | + dispatch_sound_event(d); |
831 | + free_sound_event(d); |
832 | + } |
833 | +} |
834 | + |
835 | +static gboolean idle_cb(void *userdata) { |
836 | + idle_id = 0; |
837 | + |
838 | + dispatch_queue(); |
839 | + |
840 | + return FALSE; |
841 | +} |
842 | + |
843 | +static void connect_settings(void); |
844 | + |
845 | +static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_values, const GValue *param_values, gpointer data) { |
846 | + static SoundEventData *d = NULL; |
847 | + GdkEvent *e; |
848 | + GObject *object; |
849 | + |
850 | + connect_settings(); |
851 | + |
852 | + if (disabled) |
853 | + return TRUE; |
854 | + |
855 | + object = g_value_get_object(¶m_values[0]); |
856 | + |
857 | + /* g_message("signal '%s' on object of type '%s' with name '%s'", */ |
858 | + /* g_signal_name(hint->signal_id), */ |
859 | + /* G_OBJECT_TYPE_NAME(object), */ |
860 | + /* gtk_widget_get_name(GTK_WIDGET(object))); */ |
861 | + |
862 | + /* if (GTK_IS_WINDOW(object)) */ |
863 | + /* g_message("window role='%s' title='%s' type='%u'", */ |
864 | + /* gtk_window_get_role(GTK_WINDOW(object)), */ |
865 | + /* gtk_window_get_title(GTK_WINDOW(object)), */ |
866 | + /* gtk_window_get_type_hint(GTK_WINDOW(object))); */ |
867 | + |
868 | + /* Filter a few very often occuring signals as quickly as possible */ |
869 | + if ((hint->signal_id == signal_id_widget_hide || |
870 | + hint->signal_id == signal_id_widget_show || |
871 | + hint->signal_id == signal_id_widget_window_state_event) && |
872 | + !GTK_IS_WINDOW(object)) |
873 | + return TRUE; |
874 | + |
875 | + if (hint->signal_id != signal_id_widget_hide && |
876 | + hint->signal_id != signal_id_dialog_response && |
877 | + !gtk_widget_is_drawable(GTK_WIDGET (object))) |
878 | + return TRUE; |
879 | + |
880 | + d = g_slice_new0(SoundEventData); |
881 | + |
882 | + d->object = g_object_ref(object); |
883 | + |
884 | + d->signal_id = hint->signal_id; |
885 | + |
886 | + if (d->signal_id == signal_id_widget_window_state_event) { |
887 | + d->event = gdk_event_copy(g_value_peek_pointer(¶m_values[1])); |
888 | + } else if ((e = gtk_get_current_event())) |
889 | + d->event = gdk_event_copy(e); |
890 | + |
891 | + if (n_param_values > 1) { |
892 | + g_value_init(&d->arg1, G_VALUE_TYPE(¶m_values[1])); |
893 | + g_value_copy(¶m_values[1], &d->arg1); |
894 | + d->arg1_is_set = TRUE; |
895 | + } |
896 | + |
897 | + g_queue_push_tail(&sound_event_queue, d); |
898 | + |
899 | + if (idle_id == 0) |
900 | + idle_id = gdk_threads_add_idle_full(GDK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, NULL, NULL); |
901 | + |
902 | + return TRUE; |
903 | +} |
904 | + |
905 | +static void install_hook(GType type, const char *sig, guint *sn) { |
906 | + GTypeClass *type_class; |
907 | + |
908 | + type_class = g_type_class_ref(type); |
909 | + |
910 | + *sn = g_signal_lookup(sig, type); |
911 | + g_signal_add_emission_hook(*sn, 0, emission_hook_cb, NULL, NULL); |
912 | + |
913 | + g_type_class_unref(type_class); |
914 | +} |
915 | + |
916 | +static void read_enable_input_feedback_sounds(GtkSettings *s) { |
917 | + gboolean enabled = !disabled; |
918 | + |
919 | + if (g_getenv("CANBERRA_FORCE_INPUT_FEEDBACK_SOUNDS")) |
920 | + disabled = FALSE; |
921 | + else { |
922 | + g_object_get(G_OBJECT(s), "gtk-enable-input-feedback-sounds", &enabled, NULL); |
923 | + disabled = !enabled; |
924 | + } |
925 | +} |
926 | + |
927 | +static void enable_input_feedback_sounds_changed(GtkSettings *s, GParamSpec *arg1, gpointer userdata) { |
928 | + read_enable_input_feedback_sounds(s); |
929 | +} |
930 | + |
931 | +static void connect_settings(void) { |
932 | + GtkSettings *s; |
933 | + static gboolean connected = FALSE; |
934 | + |
935 | + if (connected) |
936 | + return; |
937 | + |
938 | + if (!(s = gtk_settings_get_default())) |
939 | + return; |
940 | + |
941 | + if (g_object_class_find_property(G_OBJECT_GET_CLASS(s), "gtk-enable-input-feedback-sounds")) { |
942 | + g_signal_connect(G_OBJECT(s), "notify::gtk-enable-input-feedback-sounds", G_CALLBACK(enable_input_feedback_sounds_changed), NULL); |
943 | + read_enable_input_feedback_sounds(s); |
944 | + } else |
945 | + g_debug("This Gtk+ version doesn't have the GtkSettings::gtk-enable-input-feedback-sounds property."); |
946 | + |
947 | + connected = TRUE; |
948 | +} |
949 | + |
950 | +#if GTK_CHECK_VERSION(3,0,0) |
951 | +#warning "We really need a quit handler in Gtk 3.0, https://bugzilla.gnome.org/show_bug.cgi?id=639770" |
952 | +#else |
953 | +static gboolean quit_handler(gpointer data) { |
954 | + dispatch_queue(); |
955 | + return FALSE; |
956 | +} |
957 | +#endif |
958 | + |
959 | +G_MODULE_EXPORT void gtk_module_init(gint *argc, gchar ***argv[]) { |
960 | + |
961 | + /* This is the same quark libgnomeui uses! */ |
962 | + disable_sound_quark = g_quark_from_string("gnome_disable_sound_events"); |
963 | + was_iconized_quark = g_quark_from_string("canberra_was_iconized"); |
964 | + is_xembed_quark = g_quark_from_string("canberra_is_xembed"); |
965 | + |
966 | + /* Hook up the gtk setting */ |
967 | + connect_settings(); |
968 | + |
969 | + install_hook(GTK_TYPE_WINDOW, "show", &signal_id_widget_show); |
970 | + install_hook(GTK_TYPE_WINDOW, "hide", &signal_id_widget_hide); |
971 | + install_hook(GTK_TYPE_DIALOG, "response", &signal_id_dialog_response); |
972 | + install_hook(GTK_TYPE_MENU_ITEM, "activate", &signal_id_menu_item_activate); |
973 | + install_hook(GTK_TYPE_CHECK_MENU_ITEM, "toggled", &signal_id_check_menu_item_toggled); |
974 | + install_hook(GTK_TYPE_TOGGLE_BUTTON, "toggled", &signal_id_toggle_button_toggled); |
975 | + install_hook(GTK_TYPE_BUTTON, "pressed", &signal_id_button_pressed); |
976 | + install_hook(GTK_TYPE_BUTTON, "released", &signal_id_button_released); |
977 | + install_hook(GTK_TYPE_WIDGET, "window-state-event", &signal_id_widget_window_state_event); |
978 | + install_hook(GTK_TYPE_NOTEBOOK, "switch-page", &signal_id_notebook_switch_page); |
979 | + install_hook(GTK_TYPE_TREE_VIEW, "cursor-changed", &signal_id_tree_view_cursor_changed); |
980 | + install_hook(GTK_TYPE_ICON_VIEW, "selection-changed", &signal_id_icon_view_selection_changed); |
981 | + install_hook(GTK_TYPE_WIDGET, "drag-begin", &signal_id_widget_drag_begin); |
982 | + install_hook(GTK_TYPE_WIDGET, "drag-drop", &signal_id_widget_drag_drop); |
983 | + install_hook(GTK_TYPE_WIDGET, "drag-failed", &signal_id_widget_drag_failed); |
984 | + install_hook(GTK_TYPE_EXPANDER, "activate", &signal_id_expander_activate); |
985 | + |
986 | +#if !GTK_CHECK_VERSION(3,0,0) |
987 | + gtk_quit_add(1, quit_handler, NULL); |
988 | +#endif |
989 | +} |
990 | + |
991 | +G_MODULE_EXPORT gchar* g_module_check_init(GModule *module); |
992 | + |
993 | +G_MODULE_EXPORT gchar* g_module_check_init(GModule *module) { |
994 | + g_module_make_resident(module); |
995 | + return NULL; |
996 | +} |
997 | |
998 | === added file '.pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk.c' |
999 | --- .pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk.c 1970-01-01 00:00:00 +0000 |
1000 | +++ .pc/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch/src/canberra-gtk.c 2014-12-11 18:09:28 +0000 |
1001 | @@ -0,0 +1,544 @@ |
1002 | +/*-*- Mode: C; c-basic-offset: 8 -*-*/ |
1003 | + |
1004 | +/*** |
1005 | + This file is part of libcanberra. |
1006 | + |
1007 | + Copyright 2008 Lennart Poettering |
1008 | + |
1009 | + libcanberra is free software; you can redistribute it and/or modify |
1010 | + it under the terms of the GNU Lesser General Public License as |
1011 | + published by the Free Software Foundation, either version 2.1 of the |
1012 | + License, or (at your option) any later version. |
1013 | + |
1014 | + libcanberra is distributed in the hope that it will be useful, but |
1015 | + WITHOUT ANY WARRANTY; without even the implied warranty of |
1016 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1017 | + Lesser General Public License for more details. |
1018 | + |
1019 | + You should have received a copy of the GNU Lesser General Public |
1020 | + License along with libcanberra. If not, see |
1021 | + <http://www.gnu.org/licenses/>. |
1022 | +***/ |
1023 | + |
1024 | +#ifdef HAVE_CONFIG_H |
1025 | +#include <config.h> |
1026 | +#endif |
1027 | + |
1028 | +#include <gtk/gtk.h> |
1029 | +#include <gdk/gdk.h> |
1030 | +#include <gdk/gdkx.h> |
1031 | +#include <X11/Xatom.h> |
1032 | + |
1033 | +#include "canberra.h" |
1034 | +#include "canberra-gtk.h" |
1035 | +#include "common.h" |
1036 | +#include "malloc.h" |
1037 | +#include "proplist.h" |
1038 | +#include "fork-detect.h" |
1039 | + |
1040 | +/** |
1041 | + * SECTION:canberra-gtk |
1042 | + * @short_description: Gtk+ libcanberra Bindings |
1043 | + * |
1044 | + * libcanberra-gtk provides a few functions that simplify libcanberra |
1045 | + * usage from Gtk+ programs. It maintains a single ca_context object |
1046 | + * per #GdkScreen that is made accessible via |
1047 | + * ca_gtk_context_get_for_screen(), with a shortcut ca_gtk_context_get() |
1048 | + * to get the context for the default screen. More importantly, it provides |
1049 | + * a few functions |
1050 | + * to compile event sound property lists based on GtkWidget objects or |
1051 | + * GdkEvent events. |
1052 | + */ |
1053 | + |
1054 | +static void read_sound_theme_name(ca_context *c, GtkSettings *s) { |
1055 | + gchar *theme_name = NULL; |
1056 | + |
1057 | + g_object_get(G_OBJECT(s), "gtk-sound-theme-name", &theme_name, NULL); |
1058 | + |
1059 | + if (theme_name) { |
1060 | + ca_context_change_props(c, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL); |
1061 | + g_free(theme_name); |
1062 | + } |
1063 | +} |
1064 | + |
1065 | +static void read_enable_event_sounds(ca_context *c, GtkSettings *s) { |
1066 | + gboolean enable_event_sounds = TRUE; |
1067 | + |
1068 | + if (!g_getenv("CANBERRA_FORCE_EVENT_SOUNDS")) |
1069 | + g_object_get(G_OBJECT(s), "gtk-enable-event-sounds", &enable_event_sounds, NULL); |
1070 | + |
1071 | + ca_context_change_props(c, CA_PROP_CANBERRA_ENABLE, enable_event_sounds ? "1" : "0", NULL); |
1072 | +} |
1073 | + |
1074 | +static void sound_theme_name_changed(GtkSettings *s, GParamSpec *arg1, ca_context *c) { |
1075 | + read_sound_theme_name(c, s); |
1076 | +} |
1077 | + |
1078 | +static void enable_event_sounds_changed(GtkSettings *s, GParamSpec *arg1, ca_context *c) { |
1079 | + read_enable_event_sounds(c, s); |
1080 | +} |
1081 | + |
1082 | +/** |
1083 | + * ca_gtk_context_get: |
1084 | + * |
1085 | + * Gets the single ca_context object for the default screen. See |
1086 | + * ca_gtk_context_get_for_screen(). |
1087 | + * |
1088 | + * Returns: a ca_context object. The object is owned by libcanberra-gtk |
1089 | + * and must not be destroyed |
1090 | + */ |
1091 | +ca_context *ca_gtk_context_get(void) { |
1092 | + return ca_gtk_context_get_for_screen(NULL); |
1093 | +} |
1094 | + |
1095 | +/** |
1096 | + * ca_gtk_context_get_for_screen: |
1097 | + * @screen: the #GdkScreen to get the context for, or %NULL to use |
1098 | + * the default screen |
1099 | + * |
1100 | + * libcanberra-gtk maintains a single ca_context object for each |
1101 | + * #GdkScreen. Use this function to access it. The |
1102 | + * %CA_PROP_CANBERRA_XDG_THEME_NAME of this context property is |
1103 | + * dynamically bound to the XSETTINGS setting for the XDG theme |
1104 | + * name. CA_PROP_APPLICATION_NAME is bound to |
1105 | + * g_get_application_name(). |
1106 | + * |
1107 | + * Returns: a ca_context object. The object is owned by libcanberra-gtk |
1108 | + * and must not be destroyed |
1109 | + * |
1110 | + * Since: 0.13 |
1111 | + */ |
1112 | +ca_context *ca_gtk_context_get_for_screen(GdkScreen *screen) { |
1113 | + ca_context *c = NULL; |
1114 | + ca_proplist *p = NULL; |
1115 | + const char *name; |
1116 | + GtkSettings *s; |
1117 | + |
1118 | + if (!screen) |
1119 | + screen = gdk_screen_get_default(); |
1120 | + |
1121 | + if ((c = g_object_get_data(G_OBJECT(screen), "canberra::gtk::context"))) |
1122 | + return c; |
1123 | + |
1124 | + if (ca_context_create(&c) != CA_SUCCESS) |
1125 | + return NULL; |
1126 | + |
1127 | + if (ca_proplist_create(&p) != CA_SUCCESS) { |
1128 | + ca_context_destroy(c); |
1129 | + return NULL; |
1130 | + } |
1131 | + |
1132 | + if ((name = g_get_application_name())) |
1133 | + ca_proplist_sets(p, CA_PROP_APPLICATION_NAME, name); |
1134 | + else { |
1135 | + ca_proplist_sets(p, CA_PROP_APPLICATION_NAME, "libcanberra-gtk"); |
1136 | + ca_proplist_sets(p, CA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); |
1137 | + ca_proplist_sets(p, CA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra.gtk"); |
1138 | + } |
1139 | + |
1140 | + if ((name = gtk_window_get_default_icon_name())) |
1141 | + ca_proplist_sets(p, CA_PROP_APPLICATION_ICON_NAME, name); |
1142 | + |
1143 | + if ((name = gdk_display_get_name(gdk_screen_get_display(screen)))) |
1144 | + ca_proplist_sets(p, CA_PROP_WINDOW_X11_DISPLAY, name); |
1145 | + |
1146 | + ca_proplist_setf(p, CA_PROP_WINDOW_X11_SCREEN, "%i", gdk_screen_get_number(screen)); |
1147 | + |
1148 | + ca_context_change_props_full(c, p); |
1149 | + ca_proplist_destroy(p); |
1150 | + |
1151 | + if ((s = gtk_settings_get_for_screen(screen))) { |
1152 | + |
1153 | + if (g_object_class_find_property(G_OBJECT_GET_CLASS(s), "gtk-sound-theme-name")) { |
1154 | + g_signal_connect(G_OBJECT(s), "notify::gtk-sound-theme-name", G_CALLBACK(sound_theme_name_changed), c); |
1155 | + read_sound_theme_name(c, s); |
1156 | + } else |
1157 | + g_debug("This Gtk+ version doesn't have the GtkSettings::gtk-sound-theme-name property."); |
1158 | + |
1159 | + if (g_object_class_find_property(G_OBJECT_GET_CLASS(s), "gtk-enable-event-sounds")) { |
1160 | + g_signal_connect(G_OBJECT(s), "notify::gtk-enable-event-sounds", G_CALLBACK(enable_event_sounds_changed), c); |
1161 | + read_enable_event_sounds(c, s); |
1162 | + } else |
1163 | + g_debug("This Gtk+ version doesn't have the GtkSettings::gtk-enable-event-sounds property."); |
1164 | + } |
1165 | + |
1166 | + g_object_set_data_full(G_OBJECT(screen), "canberra::gtk::context", c, (GDestroyNotify) ca_context_destroy); |
1167 | + |
1168 | + return c; |
1169 | +} |
1170 | + |
1171 | +static GtkWindow* get_toplevel(GtkWidget *w) { |
1172 | + if (!(w = gtk_widget_get_toplevel(w))) |
1173 | + return NULL; |
1174 | + |
1175 | + if (!GTK_IS_WINDOW(w)) |
1176 | + return NULL; |
1177 | + |
1178 | + return GTK_WINDOW(w); |
1179 | +} |
1180 | + |
1181 | +static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) { |
1182 | + Atom type_return; |
1183 | + gint format_return; |
1184 | + gulong nitems_return; |
1185 | + gulong bytes_after_return; |
1186 | + guchar *data = NULL; |
1187 | + gint ret = -1; |
1188 | + |
1189 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
1190 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
1191 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1192 | + &format_return, &nitems_return, &bytes_after_return, |
1193 | + &data) != Success) |
1194 | + return -1; |
1195 | + |
1196 | + if (type_return == XA_CARDINAL && format_return == 32 && data) { |
1197 | + guint32 desktop = *(guint32*) data; |
1198 | + |
1199 | + if (desktop != 0xFFFFFFFF) |
1200 | + ret = (gint) desktop; |
1201 | + } |
1202 | + |
1203 | + if (type_return != None && data != NULL) |
1204 | + XFree(data); |
1205 | + |
1206 | + return ret; |
1207 | +} |
1208 | + |
1209 | +/** |
1210 | + * ca_gtk_proplist_set_for_widget: |
1211 | + * @p: The proplist to store these sound event properties in |
1212 | + * @w: The Gtk widget to base these sound event properties on |
1213 | + * |
1214 | + * Fill in a ca_proplist object for a sound event that shall originate |
1215 | + * from the specified Gtk Widget. This will fill in properties like |
1216 | + * %CA_PROP_WINDOW_NAME or %CA_PROP_WINDOW_X11_DISPLAY for you. |
1217 | + * |
1218 | + * Returns: 0 on success, negative error code on error. |
1219 | + */ |
1220 | + |
1221 | +int ca_gtk_proplist_set_for_widget(ca_proplist *p, GtkWidget *widget) { |
1222 | + GtkWindow *w; |
1223 | + int ret; |
1224 | + const char *t, *role; |
1225 | + |
1226 | + ca_return_val_if_fail(p, CA_ERROR_INVALID); |
1227 | + ca_return_val_if_fail(widget, CA_ERROR_INVALID); |
1228 | + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); |
1229 | + |
1230 | + if (!(w = get_toplevel(widget))) |
1231 | + return CA_ERROR_INVALID; |
1232 | + |
1233 | + if ((t = gtk_window_get_title(w))) |
1234 | + if ((ret = ca_proplist_sets(p, CA_PROP_WINDOW_NAME, t)) < 0) |
1235 | + return ret; |
1236 | + |
1237 | + if ((role = gtk_window_get_role(w))) { |
1238 | + if (role && t) { |
1239 | + char *id = ca_sprintf_malloc("%s#%s", t, role); |
1240 | + |
1241 | + if ((ret = ca_proplist_sets(p, CA_PROP_WINDOW_ID, id)) < 0) { |
1242 | + ca_free(id); |
1243 | + return ret; |
1244 | + } |
1245 | + |
1246 | + ca_free(id); |
1247 | + } |
1248 | + } else if (t) |
1249 | + if ((ret = ca_proplist_sets(p, CA_PROP_WINDOW_ID, t)) < 0) |
1250 | + return ret; |
1251 | + |
1252 | + if ((t = gtk_window_get_icon_name(w))) |
1253 | + if ((ret = ca_proplist_sets(p, CA_PROP_WINDOW_ICON_NAME, t)) < 0) |
1254 | + return ret; |
1255 | + |
1256 | + if (gtk_widget_get_realized(GTK_WIDGET(w))) { |
1257 | + GdkWindow *dw = NULL; |
1258 | + GdkScreen *screen = NULL; |
1259 | + GdkDisplay *display = NULL; |
1260 | + gint x = -1, y = -1, width = -1, height = -1, screen_width = -1, screen_height = -1; |
1261 | + |
1262 | + if ((dw = gtk_widget_get_window(GTK_WIDGET(w)))) |
1263 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long) GDK_WINDOW_XID(dw))) < 0) |
1264 | + return ret; |
1265 | + |
1266 | + if ((display = gtk_widget_get_display(GTK_WIDGET(w)))) { |
1267 | + if ((t = gdk_display_get_name(display))) |
1268 | + if ((ret = ca_proplist_sets(p, CA_PROP_WINDOW_X11_DISPLAY, t)) < 0) |
1269 | + return ret; |
1270 | + |
1271 | + if (dw) { |
1272 | + gint desktop = window_get_desktop(display, dw); |
1273 | + |
1274 | + if (desktop >= 0) |
1275 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_DESKTOP, "%i", desktop)) < 0) |
1276 | + return ret; |
1277 | + } |
1278 | + } |
1279 | + |
1280 | + if ((screen = gtk_widget_get_screen(GTK_WIDGET(w)))) { |
1281 | + |
1282 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_X11_SCREEN, "%i", gdk_screen_get_number(screen))) < 0) |
1283 | + return ret; |
1284 | + |
1285 | + if (dw) |
1286 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_X11_MONITOR, "%i", gdk_screen_get_monitor_at_window(screen, dw))) < 0) |
1287 | + return ret; |
1288 | + } |
1289 | + |
1290 | + /* FIXME, this might cause a round trip */ |
1291 | + |
1292 | + if (dw) { |
1293 | + gdk_window_get_origin(dw, &x, &y); |
1294 | + |
1295 | + if (x >= 0) |
1296 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_X, "%i", x)) < 0) |
1297 | + return ret; |
1298 | + if (y >= 0) |
1299 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_Y, "%i", y)) < 0) |
1300 | + return ret; |
1301 | + } |
1302 | + |
1303 | + gtk_window_get_size(w, &width, &height); |
1304 | + |
1305 | + if (width > 0) |
1306 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_WIDTH, "%i", width)) < 0) |
1307 | + return ret; |
1308 | + if (height > 0) |
1309 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_HEIGHT, "%i", height)) < 0) |
1310 | + return ret; |
1311 | + |
1312 | + if (x >= 0 && width > 0) { |
1313 | + screen_width = gdk_screen_get_width(gtk_widget_get_screen(GTK_WIDGET(w))); |
1314 | + |
1315 | + x += width/2; |
1316 | + x = CA_CLAMP(x, 0, screen_width-1); |
1317 | + |
1318 | + /* We use these strange format strings here to avoid that libc |
1319 | + * applies locale information on the formatting of floating |
1320 | + * numbers. */ |
1321 | + |
1322 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_HPOS, "%i.%03i", |
1323 | + (int) (x/(screen_width-1)), (int) (1000.0*x/(screen_width-1)) % 1000)) < 0) |
1324 | + return ret; |
1325 | + } |
1326 | + |
1327 | + if (y >= 0 && height > 0) { |
1328 | + screen_height = gdk_screen_get_height(gtk_widget_get_screen(GTK_WIDGET(w))); |
1329 | + |
1330 | + y += height/2; |
1331 | + y = CA_CLAMP(y, 0, screen_height-1); |
1332 | + |
1333 | + if ((ret = ca_proplist_setf(p, CA_PROP_WINDOW_VPOS, "%i.%03i", |
1334 | + (int) (y/(screen_height-1)), (int) (1000.0*y/(screen_height-1)) % 1000)) < 0) |
1335 | + return ret; |
1336 | + } |
1337 | + } |
1338 | + |
1339 | + return CA_SUCCESS; |
1340 | +} |
1341 | + |
1342 | +/** |
1343 | + * ca_gtk_proplist_set_for_event: |
1344 | + * @p: The proplist to store these sound event properties in |
1345 | + * @e: The Gdk event to base these sound event properties on |
1346 | + * |
1347 | + * Fill in a ca_proplist object for a sound event that is being |
1348 | + * triggered by the specified Gdk Event. This will fill in properties |
1349 | + * like %CA_PROP_EVENT_MOUSE_X or %CA_PROP_EVENT_MOUSE_BUTTON for |
1350 | + * you. This will internally also cal ca_gtk_proplist_set_for_widget() |
1351 | + * on the widget this event belongs to. |
1352 | + * |
1353 | + * Returns: 0 on success, negative error code on error. |
1354 | + */ |
1355 | + |
1356 | +int ca_gtk_proplist_set_for_event(ca_proplist *p, GdkEvent *e) { |
1357 | + gdouble x, y; |
1358 | + GdkWindow *gw; |
1359 | + GtkWidget *w = NULL; |
1360 | + int ret; |
1361 | + |
1362 | + ca_return_val_if_fail(p, CA_ERROR_INVALID); |
1363 | + ca_return_val_if_fail(e, CA_ERROR_INVALID); |
1364 | + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); |
1365 | + |
1366 | + if ((gw = e->any.window)) { |
1367 | + gdk_window_get_user_data(gw, (gpointer*) &w); |
1368 | + |
1369 | + if (w) |
1370 | + if ((ret = ca_gtk_proplist_set_for_widget(p, w)) < 0) |
1371 | + return ret; |
1372 | + } |
1373 | + |
1374 | + if (gdk_event_get_root_coords(e, &x, &y)) { |
1375 | + |
1376 | + if ((ret = ca_proplist_setf(p, CA_PROP_EVENT_MOUSE_X, "%0.0f", x)) < 0) |
1377 | + return ret; |
1378 | + |
1379 | + if ((ret = ca_proplist_setf(p, CA_PROP_EVENT_MOUSE_Y, "%0.0f", y)) < 0) |
1380 | + return ret; |
1381 | + |
1382 | + if (w) { |
1383 | + int width, height; |
1384 | + |
1385 | + width = gdk_screen_get_width(gtk_widget_get_screen(w)); |
1386 | + height = gdk_screen_get_height(gtk_widget_get_screen(w)); |
1387 | + |
1388 | + /* We use these strange format strings here to avoid that |
1389 | + * libc applies locale information on the formatting of |
1390 | + * floating numbers. */ |
1391 | + |
1392 | + if ((ret = ca_proplist_setf(p, CA_PROP_EVENT_MOUSE_HPOS, "%i.%03i", |
1393 | + (int) (x/(width-1)), (int) (1000.0*x/(width-1)) % 1000)) < 0) |
1394 | + return ret; |
1395 | + |
1396 | + if ((ret = ca_proplist_setf(p, CA_PROP_EVENT_MOUSE_VPOS, "%i.%03i", |
1397 | + (int) (y/(height-1)), (int) (1000.0*y/(height-1)) % 1000)) < 0) |
1398 | + return ret; |
1399 | + } |
1400 | + } |
1401 | + |
1402 | + if (e->type == GDK_BUTTON_PRESS || |
1403 | + e->type == GDK_2BUTTON_PRESS || |
1404 | + e->type == GDK_3BUTTON_PRESS || |
1405 | + e->type == GDK_BUTTON_RELEASE) { |
1406 | + |
1407 | + if ((ret = ca_proplist_setf(p, CA_PROP_EVENT_MOUSE_BUTTON, "%u", e->button.button)) < 0) |
1408 | + return ret; |
1409 | + } |
1410 | + |
1411 | + return CA_SUCCESS; |
1412 | +} |
1413 | + |
1414 | +/** |
1415 | + * ca_gtk_play_for_widget: |
1416 | + * @w: The Gtk widget to base these sound event properties on |
1417 | + * @id: The event id that can later be used to cancel this event sound |
1418 | + * using ca_context_cancel(). This can be any integer and shall be |
1419 | + * chosen be the client program. It is a good idea to pass 0 here if |
1420 | + * cancelling the sound later is not needed. If the same id is passed |
1421 | + * to multiple sounds they can be canceled with a single |
1422 | + * ca_context_cancel() call. |
1423 | + * @...: additional event properties as pairs of strings, terminated by NULL. |
1424 | + * |
1425 | + * Play a sound event for the specified widget. This will internally |
1426 | + * call ca_gtk_proplist_set_for_widget() and then merge them with the |
1427 | + * properties passed in via the NULL terminated argument |
1428 | + * list. Finally, it will call ca_context_play_full() to actually play |
1429 | + * the event sound. |
1430 | + * |
1431 | + * Returns: 0 on success, negative error code on error. |
1432 | + */ |
1433 | + |
1434 | +int ca_gtk_play_for_widget(GtkWidget *w, uint32_t id, ...) { |
1435 | + va_list ap; |
1436 | + int ret; |
1437 | + ca_proplist *p; |
1438 | + GdkScreen *s; |
1439 | + |
1440 | + ca_return_val_if_fail(w, CA_ERROR_INVALID); |
1441 | + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); |
1442 | + |
1443 | + if ((ret = ca_proplist_create(&p)) < 0) |
1444 | + return ret; |
1445 | + |
1446 | + if ((ret = ca_gtk_proplist_set_for_widget(p, w)) < 0) |
1447 | + goto fail; |
1448 | + |
1449 | + va_start(ap, id); |
1450 | + ret = ca_proplist_merge_ap(p, ap); |
1451 | + va_end(ap); |
1452 | + |
1453 | + if (ret < 0) |
1454 | + goto fail; |
1455 | + |
1456 | + s = gtk_widget_get_screen(w); |
1457 | + ret = ca_context_play_full(ca_gtk_context_get_for_screen(s), id, p, NULL, NULL); |
1458 | + |
1459 | +fail: |
1460 | + |
1461 | + ca_assert_se(ca_proplist_destroy(p) == 0); |
1462 | + |
1463 | + return ret; |
1464 | +} |
1465 | + |
1466 | +/** |
1467 | + * ca_gtk_play_for_event: |
1468 | + * @e: The Gdk event to base these sound event properties on |
1469 | + * @id: The event id that can later be used to cancel this event sound |
1470 | + * using ca_context_cancel(). This can be any integer and shall be |
1471 | + * chosen be the client program. It is a good idea to pass 0 here if |
1472 | + * cancelling the sound later is not needed. If the same id is passed |
1473 | + * to multiple sounds they can be canceled with a single |
1474 | + * ca_context_cancel() call. |
1475 | + * @...: additional event properties as pairs of strings, terminated by NULL. |
1476 | + * |
1477 | + * Play a sound event for the specified event. This will internally |
1478 | + * call ca_gtk_proplist_set_for_event() and then merge them with the |
1479 | + * properties passed in via the NULL terminated argument |
1480 | + * list. Finally, it will call ca_context_play_full() to actually play |
1481 | + * the event sound. |
1482 | + * |
1483 | + * Returns: 0 on success, negative error code on error. |
1484 | + */ |
1485 | + |
1486 | +int ca_gtk_play_for_event(GdkEvent *e, uint32_t id, ...) { |
1487 | + va_list ap; |
1488 | + int ret; |
1489 | + ca_proplist *p; |
1490 | + GdkScreen *s; |
1491 | + |
1492 | + ca_return_val_if_fail(e, CA_ERROR_INVALID); |
1493 | + ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED); |
1494 | + |
1495 | + if ((ret = ca_proplist_create(&p)) < 0) |
1496 | + return ret; |
1497 | + |
1498 | + if ((ret = ca_gtk_proplist_set_for_event(p, e)) < 0) |
1499 | + goto fail; |
1500 | + |
1501 | + va_start(ap, id); |
1502 | + ret = ca_proplist_merge_ap(p, ap); |
1503 | + va_end(ap); |
1504 | + |
1505 | + if (ret < 0) |
1506 | + goto fail; |
1507 | + |
1508 | + if (e->any.window) |
1509 | +#if GTK_CHECK_VERSION (2, 90, 7) |
1510 | + s = gdk_window_get_screen(e->any.window); |
1511 | +#else |
1512 | + s = gdk_drawable_get_screen(GDK_DRAWABLE(e->any.window)); |
1513 | +#endif |
1514 | + else |
1515 | + s = gdk_screen_get_default(); |
1516 | + |
1517 | + ret = ca_context_play_full(ca_gtk_context_get_for_screen(s), id, p, NULL, NULL); |
1518 | + |
1519 | +fail: |
1520 | + |
1521 | + ca_assert_se(ca_proplist_destroy(p) == 0); |
1522 | + |
1523 | + return ret; |
1524 | +} |
1525 | + |
1526 | +/** |
1527 | + * ca_gtk_widget_disable_sounds: |
1528 | + * @w: The Gtk widget to disable automatic event sounds for. |
1529 | + * @enable: Boolean specifying whether sound events shall be enabled or disabled for this widget. |
1530 | + * |
1531 | + * By default sound events are automatically generated for all kinds |
1532 | + * of input events. Use this function to disable this. This is |
1533 | + * intended to be used for widgets which directly generate sound |
1534 | + * events. |
1535 | + */ |
1536 | + |
1537 | +void ca_gtk_widget_disable_sounds(GtkWidget *w, gboolean enable) { |
1538 | + static GQuark disable_sound_quark = 0; |
1539 | + |
1540 | + /* This is the same quark used by libgnomeui! */ |
1541 | + if (!disable_sound_quark) |
1542 | + disable_sound_quark = g_quark_from_static_string("gnome_disable_sound_events"); |
1543 | + |
1544 | + g_object_set_qdata(G_OBJECT(w), disable_sound_quark, GINT_TO_POINTER(!!enable)); |
1545 | +} |
1546 | |
1547 | === modified file '.pc/applied-patches' |
1548 | --- .pc/applied-patches 2014-05-07 14:51:25 +0000 |
1549 | +++ .pc/applied-patches 2014-12-11 18:09:28 +0000 |
1550 | @@ -2,3 +2,4 @@ |
1551 | 02_disable_login_sound.patch |
1552 | 03_onlyshowin_unity.patch |
1553 | 04_nodisplay_autostart.patch |
1554 | +05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch |
1555 | |
1556 | === modified file 'debian/changelog' |
1557 | --- debian/changelog 2014-05-07 14:51:25 +0000 |
1558 | +++ debian/changelog 2014-12-11 18:09:28 +0000 |
1559 | @@ -1,3 +1,10 @@ |
1560 | +libcanberra (0.30-2ubuntu2) UNRELEASED; urgency=medium |
1561 | + |
1562 | + * debian/patches/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch: |
1563 | + - Import patch from upstream, to fix crashes on non X11 Gdk backends |
1564 | + |
1565 | + -- Marco Trevisan (Treviño) <marco@ubuntu.com> Thu, 11 Dec 2014 18:53:30 +0100 |
1566 | + |
1567 | libcanberra (0.30-2ubuntu1) utopic; urgency=low |
1568 | |
1569 | * Merge from Debian unstable. Remaining changes: |
1570 | |
1571 | === added file 'debian/patches/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch' |
1572 | --- debian/patches/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch 1970-01-01 00:00:00 +0000 |
1573 | +++ debian/patches/05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch 2014-12-11 18:09:28 +0000 |
1574 | @@ -0,0 +1,65 @@ |
1575 | +Description: gtk: Don't assume all GdkDisplays are GdkX11Displays |
1576 | +Author: Michael Meeks <michael.meeks@suse.com> |
1577 | +Origin: http://git.0pointer.net/libcanberra.git/commit/?id=c0620e43265 |
1578 | +Bug-Ubuntu: https://launchpad.net/bugs/1401622 |
1579 | +Applied-Upstream: commit:c0620e432650e81062c1967cc669829dbd29b310 |
1580 | + |
1581 | +--- |
1582 | +diff --git a/src/canberra-gtk-module.c b/src/canberra-gtk-module.c |
1583 | +index 67791f0..c1532ab 100644 |
1584 | +--- a/src/canberra-gtk-module.c |
1585 | ++++ b/src/canberra-gtk-module.c |
1586 | +@@ -307,6 +307,11 @@ static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) { |
1587 | + guchar *data = NULL; |
1588 | + gint ret = -1; |
1589 | + |
1590 | ++#ifdef GDK_IS_X11_DISPLAY |
1591 | ++ if (!GDK_IS_X11_DISPLAY(d)) |
1592 | ++ return 0; |
1593 | ++#endif |
1594 | ++ |
1595 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
1596 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
1597 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1598 | +@@ -335,6 +340,11 @@ static gint display_get_desktop(GdkDisplay *d) { |
1599 | + guchar *data = NULL; |
1600 | + gint ret = -1; |
1601 | + |
1602 | ++#ifdef GDK_IS_X11_DISPLAY |
1603 | ++ if (!GDK_IS_X11_DISPLAY(d)) |
1604 | ++ return 0; |
1605 | ++#endif |
1606 | ++ |
1607 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), DefaultRootWindow(GDK_DISPLAY_XDISPLAY(d)), |
1608 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_CURRENT_DESKTOP"), |
1609 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1610 | +@@ -365,6 +375,11 @@ static gboolean window_is_xembed(GdkDisplay *d, GdkWindow *w) { |
1611 | + gboolean ret = FALSE; |
1612 | + Atom xembed; |
1613 | + |
1614 | ++#ifdef GDK_IS_X11_DISPLAY |
1615 | ++ if (!GDK_IS_X11_DISPLAY(d)) |
1616 | ++ return FALSE; |
1617 | ++#endif |
1618 | ++ |
1619 | + /* Gnome Panel applets are XEMBED windows. We need to make sure we |
1620 | + * ignore them */ |
1621 | + |
1622 | +diff --git a/src/canberra-gtk.c b/src/canberra-gtk.c |
1623 | +index 34446f5..08cb668 100644 |
1624 | +--- a/src/canberra-gtk.c |
1625 | ++++ b/src/canberra-gtk.c |
1626 | +@@ -185,6 +185,11 @@ static gint window_get_desktop(GdkDisplay *d, GdkWindow *w) { |
1627 | + guchar *data = NULL; |
1628 | + gint ret = -1; |
1629 | + |
1630 | ++#ifdef GDK_IS_X11_DISPLAY |
1631 | ++ if (!GDK_IS_X11_DISPLAY(d)) |
1632 | ++ return 0; |
1633 | ++#endif |
1634 | ++ |
1635 | + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
1636 | + gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
1637 | + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1638 | +-- |
1639 | +cgit v0.9.2 |
1640 | |
1641 | === modified file 'debian/patches/series' |
1642 | --- debian/patches/series 2014-05-07 14:51:25 +0000 |
1643 | +++ debian/patches/series 2014-12-11 18:09:28 +0000 |
1644 | @@ -2,3 +2,4 @@ |
1645 | 02_disable_login_sound.patch |
1646 | 03_onlyshowin_unity.patch |
1647 | 04_nodisplay_autostart.patch |
1648 | +05_gtk_dont_assume_all_gdkdisplays_are_gdkx11displays.patch |
1649 | |
1650 | === modified file 'src/canberra-gtk-module.c' |
1651 | --- src/canberra-gtk-module.c 2014-05-07 14:51:25 +0000 |
1652 | +++ src/canberra-gtk-module.c 2014-12-11 18:09:28 +0000 |
1653 | @@ -307,6 +307,11 @@ |
1654 | guchar *data = NULL; |
1655 | gint ret = -1; |
1656 | |
1657 | +#ifdef GDK_IS_X11_DISPLAY |
1658 | + if (!GDK_IS_X11_DISPLAY(d)) |
1659 | + return 0; |
1660 | +#endif |
1661 | + |
1662 | if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
1663 | gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
1664 | 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1665 | @@ -335,6 +340,11 @@ |
1666 | guchar *data = NULL; |
1667 | gint ret = -1; |
1668 | |
1669 | +#ifdef GDK_IS_X11_DISPLAY |
1670 | + if (!GDK_IS_X11_DISPLAY(d)) |
1671 | + return 0; |
1672 | +#endif |
1673 | + |
1674 | if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), DefaultRootWindow(GDK_DISPLAY_XDISPLAY(d)), |
1675 | gdk_x11_get_xatom_by_name_for_display(d, "_NET_CURRENT_DESKTOP"), |
1676 | 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
1677 | @@ -365,6 +375,11 @@ |
1678 | gboolean ret = FALSE; |
1679 | Atom xembed; |
1680 | |
1681 | +#ifdef GDK_IS_X11_DISPLAY |
1682 | + if (!GDK_IS_X11_DISPLAY(d)) |
1683 | + return FALSE; |
1684 | +#endif |
1685 | + |
1686 | /* Gnome Panel applets are XEMBED windows. We need to make sure we |
1687 | * ignore them */ |
1688 | |
1689 | |
1690 | === modified file 'src/canberra-gtk.c' |
1691 | --- src/canberra-gtk.c 2010-12-08 11:28:26 +0000 |
1692 | +++ src/canberra-gtk.c 2014-12-11 18:09:28 +0000 |
1693 | @@ -185,6 +185,11 @@ |
1694 | guchar *data = NULL; |
1695 | gint ret = -1; |
1696 | |
1697 | +#ifdef GDK_IS_X11_DISPLAY |
1698 | + if (!GDK_IS_X11_DISPLAY(d)) |
1699 | + return 0; |
1700 | +#endif |
1701 | + |
1702 | if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), |
1703 | gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_DESKTOP"), |
1704 | 0, G_MAXLONG, False, XA_CARDINAL, &type_return, |
Thanks, but isn't our package built with a version of GTK which has that define set?