Fixed freeing data at stop & start. Previously the root window was set to NULL when...
authorfliep <fliep@ddfedd41-794f-dd11-ae45-00112f111e67>
Thu, 3 Dec 2009 20:53:22 +0000 (20:53 +0000)
committerfliep <fliep@ddfedd41-794f-dd11-ae45-00112f111e67>
Thu, 3 Dec 2009 20:53:22 +0000 (20:53 +0000)
12 files changed:
libchimara/abort.c
libchimara/chimara-glk-private.h
libchimara/chimara-glk.c
libchimara/chimara-glk.h
libchimara/garglk.c
libchimara/hyperlink.c
libchimara/stream.c
libchimara/stream.h
libchimara/style.c
libchimara/style.h
libchimara/window.c
libchimara/window.h

index 517dec5649761308b2592be09f6c78feced3c94c..8399f054901376b1d1790bc2639d5c4a27c330cc 100644 (file)
@@ -4,6 +4,7 @@
 #include <gtk/gtk.h>
 
 #include "chimara-glk-private.h"
+#include "window.h"
 
 extern GPrivate *glk_data_key;
 
@@ -66,9 +67,6 @@ shutdown_glk(void)
 {
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
 
-       if(!glk_data->in_startup)
-               g_signal_emit_by_name(glk_data->self, "stopped");
-
        /* Stop any timers */
        glk_request_timer_events(0);
 
@@ -110,5 +108,11 @@ shutdown_glk(void)
                g_cond_wait(glk_data->rearranged, glk_data->arrange_lock);
        g_mutex_unlock(glk_data->arrange_lock);
 
-       chimara_glk_reset(glk_data->self);
+       /* Default handler for 'stopped' unloads the plugin, so be absolutely sure
+        we're not calling any dispatch callbacks after this point */
+       if(!glk_data->in_startup)
+               g_signal_emit_by_name(glk_data->self, "stopped");
+       
+       _chimara_glk_free_nonwindow_private_data(glk_data);
+       glk_data->needs_reset = TRUE;
 }
index 33e8ebf5b8ea20d26d4a1658a006f697bf870df1..b3c90853795eb9465a6c62d3ae7cd5e044acd768 100644 (file)
@@ -83,6 +83,8 @@ struct _ChimaraGlkPrivate {
        void (*unregister_arr)(void *, glui32, char *, gidispatch_rock_t);
        /* Have styles been initialized */
        gboolean style_initialized;
+       /* Is widget still displaying windows from last run */
+       gboolean needs_reset;
 
        /* *** Platform-dependent Glk library data *** */
        /* Flag for functions to find out if they are being called from startup code */
@@ -93,7 +95,9 @@ struct _ChimaraGlkPrivate {
 
 #define CHIMARA_GLK_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), CHIMARA_TYPE_GLK, ChimaraGlkPrivate))
 #define CHIMARA_GLK_USE_PRIVATE(o, n) ChimaraGlkPrivate *n = CHIMARA_GLK_PRIVATE(o)
-       
+
+G_GNUC_INTERNAL void _chimara_glk_free_nonwindow_private_data(ChimaraGlkPrivate *self);
+
 G_END_DECLS
 
 #endif /* __CHIMARA_GLK_PRIVATE_H__ */
index f62c1bee2b79b372860fc7a61cca8777b6218d08..0fabb3943eabe4d6823d04a07dddc15165070e8d 100644 (file)
 #include "chimara-marshallers.h"
 #include "glk.h"
 #include "abort.h"
+#include "stream.h"
 #include "window.h"
 #include "glkstart.h"
 #include "glkunix.h"
 #include "init.h"
+#include "magic.h"
 
 #define CHIMARA_GLK_MIN_WIDTH 0
 #define CHIMARA_GLK_MIN_HEIGHT 0
@@ -109,6 +111,7 @@ chimara_glk_init(ChimaraGlk *self)
     priv->stream_list = NULL;
        priv->timer_id = 0;
        priv->style_initialized = FALSE;
+       priv->needs_reset = FALSE;
        priv->in_startup = FALSE;
        priv->current_dir = NULL;
 }
@@ -167,11 +170,9 @@ chimara_glk_get_property(GObject *object, guint prop_id, GValue *value, GParamSp
     }
 }
 
