From f7081c9e82c7dea2887bb5e23f8c20c01aa703b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Jun 2008 13:00:58 +0000 Subject: extend the the gtk module git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@60 01b60673-d06a-42c0-afdd-89cb8e0f78ac --- src/Makefile.am | 36 ++++++- src/canberra-gtk-module.c | 261 +++++++++++++++++++++++++++++++++++++--------- src/driver.h | 1 + src/dso.c | 54 ++++++++-- 4 files changed, 288 insertions(+), 64 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 3acf30e..01e8b80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,8 @@ libcanberra_la_CFLAGS = \ $(VORBIS_CFLAGS) libcanberra_la_LIBADD = \ $(VORBIS_LIBS) +libcanberra_la_LDFLAGS = \ + -export-dynamic if HAVE_PULSE if BUILTIN_PULSE @@ -70,9 +72,17 @@ lib_LTLIBRARIES += \ libcanberra_pulse_la_SOURCES = \ pulse.c libcanberra_pulse_la_CFLAGS = \ - $(PULSE_CFLAGS) + $(PULSE_CFLAGS) \ + -Ddriver_open=pulse_driver_open \ + -Ddriver_destroy=pulse_driver_destroy \ + -Ddriver_change_device=pulse_driver_change_device \ + -Ddriver_change_props=pulse_driver_change_props \ + -Ddriver_play=pulse_driver_play \ + -Ddriver_cancel=pulse_driver_cancel \ + -Ddriver_cache=pulse_driver_cache libcanberra_pulse_la_LIBADD = \ - $(PULSE_LIBS) + $(PULSE_LIBS) \ + libcanberra.la libcanberra_pulse_la_LDFLAGS = \ -avoid-version -module -export-dynamic @@ -97,9 +107,17 @@ lib_LTLIBRARIES += \ libcanberra_alsa_la_SOURCES = \ alsa.c libcanberra_alsa_la_CFLAGS = \ - $(ALSA_CFLAGS) + $(ALSA_CFLAGS) \ + -Ddriver_open=alsa_driver_open \ + -Ddriver_destroy=alsa_driver_destroy \ + -Ddriver_change_device=alsa_driver_change_device \ + -Ddriver_change_props=alsa_driver_change_props \ + -Ddriver_play=alsa_driver_play \ + -Ddriver_cancel=alsa_driver_cancel \ + -Ddriver_cache=alsa_driver_cache libcanberra_alsa_la_LIBADD = \ - $(ALSA_LIBS) + $(ALSA_LIBS) \ + libcanberra.la libcanberra_alsa_la_LDFLAGS = \ -avoid-version -module -export-dynamic @@ -119,6 +137,16 @@ lib_LTLIBRARIES += \ libcanberra_null_la_SOURCES = \ null.c +libcanberra_null_la_CFLAGS = \ + -Ddriver_open=null_driver_open \ + -Ddriver_destroy=null_driver_destroy \ + -Ddriver_change_device=null_driver_change_device \ + -Ddriver_change_props=null_driver_change_props \ + -Ddriver_play=null_driver_play \ + -Ddriver_cancel=null_driver_cancel \ + -Ddriver_cache=null_driver_cache +libcanberra_null_la_LIBADD = \ + libcanberra.la libcanberra_null_la_LDFLAGS = \ -avoid-version -module -export-dynamic endif diff --git a/src/canberra-gtk-module.c b/src/canberra-gtk-module.c index c241f1f..783d04d 100644 --- a/src/canberra-gtk-module.c +++ b/src/canberra-gtk-module.c @@ -25,6 +25,8 @@ #endif #include +#include +#include #include "canberra-gtk.h" @@ -63,11 +65,15 @@ typedef struct { menu-popup menu-popdown menu-replace + tooltip-popup + tooltip-popdown TODO: drag-start drag-accept drag-fail + expander-toggle-on + expander-toggle-off */ @@ -86,9 +92,12 @@ static guint signal_id_button_released, signal_id_widget_window_state_event, signal_id_notebook_switch_page, - signal_id_tree_view_move_cursor; + signal_id_tree_view_cursor_changed, + signal_id_icon_view_selection_changed; -static GQuark disable_sound_quark; +static GQuark + disable_sound_quark, + was_hidden_quark; /* Make sure GCC doesn't warn us about a missing prototype for this * exported function */ @@ -172,21 +181,55 @@ static void free_sound_event(SoundEventData *d) { g_slice_free(SoundEventData, d); } +static gboolean is_menu_hint(GdkWindowTypeHint hint) { + return + hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU || + hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU || + hint == GDK_WINDOW_TYPE_HINT_MENU; +} + static SoundEventData* filter_sound_event(SoundEventData *d) { - GList *i; + GList *i, *n; do { - for (i = sound_event_queue.head; i; i = i->next) { - SoundEventData *j = i->data; + for (i = sound_event_queue.head; i; i = n) { + SoundEventData *j; + + j = i->data; + n = i->next; if (d->object == j->object) { + /* Let's drop a show event immediately followed by a + * hide event */ + + if (d->signal_id == signal_id_widget_show && + j->signal_id == signal_id_widget_hide) { + + free_sound_event(d); + free_sound_event(j); + g_queue_delete_link(&sound_event_queue, i); + + return NULL; + } + /* Let's drop widget hide events in favour of dialog - * response */ + * response. + * + * Let's drop widget window state events in favour of + * widget hide/show. + * + * Let's drop double events */ + + if ((d->signal_id == signal_id_widget_hide && + j->signal_id == signal_id_dialog_response) || - if (d->signal_id == signal_id_widget_hide && - j->signal_id == signal_id_dialog_response) { + (d->signal_id == signal_id_widget_window_state_event && + j->signal_id == signal_id_widget_hide) || + + (d->signal_id == signal_id_widget_window_state_event && + j->signal_id == signal_id_widget_show)) { free_sound_event(d); d = j; @@ -194,13 +237,45 @@ static SoundEventData* filter_sound_event(SoundEventData *d) { break; } + if ((d->signal_id == signal_id_dialog_response && + j->signal_id == signal_id_widget_hide) || - if (d->signal_id == signal_id_dialog_response && - j->signal_id == signal_id_widget_hide) { + (d->signal_id == signal_id_widget_show && + j->signal_id == signal_id_widget_window_state_event) || + + (d->signal_id == signal_id_widget_hide && + j->signal_id == signal_id_widget_window_state_event) || + + (d->signal_id == j->signal_id)) { free_sound_event(j); g_queue_delete_link(&sound_event_queue, i); } + + } else if (GTK_IS_WINDOW(d->object) && GTK_IS_WINDOW(j->object)) { + + GdkWindowTypeHint dhint, jhint; + + dhint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); + jhint = gtk_window_get_type_hint(GTK_WINDOW(j->object)); + + if (is_menu_hint(dhint) && is_menu_hint(jhint)) { + + if (d->signal_id == signal_id_widget_hide && + j->signal_id == signal_id_widget_show) { + free_sound_event(d); + d = j; + g_queue_delete_link(&sound_event_queue, i); + break; + } + + if (d->signal_id == signal_id_widget_show && + j->signal_id == signal_id_widget_hide) { + + free_sound_event(j); + g_queue_delete_link(&sound_event_queue, i); + } + } } } @@ -213,38 +288,56 @@ static SoundEventData* filter_sound_event(SoundEventData *d) { return d; } +static gboolean is_hidden(GdkDisplay *d, GdkWindow *w) { + Atom type_return; + gint format_return; + gulong nitems_return; + gulong bytes_after_return; + guchar *data = NULL; + gboolean r = FALSE; + + if (XGetWindowProperty(GDK_DISPLAY_XDISPLAY(d), GDK_WINDOW_XID(w), + gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_STATE"), + 0, G_MAXLONG, False, XA_ATOM, &type_return, + &format_return, &nitems_return, &bytes_after_return, + &data) != Success) + return FALSE; + + if (type_return == XA_ATOM && format_return == 32 && data) { + unsigned i; + + for (i = 0; i < nitems_return; i++) { + Atom atom = ((Atom*) data)[i]; + + if (atom == gdk_x11_get_xatom_by_name_for_display(d, "_NET_WM_STATE_HIDDEN")) { + r = TRUE; + break; + } + } + } + + if (type_return != None && data != NULL) + XFree (data); + + return r; +} + static void dispatch_sound_event(SoundEventData *d) { int ret = CA_SUCCESS; - static gboolean menu_is_popped_up = FALSE; - - if (!GTK_WIDGET_DRAWABLE(d->object)) - return; + static gboolean menu_is_popped_up = TRUE; if (g_object_get_qdata(d->object, disable_sound_quark)) return; if (d->signal_id == signal_id_widget_show) { - gboolean played_sound = FALSE; + GdkWindowTypeHint hint; - /* Show signals for non-windows have already been filtered out + /* Show/hide signals for non-windows have already been filtered out * by the emission hook! */ - if (GTK_IS_MESSAGE_DIALOG(d->object)) { - GtkMessageType mt; - const char *id; - - g_object_get(d->object, "message_type", &mt, NULL); - - if ((id = translate_message_tye(mt))) { - ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, - CA_PROP_EVENT_ID, id, - CA_PROP_EVENT_DESCRIPTION, "Message dialog shown", - CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", - NULL); - played_sound = TRUE; - } + hint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); - } else if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { + if (is_menu_hint(hint)) { if (menu_is_popped_up) { ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, @@ -260,16 +353,46 @@ static void dispatch_sound_event(SoundEventData *d) { NULL); } - played_sound = TRUE; menu_is_popped_up = TRUE; - } - if (!played_sound) + } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) { + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, - CA_PROP_EVENT_ID, "window-new", - CA_PROP_EVENT_DESCRIPTION, "Window shown", + CA_PROP_EVENT_ID, "tooltip-popup", + CA_PROP_EVENT_DESCRIPTION, "Tooltip popped up", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); + + } else if (hint == GDK_WINDOW_TYPE_HINT_NORMAL || + hint == GDK_WINDOW_TYPE_HINT_DIALOG) { + + gboolean played_sound = FALSE; + + if (GTK_IS_MESSAGE_DIALOG(d->object)) { + GtkMessageType mt; + const char *id; + + g_object_get(d->object, "message_type", &mt, NULL); + + if ((id = translate_message_tye(mt))) { + + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, id, + CA_PROP_EVENT_DESCRIPTION, "Message dialog shown", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + played_sound = TRUE; + } + + } + + if (!played_sound) + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "window-new", + CA_PROP_EVENT_DESCRIPTION, "Window shown", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + } } if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) { @@ -295,34 +418,53 @@ static void dispatch_sound_event(SoundEventData *d) { } } else if (d->signal_id == signal_id_widget_hide) { - gboolean played_sound = FALSE; + GdkWindowTypeHint hint; + + hint = gtk_window_get_type_hint(GTK_WINDOW(d->object)); + + if (is_menu_hint(hint)) { + + if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { + + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "menu-popdown", + CA_PROP_EVENT_DESCRIPTION, "Menu popped down", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + } + + menu_is_popped_up = FALSE; - if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { + } else if (hint == GDK_WINDOW_TYPE_HINT_TOOLTIP) { ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, - CA_PROP_EVENT_ID, "menu-popdown", - CA_PROP_EVENT_DESCRIPTION, "Menu popped down", + CA_PROP_EVENT_ID, "tooltip-popdown", + CA_PROP_EVENT_DESCRIPTION, "Tooltip popped down", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); - played_sound = TRUE; - menu_is_popped_up = FALSE; - } + } else if (hint == GDK_WINDOW_TYPE_HINT_NORMAL || + hint == GDK_WINDOW_TYPE_HINT_DIALOG) { - if (!played_sound) ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, CA_PROP_EVENT_ID, "window-close", CA_PROP_EVENT_DESCRIPTION, "Window closed", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); + } } if (GTK_IS_WINDOW(d->object) && d->signal_id == signal_id_widget_window_state_event) { GdkEventWindowState *e; + gboolean h, ph; e = (GdkEventWindowState*) d->event; - if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (e->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) { + h = is_hidden(gdk_screen_get_display(gdk_event_get_screen(d->event)), e->window); + ph = !!g_object_get_qdata(d->object, was_hidden_quark); + g_object_set_qdata(d->object, was_hidden_quark, GINT_TO_POINTER(h)); + + if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && h && !ph) { ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, CA_PROP_EVENT_ID, "window-minimized", @@ -338,7 +480,7 @@ static void dispatch_sound_event(SoundEventData *d) { CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); - } else if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && !(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) { + } else if ((e->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && !(e->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && ph) { ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, CA_PROP_EVENT_ID, "window-unminimized", @@ -357,7 +499,7 @@ static void dispatch_sound_event(SoundEventData *d) { if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) { - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d))) + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d->object))) ret = ca_gtk_play_for_event(d->event, 0, CA_PROP_EVENT_ID, "button-toggle-on", CA_PROP_EVENT_DESCRIPTION, "Check menu item checked", @@ -462,7 +604,15 @@ static void dispatch_sound_event(SoundEventData *d) { NULL); } - if (GTK_IS_TREE_VIEW(d->object) && d->signal_id == signal_id_tree_view_move_cursor) { + if (GTK_IS_TREE_VIEW(d->object) && d->signal_id == signal_id_tree_view_cursor_changed) { + ret = ca_gtk_play_for_event(d->event, 0, + CA_PROP_EVENT_ID, "item-selected", + CA_PROP_EVENT_DESCRIPTION, "Item selected", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + } + + if (GTK_IS_ICON_VIEW(d->object) && d->signal_id == signal_id_icon_view_selection_changed) { ret = ca_gtk_play_for_event(d->event, 0, CA_PROP_EVENT_ID, "item-selected", CA_PROP_EVENT_DESCRIPTION, "Item selected", @@ -482,7 +632,9 @@ static gboolean idle_cb(void *userdata) { g_message("idle_cb()"); while ((d = g_queue_pop_head(&sound_event_queue))) { - d = filter_sound_event(d); + + if (!(d = filter_sound_event(d))) + continue; g_message("Dispatching signal %s on %s", g_signal_name(d->signal_id), g_type_name(G_OBJECT_TYPE(d->object))); @@ -508,6 +660,11 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu !GTK_IS_WINDOW(object)) return TRUE; + if (hint->signal_id != signal_id_widget_hide && + hint->signal_id != signal_id_dialog_response && + !GTK_WIDGET_DRAWABLE(object)) + return TRUE; + g_message("signal %s on %s", g_signal_name(hint->signal_id), g_type_name(G_OBJECT_TYPE(object))); d = g_slice_new0(SoundEventData); @@ -527,7 +684,7 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu d->arg1_is_set = TRUE; } - g_queue_push_tail(&sound_event_queue, e); + g_queue_push_tail(&sound_event_queue, d); g_message("enqueuing"); @@ -551,6 +708,7 @@ static void install_hook(GType type, const char *signal, guint *sn) { void gtk_module_init(gint *argc, gchar ***argv[]) { /* This is the same quark libgnomeui uses! */ disable_sound_quark = g_quark_from_string("gnome_disable_sound_events"); + was_hidden_quark = g_quark_from_string("canberra_was_hidden"); install_hook(GTK_TYPE_WINDOW, "show", &signal_id_widget_show); install_hook(GTK_TYPE_WINDOW, "hide", &signal_id_widget_hide); @@ -562,5 +720,6 @@ void gtk_module_init(gint *argc, gchar ***argv[]) { install_hook(GTK_TYPE_BUTTON, "released", &signal_id_button_released); install_hook(GTK_TYPE_WIDGET, "window-state-event", &signal_id_widget_window_state_event); install_hook(GTK_TYPE_NOTEBOOK, "switch-page", &signal_id_notebook_switch_page); - install_hook(GTK_TYPE_TREE_VIEW, "move-cursor", &signal_id_tree_view_move_cursor); + install_hook(GTK_TYPE_TREE_VIEW, "cursor-changed", &signal_id_tree_view_cursor_changed); + install_hook(GTK_TYPE_ICON_VIEW, "selection-changed", &signal_id_icon_view_selection_changed); } diff --git a/src/driver.h b/src/driver.h index 732a5d4..43efbf4 100644 --- a/src/driver.h +++ b/src/driver.h @@ -35,4 +35,5 @@ int driver_play(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t int driver_cancel(ca_context *c, uint32_t id); int driver_cache(ca_context *c, ca_proplist *p); + #endif diff --git a/src/dso.c b/src/dso.c index 5ff0fec..b8ed112 100644 --- a/src/dso.c +++ b/src/dso.c @@ -146,12 +146,42 @@ static int try_open(ca_context *c, const char *t) { return CA_SUCCESS; } +static void* real_dlsym(lt_module m, const char *name, const char *symbol) { + char sn[256]; + char *s; + void *r; + + ca_return_null_if_fail(m); + ca_return_null_if_fail(name); + ca_return_null_if_fail(symbol); + + snprintf(sn, sizeof(sn), "%s_%s", name, symbol); + sn[sizeof(sn)-1] = 0; + + for (s = sn; *s; s++) { + if (*s >= 'a' && *s <= 'z') + continue; + if (*s >= 'A' && *s <= 'Z') + continue; + if (*s >= '0' && *s <= '9') + continue; + + *s = '_'; + } + + if ((r = lt_dlsym(m, sn))) + return r; + + return lt_dlsym(m, symbol); +} + #define MAKE_FUNC_PTR(ret, args, x) ((ret (*) args ) (size_t) (x)) -#define GET_FUNC_PTR(module, name, ret, args) MAKE_FUNC_PTR(ret, args, lt_dlsym((module), (name))) +#define GET_FUNC_PTR(module, name, symbol, ret, args) MAKE_FUNC_PTR(ret, args, real_dlsym((module), (name), (symbol))) int driver_open(ca_context *c) { int ret; struct private_dso *p; + const char *driver; ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_return_val_if_fail(!PRIVATE_DSO(c), CA_ERROR_STATE); @@ -174,6 +204,8 @@ int driver_open(ca_context *c) { return ret; } + driver = c->driver; + } else { const char *const * e; @@ -195,17 +227,19 @@ int driver_open(ca_context *c) { driver_destroy(c); return CA_ERROR_NODRIVER; } + + driver = *e; } ca_assert(p->module); - if (!(p->driver_open = GET_FUNC_PTR(p->module, "driver_open", int, (ca_context*))) || - !(p->driver_destroy = GET_FUNC_PTR(p->module, "driver_destroy", int, (ca_context*))) || - !(p->driver_change_device = GET_FUNC_PTR(p->module, "driver_change_device", int, (ca_context*, const char *device))) || - !(p->driver_change_props = GET_FUNC_PTR(p->module, "driver_change_props", int, (ca_context *, ca_proplist *changed, ca_proplist *merged))) || - !(p->driver_play = GET_FUNC_PTR(p->module, "driver_play", int, (ca_context*, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata))) || - !(p->driver_cancel = GET_FUNC_PTR(p->module, "driver_cancel", int, (ca_context*, uint32_t id))) || - !(p->driver_cache = GET_FUNC_PTR(p->module, "driver_cache", int, (ca_context*, ca_proplist *p)))) { + if (!(p->driver_open = GET_FUNC_PTR(p->module, driver, "driver_open", int, (ca_context*))) || + !(p->driver_destroy = GET_FUNC_PTR(p->module, driver, "driver_destroy", int, (ca_context*))) || + !(p->driver_change_device = GET_FUNC_PTR(p->module, driver, "driver_change_device", int, (ca_context*, const char *device))) || + !(p->driver_change_props = GET_FUNC_PTR(p->module, driver, "driver_change_props", int, (ca_context *, ca_proplist *changed, ca_proplist *merged))) || + !(p->driver_play = GET_FUNC_PTR(p->module, driver, "driver_play", int, (ca_context*, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata))) || + !(p->driver_cancel = GET_FUNC_PTR(p->module, driver, "driver_cancel", int, (ca_context*, uint32_t id))) || + !(p->driver_cache = GET_FUNC_PTR(p->module, driver, "driver_cache", int, (ca_context*, ca_proplist *p)))) { driver_destroy(c); return CA_ERROR_CORRUPT; @@ -234,8 +268,10 @@ int driver_destroy(ca_context *c) { if (p->module) lt_dlclose(p->module); - if (p->ltdl_initialized) + if (p->ltdl_initialized) { lt_dlexit(); + p->ltdl_initialized = FALSE; + } ca_free(p); -- cgit