#include <gtk/gtk.h>
#include "chimara-glk-private.h"
+#include "window.h"
extern GPrivate *glk_data_key;
{
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);
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;
}
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 */
#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__ */
#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
priv->stream_list = NULL;
priv->timer_id = 0;
priv->style_initialized = FALSE;
+ priv->needs_reset = FALSE;
priv->in_startup = FALSE;
priv->current_dir = NULL;
}
}
}
-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);
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);
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
* 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);
* 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);
/**
/* 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 */
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);
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
#include <libchimara/glk.h>
#include "chimara-glk-private.h"
+#include "stream.h"
extern GPrivate *glk_data_key;
#include "hyperlink.h"
#include "chimara-glk-private.h"
+#include "magic.h"
extern GPrivate *glk_data_key;
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)
{
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);
}
/**
#define STREAM_H
#include <gtk/gtk.h>
+#include "chimara-glk-private.h"
#include "glk.h"
#include "gi_dispa.h"
#include "window.h"
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
-#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;
#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);
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);
}
/**
#define WINDOW_H
#include <gtk/gtk.h>
+#include "chimara-glk-private.h"
#include "glk.h"
#include "gi_dispa.h"
#include "stream.h"
struct hyperlink *current_hyperlink;
};
+G_GNUC_INTERNAL void trash_window_thread_independent(ChimaraGlkPrivate *glk_data, winid_t win);
+
#endif