-static void
-chimara_glk_free_private_data(ChimaraGlk *self)
+void
+_chimara_glk_free_nonwindow_private_data(ChimaraGlkPrivate *priv)
 {
-    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
-
     /* Free the event queue */
     g_mutex_lock(priv->event_lock);
        g_queue_foreach(priv->event_queue, (GFunc)g_free, NULL);
@@ -189,13 +190,6 @@ chimara_glk_free_private_data(ChimaraGlk *self)
        g_mutex_free(priv->abort_lock);
        priv->abort_lock = NULL;
 
-       /* Free the window arrangement signaling */
-       g_mutex_lock(priv->arrange_lock);
-       g_cond_free(priv->rearranged);
-       g_mutex_unlock(priv->arrange_lock);
-       g_mutex_free(priv->arrange_lock);
-       priv->arrange_lock = NULL;
-       
        /* Unref input queues */
        g_async_queue_unref(priv->char_input_queue);
        g_async_queue_unref(priv->line_input_queue);
@@ -210,29 +204,58 @@ chimara_glk_free_private_data(ChimaraGlk *self)
        g_hash_table_destroy(priv->current_styles->text_buffer);
        g_hash_table_destroy(priv->current_styles->text_grid);
 }
-       
+
+/* Internal function: main thread version of destroy_windows_below, only more
+ DESTRUCTO-MATIC! */
 static void
-chimara_glk_finalize(GObject *object)
+trash_windows_recursive(ChimaraGlkPrivate *priv, winid_t win)
 {
-    ChimaraGlk *self = CHIMARA_GLK(object);
-       chimara_glk_free_private_data(self);
+       switch(win->type)
+       {
+               case wintype_Blank:
+           case wintype_TextGrid:
+               case wintype_TextBuffer:
+                       gtk_widget_unparent(win->frame);
+                       break;
 
-    G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object);
+               case wintype_Pair:
+                       trash_windows_recursive(priv, win->window_node->children->data);
+                       trash_windows_recursive(priv, win->window_node->children->next->data);
+                       break;
+
+               default:
+                       ILLEGAL_PARAM("Unknown window type: %u", win->type);
+                       return;
+       }
+       trash_stream_thread_independent(priv, win->window_stream);
+       trash_window_thread_independent(priv, win);
 }
 
-/**
- * chimara_glk_reset:
- * @self: The ChimaraGLK widget to reset
- *
- * Resets the widget back to it's origional state. IE: it resets all the private data.
- */
 void
-chimara_glk_reset(ChimaraGlk *self)
+_chimara_glk_free_window_private_data(ChimaraGlkPrivate *priv)
 {
-       chimara_glk_free_private_data(self);
-       chimara_glk_init(self);
+       /* Destroy the window tree */
+       trash_windows_recursive(priv, priv->root_window->data);
+       g_node_destroy(priv->root_window);
+       
+       /* Free the window arrangement signaling */
+       g_mutex_lock(priv->arrange_lock);
+       g_cond_free(priv->rearranged);
+       g_mutex_unlock(priv->arrange_lock);
+       g_mutex_free(priv->arrange_lock);
+       priv->arrange_lock = NULL;
 }
 
+static void
+chimara_glk_finalize(GObject *object)
+{
+    ChimaraGlk *self = CHIMARA_GLK(object);
+       CHIMARA_GLK_USE_PRIVATE(self, priv);
+       _chimara_glk_free_nonwindow_private_data(priv);
+       _chimara_glk_free_window_private_data(priv);
+
+    G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object);
+}
 
 /* Internal function: Recursively get the Glk window tree's size request */
 static void
