Fixed freeing data at stop & start. Previously the root window was set to NULL when...
[rodin/chimara.git] / libchimara / stream.c
index 3d7262596011a26541dfaa9171cb46e398320346..f6abec64885e4718783485df6a72e14efd9d185d 100644 (file)
@@ -7,17 +7,19 @@
 #include <glib/gstdio.h>
 
 #include "chimara-glk-private.h"
-extern ChimaraGlkPrivate *glk_data;
+extern GPrivate *glk_data_key;
 
 /* Internal function: create a stream with a specified rock value */
-static strid_t
-stream_new_common(glui32 rock, glui32 fmode, enum StreamType type)
+strid_t
+stream_new_common(glui32 rock)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+       
        strid_t str = g_new0(struct glk_stream_struct, 1);
        str->magic = MAGIC_STREAM;
        str->rock = rock;
-       str->file_mode = fmode;
-       str->type = type;
+       if(glk_data->register_obj)
+               str->disprock = (*glk_data->register_obj)(str, gidisp_Class_Stream);
                
        /* Add it to the global stream list */
        glk_data->stream_list = g_list_prepend(glk_data->stream_list, str);
@@ -26,15 +28,50 @@ stream_new_common(glui32 rock, glui32 fmode, enum StreamType type)
        return str;
 }
 
-/* Internal function: create a window stream to go with window. */
-strid_t
-window_stream_new(winid_t window)
+/* Internal function: stream closing stuff that is safe to call from either the
+ main thread or the Glk thread. */
+void
+trash_stream_thread_independent(ChimaraGlkPrivate *glk_data, strid_t str)
 {
-       /* Create stream and connect it to window */
-       strid_t str = stream_new_common(0, filemode_Write, STREAM_TYPE_WINDOW);
-       str->window = window;
-       str->style = "normal";
-       return str;
+       /* 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;
+       
+       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)
+       {
+               (*glk_data->unregister_obj)(str, gidisp_Class_Stream, str->disprock);
+               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;
+       }
+
+       trash_stream_thread_independent(glk_data, str);
 }
 
 /**
@@ -52,7 +89,8 @@ strid_t
 glk_stream_iterate(strid_t str, glui32 *rockptr)
 {
        VALID_STREAM_OR_NULL(str, return NULL);
-       
+
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        GList *retnode;
        
        if(str == NULL)
@@ -96,6 +134,8 @@ void
 glk_stream_set_current(strid_t str)
 {
        VALID_STREAM_OR_NULL(str, return);
+
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        
        if(str != NULL && str->file_mode == filemode_Read)
        {
@@ -116,6 +156,7 @@ glk_stream_set_current(strid_t str)
 strid_t
 glk_stream_get_current()
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        return glk_data->current_stream;
 }
 
@@ -130,6 +171,7 @@ glk_stream_get_current()
 void
 glk_put_char(unsigned char ch)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_char_stream(glk_data->current_stream, ch);
 }
@@ -145,6 +187,7 @@ glk_put_char(unsigned char ch)
 void
 glk_put_char_uni(glui32 ch)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_char_stream_uni(glk_data->current_stream, ch);
 }
@@ -156,7 +199,7 @@ glk_put_char_uni(glui32 ch)
  * Prints a null-terminated string to the current stream. It is exactly
  * equivalent to
  * |[
- * for (ptr = @s; *ptr; ptr++)
+ * for (ptr = s; *ptr; ptr++)
  *     #glk_put_char(*ptr);
  * ]|
  * However, it may be more efficient.
@@ -164,6 +207,7 @@ glk_put_char_uni(glui32 ch)
 void
 glk_put_string(char *s)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_string_stream(glk_data->current_stream, s);
 }
@@ -179,6 +223,7 @@ glk_put_string(char *s)
 void
 glk_put_string_uni(glui32 *s)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_string_stream_uni(glk_data->current_stream, s);
 }
@@ -191,14 +236,15 @@ glk_put_string_uni(glui32 *s)
  * Prints a block of characters to the current stream. It is exactly equivalent
  * to:
  * |[
- * for (i = 0; i < @len; i++)
- *     #glk_put_char(@buf[i]);
+ * for (i = 0; i < len; i++)
+ *     #glk_put_char(buf[i]);
  * ]|
  * However, it may be more efficient.
  */
 void
 glk_put_buffer(char *buf, glui32 len)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_buffer_stream(glk_data->current_stream, buf, len);
 }
@@ -214,6 +260,7 @@ glk_put_buffer(char *buf, glui32 len)
 void
 glk_put_buffer_uni(glui32 *buf, glui32 len)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
        VALID_STREAM(glk_data->current_stream, return);
        glk_put_buffer_stream_uni(glk_data->current_stream, buf, len);
 }
@@ -223,7 +270,7 @@ glk_put_buffer_uni(glui32 *buf, glui32 len)
  * @buf: An allocated buffer, or %NULL.
  * @buflen: Length of @buf.
  * @fmode: Mode in which the buffer will be opened. Must be one of 
- * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
+ * %filemode_Read, %filemode_Write, or %filemode_ReadWrite.
  * @rock: The new stream's rock value.
  *
  * Opens a stream which reads from or writes to a space in memory. @buf points
