From c4adbcd5484d701351903259a7579d94f4656ee6 Mon Sep 17 00:00:00 2001
From: Peter Wang <novalazy@gmail.com>
Date: Thu, 25 Nov 2010 11:55:40 +1100
Subject: [PATCH] WIP single gtk master loop

---
 .../allegro5/internal/aintern_native_dialog.h      |    1 +
 addons/native_dialog/gtk_dialog.c                  |  355 ++++++++++++--------
 addons/native_dialog/textlog.c                     |   33 ++-
 3 files changed, 234 insertions(+), 155 deletions(-)

diff --git a/addons/native_dialog/allegro5/internal/aintern_native_dialog.h b/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
index 2e1ad9c..d141611 100644
--- a/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
+++ b/addons/native_dialog/allegro5/internal/aintern_native_dialog.h
@@ -37,6 +37,7 @@ struct ALLEGRO_NATIVE_DIALOG
    /* Only used by platform implementations. */
    bool is_active;
    void *window;
+   void *async_queue;
 };
 
 extern bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
diff --git a/addons/native_dialog/gtk_dialog.c b/addons/native_dialog/gtk_dialog.c
index fd2e15c..84c0121 100644
--- a/addons/native_dialog/gtk_dialog.c
+++ b/addons/native_dialog/gtk_dialog.c
@@ -21,140 +21,79 @@
 
 ALLEGRO_DEBUG_CHANNEL("gtk")
 
-static int global_counter = 0;
-static GStaticMutex gtk_lock = G_STATIC_MUTEX_INIT;
-static GCond *gtk_cond = NULL;
-static GThread *gtk_thread = NULL;
+typedef struct {
+   ALLEGRO_DISPLAY         *display;
+   ALLEGRO_NATIVE_DIALOG   *dialog;
+} Msg;
+
+#define ACK_OPENED   ((void *)0x1234)
+#define ACK_CLOSED   ((void *)0x5678)
 
-/* If dialogs were always blocking, the implementation would be simple in GTK:
- * - Start GTK (gtk_init) and create the GTK dialog.
- * - Run GTK (gtk_main).
- * - Exit GTK from dialog callback (gtk_main_quit) which makes the gtk_main()
- *   above return.
+/*---------------------------------------------------------------------------*/
+/* GTK thread                                                                */
+/*---------------------------------------------------------------------------*/
+
+/* GTK is not thread safe.  We launch a single thread which runs the GTK main
+ * loop, and it is the only thread which calls into GTK.  (g_timeout_add may be
+ * called from other threads without locking.)
  *
- * However, this is only if we forget about threads. With threads, things look
- * different - as they make it possible to show more than one native dialog
- * at a time. But gtk_init/gtk_main can of course only be called once by an
- * application (without calling gtk_main_quit first). Here a simple solution
- * would be to have GTK be running as long as the addon is initialized - but we
- * did a somewhat more complex solution which has GTK run in its own thread as
- * long as at least one dialog is shown.
+ * We used to attempt to use gdk_threads_enter/gdk_threads_leave but hit
+ * some problems with deadlocks so switched to this.
  */
 
-static void *gtk_thread_func(void *data)
-{
-   gdk_threads_enter();
-   ALLEGRO_DEBUG("Entering gtk_main.\n");
-   gtk_main();
-   ALLEGRO_DEBUG("Leaving gtk_main.\n");
-   gdk_threads_leave();
-   return data;
-}
+static GStaticMutex gtk_lock = G_STATIC_MUTEX_INIT;
+static GThread *gtk_thread = NULL;
+static int window_counter = 0;
 
