])
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
# 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
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
<!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"/>
<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"/>
</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"/>
</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"/>
</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"/>
</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"/>
</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"/>
</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"/>
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
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?
#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
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);
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;
}
}
/* 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;
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
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);
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
* 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)
*
* 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)
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
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);
#include "chimara-marshallers.h"
#include "init.h"
+#ifndef PLUGINDIR
+#define PLUGINDIR "."
+#endif
+
/**
* SECTION:chimara-if
* @short_description: Widget which plays an interactive fiction game
/**
* 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
* 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 */
}
/* 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)) {
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)
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.
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);
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 */
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 */
info = load_image_from_blorb(resource, image, width, height);
}
+ if(info == NULL)
+ return NULL;
+
/* Store the image in the cache */
gdk_threads_enter();
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
{
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);
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)
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;
}
-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@
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
#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(
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 = "";
}
}
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);
}
}
{
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);
}
}
}
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;
// 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");
}
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())
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