X-Git-Url: https://git.stderr.nl/gitweb?p=projects%2Fchimara%2Fchimara.git;a=blobdiff_plain;f=libchimara%2Fchimara-glk.c;h=4bbb3aa4e87cfd838bc9843de8322068195ceb31;hp=ef8e6d5896c2bb367e916fe9224315fe1a24a2b1;hb=523811df4155ac5a10b206cb16374d1383e108fc;hpb=0ae54d1f83e8581396969afcf6362be5c04c5f55 diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index ef8e6d5..4bbb3aa 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -51,18 +51,12 @@ * url="http://www.gnu.org/software/libtool/manual/html_node/Finding-the-dlname.html"> * Libtool manual). * - * You need to initialize multithreading in any program you use a #ChimaraGlk - * widget in. This means including the following incantation at the beginning - * of your program: - * |[ - * if(!g_thread_supported()) - * g_thread_init(NULL); - * gdk_threads_init(); - * ]| - * This initialization must take place before the call to - * gtk_init(). In addition to this, you must also protect your call to - * gtk_main() by calling gdk_threads_enter() right before it, and - * gdk_threads_leave() right after it. + * You need to initialize GDK threading in any program you use a #ChimaraGlk + * widget in. + * This means calling gdk_threads_init() at the beginning of your program, + * before the call to gtk_init(). + * In addition to this, you must also protect your call to gtk_main() by calling + * gdk_threads_enter() right before it, and gdk_threads_leave() right after it. * * The following sample program shows how to initialize and construct a simple * GTK window that runs a Glk program: @@ -79,8 +73,6 @@ * gchar *plugin_argv[] = { "plugin.so", "-option" }; * * /* Initialize threads and GTK */ - * if(!g_thread_supported()) - * g_thread_init(NULL); * gdk_threads_init(); * gtk_init(&argc, &argv); * @@ -158,47 +150,25 @@ chimara_glk_init(ChimaraGlk *self) priv->self = self; priv->interactive = TRUE; - priv->protect = FALSE; priv->styles = g_new0(StyleSet,1); priv->glk_styles = g_new0(StyleSet,1); - priv->pager_attr_list = pango_attr_list_new(); priv->final_message = g_strdup("[ The game has finished ]"); - priv->running = FALSE; - priv->program = NULL; - priv->thread = NULL; priv->event_queue = g_queue_new(); - priv->event_lock = g_mutex_new(); - priv->event_queue_not_empty = g_cond_new(); - priv->event_queue_not_full = g_cond_new(); - priv->abort_lock = g_mutex_new(); - priv->abort_signalled = FALSE; - priv->shutdown_lock = g_mutex_new(); - priv->shutdown_key_pressed = g_cond_new(); - priv->arrange_lock = g_mutex_new(); - priv->rearranged = g_cond_new(); - priv->needs_rearrange = FALSE; - priv->ignore_next_arrange_event = FALSE; - priv->char_input_queue = g_async_queue_new(); - priv->line_input_queue = g_async_queue_new(); - /* FIXME Should be g_async_queue_new_full(g_free); but only in GTK >= 2.16 */ - priv->resource_map = NULL; - priv->resource_lock = g_mutex_new(); - priv->resource_loaded = g_cond_new(); - priv->resource_info_available = g_cond_new(); - priv->resource_load_callback = NULL; - priv->resource_load_callback_data = NULL; - priv->image_cache = NULL; - priv->program_name = NULL; - priv->program_info = NULL; - priv->story_name = NULL; - priv->interrupt_handler = NULL; - priv->root_window = NULL; - priv->fileref_list = NULL; - priv->current_stream = NULL; - priv->stream_list = NULL; - priv->timer_id = 0; - priv->in_startup = FALSE; - priv->current_dir = NULL; + priv->char_input_queue = g_async_queue_new_full(g_free); + priv->line_input_queue = g_async_queue_new_full(g_free); + + g_mutex_init(&priv->event_lock); + g_mutex_init(&priv->abort_lock); + g_mutex_init(&priv->shutdown_lock); + g_mutex_init(&priv->arrange_lock); + g_mutex_init(&priv->resource_lock); + + g_cond_init(&priv->event_queue_not_empty); + g_cond_init(&priv->event_queue_not_full); + g_cond_init(&priv->shutdown_key_pressed); + g_cond_init(&priv->rearranged); + g_cond_init(&priv->resource_loaded); + g_cond_init(&priv->resource_info_available); style_init(self); } @@ -262,6 +232,7 @@ chimara_glk_finalize(GObject *object) { ChimaraGlk *self = CHIMARA_GLK(object); CHIMARA_GLK_USE_PRIVATE(self, priv); + priv->after_finalize = TRUE; /* Free widget properties */ g_free(priv->final_message); @@ -270,39 +241,40 @@ chimara_glk_finalize(GObject *object) g_hash_table_destroy(priv->styles->text_grid); g_hash_table_destroy(priv->glk_styles->text_buffer); g_hash_table_destroy(priv->glk_styles->text_grid); - pango_attr_list_unref(priv->pager_attr_list); - + /* Free the event queue */ - g_mutex_lock(priv->event_lock); + g_mutex_lock(&priv->event_lock); g_queue_foreach(priv->event_queue, (GFunc)g_free, NULL); g_queue_free(priv->event_queue); - g_cond_free(priv->event_queue_not_empty); - g_cond_free(priv->event_queue_not_full); + g_cond_clear(&priv->event_queue_not_empty); + g_cond_clear(&priv->event_queue_not_full); priv->event_queue = NULL; - g_mutex_unlock(priv->event_lock); - g_mutex_free(priv->event_lock); + g_mutex_unlock(&priv->event_lock); + g_mutex_clear(&priv->event_lock); + /* Free the abort signaling mechanism */ - g_mutex_lock(priv->abort_lock); + g_mutex_lock(&priv->abort_lock); /* Make sure no other thread is busy with this */ - g_mutex_unlock(priv->abort_lock); - g_mutex_free(priv->abort_lock); - priv->abort_lock = NULL; + g_mutex_unlock(&priv->abort_lock); + g_mutex_clear(&priv->abort_lock); + /* Free the shutdown keypress signaling mechanism */ - g_mutex_lock(priv->shutdown_lock); - g_cond_free(priv->shutdown_key_pressed); - g_mutex_unlock(priv->shutdown_lock); - priv->shutdown_lock = NULL; + g_mutex_lock(&priv->shutdown_lock); + g_cond_clear(&priv->shutdown_key_pressed); + g_mutex_unlock(&priv->shutdown_lock); + g_mutex_clear(&priv->shutdown_lock); + /* 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; - g_mutex_lock(priv->resource_lock); - g_cond_free(priv->resource_loaded); - g_cond_free(priv->resource_info_available); - g_mutex_unlock(priv->resource_lock); - g_mutex_free(priv->resource_lock); + g_mutex_lock(&priv->arrange_lock); + g_cond_clear(&priv->rearranged); + g_mutex_unlock(&priv->arrange_lock); + g_mutex_clear(&priv->arrange_lock); + + g_mutex_lock(&priv->resource_lock); + g_cond_clear(&priv->resource_loaded); + g_cond_clear(&priv->resource_info_available); + g_mutex_unlock(&priv->resource_lock); + g_mutex_clear(&priv->resource_lock); g_slist_foreach(priv->image_cache, (GFunc)clear_image_cache, NULL); g_slist_free(priv->image_cache); /* Unref input queues (this should destroy them since any Glk thread has stopped by now */ @@ -585,7 +557,7 @@ chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) /* arrange points to a window that contains all text grid and graphics windows which have been resized */ - g_mutex_lock(priv->arrange_lock); + g_mutex_lock(&priv->arrange_lock); if(!priv->ignore_next_arrange_event) { if(arrange) @@ -594,8 +566,8 @@ chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) else priv->ignore_next_arrange_event = FALSE; priv->needs_rearrange = FALSE; - g_cond_signal(priv->rearranged); - g_mutex_unlock(priv->arrange_lock); + g_cond_signal(&priv->rearranged); + g_mutex_unlock(&priv->arrange_lock); } } @@ -649,36 +621,6 @@ chimara_glk_started(ChimaraGlk *self) priv->running = TRUE; } -static void -chimara_glk_waiting(ChimaraGlk *self) -{ - /* Default signal handler */ -} - -static void -chimara_glk_char_input(ChimaraGlk *self, guint window_rock, guint keysym) -{ - /* Default signal handler */ -} - -static void -chimara_glk_line_input(ChimaraGlk *self, guint window_rock, gchar *text) -{ - /* Default signal handler */ -} - -static void -chimara_glk_text_buffer_output(ChimaraGlk *self, guint window_rock, gchar *text) -{ - /* Default signal handler */ -} - -static void -chimara_glk_iliad_screen_update(ChimaraGlk *self, gboolean typing) -{ - /* Default signal handler */ -} - static void chimara_glk_class_init(ChimaraGlkClass *klass) { @@ -702,11 +644,6 @@ chimara_glk_class_init(ChimaraGlkClass *klass) /* Signals */ klass->stopped = chimara_glk_stopped; klass->started = chimara_glk_started; - klass->waiting = chimara_glk_waiting; - klass->char_input = chimara_glk_char_input; - klass->line_input = chimara_glk_line_input; - klass->text_buffer_output = chimara_glk_text_buffer_output; - klass->iliad_screen_update = chimara_glk_iliad_screen_update; /** * ChimaraGlk::stopped: @@ -743,46 +680,64 @@ chimara_glk_class_init(ChimaraGlkClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * ChimaraGlk::char-input: - * @glk: The widget that received the signal + * @self: The widget that received the signal * @window_rock: The rock value of the window that received character input * (see Rocks) - * @keysym: The key that was typed, in the form of a key symbol from + * @window_id_string: A string value uniquely identifying the window that + * received character input + * @keysym: The key that was typed, in the form of a key symbol from * gdk/gdkkeysyms.h - * + * * Emitted when a Glk window receives character input. + * The @window_rock can be used to identify the window. + * However, rock values in Glk are allowed to be identical for different + * windows, so Chimara also provides a string value with which the window + * can be uniquely identified. */ chimara_glk_signals[CHAR_INPUT] = g_signal_new("char-input", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, char_input), NULL, NULL, - _chimara_marshal_VOID__UINT_UINT, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + _chimara_marshal_VOID__UINT_STRING_UINT, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT); /** * ChimaraGlk::line-input: - * @glk: The widget that received the signal + * @self: The widget that received the signal * @window_rock: The rock value of the window that received line input (see * Rocks) + * @window_id_string: A string value uniquely identifying the window that + * received the input * @text: The text that was typed - * + * * Emitted when a Glk window receives line input. + * The @window_rock can be used to identify the window. + * However, rock values in Glk are allowed to be identical for different + * windows, so Chimara also provides a string value with which the window + * can be uniquely identified. */ chimara_glk_signals[LINE_INPUT] = g_signal_new("line-input", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, line_input), NULL, NULL, - _chimara_marshal_VOID__UINT_STRING, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + _chimara_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); /** * ChimaraGlk::text-buffer-output: - * @glk: The widget that received the signal + * @self: The widget that received the signal * @window_rock: The rock value of the window that was printed to (see Rocks) - * + * @window_id_string: A string value uniquely identifying the window that + * was printed to + * * Emitted when text is printed to a text buffer window. + * The @window_rock can be used to identify the window. + * However, rock values in Glk are allowed to be identical for different + * windows, so Chimara also provides a string value with which the window + * can be uniquely identified. */ chimara_glk_signals[TEXT_BUFFER_OUTPUT] = g_signal_new("text-buffer-output", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, text_buffer_output), NULL, NULL, - _chimara_marshal_VOID__UINT_STRING, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + _chimara_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); /** * ChimaraGlk::iliad-screen-update: * @self: The widget that received the signal @@ -1132,42 +1087,49 @@ struct StartupData { ChimaraGlkPrivate *glk_data; }; -/* glk_enter() is the actual function called in the new thread in which glk_main() runs. */ +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); +} + +/* glk_enter() is the actual function called in the new thread in which +glk_main() runs. Takes ownership of @startup and will free it. */ static gpointer glk_enter(struct StartupData *startup) { - extern GPrivate *glk_data_key; - g_private_set(glk_data_key, startup->glk_data); - + extern GPrivate glk_data_key; + g_private_set(&glk_data_key, startup->glk_data); + /* Acquire the Glk thread's references to the input queues */ g_async_queue_ref(startup->glk_data->char_input_queue); g_async_queue_ref(startup->glk_data->line_input_queue); - + /* Run startup function */ if(startup->glkunix_startup_code) { startup->glk_data->in_startup = TRUE; int result = startup->glkunix_startup_code(&startup->args); startup->glk_data->in_startup = FALSE; - - int i = 0; - while(i < startup->args.argc) - g_free(startup->args.argv[i++]); - g_free(startup->args.argv); - - if(!result) + + if(!result) { + free_startup_data(startup); return NULL; + } } - + /* Run main function */ glk_main_t glk_main = startup->glk_main; - - /* COMPAT: avoid usage of slices */ - g_free(startup); - g_signal_emit_by_name(startup->glk_data->self, "started"); + + g_signal_emit_by_name(startup->glk_data->self, "started"); glk_main(); + free_startup_data(startup); 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() */ - return NULL; + return NULL; } /** @@ -1204,9 +1166,8 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk); - /* COMPAT: avoid usage of slices */ - struct StartupData *startup = g_new0(struct StartupData,1); - + struct StartupData *startup = g_slice_new0(struct StartupData); + g_assert( g_module_supported() ); /* If there is already a module loaded, free it first -- you see, we want to * keep modules loaded as long as possible to avoid crashes in stack unwinding */ @@ -1245,10 +1206,13 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE /* Set the program name */ priv->program_name = g_path_get_basename(plugin); g_object_notify(G_OBJECT(glk), "program-name"); - + + /* Set Glk styles to defaults */ + style_reset_glk(glk); + /* Run in a separate thread */ - priv->thread = g_thread_create((GThreadFunc)glk_enter, startup, TRUE, error); - + priv->thread = g_thread_try_new("glk", (GThreadFunc)glk_enter, startup, error); + return !(priv->thread == NULL); } @@ -1301,16 +1265,16 @@ chimara_glk_stop(ChimaraGlk *glk) if(!priv->running) return; - if(priv->abort_lock) { - g_mutex_lock(priv->abort_lock); + if(!priv->after_finalize) { + g_mutex_lock(&priv->abort_lock); priv->abort_signalled = TRUE; - g_mutex_unlock(priv->abort_lock); + g_mutex_unlock(&priv->abort_lock); /* Stop blocking on the event queue condition */ event_throw(glk, evtype_Abort, NULL, 0, 0); /* Stop blocking on the shutdown key press condition */ - g_mutex_lock(priv->shutdown_lock); - g_cond_signal(priv->shutdown_key_pressed); - g_mutex_unlock(priv->shutdown_lock); + g_mutex_lock(&priv->shutdown_lock); + g_cond_signal(&priv->shutdown_key_pressed); + g_mutex_unlock(&priv->shutdown_lock); } } @@ -1355,6 +1319,7 @@ chimara_glk_unload_plugin(ChimaraGlk *glk) CHIMARA_GLK_USE_PRIVATE(glk, priv); if( priv->program && !g_module_close(priv->program) ) g_warning( "Error closing module :%s", g_module_error() ); + priv->program = NULL; } /** @@ -1525,28 +1490,6 @@ chimara_glk_get_tag_names(ChimaraGlk *glk, unsigned int *num_tags) return style_get_tag_names(); } -/** - * chimara_glk_update_style: - * @glk: a #ChimaraGlk widget - * - * Processes style updates and updates the widget to reflect the new style. - * Call this every time you change a property of a #GtkTextTag retrieved by - * chimara_glk_get_tag(). - */ -void -chimara_glk_update_style(ChimaraGlk *glk) -{ - CHIMARA_GLK_USE_PRIVATE(glk, priv); - style_update(glk); - - /* Schedule a redraw */ - g_mutex_lock(priv->arrange_lock); - priv->needs_rearrange = TRUE; - priv->ignore_next_arrange_event = TRUE; - g_mutex_unlock(priv->arrange_lock); - gtk_widget_queue_resize( GTK_WIDGET(priv->self) ); -} - /** * chimara_glk_set_resource_load_callback: * @glk: a #ChimaraGlk widget