-static bool gtk_start_and_lock(ALLEGRO_NATIVE_DIALOG *fd)
+static void *gtk_thread_func(void *data)
 {
    int argc = 0;
    char **argv = NULL;
+   (void)data;
 
-   if (!g_thread_supported())
-      g_thread_init(NULL);
-
-   g_static_mutex_lock(&gtk_lock);
-
-   global_counter++;
-   if (global_counter == 1) {
-      gdk_threads_init();
+   ALLEGRO_DEBUG("Calling gtk_init_check.\n");
+   if (!gtk_init_check(&argc, &argv)) {
+      ALLEGRO_ERROR("GTK initialisation failed.\n");
+      return NULL;
+   }
 
-      if (!gtk_init_check(&argc, &argv)) {
-         global_counter--;
-         g_static_mutex_unlock(&gtk_lock);
-         ALLEGRO_WARN("GTK init failed.\n");
-         return false;
-      }
+   ALLEGRO_INFO("GTK started.\n");
+   gtk_main();
 
-      gtk_cond = g_cond_new();
-      gtk_thread = g_thread_create(gtk_thread_func, NULL, TRUE, NULL);
-      ALLEGRO_INFO("GTK started.\n");
-   }
+   ALLEGRO_DEBUG("Stopping GTK.\n");
+   g_static_mutex_lock(&gtk_lock);
+   gtk_thread = NULL;
+   g_static_mutex_unlock(&gtk_lock);
 
-   gdk_threads_enter();
-   fd->is_active = true;
-   return true;
+   ALLEGRO_INFO("GTK stopped.\n");
+   // XXX does this thread need joining?
+   return NULL;
 }
 
-static void gtk_unlock_and_wait(ALLEGRO_NATIVE_DIALOG *nd)
+static bool gmaster_ensure_start(void)
 {
-   gdk_threads_leave();
+   bool ok = true;
 
-   while (nd->is_active)
-      g_cond_wait(gtk_cond, g_static_mutex_get_mutex(&gtk_lock));
-
-   global_counter--;
-   if (global_counter == 0) {
-      gtk_main_quit();
-      g_thread_join(gtk_thread);
-      gtk_thread = NULL;
-      g_cond_free(gtk_cond);
-      gtk_cond = NULL;
-   }
-
-   g_static_mutex_unlock(&gtk_lock);
-}
+   if (!g_thread_supported())
+      g_thread_init(NULL);
 
-static void set_dialog_inactive(ALLEGRO_NATIVE_DIALOG *nd)
-{
    g_static_mutex_lock(&gtk_lock);
 
-   nd->is_active = false;
-   g_cond_broadcast(gtk_cond);
+   if (!gtk_thread) {
+      gtk_thread = g_thread_create(gtk_thread_func, NULL, TRUE, NULL);
+      // XXX Wait until the thread has really started.
+      al_rest(0.2);
+      // XXX ok might need to change
+   }
 
    g_static_mutex_unlock(&gtk_lock);
-}
-
-static void destroy(GtkWidget *w, gpointer data)
-{
-   ALLEGRO_NATIVE_DIALOG *nd = data;
-   (void)w;
-   set_dialog_inactive(nd);
-}
 
-static void ok(GtkWidget *w, GtkFileSelection *fs)
-{
-   ALLEGRO_NATIVE_DIALOG *fc;
-   fc = g_object_get_data(G_OBJECT(w), "ALLEGRO_NATIVE_DIALOG");
-   gchar **paths = gtk_file_selection_get_selections(fs);
-   int n = 0, i;
-   while (paths[n]) {
-      n++;
-   }
-   fc->fc_path_count = n;
-   fc->fc_paths = al_malloc(n * sizeof(void *));
-   for (i = 0; i < n; i++)
-      fc->fc_paths[i] = al_create_path(paths[i]);
-   g_strfreev(paths);
+   return ok;
 }
 
-static void response(GtkDialog *dialog, gint response_id, gpointer user_data)
-{
-   ALLEGRO_NATIVE_DIALOG *nd = (void *)user_data;
-   (void)dialog;
-   switch (response_id) {
-      case GTK_RESPONSE_DELETE_EVENT:
-         nd->mb_pressed_button = 0;
-         break;
-      case GTK_RESPONSE_YES:
-      case GTK_RESPONSE_OK:
-         nd->mb_pressed_button = 1;
-         break;
-      case GTK_RESPONSE_NO:
-      case GTK_RESPONSE_CANCEL:
-         nd->mb_pressed_button = 2;
-         break;
-      default:
-         nd->mb_pressed_button = response_id;
-   }
-   set_dialog_inactive(nd);
-}
+/*---------------------------------------------------------------------------*/
+/* Shared functions                                                          */
+/*---------------------------------------------------------------------------*/
 
 #ifdef ALLEGRO_WITH_XWINDOWS
 static void really_make_transient(GtkWidget *window, ALLEGRO_DISPLAY_XGLX *glx)