@@ -240,11 +287,21 @@ glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock)
 {
        g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
        
-       strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_MEMORY);
-       str->buffer = buf;
+       strid_t str = stream_new_common(rock);
+       str->file_mode = fmode;
+       str->type = STREAM_TYPE_MEMORY;
        str->mark = 0;
-       str->buflen = buflen;
        str->unicode = FALSE;
+
+       if(buf && buflen) 
+       {
+               ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+               str->buffer = buf;
+               str->buflen = buflen;
+               if(glk_data->register_arr) 
+                       str->buffer_rock = (*glk_data->register_arr)(buf, buflen, "&+#!Cn");
+       }
+       
        return str;
 }
 
@@ -253,7 +310,7 @@ glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock)
  * @buf: An allocated buffer, or %NULL.
  * @buflen: Length of @buf.
  * @fmode: Mode in which the buffer will be opened. Must be one of 
- * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
+ * %filemode_Read, %filemode_Write, or %filemode_ReadWrite.
  * @rock: The new stream's rock value.
  *
  * Works just like glk_stream_open_memory(), except that the buffer is an array
@@ -268,11 +325,21 @@ glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, glui32 rock
 {
        g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
        
-       strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_MEMORY);
-       str->ubuffer = buf;
+       strid_t str = stream_new_common(rock);
+       str->file_mode = fmode;
+       str->type = STREAM_TYPE_MEMORY;
        str->mark = 0;
-       str->buflen = buflen;
        str->unicode = TRUE;
+
+       if(buf && buflen) 
+       {
+               ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+               str->ubuffer = buf;
+               str->buflen = buflen;
+               if(glk_data->register_arr) 
+                       str->buffer_rock = (*glk_data->register_arr)(buf, buflen, "&+#!Iu");
+       }
+       
        return str;
 }
 
@@ -339,7 +406,9 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode)
                }
        }
        
-       strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_FILE);
+       strid_t str = stream_new_common(rock);
+       str->file_mode = fmode;
+       str->type = STREAM_TYPE_FILE;
        str->file_pointer = fp;
        str->binary = binary;
        str->unicode = unicode;
@@ -353,15 +422,15 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode)
 /**
  * glk_stream_open_file:
  * @fileref: Indicates the file which will be opened.
- * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
- * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
+ * @fmode: Mode in which the file will be opened. Can be any of %filemode_Read,
+ * %filemode_Write, %filemode_WriteAppend, or %filemode_ReadWrite.
  * @rock: The new stream's rock value.
  *
  * Opens a stream which reads to or writes from a disk file. If @fmode is
- * #filemode_Read, the file must already exist; for the other modes, an empty
- * file is created if none exists. If @fmode is #filemode_Write, and the file
+ * %filemode_Read, the file must already exist; for the other modes, an empty
+ * file is created if none exists. If @fmode is %filemode_Write, and the file
  * already exists, it is truncated down to zero length (an empty file). If
- * @fmode is #filemode_WriteAppend, the file mark is set to the end of the 
+ * @fmode is %filemode_WriteAppend, the file mark is set to the end of the 
  * file.
  *
  * When writing in binary mode, Unicode values (characters greater than 255)
@@ -381,8 +450,8 @@ glk_stream_open_file(frefid_t fileref, glui32 fmode, glui32 rock)
 /**
  * glk_stream_open_file_uni:
  * @fileref: Indicates the file which will be opened.
- * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
- * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
+ * @fmode: Mode in which the file will be opened. Can be any of %filemode_Read,
+ * %filemode_Write, %filemode_WriteAppend, or %filemode_ReadWrite.
  * @rock: The new stream's rock value.
  *
  * This works just like glk_stream_open_file(), except that in binary mode,
@@ -432,9 +501,18 @@ glk_stream_close(strid_t str, stream_result_t *result)
                        return;
                        
                case STREAM_TYPE_MEMORY:
-                       /* Do nothing */
+               {
+                       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+                       if(glk_data->unregister_arr) 
+                       {
+                               if(str->unicode)
+                                       (*glk_data->unregister_arr)(str->ubuffer, str->buflen, "&+#!Iu", str->buffer_rock);
+                               else
+                                       (*glk_data->unregister_arr)(str->buffer, str->buflen, "&+#!Cn", str->buffer_rock);
+            }
+               }
                        break;
-                       
+               
                case STREAM_TYPE_FILE:
                        if(fclose(str->file_pointer) != 0)
                                IO_WARNING( "Failed to close file", str->filename, g_strerror(errno) );
@@ -447,32 +525,3 @@ glk_stream_close(strid_t str, stream_result_t *result)
 
        stream_close_common(str, result);
 }
-
-/* Internal function: Stuff to do upon closing any type of stream. */
-void
-stream_close_common(strid_t str, stream_result_t *result)
-{
-       /* 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;
-                       
-       /* Return the character counts */
-       if(result) 
-       {
-               result->readcount = str->read_count;
-               result->writecount = str->write_count;
-       }
-       
-       str->magic = MAGIC_FREE;
-       g_free(str);
-}