@@ -648,7 +671,7 @@ chimara_glk_class_init(ChimaraGlkClass *klass)
      * it ended normally, or was interrupted.
      */ 
     chimara_glk_signals[STOPPED] = g_signal_new("stopped", 
-        G_OBJECT_CLASS_TYPE(klass), 0
+        G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST
         /* FIXME: Should be G_SIGNAL_RUN_CLEANUP but that segfaults??! */
         G_STRUCT_OFFSET(ChimaraGlkClass, stopped), NULL, NULL,
                g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
@@ -659,7 +682,7 @@ chimara_glk_class_init(ChimaraGlkClass *klass)
         * Emitted when a Glk program starts executing in the widget.
         */
        chimara_glk_signals[STARTED] = g_signal_new ("started",
-               G_OBJECT_CLASS_TYPE(klass), 0,
+               G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
                G_STRUCT_OFFSET(ChimaraGlkClass, started), NULL, NULL,
                g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
        /**
@@ -1095,12 +1118,7 @@ glk_enter(struct StartupData *startup)
        
        /* Run main function */
     g_signal_emit_by_name(startup->glk_data->self, "started");
-       /* FIXME: hack. should be done by the signal above but for some reason
-        * this doesn't work */
-       chimara_glk_started(startup->glk_data->self);
-
        (startup->glk_main)();
-
        g_signal_emit_by_name(startup->glk_data->self, "stopped");
        /* FIXME: hack. should be done by the signal above but for some reason
         * this doesn't work */
@@ -1138,7 +1156,14 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE
     
     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
        struct StartupData *startup = g_slice_new0(struct StartupData);
-    
+
+       /* If anything was left over from the previous run, destroy it */
+       if(priv->needs_reset) {
+               _chimara_glk_free_window_private_data(priv);
+               priv->needs_reset = FALSE;
+               chimara_glk_init(glk);
+       }
+       
     /* Open the module to run */
     g_assert( g_module_supported() );
     priv->program = g_module_open(plugin, G_MODULE_BIND_LAZY);
index 8713efe46ef2887c6ba4f2791bd5413d84d2d954..dfa669e3afa1b4f281712ec65a47876be89dfbd3 100644 (file)
@@ -96,7 +96,6 @@ void chimara_glk_wait(ChimaraGlk *glk);
 gboolean chimara_glk_get_running(ChimaraGlk *glk);
 void chimara_glk_feed_char_input(ChimaraGlk *glk, guint32 keycode);
 void chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text);
-void chimara_glk_reset(ChimaraGlk *self);
 
 G_END_DECLS
 
index 1a76bd7e4e36ca9b7076966484b07f375c943b3e..5e801683233c1ca0208503f9ecf3afd1dce57fa6 100644 (file)
@@ -1,5 +1,6 @@
 #include <libchimara/glk.h>
 #include "chimara-glk-private.h"
+#include "stream.h"
 
 extern GPrivate *glk_data_key;
 
index 7d4be655bfd894cb3a86106687cdbbf95396de23..e58f9818f8d58c8310ff2b000cd533ea630e76d0 100644 (file)
@@ -1,5 +1,6 @@
 #include "hyperlink.h"
 #include "chimara-glk-private.h"
+#include "magic.h"
 
 extern GPrivate *glk_data_key;
 
index a7f991d9f0805b370300c727ee859fcc10ad9bda..f6abec64885e4718783485df6a72e14efd9d185d 100644 (file)
@@ -28,25 +28,28 @@ stream_new_common(glui32 rock)
        return str;
 }
 
-/* Internal function: Stuff to do upon closing any type of stream. */
+/* Internal function: stream closing stuff that is safe to call from either the
+ main thread or the Glk thread. */
 void