@@ -186,13 +125,41 @@ static void make_transient(ALLEGRO_DISPLAY *display, GtkWidget *window)
    #endif
 }
 
-bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
-   ALLEGRO_NATIVE_DIALOG *fd)
+static void dialog_destroy(GtkWidget *w, gpointer data)
 {
-   GtkWidget *window;
+   ALLEGRO_NATIVE_DIALOG *nd = data;
+   (void)w;
+   ASSERT(nd->async_queue);
+   g_async_queue_push(nd->async_queue, ACK_CLOSED);
+}
 
-   if (!gtk_start_and_lock(fd))
-      return false;
+/*---------------------------------------------------------------------------*/
+/* File selector                                                             */
+/*---------------------------------------------------------------------------*/
+
+static void filesel_ok(GtkWidget *w, GtkFileSelection *fs)
+{
+   ALLEGRO_NATIVE_DIALOG *fc;
+   fc = g_object_get_data(G_OBJECT(w), "ALLEGRO_NATIVE_DIALOG");
+   gchar **paths = gtk_file_selection_get_selections(fs);
+   int n = 0, i;
+   while (paths[n]) {
+      n++;
+   }
+   fc->fc_path_count = n;
+   fc->fc_paths = al_malloc(n * sizeof(void *));
+   for (i = 0; i < n; i++)
+      fc->fc_paths[i] = al_create_path(paths[i]);
+   g_strfreev(paths);
+}
+
+/* [gtk thread] */
+static gboolean create_native_file_dialog(gpointer data)
+{
+   Msg *msg = data;
+   ALLEGRO_DISPLAY *display = msg->display;
+   ALLEGRO_NATIVE_DIALOG *fd = msg->dialog;
+   GtkWidget *window;
 
    /* Create a new file selection widget */
    window = gtk_file_selection_new(al_cstr(fd->title));
@@ -200,11 +167,11 @@ bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
    make_transient(display, window);
 
    /* Connect the destroy signal */
-   g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), fd);
+   g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(dialog_destroy), fd);
 
    /* Connect the ok_button */
    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
-                    "clicked", G_CALLBACK(ok), (gpointer) window);
+                    "clicked", G_CALLBACK(filesel_ok), (gpointer) window);
 
    /* Connect both buttons to gtk_widget_destroy */
    g_signal_connect_swapped(GTK_FILE_SELECTION(window)->ok_button,
@@ -224,18 +191,75 @@ bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
       gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(window), true);
 
    gtk_widget_show(window);
+   return FALSE;
+}
+
+/* [user thread] */
+bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
+   ALLEGRO_NATIVE_DIALOG *fd)
+{
+   Msg msg;
+
+   if (!gmaster_ensure_start())
+      return false;
+
+   fd->async_queue = g_async_queue_new();
+
+   msg.display = display;
+   msg.dialog = fd;
+   g_timeout_add(0, create_native_file_dialog, &msg);
 
-   gtk_unlock_and_wait(fd);
+   /* Wait for a signal that the window is closed. */
+   while (g_async_queue_pop(fd->async_queue) != ACK_CLOSED)
+      ;
+   g_async_queue_unref(fd->async_queue);
+   fd->async_queue = NULL;
    return true;
 }
 
