X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fchimara-glk.c;h=ca01f44510d7c7dd86a0db2fa9698385cea1197b;hb=8dc72fc5acf19793c8edb5d69ac2d55f7c5a7510;hp=625460243e7caa9579e2b2138f29993b8464a2d9;hpb=07c4e25bfcbc35efcdd99139cff3ba61e03a507e;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 6254602..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; * } @@ -178,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; @@ -305,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); @@ -1360,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) @@ -1372,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; +}