X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fchimara-glk.c;h=ca01f44510d7c7dd86a0db2fa9698385cea1197b;hb=8dc72fc5acf19793c8edb5d69ac2d55f7c5a7510;hp=5162046fee0470530782c1f09fa28f2b35287bd6;hpb=d499451f3eb60ed8e0988300fd7b3cb01a3d63ec;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 5162046..ca01f44 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "chimara-glk.h" #include "chimara-glk-private.h" #include "chimara-marshallers.h" @@ -22,6 +23,7 @@ #include "glkunix.h" #include "init.h" #include "magic.h" +#include "style.h" #define CHIMARA_GLK_MIN_WIDTH 0 #define CHIMARA_GLK_MIN_HEIGHT 0 @@ -70,13 +72,6 @@ * #include * #include * - * static gboolean - * quit(void) - * { - * gtk_main_quit(); - * return TRUE; - * } - * * int * main(int argc, char *argv[]) * { @@ -93,10 +88,15 @@ * /* Construct the window and its contents. We quit the GTK main loop * * when the window's close button is clicked. */ * window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - * g_signal_connect(window, "delete-event", G_CALLBACK(quit), NULL); + * g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL); * glk = chimara_glk_new(); * gtk_container_add(GTK_CONTAINER(window), glk); * gtk_widget_show_all(window); + * + * /* Add a reference to the ChimaraGlk widget, since we want it to + * * persist after the window's delete-event -- otherwise it will be destroyed + * * with the window. */ + * g_object_ref(glk); * * /* Start the Glk program in a separate thread */ * if(!chimara_glk_run(CHIMARA_GLK(glk), "./plugin.so", 2, plugin_argv, &error)) @@ -111,6 +111,7 @@ * * it is still running, and wait for it to exit. */ * chimara_glk_stop(CHIMARA_GLK(glk)); * chimara_glk_wait(CHIMARA_GLK(glk)); + * g_object_unref(glk); * * return 0; * } @@ -137,6 +138,7 @@ enum { CHAR_INPUT, LINE_INPUT, TEXT_BUFFER_OUTPUT, + ILIAD_SCREEN_UPDATE, LAST_SIGNAL }; @@ -155,8 +157,8 @@ chimara_glk_init(ChimaraGlk *self) priv->self = self; priv->interactive = TRUE; priv->protect = FALSE; - priv->default_styles = g_new0(StyleSet,1); - priv->current_styles = g_new0(StyleSet,1); + 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; @@ -177,9 +179,12 @@ chimara_glk_init(ChimaraGlk *self) priv->char_input_queue = g_async_queue_new(); priv->line_input_queue = g_async_queue_new(); /* 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; @@ -256,10 +261,10 @@ chimara_glk_finalize(GObject *object) /* Free widget properties */ g_free(priv->final_message); /* Free styles */ - g_hash_table_destroy(priv->default_styles->text_buffer); - g_hash_table_destroy(priv->default_styles->text_grid); - g_hash_table_destroy(priv->current_styles->text_buffer); - g_hash_table_destroy(priv->current_styles->text_grid); + g_hash_table_destroy(priv->styles->text_buffer); + 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 */ @@ -304,6 +309,8 @@ chimara_glk_finalize(GObject *object) g_free(priv->program_name); g_free(priv->program_info); g_free(priv->story_name); + g_free(priv->styles); + g_free(priv->glk_styles); /* Chain up to parent */ G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object); @@ -687,6 +694,12 @@ 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 */ +} + /* COMPAT: G_PARAM_STATIC_STRINGS only appeared in GTK 2.13.0 */ #ifndef G_PARAM_STATIC_STRINGS @@ -722,6 +735,8 @@ chimara_glk_class_init(ChimaraGlkClass *klass) 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: * @glk: The widget that received the signal @@ -797,6 +812,20 @@ chimara_glk_class_init(ChimaraGlkClass *klass) G_STRUCT_OFFSET(ChimaraGlkClass, text_buffer_output), NULL, NULL, _chimara_marshal_VOID__UINT_STRING, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + /** + * ChimaraGlk::iliad-update-screen: + * @self: The widget that received the signal + * @typing: Whether to perform a typing or full screen update + * + * Iliad specific signal which is emitted whenever the screen needs to be updated. + * Since iliad screen updates are very slow, updating should only be done when + * necessary. + */ + chimara_glk_signals[ILIAD_SCREEN_UPDATE] = g_signal_new("iliad-screen-update", + G_OBJECT_CLASS_TYPE(klass), 0, + G_STRUCT_OFFSET(ChimaraGlkClass, iliad_screen_update), NULL, NULL, + _chimara_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); /* Properties */ /** @@ -1044,10 +1073,6 @@ chimara_glk_set_css_from_file(ChimaraGlk *glk, const gchar *filename, GError **e scanner->input_name = filename; scan_css_file(scanner, glk); - /* Set the current style to a copy of the default style */ - /* FIXME this is not correct */ - copy_default_styles_to_current_styles(glk); - if(close(fd) == -1) { *error = g_error_new(G_IO_ERROR, g_io_error_from_errno(errno), _("Error closing file \"%s\": %s"), filename, g_strerror(errno)); @@ -1076,10 +1101,6 @@ chimara_glk_set_css_from_string(ChimaraGlk *glk, const gchar *css) g_scanner_input_text(scanner, css, strlen(css)); scanner->input_name = ""; scan_css_file(scanner, glk); - - /* Set the current style to a copy of the default style */ - /* FIXME this is not correct */ - copy_default_styles_to_current_styles(glk); } /** @@ -1345,8 +1366,8 @@ chimara_glk_feed_char_input(ChimaraGlk *glk, guint keyval) * request. @text does not need to end with a newline. You can call this * function even when no window has requested line input, in which case the text * will be saved for the following window that requests line input. This has the - * disadvantage that if more than one window has requested character input, it - * is arbitrary which one gets the text. + * disadvantage that if more than one window has requested line input, it is + * arbitrary which one gets the text. */ void chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text) @@ -1357,3 +1378,160 @@ chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text) g_async_queue_push(priv->line_input_queue, g_strdup(text)); event_throw(glk, evtype_ForcedLineInput, NULL, 0, 0); } + +/** + * chimara_glk_is_char_input_pending: + * @glk: a #ChimaraGlk widget + * + * Use this function to tell if character input forced by + * chimara_glk_feed_char_input() has been passed to an input request or not. + * + * Returns: %TRUE if forced character input is pending, %FALSE otherwise. + */ +gboolean +chimara_glk_is_char_input_pending(ChimaraGlk *glk) +{ + g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE); + CHIMARA_GLK_USE_PRIVATE(glk, priv); + return g_async_queue_length(priv->char_input_queue) > 0; +} + +/** + * chimara_glk_is_line_input_pending: + * @glk: a #ChimaraGlk widget + * + * Use this function to tell if line input forced by + * chimara_glk_feed_line_input() has been passed to an input request or not. + * + * Returns: %TRUE if forced line input is pending, %FALSE otherwise. + */ +gboolean +chimara_glk_is_line_input_pending(ChimaraGlk *glk) +{ + g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE); + CHIMARA_GLK_USE_PRIVATE(glk, priv); + return g_async_queue_length(priv->line_input_queue) > 0; +} + +/** + * chimara_glk_get_tag: + * @glk: a #ChimarGlk widget + * @window: The type of window to retrieve the tag for + * @name: The name of the tag to retrieve + * + * Use this function to get a #GtkTextTag so style properties can be changed. + * See also #chimara_glk_set_css_from_string. + * + * The layout of the text in Chimara is controlled by two sets of tags: one set + * describing the style in text buffers and one for text grids. See also the + * GLK specification for the difference between the two. The main narrative of + * a game is usually rendered in text buffers, whereas text grids are mostly + * used for status bars and in game menus. + * + * The following tag names are supported: + * + * normal + * emphasized + * preformatted + * header + * subheader + * alert + * note + * block-quote + * input + * user1 + * user2 + * hyperlink + * pager + * + */ +GtkTextTag* +chimara_glk_get_tag(ChimaraGlk *glk, ChimaraGlkWindowType window, const gchar *name) +{ + CHIMARA_GLK_USE_PRIVATE(glk, priv); + + switch(window) { + case CHIMARA_GLK_TEXT_BUFFER: + return GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_buffer, name) ); + break; + case CHIMARA_GLK_TEXT_GRID: + return GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_grid, name) ); + break; + default: + ILLEGAL_PARAM("Unknown window type: %u", window); + return NULL; + } +} + +/** + * chimara_glk_get_tag: + * @glk: a #ChimarGlk widget + * + * Retrieves the possible tag names to use in #chimara_glk_get_tag. + */ +const gchar** +chimara_glk_get_tag_names(ChimaraGlk *glk) +{ + return style_get_tag_names(); +} + +/** + * chimara_glk_get_num_tag_names: + * @glk: a #ChimaraGlk widget + * + * Retrieves the number of style tags returned by #chimara_glk_get_tag_names. + */ +gint +chimara_glk_get_num_tag_names(ChimaraGlk *glk) +{ + return CHIMARA_NUM_STYLES; +} + +/** + * 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 + * @func: a function to call for loading resources, or %NULL + * @user_data: user data to pass to @func, or %NULL + * + * Sometimes it is preferable to load image and sound resources from somewhere + * else than a Blorb file, for example while developing a game. Section 14 of + * the Blorb + * specification allows for this possibility. This function sets @func + * to be called when the Glk program requests loading an image or sound without + * a Blorb resource map having been loaded, optionally passing @user_data as an + * extra parameter. + * + * Note that @func is only called if no Blorb resource map has been set; having + * a resource map in place overrides this function. + * + * To deactivate the callback, call this function with @func set to %NULL. + */ +void +chimara_glk_set_resource_load_callback(ChimaraGlk *glk, ChimaraResourceLoadFunc func, gpointer user_data) +{ + CHIMARA_GLK_USE_PRIVATE(glk, priv); + priv->resource_load_callback = func; + priv->resource_load_callback_data = user_data; +}