-int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
-   ALLEGRO_NATIVE_DIALOG *fd)
+/*---------------------------------------------------------------------------*/
+/* Message box                                                               */
+/*---------------------------------------------------------------------------*/
+
+static void msgbox_response(GtkDialog *dialog, gint response_id,
+   gpointer user_data)
 {
-   GtkWidget *window;
+   ALLEGRO_NATIVE_DIALOG *nd = (void *)user_data;
+   (void)dialog;
+   switch (response_id) {
+      case GTK_RESPONSE_DELETE_EVENT:
+         nd->mb_pressed_button = 0;
+         break;
+      case GTK_RESPONSE_YES:
+      case GTK_RESPONSE_OK:
+         nd->mb_pressed_button = 1;
+         break;
+      case GTK_RESPONSE_NO:
+      case GTK_RESPONSE_CANCEL:
+         nd->mb_pressed_button = 2;
+         break;
+      default:
+         nd->mb_pressed_button = response_id;
+   }
+   //set_dialog_inactive(nd);
 
-   if (!gtk_start_and_lock(fd))
-      return 0; /* "cancelled" */
+   ASSERT(nd->async_queue);
+   g_async_queue_push(nd->async_queue, ACK_CLOSED);
+
+   g_static_mutex_lock(&gtk_lock);
+   if (--window_counter == 0) {
+      gtk_main_quit();
+   }
+   g_static_mutex_unlock(&gtk_lock);
+}
+
+/* [gtk thread] */
+static gboolean create_native_message_box(gpointer data)
+{
+   Msg *msg = data;
+   ALLEGRO_DISPLAY *display = msg->display;
+   ALLEGRO_NATIVE_DIALOG *fd = msg->dialog;
+   GtkWidget *window;
 
    /* Create a new file selection widget */
    GtkMessageType type = GTK_MESSAGE_INFO;
@@ -277,16 +301,40 @@ int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
 
    gtk_window_set_title(GTK_WINDOW(window), al_cstr(fd->title));
 
-   g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(response), fd);
+   g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(msgbox_response), fd);
    g_signal_connect_swapped(G_OBJECT(window), "response", G_CALLBACK(gtk_widget_destroy), window);
 
    gtk_widget_show(window);
+   return FALSE;
+}
+
+/* [user thread] */
+int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
+   ALLEGRO_NATIVE_DIALOG *fd)
+{
+   Msg msg;
+
+   if (!gmaster_ensure_start())
+      return 0; /* "cancelled" */
 
-   gtk_unlock_and_wait(fd);
+   fd->async_queue = g_async_queue_new();
 
+   msg.display = display;
+   msg.dialog = fd;
+   g_timeout_add(0, create_native_message_box, &msg);
+
+   /* Wait for a signal that the window is closed. */
+   while (g_async_queue_pop(fd->async_queue) != ACK_CLOSED)
+      ;
+   g_async_queue_unref(fd->async_queue);
+   fd->async_queue = NULL;
    return fd->mb_pressed_button;
 }
 
+/*---------------------------------------------------------------------------*/
+/* Text log                                                                  */
+/*---------------------------------------------------------------------------*/
+
 static void emit_close_event(ALLEGRO_NATIVE_DIALOG *textlog, bool keypress)
 {
    ALLEGRO_EVENT event;
@@ -325,14 +373,11 @@ static gboolean textlog_key_press(GtkWidget *w, GdkEventKey *gevent,
    return FALSE;
 }
 
-bool _al_open_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
+/* [gtk thread] */
+static gboolean create_native_text_log(gpointer data)
 {
-   al_lock_mutex(textlog->tl_text_mutex);
-
-   if (!gtk_start_and_lock(textlog)) {
-      al_unlock_mutex(textlog->tl_text_mutex);
-      return false;
-   }
+   Msg *msg = data;
+   ALLEGRO_NATIVE_DIALOG *textlog = msg->dialog;
 
    /* Create a new text log window. */
    GtkWidget *top = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -346,7 +391,7 @@ bool _al_open_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
       g_signal_connect(G_OBJECT(top), "key-press-event", G_CALLBACK(textlog_key_press), textlog);
    }
    g_signal_connect(G_OBJECT(top), "delete-event", G_CALLBACK(textlog_delete), textlog);
-   g_signal_connect(G_OBJECT(top), "destroy", G_CALLBACK(destroy), textlog);
+   g_signal_connect(G_OBJECT(top), "destroy", G_CALLBACK(dialog_destroy), textlog);
    GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
@@ -366,23 +411,33 @@ bool _al_open_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
    textlog->window = top;
    textlog->tl_textview = view;
 
-   /* Now notify al_show_native_textlog that the text log is ready. */
-   textlog->tl_done = true;
-   al_signal_cond(textlog->tl_text_cond);
-   al_unlock_mutex(textlog->tl_text_mutex);
+   // Signal.
+   ASSERT(textlog->async_queue);
+   g_async_queue_push(textlog->async_queue, ACK_OPENED);
+   return FALSE;
+}
 
