From: Philip Chimento Date: Wed, 18 Jan 2012 12:59:59 +0000 (+0100) Subject: Merge branch 'new-sound-api' X-Git-Tag: v0.9~47 X-Git-Url: https://git.stderr.nl/gitweb?a=commitdiff_plain;h=1461483626f28b52f8f9f3cc350f9cb330579285;hp=6f7ca2daf591a9cc427e8174daf4a07f3e7728ab;p=projects%2Fchimara%2Fchimara.git Merge branch 'new-sound-api' Conflicts: docs/reference/chimara-docs.sgml --- diff --git a/configure.ac b/configure.ac index 3fb5d01..fa284e1 100644 --- a/configure.ac +++ b/configure.ac @@ -133,11 +133,18 @@ PKG_CHECK_MODULES([CHIMARA], [ ]) CHIMARA_LIBS="$CHIMARA_LIBS -lm" AC_SUBST(CHIMARA_LIBS) +# Libraries needed to build Chimara player +PKG_CHECK_MODULES([PLAYER], [ + glib-2.0 >= $GLIB_REQUIRED_VERSION + gtk+-2.0 >= $GTK_REQUIRED_VERSION + gmodule-2.0 + libgda-4.0 + libsoup-2.4 +]) # Libraries needed to build test programs PKG_CHECK_MODULES([TEST], [ gtk+-2.0 >= $GTK_REQUIRED_VERSION gmodule-2.0 >= $GLIB_REQUIRED_VERSION - libgda-4.0 ]) # GStreamer plugins needed to run library diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index 7994e0e..7fd58a2 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -115,7 +115,7 @@ expand_content_files = \ # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. GTKDOC_CFLAGS = -I$(top_srcdir) $(CHIMARA_CFLAGS) -GTKDOC_LIBS = $(top_builddir)/libchimara/libchimara.la +GTKDOC_LIBS = $(top_builddir)/libchimara/libchimara.la $(CHIMARA_LIBS) # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make @@ -139,7 +139,7 @@ DISTCLEANFILES = version.xml $(DOC_MODULE)-overrides.txt dist_noinst_SCRIPTS = build-selector-table.pl selectors.xml: $(srcdir)/../../libchimara/gi_dispa.c - $(PERL) build-selector-table.pl $< > $@ + $(AM_V_GEN)$(PERL) build-selector-table.pl $< > $@ -include $(top_srcdir)/git.mk diff --git a/docs/reference/chimara-docs.sgml b/docs/reference/chimara-docs.sgml index c98d874..af494a7 100644 --- a/docs/reference/chimara-docs.sgml +++ b/docs/reference/chimara-docs.sgml @@ -4,18 +4,21 @@ ]> + + + Chimara Reference Manual for Chimara &version; - + Chimara API Reference - + Glk API Specification, version 0.7.0 @@ -23,7 +26,7 @@ - + Overall Structure @@ -37,7 +40,7 @@ - + Character Encoding @@ -49,7 +52,7 @@ - + Windows @@ -62,7 +65,7 @@ - + Events @@ -73,7 +76,7 @@ - + Streams @@ -88,7 +91,7 @@ - + File References @@ -96,7 +99,7 @@ - + Graphics @@ -105,7 +108,7 @@ - + Sound @@ -115,7 +118,7 @@ - + Hyperlinks @@ -123,7 +126,7 @@ - + The System Clock @@ -134,7 +137,7 @@ - + The Dispatch Layer @@ -144,7 +147,7 @@ - + The Blorb Layer @@ -153,7 +156,7 @@ - + Glk Extensions diff --git a/docs/reference/chimara-sections.txt b/docs/reference/chimara-sections.txt index 74e9e6a..994f21b 100644 --- a/docs/reference/chimara-sections.txt +++ b/docs/reference/chimara-sections.txt @@ -22,12 +22,12 @@ chimara_glk_set_css_from_string chimara_glk_run chimara_glk_stop chimara_glk_wait +chimara_glk_unload_plugin chimara_glk_get_running chimara_glk_feed_char_input chimara_glk_feed_line_input chimara_glk_is_char_input_pending chimara_glk_is_line_input_pending -chimara_glk_get_num_tag_names chimara_glk_get_tag chimara_glk_get_tag_names chimara_glk_update_style diff --git a/libchimara/Makefile.am b/libchimara/Makefile.am index f1ef3f4..2496948 100644 --- a/libchimara/Makefile.am +++ b/libchimara/Makefile.am @@ -103,6 +103,7 @@ typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(gir_DATA) $(typelib_DATA) endif +GITIGNOREFILES = Chimara-1.0.gir Chimara-1.0.typelib # Currently, install the Vala VAPI file statically - generation is broken? diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 1992556..b7c0a7d 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -28,6 +28,27 @@ #define CHIMARA_GLK_MIN_WIDTH 0 #define CHIMARA_GLK_MIN_HEIGHT 0 +/* Substitute functions for compiling on iLiad */ + +#if !GTK_CHECK_VERSION(2, 18, 0) +#define gtk_widget_get_allocation(w, a) \ + G_STMT_START { \ + (a)->x = (w)->allocation.x; \ + (a)->y = (w)->allocation.y; \ + (a)->width = (w)->allocation.width; \ + (a)->height = (w)->allocation.height; \ + } G_STMT_END +#define gtk_widget_set_allocation(w, a) \ + G_STMT_START { (w)->allocation = *(a); } G_STMT_END +#define gtk_widget_set_has_window(w, f) \ + G_STMT_START { \ + if(f) \ + GTK_WIDGET_UNSET_FLAGS((w), GTK_NO_WINDOW); \ + else \ + GTK_WIDGET_SET_FLAGS((w), GTK_NO_WINDOW); \ + } G_STMT_END +#endif /* GTK 2.18 */ + /** * SECTION:chimara-glk * @short_description: Widget which executes a Glk program @@ -149,7 +170,7 @@ G_DEFINE_TYPE(ChimaraGlk, chimara_glk, GTK_TYPE_CONTAINER); static void chimara_glk_init(ChimaraGlk *self) { - GTK_WIDGET_SET_FLAGS(GTK_WIDGET(self), GTK_NO_WINDOW); + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self); @@ -392,17 +413,18 @@ chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition) ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget); + guint border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); /* For now, just pass the size request on to the root Glk window */ if(priv->root_window) { request_recurse(priv->root_window->data, requisition, priv->spacing); - requisition->width += 2 * GTK_CONTAINER(widget)->border_width; - requisition->height += 2 * GTK_CONTAINER(widget)->border_width; + requisition->width += 2 * border_width; + requisition->height += 2 * border_width; } else { - requisition->width = CHIMARA_GLK_MIN_WIDTH + 2 * GTK_CONTAINER(widget)->border_width; - requisition->height = CHIMARA_GLK_MIN_HEIGHT + 2 * GTK_CONTAINER(widget)->border_width; + requisition->width = CHIMARA_GLK_MIN_WIDTH + 2 * border_width; + requisition->height = CHIMARA_GLK_MIN_HEIGHT + 2 * border_width; } } @@ -522,8 +544,10 @@ allocate_recurse(winid_t win, GtkAllocation *allocation, guint spacing) /* It says in the spec that when a text grid window is resized smaller, the bottom or right area is thrown away; when it is resized larger, the bottom or right area is filled with blanks. */ - glui32 newwidth = (glui32)(win->widget->allocation.width / win->unit_width); - glui32 newheight = (glui32)(win->widget->allocation.height / win->unit_height); + 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; @@ -598,14 +622,15 @@ chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget); - widget->allocation = *allocation; + gtk_widget_set_allocation(widget, allocation); if(priv->root_window) { GtkAllocation child; - child.x = allocation->x + GTK_CONTAINER(widget)->border_width; - child.y = allocation->y + GTK_CONTAINER(widget)->border_width; - child.width = CLAMP(allocation->width - 2 * GTK_CONTAINER(widget)->border_width, 0, allocation->width); - child.height = CLAMP(allocation->height - 2 * GTK_CONTAINER(widget)->border_width, 0, allocation->height); + guint border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); + child.x = allocation->x + border_width; + child.y = allocation->y + border_width; + child.width = CLAMP(allocation->width - 2 * border_width, 0, allocation->width); + child.height = CLAMP(allocation->height - 2 * border_width, 0, allocation->height); winid_t arrange = allocate_recurse(priv->root_window->data, &child, priv->spacing); /* arrange points to a window that contains all text grid and graphics @@ -1232,8 +1257,7 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE 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 */ - if( priv->program && !g_module_close(priv->program) ) - g_warning( "Error closing module :%s", g_module_error() ); + chimara_glk_unload_plugin(glk); /* Open the module to run */ priv->program = g_module_open(plugin, G_MODULE_BIND_LAZY); @@ -1275,6 +1299,35 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE return !(priv->thread == NULL); } +/** + * chimara_glk_run_file: + * @self: a #ChimaraGlk widget + * @plugin_file: a #GFile pointing to a plugin module compiled with glk.h + * @argc: Number of command line arguments in @argv + * @argv: Array of command line arguments to pass to the plugin + * @error: location to store a GError, or %NULL + * + * Opens a Glk program compiled as a plugin, from a #GFile. See + * chimara_glk_run() for details. + * + * Return value: %TRUE if the Glk program was started successfully. + */ +gboolean +chimara_glk_run_file(ChimaraGlk *self, GFile *plugin_file, int argc, char *argv[], GError **error) +{ + g_return_val_if_fail(self || CHIMARA_IS_GLK(self), FALSE); + g_return_val_if_fail(plugin_file || G_IS_FILE(plugin_file), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + char *path = g_file_get_path(plugin_file); + gboolean retval = chimara_glk_run(self, path, argc, argv, error); + g_free(path); + + return retval; +} + /** * chimara_glk_stop: * @glk: a #ChimaraGlk widget @@ -1282,6 +1335,8 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE * Signals the Glk program running in @glk to abort. Note that if the program is * caught in an infinite loop in which glk_tick() is not called, this may not * work. + * + * This function does nothing if no Glk program is running. */ void chimara_glk_stop(ChimaraGlk *glk) @@ -1312,6 +1367,8 @@ chimara_glk_stop(ChimaraGlk *glk) * * Holds up the main thread and waits for the Glk program running in @glk to * finish. + * + * This function does nothing if no Glk program is running. */ void chimara_glk_wait(ChimaraGlk *glk) @@ -1327,6 +1384,26 @@ chimara_glk_wait(ChimaraGlk *glk) gdk_threads_enter(); } +/** + * chimara_glk_unload_plugin: + * @glk: a #ChimaraGlk widget + * + * The plugin containing the Glk program is unloaded as late as possible before + * loading a new plugin, in order to prevent crashes while printing stack + * backtraces during debugging. Sometimes this behavior is not desirable. This + * function forces @glk to unload the plugin running in it. + * + * This function does nothing if there is no plugin loaded. + */ +void +chimara_glk_unload_plugin(ChimaraGlk *glk) +{ + g_return_if_fail(glk || CHIMARA_IS_GLK(glk)); + CHIMARA_GLK_USE_PRIVATE(glk, priv); + if( priv->program && !g_module_close(priv->program) ) + g_warning( "Error closing module :%s", g_module_error() ); +} + /** * chimara_glk_get_running: * @glk: a #ChimaraGlk widget diff --git a/libchimara/chimara-glk.h b/libchimara/chimara-glk.h index 37d2220..b3ea8b8 100644 --- a/libchimara/chimara-glk.h +++ b/libchimara/chimara-glk.h @@ -122,8 +122,10 @@ void chimara_glk_set_css_from_string(ChimaraGlk *glk, const gchar *css); void chimara_glk_set_spacing(ChimaraGlk *glk, guint spacing); guint chimara_glk_get_spacing(ChimaraGlk *glk); gboolean chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GError **error); +gboolean chimara_glk_run_file(ChimaraGlk *self, GFile *plugin_file, int argc, char *argv[], GError **error); void chimara_glk_stop(ChimaraGlk *glk); void chimara_glk_wait(ChimaraGlk *glk); +void chimara_glk_unload_plugin(ChimaraGlk *glk); gboolean chimara_glk_get_running(ChimaraGlk *glk); void chimara_glk_feed_char_input(ChimaraGlk *glk, guint32 keyval); void chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text); diff --git a/libchimara/chimara-if.c b/libchimara/chimara-if.c index af1179c..ed132bb 100644 --- a/libchimara/chimara-if.c +++ b/libchimara/chimara-if.c @@ -10,6 +10,10 @@ #include "chimara-marshallers.h" #include "init.h" +#ifndef PLUGINDIR +#define PLUGINDIR "." +#endif + /** * SECTION:chimara-if * @short_description: Widget which plays an interactive fiction game @@ -558,7 +562,7 @@ chimara_if_get_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format) /** * chimara_if_run_game: * @self: A #ChimaraIF widget. - * @gamefile: Path to an interactive fiction game file. + * @game_path: Path to an interactive fiction game file. * @error: Return location for an error, or %NULL. * * Autodetects the type of a game file and runs it using an appropriate @@ -570,27 +574,28 @@ chimara_if_get_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format) * case @error is set. */ gboolean -chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error) +chimara_if_run_game(ChimaraIF *self, const char *game_path, GError **error) { g_return_val_if_fail(self && CHIMARA_IS_IF(self), FALSE); - g_return_val_if_fail(gamefile, FALSE); + g_return_val_if_fail(game_path, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); CHIMARA_IF_USE_PRIVATE(self, priv); /* Find out what format the game is */ /* TODO: Look inside the file instead of just looking at the extension */ ChimaraIFFormat format = CHIMARA_IF_FORMAT_Z5; - if(g_str_has_suffix(gamefile, ".z5")) + if(g_str_has_suffix(game_path, ".z5")) format = CHIMARA_IF_FORMAT_Z5; - else if(g_str_has_suffix(gamefile, ".z6")) + else if(g_str_has_suffix(game_path, ".z6")) format = CHIMARA_IF_FORMAT_Z6; - else if(g_str_has_suffix(gamefile, ".z8")) + else if(g_str_has_suffix(game_path, ".z8")) format = CHIMARA_IF_FORMAT_Z8; - else if(g_str_has_suffix(gamefile, ".zlb") || g_str_has_suffix(gamefile, ".zblorb")) + else if(g_str_has_suffix(game_path, ".zlb") || g_str_has_suffix(game_path, ".zblorb")) format = CHIMARA_IF_FORMAT_Z_BLORB; - else if(g_str_has_suffix(gamefile, ".ulx")) + else if(g_str_has_suffix(game_path, ".ulx")) format = CHIMARA_IF_FORMAT_GLULX; - else if(g_str_has_suffix(gamefile, ".blb") || g_str_has_suffix(gamefile, ".blorb") || g_str_has_suffix(gamefile, ".glb") || g_str_has_suffix(gamefile, ".gblorb")) + else if(g_str_has_suffix(game_path, ".blb") || g_str_has_suffix(game_path, ".blorb") || g_str_has_suffix(game_path, ".glb") || g_str_has_suffix(game_path, ".gblorb")) format = CHIMARA_IF_FORMAT_GLULX_BLORB; /* Now decide what interpreter to use */ @@ -675,7 +680,7 @@ chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error) } /* Game file and external blorb file */ - args = g_slist_prepend(args, gamefile); + args = g_slist_prepend(args, (gpointer)game_path); if(priv->graphics_file && (interpreter == CHIMARA_IF_INTERPRETER_FROTZ || interpreter == CHIMARA_IF_INTERPRETER_NITFOL) && g_file_test(priv->graphics_file, G_FILE_TEST_EXISTS)) { @@ -692,17 +697,17 @@ chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error) int count; GSList *ptr; for(count = 0, ptr = args; ptr; count++, ptr = g_slist_next(ptr)) - argv[count] = ptr->data; + argv[count] = g_strdup(ptr->data); /* Set the story name */ /* We peek into ChimaraGlk's private data here, because GObject has no equivalent to "protected" */ CHIMARA_GLK_USE_PRIVATE(self, glk_priv); - glk_priv->story_name = g_path_get_basename(gamefile); + glk_priv->story_name = g_path_get_basename(game_path); g_object_notify(G_OBJECT(self), "story-name"); gboolean retval = chimara_glk_run(CHIMARA_GLK(self), pluginpath, argc, argv, error); - g_free(argv); + g_strfreev(argv); if(terpnumstr) g_free(terpnumstr); if(randomstr) @@ -718,6 +723,31 @@ chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error) return retval; } +/** + * chimara_if_run_game_file: + * @self: A #ChimaraIF widget. + * @game_file: a #GFile pointing to an interactive fiction game file. + * @error: Return location for an error, or %NULL. + * + * Autodetects the type of a game file and runs it using an appropriate + * interpreter plugin. See chimara_if_run_game() for more information. + * + * Returns: %TRUE if the game was started successfully, %FALSE if not, in which + * case @error is set. + */ +gboolean +chimara_if_run_game_file(ChimaraIF *self, GFile *game_file, GError **error) +{ + g_return_val_if_fail(self || CHIMARA_IS_IF(self), FALSE); + g_return_val_if_fail(game_file || G_IS_FILE(game_file), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + char *path = g_file_get_path(game_file); + gboolean retval = chimara_if_run_game(self, path, error); + g_free(path); + return retval; +} + /** * chimara_if_get_format: * @self: A #ChimaraIF widget. diff --git a/libchimara/chimara-if.h b/libchimara/chimara-if.h index deda647..3bbe06a 100644 --- a/libchimara/chimara-if.h +++ b/libchimara/chimara-if.h @@ -116,7 +116,8 @@ GType chimara_if_get_type(void) G_GNUC_CONST; GtkWidget *chimara_if_new(void); void chimara_if_set_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format, ChimaraIFInterpreter interpreter); ChimaraIFInterpreter chimara_if_get_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format); -gboolean chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error); +gboolean chimara_if_run_game(ChimaraIF *self, const char *game_path, GError **error); +gboolean chimara_if_run_game_file(ChimaraIF *self, GFile *game_file, GError **error); ChimaraIFFormat chimara_if_get_format(ChimaraIF *self); ChimaraIFInterpreter chimara_if_get_interpreter(ChimaraIF *self); diff --git a/libchimara/gi_dispa.c b/libchimara/gi_dispa.c index f38eff0..f0099b9 100644 --- a/libchimara/gi_dispa.c +++ b/libchimara/gi_dispa.c @@ -435,7 +435,7 @@ char *gidispatch_prototype(glui32 funcnum) case 0x0042: /* stream_open_file */ return "4QcIuIu:Qb"; case 0x0043: /* stream_open_memory */ - return "4&+#!CnIuIu:Qb"; + return "4&#!CnIuIu:Qb"; case 0x0044: /* stream_close */ return "2Qb<[2IuIu]:"; case 0x0045: /* stream_set_position */ @@ -607,7 +607,7 @@ char *gidispatch_prototype(glui32 funcnum) case 0x0138: /* stream_open_file_uni */ return "4QcIuIu:Qb"; case 0x0139: /* stream_open_memory_uni */ - return "4&+#!IuIuIu:Qb"; + return "4&#!IuIuIu:Qb"; case 0x0140: /* request_char_event_uni */ return "1Qa:"; case 0x0141: /* request_line_event_uni */ diff --git a/libchimara/graphics.c b/libchimara/graphics.c index afe3e76..0c3215a 100644 --- a/libchimara/graphics.c +++ b/libchimara/graphics.c @@ -123,6 +123,9 @@ load_image_in_cache(glui32 image, gint width, gint height) info = load_image_from_blorb(resource, image, width, height); } + if(info == NULL) + return NULL; + /* Store the image in the cache */ gdk_threads_enter(); diff --git a/libchimara/input.c b/libchimara/input.c index bb9b9d1..7f9b693 100644 --- a/libchimara/input.c +++ b/libchimara/input.c @@ -783,7 +783,9 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar GtkTextIter input_iter; GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); gtk_text_buffer_get_iter_at_mark(window_buffer, &input_iter, input_position); + gtk_text_buffer_apply_tag_by_name(window_buffer, "default", &input_iter, &end_iter); gtk_text_buffer_apply_tag_by_name(window_buffer, "input", &input_iter, &end_iter); + gtk_text_buffer_apply_tag_by_name(window_buffer, "glk-input", &input_iter, &end_iter); } /* Internal function: Callback for signal activate on the line input GtkEntry @@ -965,7 +967,7 @@ force_line_input_from_queue(winid_t win, event_t *event) { gtk_text_buffer_get_end_iter(buffer, &end); gchar *text_to_insert = g_strconcat(text, "\n", NULL); - gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "default", "input", NULL); + gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "default", "input", "glk-input", NULL); } chars_written = finish_text_buffer_line_input(win, TRUE); diff --git a/libchimara/magic.h b/libchimara/magic.h index 406b570..3f77d8d 100644 --- a/libchimara/magic.h +++ b/libchimara/magic.h @@ -15,9 +15,9 @@ G_GNUC_INTERNAL gboolean magic_is_valid_or_null(const glui32 goodmagic, const gl G_GNUC_INTERNAL gboolean magic_is_valid(const void *obj, const glui32 goodmagic, const glui32 realmagic, const gchar *function); #define VALID_MAGIC(obj, goodmagic, die) \ - if( !magic_is_valid(obj, goodmagic, obj->magic, G_STRFUNC) ) die + if( !magic_is_valid((obj), (goodmagic), (obj)? (obj)->magic : 0, G_STRFUNC) ) die #define VALID_MAGIC_OR_NULL(obj, goodmagic, die) \ - if( !magic_is_valid_or_null(goodmagic, obj? obj->magic : MAGIC_NULL, G_STRFUNC) ) die + if( !magic_is_valid_or_null((goodmagic), (obj)? (obj)->magic : MAGIC_NULL, G_STRFUNC) ) die #define VALID_WINDOW(o, d) VALID_MAGIC(o, MAGIC_WINDOW, d) #define VALID_WINDOW_OR_NULL(o, d) VALID_MAGIC_OR_NULL(o, MAGIC_WINDOW, d) diff --git a/libchimara/style.c b/libchimara/style.c index 125dbd6..f9b231b 100644 --- a/libchimara/style.c +++ b/libchimara/style.c @@ -1097,22 +1097,46 @@ PangoFontDescription * get_current_font(guint32 wintype) { ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); - GtkTextTag *tag; + GHashTable *styles, *glk_styles; + PangoFontDescription *font; switch(wintype) { case wintype_TextGrid: - tag = g_hash_table_lookup(glk_data->styles->text_grid, "default"); - return pango_font_description_from_string("Monospace"); + styles = glk_data->styles->text_grid; + glk_styles = glk_data->glk_styles->text_grid; + font = pango_font_description_from_string("Monospace"); break; case wintype_TextBuffer: - tag = g_hash_table_lookup(glk_data->styles->text_buffer, "default"); - return pango_font_description_from_string("Serif"); + styles = glk_data->styles->text_buffer; + glk_styles = glk_data->glk_styles->text_buffer; + font = pango_font_description_from_string("Serif"); break; default: return NULL; } - PangoFontDescription *font; + PangoAttrList *list = pango_attr_list_new(); + + text_tag_to_attr_list( g_hash_table_lookup(styles, "default"), list ); + PangoAttrIterator *it = pango_attr_list_get_iterator(list); + pango_attr_iterator_get_font(it, font, NULL, NULL); + pango_attr_iterator_destroy(it); + + text_tag_to_attr_list( g_hash_table_lookup(styles, "normal"), list ); + it = pango_attr_list_get_iterator(list); + pango_attr_iterator_get_font(it, font, NULL, NULL); + pango_attr_iterator_destroy(it); + + text_tag_to_attr_list( g_hash_table_lookup(glk_styles, "glk-normal"), list ); + it = pango_attr_list_get_iterator(list); + pango_attr_iterator_get_font(it, font, NULL, NULL); + pango_attr_iterator_destroy(it); + + /* Make a copy of the family, preventing it's destruction at the end of this function. */ + pango_font_description_set_family( font, pango_font_description_get_family(font) ); + + pango_attr_list_unref(list); + return font; } diff --git a/player/Makefile.am b/player/Makefile.am index 2482798..588f183 100644 --- a/player/Makefile.am +++ b/player/Makefile.am @@ -21,8 +21,8 @@ chimara_CPPFLAGS = $(AM_CPPFLAGS) \ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" -chimara_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS) -chimara_LDADD = @TEST_LIBS@ $(top_builddir)/libchimara/libchimara.la +chimara_CFLAGS = @PLAYER_CFLAGS@ $(AM_CFLAGS) +chimara_LDADD = @PLAYER_LIBS@ $(top_builddir)/libchimara/libchimara.la gsettings_SCHEMAS = org.chimara-if.gschema.xml @GSETTINGS_RULES@ diff --git a/tests/Makefile.am b/tests/Makefile.am index c8ae301..1fed105 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,8 +30,8 @@ test_close_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS) test_close_LDADD = @TEST_LIBS@ $(top_builddir)/libchimara/libchimara.la babeltest_SOURCES = babeltest.c -babeltest_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS) -babeltest_LDADD = @TEST_LIBS@ $(top_builddir)/babel/libbabel_functions.la $(top_builddir)/babel/libbabel.la $(top_builddir)/babel/libifiction.la +babeltest_CFLAGS = @PLAYER_CFLAGS@ $(AM_CFLAGS) +babeltest_LDADD = @PLAYER_LIBS@ $(top_builddir)/babel/libbabel_functions.la $(top_builddir)/babel/libbabel.la $(top_builddir)/babel/libifiction.la noinst_LTLIBRARIES = first.la model.la gridtest.la splittest.la multiwin.la \ styletest.la soundtest.la test-userstyle.la fileio.la diff --git a/tests/babeltest.c b/tests/babeltest.c index 44537da..2e8827e 100644 --- a/tests/babeltest.c +++ b/tests/babeltest.c @@ -6,14 +6,18 @@ #include #include #include -#include +#include +#include typedef struct _metadata { const gchar *element_name; gchar *ifid; gchar *title; gchar *author; - gchar *year; + gchar *firstpublished; + gboolean error; + gchar *error_message; + gchar *error_code; } metadata; void start_element( @@ -27,11 +31,17 @@ void start_element( metadata *md = (metadata*) data; md->element_name = element_name; + if( !strcmp(element_name, "errorCode") ) { + md->error = 1; + md->error_message = ""; + md->error_code = ""; + } + if( !strcmp(element_name, "ifindex") ) { - md->ifid = g_strdup(""); - md->title = g_strdup(""); - md->author = g_strdup(""); - md->year = g_strdup(""); + md->ifid = ""; + md->title = ""; + md->author = ""; + md->firstpublished = ""; } } @@ -43,31 +53,28 @@ void text( GError **error) { metadata *md = (metadata*) data; - gchar *stripped_text; - if( !strcmp(md->element_name, "ifid") ) { - //stripped_text = g_strstrip( g_strndup(text, text_len) ); - stripped_text = g_strndup(text, text_len); - md->ifid = g_strconcat(md->ifid, stripped_text, NULL); - g_free(stripped_text); + if( !strcmp(md->element_name, "errorCode") ) { + md->error_code = g_strndup(text, text_len); + } + else if( !strcmp(md->element_name, "errorMessage") ) { + md->error_message = g_strndup(text, text_len); + } + else if( !strcmp(md->element_name, "ifid") ) { + if( strlen(md->ifid) < text_len ) + md->ifid = g_strndup(text, text_len); } else if( !strcmp(md->element_name, "title") ) { - //stripped_text = g_strstrip( g_strndup(text, text_len) ); - stripped_text = g_strndup(text, text_len); - md->title = g_strconcat(md->title, stripped_text, NULL); - g_free(stripped_text); + if( strlen(md->title) < text_len ) + md->title = g_strndup(text, text_len); } else if( !strcmp(md->element_name, "author") ) { - //stripped_text = g_strstrip( g_strndup(text, text_len) ); - stripped_text = g_strndup(text, text_len); - md->author = g_strconcat(md->author, stripped_text, NULL); - g_free(stripped_text); + if( strlen(md->author) < text_len ) + md->author = g_strndup(text, text_len); } else if( !strcmp(md->element_name, "firstpublished") ) { - //stripped_text = g_strstrip( g_strndup(text, text_len) ); - stripped_text = g_strndup(text, text_len); - md->year = g_strconcat(md->year, stripped_text, NULL); - g_free(stripped_text); + if( strlen(md->firstpublished) < text_len ) + md->firstpublished = g_strndup(text, text_len); } } @@ -79,7 +86,7 @@ void end_element( { if( !strcmp(element_name, "ifindex") ) { metadata *md = (metadata*) data; - printf("IFID: %s\nTitle: %s\nAuthor: %s\nYear: %s\n", md->ifid, md->title, md->author, md->year); + printf("IFID: %s\nTitle: %s\nAuthor: %s\nFirst published: %s\n", md->ifid, md->title, md->author, md->firstpublished); } } @@ -107,37 +114,69 @@ run_sql_non_select(GdaConnection *cnc, const gchar *sql) } int main(int argc, char **argv) { + GError *err = NULL; + metadata data; + data.error = 0; + if(argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } + g_type_init(); + babel_init(argv[1]); int len = babel_treaty(GET_STORY_FILE_METADATA_EXTENT_SEL, NULL, 0); - if(len == 0) { - printf("No metadata found.\n"); + gchar *ifiction; + if(len) { + printf("Metadata found in file.\n"); + gchar *buffer = malloc(len * sizeof(gchar)); + babel_treaty(GET_STORY_FILE_METADATA_SEL, buffer, len); + ifiction = g_strndup(buffer, len); + g_free(buffer); + } else { + printf("No metadata found in file, performing IFDB lookup.\n"); + gchar *ifid = malloc(TREATY_MINIMUM_EXTENT * sizeof(gchar)); + if( !babel_treaty(GET_STORY_FILE_IFID_SEL, ifid, TREATY_MINIMUM_EXTENT) ) { + fprintf(stderr, "Unable to create an IFID (A serious problem occurred while loading the file).\n"); + babel_release(); + return 1; + } + printf("Looking up IFID: %s.\n", ifid); babel_release(); - return 0; + + SoupSession *session = soup_session_async_new(); + char *uri_string = g_strconcat("http://ifdb.tads.org/viewgame?ifiction&ifid=", ifid, NULL); + SoupMessage *message = soup_message_new("GET", uri_string); + g_free(uri_string); + soup_message_headers_append(message->request_headers, "Connection", "close"); + if(soup_session_send_message(session, message) != 200) + g_printerr("ERROR: did not get HTTP status 200\n"); + ifiction = g_strndup(message->response_body->data, message->response_body->length); + g_object_unref(message); + g_object_unref(session); } - gchar *buffer = malloc(len * sizeof(gchar)); - babel_treaty(GET_STORY_FILE_METADATA_SEL, buffer, len); - g_strchomp(buffer); - len = strlen(buffer); + ifiction = g_strchomp(ifiction); - metadata data; GMarkupParser xml_parser = {start_element, end_element, text, NULL, NULL}; GMarkupParseContext *context = g_markup_parse_context_new(&xml_parser, 0, &data, NULL); - GError *err = NULL; - if( g_markup_parse_context_parse(context, buffer, len, &err) == FALSE ) { + if( g_markup_parse_context_parse(context, ifiction, strlen(ifiction), &err) == FALSE ) { fprintf(stderr, "Metadata parse failed: %s\n", err->message); } - free(buffer); g_markup_parse_context_free(context); + g_free(ifiction); + babel_release(); + // Check for errors + if(data.error) { + fprintf(stderr, "ERROR %s: %s\n", data.error_code, data.error_message); + return 1; + } + // Open DB connection GdaConnection *cnc; GdaSqlParser *sql_parser; @@ -157,16 +196,16 @@ int main(int argc, char **argv) { // Create stories table //run_sql_non_select(cnc, "DROP TABLE IF EXISTS stories"); - run_sql_non_select(cnc, "CREATE TABLE IF NOT EXISTS stories (ifid text not null primary key, title text, author text, year integer)"); + run_sql_non_select(cnc, "CREATE TABLE IF NOT EXISTS stories (ifid text not null primary key, title text, author text, firstpublished text)"); // Populate the table GValue *v1, *v2, *v3, *v4; v1 = gda_value_new_from_string(data.ifid, G_TYPE_STRING); v2 = gda_value_new_from_string(data.title, G_TYPE_STRING); v3 = gda_value_new_from_string(data.author, G_TYPE_STRING); - v4 = gda_value_new_from_string(data.year, G_TYPE_UINT); + v4 = gda_value_new_from_string(data.firstpublished, G_TYPE_STRING); - if( !gda_insert_row_into_table(cnc, "stories", &err, "ifid", v1, "title", v2, "author", v3, "year", v4, NULL) ) { + if( !gda_insert_row_into_table(cnc, "stories", &err, "ifid", v1, "title", v2, "author", v3, "firstpublished", v4, NULL) ) { g_warning("Could not INSERT data into the 'stories' table: %s\n", err && err->message ? err->message : "No details"); } diff --git a/tests/memstreamtest.ulx b/tests/memstreamtest.ulx index 7efb581..138ca5f 100644 Binary files a/tests/memstreamtest.ulx and b/tests/memstreamtest.ulx differ diff --git a/tests/test-close.c b/tests/test-close.c index fc30203..68ea15f 100644 --- a/tests/test-close.c +++ b/tests/test-close.c @@ -13,10 +13,24 @@ on_command(ChimaraIF *glk, gchar *input, gchar *response, GtkWindow *window) gtk_widget_destroy(dialog); } +void +on_stop(GtkButton *button, ChimaraIF *glk) +{ + chimara_glk_stop(CHIMARA_GLK(glk)); + chimara_glk_wait(CHIMARA_GLK(glk)); +} + +void +on_go(GtkButton *button, ChimaraIF *glk) +{ + on_stop(button, glk); + g_assert(chimara_if_run_game(CHIMARA_IF(glk), "unicodetest.ulx", NULL)); +} + int main(int argc, char *argv[]) { - GtkWidget *window, *glk; + GtkWidget *window, *vbox, *hbox, *stop, *go, *glk; /* Initialize threads and GTK */ if(!g_thread_supported()) @@ -29,9 +43,20 @@ main(int argc, char *argv[]) window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL); + vbox = gtk_vbox_new(FALSE, 6); glk = chimara_if_new(); - g_signal_connect(glk, "command", G_CALLBACK(on_command), window); - gtk_container_add(GTK_CONTAINER(window), glk); + //g_signal_connect(glk, "command", G_CALLBACK(on_command), window); + hbox = gtk_hbutton_box_new(); + stop = gtk_button_new_with_label("Stop"); + g_signal_connect(stop, "clicked", G_CALLBACK(on_stop), glk); + go = gtk_button_new_with_label("Go"); + g_signal_connect(go, "clicked", G_CALLBACK(on_go), glk); + + gtk_box_pack_start(GTK_BOX(hbox), stop, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), go, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), glk, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show_all(window); /* Add a reference to the ChimaraGlk widget, because we want to keep it