diff options
| author | Lennart Poettering <lennart@poettering.net> | 2008-06-10 13:00:58 +0000 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2008-06-10 13:00:58 +0000 | 
| commit | f7081c9e82c7dea2887bb5e23f8c20c01aa703b0 (patch) | |
| tree | 1b32762c13c06ea9cc0ab61a26001b3f9be2f8c6 | |
| parent | b1dccb6dfdab87e5e7d84e7e687eef93319aa332 (diff) | |
extend the the gtk module
git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@60 01b60673-d06a-42c0-afdd-89cb8e0f78ac
| -rw-r--r-- | src/Makefile.am | 36 | ||||
| -rw-r--r-- | src/canberra-gtk-module.c | 261 | ||||
| -rw-r--r-- | src/driver.h | 1 | ||||
| -rw-r--r-- | 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 <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <X11/Xatom.h>  #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 @@ -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);  | 