-   /* Keep running until the textlog is closed. */
-   gtk_unlock_and_wait(textlog);
+/* [user thread] */
+bool _al_open_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
+{
+   Msg msg;
 
-   /* Notify everyone that we're gone. */
-   al_lock_mutex(textlog->tl_text_mutex);
-   textlog->tl_done = true;
-   al_signal_cond(textlog->tl_text_cond);
-   al_unlock_mutex(textlog->tl_text_mutex);
+   if (!gmaster_ensure_start())
+      return false;
+
+   textlog->async_queue = g_async_queue_new();
+
+   msg.display = NULL;
+   msg.dialog = textlog;
+   g_timeout_add(0, create_native_text_log, &msg);
+
+   while (g_async_queue_pop(textlog->async_queue) != ACK_OPENED)
+      ;
 
    return true;
 }
 
+/* [gtk thread] */
 static gboolean do_append_native_text_log(gpointer data)
 {
    ALLEGRO_NATIVE_DIALOG *textlog = data;
@@ -408,6 +463,7 @@ static gboolean do_append_native_text_log(gpointer data)
    return false;
 }
 
+/* [user thread] */
 void _al_append_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
 {
    if (textlog->tl_have_pending)
@@ -417,13 +473,14 @@ void _al_append_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
    gdk_threads_add_timeout(100, do_append_native_text_log, textlog);
 }
 
+/* [gtk thread] */
 static gboolean do_close_native_text_log(gpointer data)
 {
    ALLEGRO_NATIVE_DIALOG *textlog = data;
 
    /* Delay closing until appends are completed. */
    if (textlog->tl_have_pending)
-      return true;
+      return TRUE;
 
    /* This causes the GTK window as well as all of its children to
     * be freed. Further it will call the destroy function which we
@@ -431,12 +488,18 @@ static gboolean do_close_native_text_log(gpointer data)
     * gtk thread to quit.
     */
    gtk_widget_destroy(textlog->window);
-   return false;
+   return FALSE;
 }
 
+/* [user thread] */
 void _al_close_native_text_log(ALLEGRO_NATIVE_DIALOG *textlog)
 {
    gdk_threads_add_timeout(0, do_close_native_text_log, textlog);
+
+   while (g_async_queue_pop(textlog->async_queue) != ACK_CLOSED)
+      ;
+   g_async_queue_unref(textlog->async_queue);
+   textlog->async_queue = NULL;
 }
 
 /* vim: set sts=3 sw=3 et: */
diff --git a/addons/native_dialog/textlog.c b/addons/native_dialog/textlog.c
index 1014296..0fe08aa 100644
--- a/addons/native_dialog/textlog.c
+++ b/addons/native_dialog/textlog.c
@@ -7,13 +7,17 @@
 #include "allegro5/internal/aintern_dtor.h"
 #include "allegro5/internal/aintern_system.h"
 
-/* Text logs are only implemented for GTK+ and Windows so far. */
-#if defined(ALLEGRO_CFG_NATIVE_DIALOG_GTK) || defined(ALLEGRO_CFG_NATIVE_DIALOG_WINDOWS) || defined(ALLEGRO_CFG_NATIVE_DIALOG_OSX)
-   #define HAVE_TEXT_LOG
+#if defined(ALLEGRO_CFG_NATIVE_DIALOG_WINDOWS) || defined(ALLEGRO_CFG_NATIVE_DIALOG_OSX)
+   /* The Windows and OSX text log implementations expect this module to call
+    * _al_open_native_text_log from a dedicated thread.  The GTK implementation
+    * was changed in the mean time.  It would be nice to reduce the use of
+    * threads in the other implementations if possible.
+    */
+   #define TEXT_LOG_EXTRA_THREAD
 #endif
 
 
