### DECLARE COMPILERS #########################################################
-AC_PROG_CC # C compiler
AC_USE_SYSTEM_EXTENSIONS # Define _GNU_SOURCE if using GCC
+AC_PROG_CC # C compiler
AM_PROG_CC_C_O # Automake requires this for per-target CFLAGS
AC_C_INLINE # Define inline keyword
AC_PROG_YACC # Building nitfol requires yacc
gmodule-2.0
])
+# Plugin flags; include '-module' in each Makefile.am, because AC_SUBSTed
+# variables are black boxes to Automake, so it has to know about it being a
+# module in the makefile itself.
+PLUGIN_LIBTOOL_FLAGS='-avoid-version -shared -export-symbols-regex "^glk"'
+AC_SUBST(PLUGIN_LIBTOOL_FLAGS)
+
### OUTPUT ####################################################################
# Output platform-specific definitions to config.h
-PLUGIN_LIBTOOL_FLAGS=-module -avoid-version -export-symbols-regex "^glk"
-
pkglib_LTLIBRARIES = frotz.la
frotz_la_SOURCES = buffer.c err.c fastmem.c files.c input.c main.c math.c \
object.c process.c quetzal.c random.c redirect.c sound.c stream.c table.c \
text.c variable.c glkscreen.c glkmisc.c frotz.h glkfrotz.h glkio.h setup.h
frotz_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara
frotz_la_CFLAGS = -Wno-pointer-sign $(AM_CFLAGS)
-frotz_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+frotz_la_LDFLAGS = -module $(PLUGIN_LIBTOOL_FLAGS)
frotzdocdir = $(datadir)/doc/$(PACKAGE)/frotz
dist_frotzdoc_DATA = AUTHORS COPYING README TODO
-PLUGIN_LIBTOOL_FLAGS=-module -avoid-version -export-symbols-regex "^glk"
-
# Automatically generate version.h
MAJOR = 1
MINOR = 2
accel.c
git_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara
git_la_CFLAGS = -DCHIMARA_EXTENSIONS -DUSE_INLINE $(AM_CFLAGS)
-git_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+git_la_LDFLAGS = -module $(PLUGIN_LIBTOOL_FLAGS)
gitdocdir = $(datadir)/doc/$(PACKAGE)/git
dist_gitdoc_DATA = README.txt
-PLUGIN_LIBTOOL_FLAGS=-module -avoid-version -export-symbols-regex "^glk"
-
pkglib_LTLIBRARIES = glulxe.la
glulxe_la_SOURCES = accel.c exec.c files.c funcs.c gestalt.c gestalt.h glkop.c \
glulxe.h heap.c main.c opcodes.h operand.c osdepend.c profile.c search.c \
glulxe_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara
glulxe_la_CFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -Wno-unused \
-DOS_UNIX $(AM_CFLAGS)
-glulxe_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+glulxe_la_LDFLAGS = -module $(PLUGIN_LIBTOOL_FLAGS)
glulxedocdir = $(datadir)/doc/$(PACKAGE)/glulxe
dist_glulxedoc_DATA = README
-PLUGIN_LIBTOOL_FLAGS=-module -avoid-version -export-symbols-regex "^glk"
-
GRAPHICS = no_graph.c no_graph.h
# GRAPHICS = graphics.c graphics.h
BLORB = blorb.c
nodist_nitfol_la_SOURCES = copying.c dbg_help.h startunix.c
nitfol_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara \
-DSMART_TOKENISER -DUSE_INLINE
-nitfol_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+nitfol_la_LDFLAGS = -module $(PLUGIN_LIBTOOL_FLAGS)
info_TEXINFOS = nitfol.texi
nitfol_TEXINFOS = dbg_help.texi options.texi
libchimara_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"Chimara\" \
-DLOCALEDIR=\""$(datadir)/locale"\" \
+ -DPLUGINDIR=\""$(pkglibdir)"\" \
-I$(top_srcdir)
libchimara_la_CFLAGS = @CHIMARA_CFLAGS@ $(AM_CFLAGS)
libchimara_la_LIBADD = @CHIMARA_LIBS@
libchimara_la_LDFLAGS = -version-info $(LT_VERSION_INFO) \
-no-undefined \
- -export-symbols-regex "^(glk|chimara_glk|glkunix|giblorb|gidispatch|garglk)_"
+ -export-symbols-regex "^(glk|chimara|glkunix|giblorb|gidispatch|garglk)_"
libchimara_includedir = $(includedir)/chimara/libchimara
libchimara_include_HEADERS = \
chimara-glk.h \
+ chimara-if.h \
glk.h \
glkstart.h \
gi_blorb.h \
priv->default_styles = g_hash_table_new(g_str_hash, g_str_equal);
priv->program = NULL;
priv->thread = NULL;
- priv->event_queue = NULL;
- priv->event_lock = NULL;
- priv->event_queue_not_empty = NULL;
- priv->event_queue_not_full = NULL;
- priv->abort_lock = NULL;
+ priv->event_queue = g_queue_new();
+ priv->event_lock = g_mutex_new();
+ priv->event_queue_not_empty = g_cond_new();
+ priv->event_queue_not_full = g_cond_new();
+ priv->abort_lock = g_mutex_new();
priv->abort_signalled = FALSE;
- priv->arrange_lock = NULL;
- priv->rearranged = NULL;
+ priv->arrange_lock = g_mutex_new();
+ priv->rearranged = g_cond_new();
priv->needs_rearrange = FALSE;
priv->ignore_next_arrange_event = FALSE;
priv->interrupt_handler = NULL;
static void
chimara_glk_started(ChimaraGlk *self)
{
- /* TODO: Add default signal handler implementation here */
+ /* Default signal handler */
}
static void
chimara_glk_waiting(ChimaraGlk *self)
{
- /* TODO: Add default signal handler */
+ /* Default signal handler */
}
static void
chimara_glk_char_input(ChimaraGlk *self, guint window_rock, guint keysym)
{
- /* TODO: Add default signal handler */
+ /* Default signal handler */
}
static void
chimara_glk_line_input(ChimaraGlk *self, guint window_rock, gchar *text)
{
- /* TODO: Add default signal handler */
+ /* Default signal handler */
}
static void
chimara_glk_text_buffer_output(ChimaraGlk *self, guint window_rock, gchar *text)
{
- /* TODO: Add default signal handler */
+ /* Default signal handler */
}
/* G_PARAM_STATIC_STRINGS only appeared in GTK 2.13.0 */
/* This is a library entry point; initialize the library */
chimara_init();
- ChimaraGlk *self = CHIMARA_GLK(g_object_new(CHIMARA_TYPE_GLK, NULL));
- ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
-
- priv->event_queue = g_queue_new();
- priv->event_lock = g_mutex_new();
- priv->event_queue_not_empty = g_cond_new();
- priv->event_queue_not_full = g_cond_new();
- priv->abort_lock = g_mutex_new();
- priv->arrange_lock = g_mutex_new();
- priv->rearranged = g_cond_new();
-
- return GTK_WIDGET(self);
+ return GTK_WIDGET(g_object_new(CHIMARA_TYPE_GLK, NULL));
}
/**
* Return value: %TRUE if the Glk program was started successfully.
*/
gboolean
-chimara_glk_run(ChimaraGlk *glk, gchar *plugin, int argc, char *argv[], GError **error)
+chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GError **error)
{
g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
g_return_val_if_fail(plugin, FALSE);
if(!priv->program)
{
g_warning( "Error opening module: %s", g_module_error() );
+ /* TODO: set error */
return FALSE;
}
if( !g_module_symbol(priv->program, "glk_main", (gpointer *) &startup->glk_main) )
{
g_warning( "Error finding glk_main(): %s", g_module_error() );
+ /* TODO: set error */
return FALSE;
}
PangoFontDescription *chimara_glk_get_monospace_font_description(ChimaraGlk *glk);
void chimara_glk_set_spacing(ChimaraGlk *glk, guint spacing);
guint chimara_glk_get_spacing(ChimaraGlk *glk);
-gboolean chimara_glk_run(ChimaraGlk *glk, gchar *plugin, int argc, char *argv[], GError **error);
+gboolean chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GError **error);
void chimara_glk_stop(ChimaraGlk *glk);
void chimara_glk_wait(ChimaraGlk *glk);
+#include <errno.h>
+#include <stdlib.h>
#include <glib.h>
#include <glib-object.h>
#include <config.h>
{ TRUE, TRUE, FALSE, FALSE }, /* Z5 */
{ TRUE, TRUE, FALSE, FALSE }, /* Z6 */
{ TRUE, TRUE, FALSE, FALSE }, /* Z8 */
- { FALSE, FALSE, TRUE, TRUE } /* Glulx */
+ { TRUE, TRUE, FALSE, FALSE }, /* Zblorb */
+ { FALSE, FALSE, TRUE, TRUE }, /* Glulx */
+ { FALSE, FALSE, TRUE, TRUE } /* Gblorb */
};
static gchar *format_names[CHIMARA_IF_NUM_FORMATS] = {
N_("Z-code version 5"),
N_("Z-code version 6"),
N_("Z-code version 8"),
- N_("Glulx")
+ N_("Blorbed Z-code"),
+ N_("Glulx"),
+ N_("Blorbed Glulx")
};
static gchar *interpreter_names[CHIMARA_IF_NUM_INTERPRETERS] = {
N_("Frotz"), N_("Nitfol"), N_("Glulxe"), N_("Git")
};
+static gchar *plugin_names[CHIMARA_IF_NUM_INTERPRETERS] = {
+ "frotz", "nitfol", "glulxe", "git"
+};
typedef struct _ChimaraIFPrivate {
ChimaraIFInterpreter preferred_interpreter[CHIMARA_IF_NUM_FORMATS];
priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z5] = CHIMARA_IF_INTERPRETER_FROTZ;
priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z6] = CHIMARA_IF_INTERPRETER_FROTZ;
priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z8] = CHIMARA_IF_INTERPRETER_FROTZ;
+ priv->preferred_interpreter[CHIMARA_IF_FORMAT_Z_BLORB] = CHIMARA_IF_INTERPRETER_FROTZ;
priv->preferred_interpreter[CHIMARA_IF_FORMAT_GLULX] = CHIMARA_IF_INTERPRETER_GLULXE;
+ priv->preferred_interpreter[CHIMARA_IF_FORMAT_GLULX_BLORB] = CHIMARA_IF_INTERPRETER_GLULXE;
}
static void
static void
chimara_if_command(ChimaraIF *self, gchar *input, gchar *response)
{
- /* TODO: Add default signal handler */
+ /* Default signal handler */
}
static void
void
chimara_if_set_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format, ChimaraIFInterpreter interpreter)
{
- g_return_if_fail(self);
+ g_return_if_fail(self && CHIMARA_IS_IF(self));
g_return_if_fail(format < CHIMARA_IF_NUM_FORMATS);
g_return_if_fail(format < CHIMARA_IF_NUM_INTERPRETERS);
ChimaraIFInterpreter
chimara_if_get_preferred_interpreter(ChimaraIF *self, ChimaraIFFormat format)
{
- g_return_val_if_fail(self, -1);
+ g_return_val_if_fail(self && CHIMARA_IS_IF(self), -1);
g_return_val_if_fail(format < CHIMARA_IF_NUM_FORMATS, -1);
CHIMARA_IF_USE_PRIVATE(self, priv);
return priv->preferred_interpreter[format];
}
+
+/* Opens a '.la' file and finds the name of the plugin library. g_free() the
+ * string when done. */
+static gchar *
+find_dlname(const gchar *pluginfile, GError **error)
+{
+ /* Find the name of the shared library */
+ gchar *dlname;
+ FILE *plugin = fopen(pluginfile, "r");
+ if(!plugin)
+ {
+ g_set_error(error, G_FILE_ERROR, errno, "Error opening '%s': %s", pluginfile, g_strerror(errno));
+ return NULL;
+ }
+ gchar *line = NULL;
+ size_t buflen;
+ ssize_t length;
+ while((length = getline(&line, &buflen, plugin)) != -1)
+ {
+ if(g_str_has_prefix(line, "dlname='"))
+ {
+ dlname = g_strndup(line + 8, length - 10);
+ break;
+ }
+ }
+ free(line);
+ if(!dlname)
+ {
+ g_set_error(error, G_FILE_ERROR, errno, "Error reading '%s': %s", pluginfile, g_strerror(errno));
+ return NULL;
+ }
+ if(fclose(plugin))
+ {
+ g_set_error(error, G_FILE_ERROR, errno, "Error closing '%s': %s", pluginfile, g_strerror(errno));
+ return NULL;
+ }
+ return dlname;
+}
+
+gboolean
+chimara_if_run_game(ChimaraIF *self, gchar *gamefile, GError **error)
+{
+ g_return_val_if_fail(self && CHIMARA_IS_IF(self), FALSE);
+ g_return_val_if_fail(gamefile, 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"))
+ format = CHIMARA_IF_FORMAT_Z5;
+ else if(g_str_has_suffix(gamefile, ".z6"))
+ format = CHIMARA_IF_FORMAT_Z6;
+ else if(g_str_has_suffix(gamefile, ".z8"))
+ format = CHIMARA_IF_FORMAT_Z8;
+ else if(g_str_has_suffix(gamefile, ".zlb") || g_str_has_suffix(gamefile, ".zblorb"))
+ format = CHIMARA_IF_FORMAT_Z_BLORB;
+ else if(g_str_has_suffix(gamefile, ".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"))
+ format = CHIMARA_IF_FORMAT_GLULX_BLORB;
+
+ /* Now decide what interpreter to use */
+ ChimaraIFInterpreter interpreter = priv->preferred_interpreter[format];
+ gchar *libtoolfile = g_strconcat(plugin_names[interpreter], ".la", NULL);
+#ifndef RELEASE
+ /* If there is a plugin in the source tree, use that */
+ gboolean use_installed = FALSE;
+ gchar *libtoolpath = g_build_filename("..", "interpreters", plugin_names[interpreter], libtoolfile, NULL);
+ if( !g_file_test(libtoolpath, G_FILE_TEST_EXISTS) )
+ {
+ g_free(libtoolpath);
+#endif
+ gchar *libtoolpath = g_build_filename(PLUGINDIR, libtoolfile, NULL);
+ if( !g_file_test(libtoolpath, G_FILE_TEST_EXISTS) )
+ {
+ g_free(libtoolpath);
+ g_free(libtoolfile);
+ g_warning("Cannot open %s plugin file", interpreter_names[interpreter]);
+ /* TODO: set error */
+ return FALSE;
+ }
+#ifndef RELEASE
+ use_installed = TRUE;
+ }
+#endif
+ g_free(libtoolfile);
+
+ gchar *dlname = find_dlname(libtoolpath, error);
+ g_free(libtoolpath);
+ if(!dlname)
+ return FALSE;
+ gchar *pluginpath;
+#ifndef RELEASE
+ if(use_installed)
+#endif
+ pluginpath = g_build_filename(PLUGINDIR, dlname, NULL);
+#ifndef RELEASE
+ else
+ pluginpath = g_build_filename("..", "interpreters", plugin_names[interpreter], LT_OBJDIR, dlname, NULL);
+#endif
+ char *argv[3] = { pluginpath, gamefile, NULL };
+ gboolean retval = chimara_glk_run(CHIMARA_GLK(self), pluginpath, 2, argv, error);
+ g_free(dlname);
+ return retval;
+}
CHIMARA_IF_FORMAT_Z5,
CHIMARA_IF_FORMAT_Z6,
CHIMARA_IF_FORMAT_Z8,
+ CHIMARA_IF_FORMAT_Z_BLORB,
CHIMARA_IF_FORMAT_GLULX,
+ CHIMARA_IF_FORMAT_GLULX_BLORB,
CHIMARA_IF_NUM_FORMATS
} ChimaraIFFormat;
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);
G_END_DECLS
#include "callbacks.h"
#include "error.h"
#include <libchimara/chimara-glk.h>
+#include <libchimara/chimara-if.h>
/* Global pointers to widgets */
GtkBuilder *builder = NULL;
}
static void
-on_waiting(ChimaraGlk *glk)
+on_command(ChimaraGlk *glk, gchar *input, gchar *response)
{
- g_printerr("Waiting!\n");
-}
-
-static void
-on_char_input(ChimaraGlk *glk, guint32 window_rock, guint keysym)
-{
- g_printerr("Character input in window %d: key %d\n", window_rock, keysym);
-}
-
-static void
-on_line_input(ChimaraGlk *glk, guint32 window_rock, gchar *text)
-{
- g_printerr("Line input in window %d: '%s'\n", window_rock, text);
-}
-
-static void
-on_text_buffer_output(ChimaraGlk *glk, guint32 window_rock, gchar *text)
-{
- g_printerr("Text buffer output in window %d: '%s'\n", window_rock, text);
+ g_printerr("Command!\n");
}
static GObject *
return;
}
- glk = chimara_glk_new();
+ glk = chimara_if_new();
g_object_set(glk, "border-width", 6, "spacing", 6, NULL);
chimara_glk_set_default_font_string(CHIMARA_GLK(glk), "Serif 12");
chimara_glk_set_monospace_font_string(CHIMARA_GLK(glk), "Monospace 12");
g_signal_connect(glk, "started", G_CALLBACK(on_started), NULL);
g_signal_connect(glk, "stopped", G_CALLBACK(on_stopped), NULL);
- g_signal_connect(glk, "waiting", G_CALLBACK(on_waiting), NULL);
- g_signal_connect(glk, "char-input", G_CALLBACK(on_char_input), NULL);
- g_signal_connect(glk, "line-input", G_CALLBACK(on_line_input), NULL);
- g_signal_connect(glk, "text-buffer-output", G_CALLBACK(on_text_buffer_output), NULL);
+ g_signal_connect(glk, "command", G_CALLBACK(on_command), NULL);
GtkBox *vbox = GTK_BOX( gtk_builder_get_object(builder, "vbox") );
if(vbox == NULL)
g_object_unref( G_OBJECT(builder) );
g_object_unref( G_OBJECT(uimanager) );
- if( !chimara_glk_run(CHIMARA_GLK(glk), "../interpreters/frotz/.libs/frotz.so", argc, argv, &error) ) {
+ if(argc < 2) {
+ error_dialog(GTK_WINDOW(window), NULL, "Must provide a game file");
+ return 1;
+ }
+
+ if( !chimara_if_run_game(CHIMARA_IF(glk), argv[1], &error) ) {
error_dialog(GTK_WINDOW(window), error, "Error starting Glk library: ");
return 1;
}