Merge branch 'new-sound-api'
authorPhilip Chimento <philip.chimento@gmail.com>
Wed, 18 Jan 2012 12:59:59 +0000 (13:59 +0100)
committerPhilip Chimento <philip.chimento@gmail.com>
Wed, 18 Jan 2012 12:59:59 +0000 (13:59 +0100)
Conflicts:
docs/reference/chimara-docs.sgml

19 files changed:
configure.ac
docs/reference/Makefile.am
docs/reference/chimara-docs.sgml
docs/reference/chimara-sections.txt
libchimara/Makefile.am
libchimara/chimara-glk.c
libchimara/chimara-glk.h
libchimara/chimara-if.c
libchimara/chimara-if.h
libchimara/gi_dispa.c
libchimara/graphics.c
libchimara/input.c
libchimara/magic.h
libchimara/style.c
player/Makefile.am
tests/Makefile.am
tests/babeltest.c
tests/memstreamtest.ulx
tests/test-close.c

index 3fb5d0196b8f1c485ed22fdb7d3a536d0a68a348..fa284e1051ccfc0ed7e6a23b9497f095833dbe43 100644 (file)
@@ -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
index 7994e0e828c931c8b5f0aa23fbff0262af198dc0..7fd58a2acb15e6947a4fa7e452e3b2cd09381ca7 100644 (file)
@@ -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
index c98d874d1ba089384fc8af7c580188cf68ef08a9..af494a7332962aa823c35fbbb6f863e6e65021c9 100644 (file)
@@ -4,18 +4,21 @@
 <!ENTITY version SYSTEM "version.xml">
 ]>
 <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+
+  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+
   <bookinfo>
     <title>Chimara Reference Manual</title>
     <releaseinfo>for Chimara &version;</releaseinfo>
   </bookinfo>
 
-  <reference>
+  <reference id="chimara-api-reference">
     <title>Chimara API Reference</title>
     <xi:include href="xml/chimara-glk.xml"/>
     <xi:include href="xml/chimara-if.xml"/>
   </reference>
   
-  <reference>
+  <reference id="chimara-glk-api-spec">
     <title>Glk API Specification, version 0.7.0</title>
     <xi:include href="glk-front-matter.sgml"/>
     
@@ -23,7 +26,7 @@
     <xi:include href="glk-introduction.sgml"/>
     
     <!-- Chapter 1. Overall Structure -->
-    <chapter>
+    <chapter id="chimara-glk-spec-overall-structure">
       <title>Overall Structure</title>
       <!-- For Gtk-Doc post-processed SGML files, look in the xml directory-->
       <xi:include href="xml/glk-main-function.sgml"/>
@@ -37,7 +40,7 @@
     </chapter>
     
     <!-- Chapter 2. Character Encoding -->
-    <chapter>
+    <chapter id="chimara-glk-spec-character-encoding">
       <title>Character Encoding</title>
       <xi:include href="glk-character-encoding.sgml"/>
       <xi:include href="xml/glk-output.sgml"/>
@@ -49,7 +52,7 @@
     </chapter>
     
     <!-- Chapter 3. Windows -->
-    <chapter>
+    <chapter id="chimara-glk-spec-windows">
       <title>Windows</title>
       <xi:include href="glk-windows.sgml"/>
       <xi:include href="xml/glk-window-arrangement.sgml"/>
@@ -62,7 +65,7 @@
     </chapter>
     
     <!-- Chapter 4. Events -->
-    <chapter>
+    <chapter id="chimara-glk-spec-events">
       <title>Events</title>
       <xi:include href="xml/glk-events.xml"/>
       <xi:include href="xml/glk-character-input-events.xml"/>
@@ -73,7 +76,7 @@
        </chapter>
        
        <!-- Chapter 5. Streams -->
-       <chapter>
+       <chapter id="chimara-glk-spec-streams">
          <title>Streams</title>
          <xi:include href="xml/glk-streams.xml"/>
          <xi:include href="xml/glk-print.xml"/>
@@ -88,7 +91,7 @@
        </chapter>
        
        <!-- Chapter 6. File References -->
-       <chapter>
+       <chapter id="chimara-glk-spec-file-references">
          <title>File References</title>
          <xi:include href="xml/glk-fileref.xml"/>
          <xi:include href="xml/glk-fileref-types.xml"/>      
@@ -96,7 +99,7 @@
     </chapter>
     
     <!-- Chapter 7. Graphics -->
-    <chapter>
+    <chapter id="chimara-glk-spec-graphics">
       <title>Graphics</title>
       <xi:include href="xml/glk-image-resources.xml"/>
       <xi:include href="xml/glk-graphics-windows.xml"/>
     </chapter>
     
     <!-- Chapter 8. Sound -->
-    <chapter id="chimara-chapter-Sound">
+    <chapter id="chimara-glk-spec-sound">
       <title>Sound</title>
       <xi:include href="glk-sound-resources.sgml"/>
       <xi:include href="xml/glk-sound-channels.xml"/>
     </chapter>
     
     <!-- Chapter 9. Hyperlinks -->