-#ifdef HAVE_TEXT_LOG
+#ifdef TEXT_LOG_EXTRA_THREAD
 /* This will only return when the text window is closed. */
 static void *textlog_thread_proc(ALLEGRO_THREAD *thread, void *arg)
 {
@@ -36,21 +40,24 @@ static void *textlog_thread_proc(ALLEGRO_THREAD *thread, void *arg)
 ALLEGRO_TEXTLOG *al_open_native_text_log(char const *title, int flags)
 {
    ALLEGRO_NATIVE_DIALOG *textlog = NULL;
+   bool error;
 
    /* Avoid warnings when log windows are unimplemented. */
    (void)title;
    (void)flags;
 
-#ifdef HAVE_TEXT_LOG
    textlog = al_calloc(1, sizeof *textlog);
    textlog->title = al_ustr_new(title);
    textlog->flags = flags;
+#ifdef TEXT_LOG_EXTRA_THREAD
    textlog->tl_thread = al_create_thread(textlog_thread_proc, textlog);
+#endif
    textlog->tl_text_cond = al_create_cond();
    textlog->tl_text_mutex = al_create_mutex();
    textlog->tl_pending_text = al_ustr_new("");
    al_init_user_event_source(&textlog->tl_events);
 
+#ifdef TEXT_LOG_EXTRA_THREAD
    /* Unlike the other dialogs, this one never blocks as the intended
     * use case is a log window running in the background for debugging
     * purposes when no console can be used. Therefore we have it run
@@ -64,15 +71,19 @@ ALLEGRO_TEXTLOG *al_open_native_text_log(char const *title, int flags)
       al_wait_cond(textlog->tl_text_cond, textlog->tl_text_mutex);
    }
    al_unlock_mutex(textlog->tl_text_mutex);
+   error = textlog->tl_init_error;
+#else
+   error = !_al_open_native_text_log(textlog);
+#endif
 
-   if (textlog->tl_init_error) {
+   if (error) {
+      // XXX check this works
       al_close_native_text_log((ALLEGRO_TEXTLOG *)textlog);
       return NULL;
    }
 
    _al_register_destructor(_al_dtor_list, textlog,
       (void (*)(void *))al_close_native_text_log);
-#endif
 
    return (ALLEGRO_TEXTLOG *)textlog;
 }
@@ -87,16 +98,19 @@ void al_close_native_text_log(ALLEGRO_TEXTLOG *textlog)
    if (!dialog)
       return;
 
-#ifdef HAVE_TEXT_LOG
    if (!dialog->tl_init_error) {
+#ifdef TEXT_LOG_EXTRA_THREAD
       al_lock_mutex(dialog->tl_text_mutex);
       dialog->tl_done = false;
+#endif
 
       _al_close_native_text_log(dialog);
 
+#ifdef TEXT_LOG_EXTRA_THREAD
       while (!dialog->tl_done) {
          al_wait_cond(dialog->tl_text_cond, dialog->tl_text_mutex);
       }
+#endif
 
       _al_unregister_destructor(_al_dtor_list, dialog);
    }
@@ -108,11 +122,12 @@ void al_close_native_text_log(ALLEGRO_TEXTLOG *textlog)
 
    al_unlock_mutex(dialog->tl_text_mutex);
 
+#ifdef TEXT_LOG_EXTRA_THREAD
    al_destroy_thread(dialog->tl_thread);
+#endif
    al_destroy_cond(dialog->tl_text_cond);
    al_destroy_mutex(dialog->tl_text_mutex);
    al_free(dialog);
-#endif
 }
 
 
-- 
1.7.3.2

