X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fchimara-glk.c;h=a350c5aa0442bd35dd5dcb9aff389fa567eedd4a;hb=1e0dc5378f314f555e3b923c6d95f5017abd528b;hp=6bd6a2ffb5cd3e5c2038c71b6d0d3f698750e6ac;hpb=2fe03fa2bb6ccf5450e644dcbd08c51d971aab66;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 6bd6a2f..a350c5a 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -158,47 +158,26 @@ 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; + + 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 +241,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 +250,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 */ @@ -473,64 +454,92 @@ allocate_recurse(winid_t win, GtkAllocation *allocation, guint spacing) bottom or right area is filled with blanks. */ GtkAllocation widget_allocation; gtk_widget_get_allocation(win->widget, &widget_allocation); - glui32 newwidth = (glui32)(widget_allocation.width / win->unit_width); - glui32 newheight = (glui32)(widget_allocation.height / win->unit_height); - gint line; - GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); - GtkTextIter start, end; - - for(line = 0; line < win->height; line++) + glui32 new_width = (glui32)(widget_allocation.width / win->unit_width); + glui32 new_height = (glui32)(widget_allocation.height / win->unit_height); + + if(new_width != win->width || new_height != win->height) { - gtk_text_buffer_get_iter_at_line(textbuffer, &start, line); - /* If this line is going to fall off the bottom, delete it */ - if(line >= newheight) - { - end = start; - gtk_text_iter_forward_to_line_end(&end); - gtk_text_iter_forward_char(&end); - gtk_text_buffer_delete(textbuffer, &start, &end); - break; + // Window has changed size, trim or expand the textbuffer if necessary. + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); + GtkTextIter start, end; + + // Add or remove lines + if(new_height == 0) { + gtk_text_buffer_get_start_iter(buffer, &start); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_delete(buffer, &start, &end); } - /* If this line is not long enough, add spaces on the end */ - if(newwidth > win->width) + else if(new_height < win->height) { - gchar *spaces = g_strnfill(newwidth - win->width, ' '); + // Remove surplus lines + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_get_iter_at_line(buffer, &start, new_height-1); gtk_text_iter_forward_to_line_end(&start); - gtk_text_buffer_insert(textbuffer, &start, spaces, -1); - g_free(spaces); + gtk_text_buffer_delete(buffer, &start, &end); + } - /* But if it's too long, delete characters from the end */ - else if(newwidth < win->width) + else if(new_height > win->height) { + // Add extra lines + gint lines_to_add = new_height - win->height; + gtk_text_buffer_get_end_iter(buffer, &end); + start = end; + + gchar *blanks = g_strnfill(win->width, ' '); + gchar **blanklines = g_new0(gchar *, lines_to_add + 1); + int count; + for(count = 0; count < lines_to_add; count++) + blanklines[count] = blanks; + blanklines[lines_to_add] = NULL; + gchar *vertical_blanks = g_strjoinv("\n", blanklines); + g_free(blanklines); + g_free(blanks); + + if(win->height > 0) + gtk_text_buffer_insert(buffer, &end, "\n", 1); + + gtk_text_buffer_insert(buffer, &end, vertical_blanks, -1); + } + + // Trim or expand lines + if(new_width < win->width) { + gtk_text_buffer_get_start_iter(buffer, &start); + end = start; + + gint line; + for(line = 0; line <= new_height; line++) { + // Trim the line + gtk_text_iter_forward_cursor_positions(&start, new_width); + gtk_text_iter_forward_to_line_end(&end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_iter_forward_line(&start); + end = start; + } + } else if(new_width > win->width) { + gint chars_to_add = new_width - win->width; + gchar *horizontal_blanks = g_strnfill(chars_to_add, ' '); + + gtk_text_buffer_get_start_iter(buffer, &start); end = start; - gtk_text_iter_forward_chars(&start, newwidth); - gtk_text_iter_forward_to_line_end(&end); - gtk_text_buffer_delete(textbuffer, &start, &end); + + gint line; + for(line = 0; line <= new_height; line++) { + gtk_text_iter_forward_to_line_end(&start); + end = start; + gint start_offset = gtk_text_iter_get_offset(&start); + gtk_text_buffer_insert(buffer, &end, horizontal_blanks, -1); + gtk_text_buffer_get_iter_at_offset(buffer, &start, start_offset); + gtk_text_iter_forward_line(&start); + end = start; + } + + g_free(horizontal_blanks); } - /* Note: if the widths are equal, do nothing */ - } - /* Add blank lines if there aren't enough lines to fit the new size */ - if(newheight > win->height) - { - gchar *blanks = g_strnfill(win->width, ' '); - gchar **blanklines = g_new0(gchar *, (newheight - win->height) + 1); - int count; - for(count = 0; count < newheight - win->height; count++) - blanklines[count] = blanks; - blanklines[newheight - win->height] = NULL; - gchar *text = g_strjoinv("\n", blanklines); - g_free(blanklines); /* not g_strfreev() */ - g_free(blanks); - - gtk_text_buffer_get_end_iter(textbuffer, &start); - gtk_text_buffer_insert(textbuffer, &start, "\n", -1); - gtk_text_buffer_insert(textbuffer, &start, text, -1); - g_free(text); } - gboolean arrange = !(win->width == newwidth && win->height == newheight); - win->width = newwidth; - win->height = newheight; + gboolean arrange = !(win->width == new_width && win->height == new_height); + win->width = new_width; + win->height = new_height; return arrange? win : NULL; } @@ -557,7 +566,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) @@ -566,8 +575,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); } } @@ -1104,42 +1113,50 @@ 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_free(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; } /** @@ -1219,8 +1236,8 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE g_object_notify(G_OBJECT(glk), "program-name"); /* 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); } @@ -1273,16 +1290,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); } } @@ -1497,28 +1514,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