-    <chapter>
+    <chapter id="chimara-glk-spec-hyperlinks">
       <title>Hyperlinks</title>
       <xi:include href="xml/glk-creating-hyperlinks.xml"/>
       <xi:include href="xml/glk-accepting-hyperlinks.xml"/>
     </chapter>
 
        <!-- Chapter 10. The System Clock -->
-       <chapter>
+       <chapter id="chimara-glk-spec-the-system-clock">
          <title>The System Clock</title>
          <xi:include href="xml/glk-clock.xml"/>
          <xi:include href="xml/glk-clock-conversions.xml"/>
     <xi:include href="glk-porting.sgml"/>
    
     <!-- Appendices -->
-    <appendix>
+    <appendix id="chimara-glk-spec-the-dispatch-layer">
       <title>The Dispatch Layer</title>
       <xi:include href="dispatch.sgml"/>
       <xi:include href="xml/dispatch-interrogating.xml"/>
       <xi:include href="dispatch-selectors.sgml"/>
     </appendix>
 
-    <appendix>
+    <appendix id="chimara-glk-spec-the-blorb-layer">
       <title>The Blorb Layer</title>
       <xi:include href="blorb.sgml"/>
       <xi:include href="xml/blorb-program.xml"/>
       <xi:include href="xml/blorb-errors.xml"/>
     </appendix>
     
-    <appendix>
+    <appendix id="chimara-glk-spec-glk-extensions">
      <title>Glk Extensions</title>
      <xi:include href="xml/glkext-startup.xml"/>
      <xi:include href="xml/glkext-unix.xml"/>
index 74e9e6a79a2c73ca6603ee0abf0599cb10ec848a..994f21b084c551ca3ed2412bb0778d0f64459249 100644 (file)
@@ -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
index f1ef3f4a35bef37afc962277b89adefa391d0826..2496948200c121a06fc1f64141d04d1625c29450 100644 (file)
@@ -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?
 
index 19925569823b16569e2246a0f457a74f974348cb..b7c0a7d6f3df2f9290ed5e459ea02d9c481b9e26 100644 (file)
 #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 <filename
+ * class="header">glk.h</filename>
+ * @argc: Number of command line arguments in @argv
+ * @argv: Array of command line arguments to pass to the plugin
+ * @error: location to store a <link
+ * linkend="glib-Error-Reporting">GError</link>, 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
index 37d2220fb8776665cf07e947257dc01601db4fe6..b3ea8b86002db3d8bfcaac774929217eaf3898db 100644 (file)
@@ -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);
index af1179c49fd0db5219caed2d2bca7d7be8f80cc8..ed132bb5f01d32e0e1c3080b7166e77440967de1 100644 (file)
 #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.
index deda6474e8e893f85645ee66f603ff2a31326a08..3bbe06ac7e0b2fd16d10976a6adccd9b3b47151a 100644 (file)
@@ -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);
 
index f38eff0e832ee2d09d18511d191e94789da806f1..f0099b919d7e5829b3e95090a181aac92d16fa55 100644 (file)
@@ -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 */
index afe3e76386a75899cda8850260cd68f566513cb8..0c3215a6f3c66f01b1c50bb63a259ff844a49d3f 100644 (file)
@@ -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();
 
index bb9b9d1fae43f7e5ca1cc9b66567bd62043257a0..7f9b693cf9c8bc7738a7482789d84387f23d7024 100644 (file)
@@ -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);
index 406b570abfe99aeab12fbdbbf37e3defe35c6e4b..3f77d8de7be36eb78b9276f80e7f01f3b076bec0 100644 (file)
@@ -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)
index 125dbd6d38128c41032eba935b7a34a4c1d25ca7..f9b231bf40bd4aff4e6a425b096b6578a12f212e 100644 (file)
@@ -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;
 }
 
index 2482798027e91e91212bf89dfa0df9fec4b0c3a2..588f18325395ffa69e5daa083f8acbe441bdc87d 100644 (file)
@@ -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@
index c8ae301164154a7bd652fc108d7c628a1d358da8..1fed1058916e7db17e65045192487de13d5b9a5c 100644 (file)
@@ -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
index 44537da9e3e41e1b646f162282acc736121d709b..2e8827e5b98f33be1dc8b844d026da06a1c86ae8 100644 (file)
@@ -6,14 +6,18 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <libgda/libgda.h>
-#include <sql-parser/gda-sql-parser.h>
+#include <libgda/sql-parser/gda-sql-parser.h>
+#include <libsoup/soup.h>
 
 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 <story file>\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");
        }
 
index 7efb5815be187ea23f74b8e11fa3c34daeba79e6..138ca5f158335b77f5ef392fcd4458c02907b754 100644 (file)
Binary files a/tests/memstreamtest.ulx and b/tests/memstreamtest.ulx differ
index fc302034685695b43e0e2a776d34c250a5276365..68ea15f1a9bf8487cf2bc7ea12f4fc07ae3f8077 100644 (file)
@@ -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