From 6d93143fd5cacd11648e1a3220eeeeb4bee0cc70 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 31 Aug 2023 22:40:21 -0700 Subject: [PATCH 1/4] Fix documentation of some of the giblorb_ID_ constants The resource usage constants are Snd, Exec, Pict, and Data. The others are chunk type constants. --- libchimara/doc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libchimara/doc.c b/libchimara/doc.c index 80f4714..248d950 100644 --- a/libchimara/doc.c +++ b/libchimara/doc.c @@ -3670,7 +3670,7 @@ /** * giblorb_ID_Copyright: * - * Resource usage constant representing the copyright message (date and holder, + * Chunk type constant representing the copyright message (date and holder, * without the actual copyright symbol). There should only be one such chunk per * file. */ @@ -3678,28 +3678,28 @@ /** * giblorb_ID_AUTH: * - * Resource usage constant representing the name of the author or creator of the + * Chunk type constant representing the name of the author or creator of the * file. This could be a login name on multi-user systems, for example. There * should only be one such chunk per file. */ - + /** * giblorb_ID_ANNO: * - * Resource usage constant representing any textual annotation that the user or + * Chunk type constant representing any textual annotation that the user or * writing program sees fit to include. - */ + */ /** * giblorb_ID_TEXT: * - * Resource usage constant representing a text data file. + * Chunk type constant representing a text data file. */ /** * giblorb_ID_BINA: * - * Resource usage constant representing a binary data file. + * Chunk type constant representing a binary data file. */ /** From b5c356b7f4dbf78b8951c10f6cbd58a3a0a802e3 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sat, 2 Sep 2023 12:03:15 -0700 Subject: [PATCH 2/4] Print TAP descriptor in Glk unit test runner Tests probably work fine without it but it's good to have it. --- tests/unit/glkunit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/glkunit.c b/tests/unit/glkunit.c index 54aa3c4..575366d 100644 --- a/tests/unit/glkunit.c +++ b/tests/unit/glkunit.c @@ -42,6 +42,8 @@ glk_main(void) struct TestDescription *test = tests; int total, tested, failed = 0; + printf("TAP version 13\n"); + /* Print test plan. Use stdio.h to print to stdout, Glk can't do that */ for(total = 0; tests[total].name != NULL; total++) ; /* count tests for test plan*/ From 81a727886cb0b00efd9dfe6a3f366de3c8db9df0 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sun, 3 Sep 2023 11:41:52 -0700 Subject: [PATCH 3/4] Fix emitting signals when glkunix_startup_code fails We would not run glk_main() but exit the Glk thread immediately if glkunix_startup_code() returns FALSE, signifying failure. However, in this situation, neither the ::started nor ::stopped signals would be emitted. We need to emit at least ::stopped, because programs may be relying on it to know when to call gtk_main_quit(). And if we emit ::stopped, we should emit ::started before it. --- libchimara/abort.c | 70 +++++++++++++++++++++++++++++++++++++++ libchimara/abort.h | 1 + libchimara/chimara-glk.c | 9 ++--- libchimara/glk.c | 71 +--------------------------------------- 4 files changed, 77 insertions(+), 74 deletions(-) diff --git a/libchimara/abort.c b/libchimara/abort.c index 5637bf5..4d97cbb 100644 --- a/libchimara/abort.c +++ b/libchimara/abort.c @@ -163,3 +163,73 @@ shutdown_glk_post(void) glk_data->unregister_arr = NULL; #endif } + +static gboolean +emit_waiting_signal(ChimaraGlk *glk) +{ + g_signal_emit_by_name(glk, "waiting"); + return G_SOURCE_REMOVE; +} + +static gboolean +emit_stopped_signal(ChimaraGlk *glk) +{ + g_signal_emit_by_name(glk, "stopped"); + return G_SOURCE_REMOVE; +} + +void +shutdown_glk_full(void) +{ + ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key); + + shutdown_glk_pre(); + + /* Find the biggest text buffer window */ + winid_t win, largewin = NULL; + glui32 largearea = 0; + for (win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { + if (win->type == wintype_TextBuffer) { + glui32 w, h; + if (!largewin) { + largewin = win; + glk_window_get_size(largewin, &w, &h); + largearea = w * h; + } else { + glk_window_get_size(win, &w, &h); + if(w * h > largearea) { + largewin = win; + largearea = w * h; + } + } + } + } + if (largewin) { + glk_set_window(largewin); + glk_set_style(style_Alert); + glk_put_string("\n"); + glk_put_string(glk_data->final_message); + glk_put_string("\n"); + flush_window_buffer(largewin); + } + + /* Wait for a keypress if any text grid or buffer windows are open */ + gboolean should_wait = FALSE; + g_mutex_lock(&glk_data->shutdown_lock); + for (win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { + if (win->type == wintype_TextGrid || win->type == wintype_TextBuffer) { + g_signal_handler_unblock(win->widget, win->shutdown_keypress_handler); + should_wait = TRUE; + } + } + if (should_wait) { + gdk_threads_add_idle((GSourceFunc)emit_waiting_signal, glk_data->self); + if (glk_data->interactive) + g_cond_wait(&glk_data->shutdown_key_pressed, &glk_data->shutdown_lock); + } + g_mutex_unlock(&glk_data->shutdown_lock); + + shutdown_glk_post(); + + gdk_threads_add_idle((GSourceFunc)emit_stopped_signal, glk_data->self); +} diff --git a/libchimara/abort.h b/libchimara/abort.h index c583166..0a66b40 100644 --- a/libchimara/abort.h +++ b/libchimara/abort.h @@ -6,6 +6,7 @@ G_GNUC_INTERNAL void check_for_abort(void); G_GNUC_INTERNAL void shutdown_glk_pre(void); G_GNUC_INTERNAL void shutdown_glk_post(void); +G_GNUC_INTERNAL void shutdown_glk_full(void); #endif diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 6a24d75..4fbc613 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -11,6 +11,7 @@ #include #include +#include "abort.h" #include "chimara-glk-private.h" #include "chimara-marshallers.h" #include "event.h" @@ -1392,6 +1393,8 @@ glk_enter(struct StartupData *startup) g_async_queue_ref(startup->glk_data->char_input_queue); g_async_queue_ref(startup->glk_data->line_input_queue); + gdk_threads_add_idle((GSourceFunc)emit_started_signal, startup->glk_data->self); + /* Run startup function */ if(startup->glkunix_startup_code) { startup->glk_data->in_startup = TRUE; @@ -1400,18 +1403,16 @@ glk_enter(struct StartupData *startup) if(!result) { free_startup_data(startup); + shutdown_glk_full(); return NULL; } } - gdk_threads_add_idle((GSourceFunc)emit_started_signal, startup->glk_data->self); - /* Run main function */ glk_main_t glk_main = startup->glk_main; free_startup_data(startup); glk_main(); - glk_exit(); /* Run shutdown code in glk_exit() even if glk_main() returns normally */ - g_assert_not_reached(); /* because glk_exit() calls g_thread_exit() */ + shutdown_glk_full(); return NULL; } diff --git a/libchimara/glk.c b/libchimara/glk.c index 2a92da6..1d0b6f5 100644 --- a/libchimara/glk.c +++ b/libchimara/glk.c @@ -1,27 +1,9 @@ #include #include "abort.h" -#include "chimara-glk-private.h" -#include "strio.h" -#include "ui-message.h" -#include "window.h" G_GNUC_INTERNAL GPrivate glk_data_key = G_PRIVATE_INIT(NULL); -static gboolean -emit_waiting_signal(ChimaraGlk *glk) -{ - g_signal_emit_by_name(glk, "waiting"); - return G_SOURCE_REMOVE; -} - -static gboolean -emit_stopped_signal(ChimaraGlk *glk) -{ - g_signal_emit_by_name(glk, "stopped"); - return G_SOURCE_REMOVE; -} - /** * glk_exit: * @@ -56,58 +38,7 @@ emit_stopped_signal(ChimaraGlk *glk) void glk_exit(void) { - ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key); - - shutdown_glk_pre(); - - /* Find the biggest text buffer window */ - winid_t win, largewin = NULL; - glui32 largearea = 0; - for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { - if(win->type == wintype_TextBuffer) { - glui32 w, h; - if(!largewin) { - largewin = win; - glk_window_get_size(largewin, &w, &h); - largearea = w * h; - } else { - glk_window_get_size(win, &w, &h); - if(w * h > largearea) { - largewin = win; - largearea = w * h; - } - } - } - } - if(largewin) { - glk_set_window(largewin); - glk_set_style(style_Alert); - glk_put_string("\n"); - glk_put_string(glk_data->final_message); - glk_put_string("\n"); - flush_window_buffer(largewin); - } - - /* Wait for a keypress if any text grid or buffer windows are open */ - gboolean should_wait = FALSE; - g_mutex_lock(&glk_data->shutdown_lock); - for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { - if(win->type == wintype_TextGrid || win->type == wintype_TextBuffer) { - g_signal_handler_unblock(win->widget, win->shutdown_keypress_handler); - should_wait = TRUE; - } - } - if (should_wait) { - gdk_threads_add_idle((GSourceFunc)emit_waiting_signal, glk_data->self); - if (glk_data->interactive) - g_cond_wait(&glk_data->shutdown_key_pressed, &glk_data->shutdown_lock); - } - g_mutex_unlock(&glk_data->shutdown_lock); - - shutdown_glk_post(); - - gdk_threads_add_idle((GSourceFunc)emit_stopped_signal, glk_data->self); - + shutdown_glk_full(); g_thread_exit(NULL); } From 47fe61f10086c823e88ce7a0741121f701988b65 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 6 Sep 2023 20:27:52 -0700 Subject: [PATCH 4/4] Retain startup args during Glk program lifetime Previously we would free the startup argument strings right after executing the Unix startup function in the Glk program. However, the Glk program may hold on to these strings; Glulxe does, for one. Move them to the ChimaraGlkPrivate data structure to ensure they last for the lifetime of the Glk program. --- libchimara/chimara-glk-private.h | 3 +++ libchimara/chimara-glk.c | 20 +++++++++----------- libchimara/glkunix.c | 6 +++++- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/libchimara/chimara-glk-private.h b/libchimara/chimara-glk-private.h index 1b74795..1367ca7 100644 --- a/libchimara/chimara-glk-private.h +++ b/libchimara/chimara-glk-private.h @@ -6,6 +6,7 @@ #include "chimara-glk.h" #include "glk.h" +#include "glkstart.h" #include "gi_blorb.h" #include "gi_dispa.h" @@ -91,6 +92,8 @@ struct _ChimaraGlkPrivate { gchar *program_name; gchar *program_info; gchar *story_name; + /* Startup args (have Glk program lifetime) */ + glkunix_startup_t args; /* User-defined interrupt handler */ void (*interrupt_handler)(void); /* Global tree of all windows */ diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 4fbc613..9128230 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -551,6 +551,10 @@ chimara_glk_finalize(GObject *object) g_free(priv->glk_styles); g_clear_pointer(&priv->thread, g_thread_unref); + for (size_t ix = 0; ix < priv->args.argc; ix++) + g_free(priv->args.argv[ix]); + g_free(priv->args.argv); + /* Chain up to parent */ G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object); } @@ -1360,17 +1364,12 @@ chimara_glk_get_spacing(ChimaraGlk *self) struct StartupData { glk_main_t glk_main; glkunix_startup_code_t glkunix_startup_code; - glkunix_startup_t args; ChimaraGlkPrivate *glk_data; }; static void free_startup_data(struct StartupData *startup) { - int i = 0; - while(i < startup->args.argc) - g_free(startup->args.argv[i++]); - g_free(startup->args.argv); g_slice_free(struct StartupData, startup); } @@ -1398,7 +1397,7 @@ glk_enter(struct StartupData *startup) /* Run startup function */ if(startup->glkunix_startup_code) { startup->glk_data->in_startup = TRUE; - int result = startup->glkunix_startup_code(&startup->args); + int result = startup->glkunix_startup_code(&startup->glk_data->args); startup->glk_data->in_startup = FALSE; if(!result) { @@ -1502,15 +1501,14 @@ chimara_glk_run(ChimaraGlk *self, const gchar *plugin, int argc, char *argv[], G glkunix_argumentlist_t *glkunix_arguments; if( !(g_module_symbol(priv->program, "glkunix_arguments", (gpointer *) &glkunix_arguments) - && parse_command_line(glkunix_arguments, argc, argv, &startup->args)) ) - { + && parse_command_line(glkunix_arguments, argc, argv, &priv->args))) { /* arguments could not be parsed, so create data ourselves */ - startup->args.argc = 1; - startup->args.argv = g_new0(gchar *, 1); + priv->args.argc = 1; + priv->args.argv = g_new0(gchar *, 1); } /* Set the program invocation name */ - startup->args.argv[0] = g_strdup(plugin); + priv->args.argv[0] = g_strdup(plugin); } startup->glk_data = priv; diff --git a/libchimara/glkunix.c b/libchimara/glkunix.c index 9e65e5e..3600038 100644 --- a/libchimara/glkunix.c +++ b/libchimara/glkunix.c @@ -182,7 +182,11 @@ parse_command_line(glkunix_argumentlist_t glkunix_arguments[], int argc, char *a } } } - + + for (size_t ix = 0; ix < data->argc; ix++) + g_free(data->argv[ix]); + g_free(data->argv); + data->argc = g_slist_length(arglist) + 1; data->argv = g_new0(char *, data->argc); arglist = g_slist_reverse(arglist);