-stream_close_common(strid_t str, stream_result_t *result)
+trash_stream_thread_independent(ChimaraGlkPrivate *glk_data, strid_t str)
 {
-       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       
        /* Remove the stream from the global stream list */
        glk_data->stream_list = g_list_delete_link(glk_data->stream_list, str->stream_list);
        
        /* If it was the current output stream, set that to NULL */
        if(glk_data->current_stream == str)
                glk_data->current_stream = NULL;
-               
-       /* If it was one or more windows' echo streams, set those to NULL */
-       winid_t win;
-       for(win = glk_window_iterate(NULL, NULL); win; 
-               win = glk_window_iterate(win, NULL))
-               if(win->echo_stream == str)
-                       win->echo_stream = NULL;
+       
+       str->magic = MAGIC_FREE;
+       g_free(str);
+}
+
+/* Internal function: Stuff to do upon closing any type of stream. Call only 
+ from Glk thread. */
+void
+stream_close_common(strid_t str, stream_result_t *result)
+{
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
 
        if(glk_data->unregister_obj)
        {
@@ -54,15 +57,21 @@ stream_close_common(strid_t str, stream_result_t *result)
                str->disprock.ptr = NULL;
        }
        
+       /* If the stream was one or more windows' echo streams, set those to NULL */
+       winid_t win;
+       for(win = glk_window_iterate(NULL, NULL); win; 
+               win = glk_window_iterate(win, NULL))
+               if(win->echo_stream == str)
+                       win->echo_stream = NULL;
+       
        /* Return the character counts */
        if(result) 
        {
                result->readcount = str->read_count;
                result->writecount = str->write_count;
        }
-       
-       str->magic = MAGIC_FREE;
-       g_free(str);
+
+       trash_stream_thread_independent(glk_data, str);
 }
 
 /**
index 193da479a174d543eaf4b62e1ed2402c4aea09b5..87b8b7a90bdc64fdf1ef4cf66adbd2740092e62c 100644 (file)
@@ -2,6 +2,7 @@
 #define STREAM_H
 
 #include <gtk/gtk.h>
+#include "chimara-glk-private.h"
 #include "glk.h"
 #include "gi_dispa.h"
 #include "window.h"
@@ -55,5 +56,6 @@ struct glk_stream_struct
 G_GNUC_INTERNAL strid_t file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode);
 G_GNUC_INTERNAL strid_t stream_new_common(glui32 rock);
 G_GNUC_INTERNAL void stream_close_common(strid_t str, stream_result_t *result);
+G_GNUC_INTERNAL void trash_stream_thread_independent(ChimaraGlkPrivate *glk_data, strid_t str);
 
 #endif
index 4a6b5119a52da9e04ca57244730c128a564e53a8..af371d1a52fbd0b1ebb5526507328751c336e829 100644 (file)
@@ -1,6 +1,12 @@
-#include "style.h"
 #include <stdio.h>
 #include <fcntl.h>
+#include <string.h>
+#include "chimara-glk-private.h"
+#include "glk.h"
+#include "style.h"
+#include "magic.h"
+#include "stream.h"
+#include "strio.h"
 
 extern GPrivate *glk_data_key;
 
index 8d7c69e104540906bd40a092b0ca0d9c0d282d60..2b8afe38a8947b9f03c822cb7dcfbf4991dfd691 100644 (file)
@@ -3,13 +3,6 @@
 
 #include <gtk/gtk.h>
 #include <glib.h>
-#include <glib/gstdio.h>
-#include <stdio.h>
-#include "glk.h"
-#include "magic.h"
-#include "chimara-glk-private.h"
-#include "stream.h"
-#include "strio.h"
 
 G_GNUC_INTERNAL void style_init_textbuffer(GtkTextBuffer *buffer);
 G_GNUC_INTERNAL void style_init_textgrid(GtkTextBuffer *buffer);
index 5cef17eee064870c5a9395e4bd4a4a738b20b9df..31b157f17077278a3b01bb357f4308acb882eade 100644 (file)
@@ -41,28 +41,39 @@ window_new_common(glui32 rock)
        return win;
 }
 
+/* Internal function: window closing stuff that is safe to call from either the
+ main thread or the Glk thread. */
+void
+trash_window_thread_independent(ChimaraGlkPrivate *glk_data, winid_t win)
+{
+       win->magic = MAGIC_FREE;
+       
+       g_list_foreach(win->history, (GFunc)g_free, NULL);
+       g_list_free(win->history);
+
+       g_string_free(win->buffer, TRUE);
+       g_hash_table_destroy(win->hyperlinks);
+       g_free(win->current_hyperlink);
+       g_free(win);
+}
+
+/* Internal function: do all the stuff necessary to close a window. Call only
+ from Glk thread. */
 static void
 window_close_common(winid_t win, gboolean destroy_node)
 {
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       
+
        if(glk_data->unregister_obj) 
        {
         (*glk_data->unregister_obj)(win, gidisp_Class_Window, win->disprock);
         win->disprock.ptr = NULL;
     }
-
+       
        if(destroy_node)
                g_node_destroy(win->window_node);
-       win->magic = MAGIC_FREE;
-
-       g_list_foreach(win->history, (GFunc)g_free, NULL);
-       g_list_free(win->history);
-
-       g_string_free(win->buffer, TRUE);
-       g_hash_table_destroy(win->hyperlinks);
-       g_free(win->current_hyperlink);
-       g_free(win);
+       
+       trash_window_thread_independent(glk_data, win);
 }
 
 /**
index e0222b0215e47cccd8e9ab3432309453472d2771..845550ea6e32a740febc526ee449d2ec723a568b 100644 (file)
@@ -2,6 +2,7 @@
 #define WINDOW_H
 
 #include <gtk/gtk.h>
+#include "chimara-glk-private.h"
 #include "glk.h"
 #include "gi_dispa.h"
 #include "stream.h"
@@ -78,4 +79,6 @@ struct glk_window_struct
        struct hyperlink *current_hyperlink;
 };
 
+G_GNUC_INTERNAL void trash_window_thread_independent(ChimaraGlkPrivate *glk_data, winid_t win);
+
 #endif