X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=src%2Fchimara-glk.c;h=4bc1c156bc83f6e75148faa3876460f5b8934416;hb=d3445e43a2ce2453f8e2cd5ed9edd8b23ae54446;hp=a92377f3925f67c679e148ef604deea2402e698b;hpb=a578af18fe4946e35e866c1eec541c69989e0d5f;p=rodin%2Fchimara.git diff --git a/src/chimara-glk.c b/src/chimara-glk.c index a92377f..4bc1c15 100644 --- a/src/chimara-glk.c +++ b/src/chimara-glk.c @@ -6,11 +6,37 @@ #include "chimara-glk.h" #include "chimara-glk-private.h" #include "glk.h" +#include "abort.h" #include "window.h" #define CHIMARA_GLK_MIN_WIDTH 0 #define CHIMARA_GLK_MIN_HEIGHT 0 +/** + * SECTION:chimara-glk + * @short_description: Widget which executes a Glk program + * @stability: Unstable + * @include: chimara/chimara-glk.h + * + * The ChimaraGlk widget opens and runs a Glk program. The program must be + * compiled as a plugin module, with a function glk_main() + * that the Glk library can hook into. + * + * On Linux systems, this is a file with a name like + * plugin.so. For portability, you can use libtool and + * automake: + * + * pkglib_LTLIBRARIES = plugin.la + * plugin_la_SOURCES = plugin.c foo.c bar.c + * plugin_la_LDFLAGS = -module -shared -avoid-version -export-symbols-regex "^glk_main$$" + * + * This will produce plugin.la which is a text file + * containing the correct plugin file to open (see the relevant section of the + * + * Libtool manual). + */ + typedef void (* glk_main_t) (void); enum { @@ -40,6 +66,7 @@ chimara_glk_init(ChimaraGlk *self) priv->self = self; priv->interactive = TRUE; priv->protect = FALSE; + priv->program = NULL; priv->thread = NULL; priv->event_queue = NULL; priv->event_lock = NULL; @@ -112,7 +139,7 @@ chimara_glk_finalize(GObject *object) g_mutex_unlock(priv->abort_lock); g_mutex_free(priv->abort_lock); priv->abort_lock = NULL; - + G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object); } @@ -128,7 +155,7 @@ chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition) /* For now, just pass the size request on to the root Glk window */ if(priv->root_window) { GtkWidget *child = ((winid_t)(priv->root_window->data))->frame; - if(GTK_WIDGET_VISIBLE(child)) + if(GTK_WIDGET_VISIBLE(child)) gtk_widget_size_request(child, requisition); } else { requisition->width = CHIMARA_GLK_MIN_WIDTH; @@ -172,7 +199,11 @@ chimara_glk_forall(GtkContainer *container, gboolean include_internals, static void chimara_glk_stopped(ChimaraGlk *self) { - /* TODO: Add default signal handler implementation here */ + ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self); + + /* Free the plugin */ + if( priv->program && !g_module_close(priv->program) ) + g_warning( "Error closing module: %s", g_module_error() ); } static void @@ -200,10 +231,24 @@ chimara_glk_class_init(ChimaraGlkClass *klass) /* Signals */ klass->stopped = chimara_glk_stopped; klass->started = chimara_glk_started; + /** + * ChimaraGlk::stopped: + * @glk: The widget that received the signal + * + * The ::stopped signal is emitted when the a Glk program finishes + * executing in the widget, whether it ended normally, or was interrupted. + */ chimara_glk_signals[STOPPED] = g_signal_new("stopped", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, stopped), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * ChimaraGlk::started: + * @glk: The widget that received the signal + * + * The ::started signal is emitted when a Glk program starts executing in + * the widget. + */ chimara_glk_signals[STARTED] = g_signal_new ("started", G_OBJECT_CLASS_TYPE (klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, started), NULL, NULL, @@ -216,12 +261,28 @@ chimara_glk_class_init(ChimaraGlkClass *klass) TRUE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); + /** + * ChimaraGlk:interactive: + * + * Sets whether the widget is interactive. A Glk widget is normally + * interactive, but in non-interactive mode, keyboard and mouse input are + * ignored and the Glk program is controlled by chimara_glk_feed_text(). + * "More" prompts when a lot of text is printed to a text buffer are also + * disabled. This is typically used when you wish to control an interpreter + * program by feeding it a predefined list of commands. + */ g_object_class_install_property(object_class, PROP_INTERACTIVE, pspec); pspec = g_param_spec_boolean("protect", _("Protected"), _("Whether the Glk program is barred from doing file operations"), FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); + /** + * ChimaraGlk:protect: + * + * Sets whether the Glk program is allowed to do file operations. In protect + * mode, all file operations will fail. + */ g_object_class_install_property(object_class, PROP_PROTECT, pspec); /* Private data */ @@ -230,6 +291,13 @@ chimara_glk_class_init(ChimaraGlkClass *klass) /* PUBLIC FUNCTIONS */ +/** + * chimara_glk_new: + * + * Creates and initializes a new #ChimaraGlk widget. + * + * Return value: a #ChimaraGlk widget, with a floating reference. + */ GtkWidget * chimara_glk_new(void) { @@ -245,6 +313,13 @@ chimara_glk_new(void) return GTK_WIDGET(self); } +/** + * chimara_glk_set_interactive: + * @glk: a #ChimaraGlk widget + * @interactive: whether the widget should expect user input + * + * Sets the #ChimaraGlk:interactive property of @glk. + */ void chimara_glk_set_interactive(ChimaraGlk *glk, gboolean interactive) { @@ -254,6 +329,15 @@ chimara_glk_set_interactive(ChimaraGlk *glk, gboolean interactive) priv->interactive = interactive; } +/** + * chimara_glk_get_interactive: + * @glk: a #ChimaraGlk widget + * + * Returns whether @glk is interactive (expecting user input). See + * #ChimaraGlk:interactive. + * + * Return value: %TRUE if @glk is interactive. + */ gboolean chimara_glk_get_interactive(ChimaraGlk *glk) { @@ -263,6 +347,15 @@ chimara_glk_get_interactive(ChimaraGlk *glk) return priv->interactive; } +/** + * chimara_glk_set_protect: + * @glk: a #ChimaraGlk widget + * @protect: whether the widget should allow the Glk program to do file + * operations + * + * Sets the #ChimaraGlk:protect property of @glk. In protect mode, the Glk + * program is not allowed to do file operations. + */ void chimara_glk_set_protect(ChimaraGlk *glk, gboolean protect) { @@ -272,6 +365,15 @@ chimara_glk_set_protect(ChimaraGlk *glk, gboolean protect) priv->protect = protect; } +/** + * chimara_glk_get_protect: + * @glk: a #ChimaraGlk widget + * + * Returns whether @glk is in protect mode (banned from doing file operations). + * See #ChimaraGlk:protect. + * + * Return value: %TRUE if @glk is in protect mode. + */ gboolean chimara_glk_get_protect(ChimaraGlk *glk) { @@ -285,34 +387,50 @@ chimara_glk_get_protect(ChimaraGlk *glk) static gpointer glk_enter(gpointer glk_main) { + extern ChimaraGlkPrivate *glk_data; + g_signal_emit_by_name(glk_data->self, "started"); ((glk_main_t)glk_main)(); + g_signal_emit_by_name(glk_data->self, "stopped"); return NULL; } +/** + * chimara_glk_run: + * @glk: a #ChimaraGlk widget + * @plugin: path to a plugin module compiled with glk.h + * @error: location to store a GError, or + * %NULL + * + * Opens a Glk program compiled as a plugin and runs its glk_main() function in + * a separate thread. On failure, returns %FALSE and sets @error. + * + * Return value: %TRUE if the Glk program was started successfully. + */ gboolean chimara_glk_run(ChimaraGlk *glk, gchar *plugin, GError **error) { g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE); g_return_val_if_fail(plugin, FALSE); + ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk); + /* Open the module to run */ - GModule *module; glk_main_t glk_main; g_assert( g_module_supported() ); - module = g_module_open(plugin, G_MODULE_BIND_LAZY); + priv->program = g_module_open(plugin, G_MODULE_BIND_LAZY); - if(!module) + if(!priv->program) { g_warning( "Error opening module: %s", g_module_error() ); return FALSE; } - if( !g_module_symbol(module, "glk_main", (gpointer *) &glk_main) ) + if( !g_module_symbol(priv->program, "glk_main", (gpointer *) &glk_main) ) { g_warning( "Error finding glk_main(): %s", g_module_error() ); return FALSE; } - - ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk); + extern ChimaraGlkPrivate *glk_data; /* Set the thread's private data */ /* TODO: Do this with a GPrivate */ @@ -321,27 +439,37 @@ chimara_glk_run(ChimaraGlk *glk, gchar *plugin, GError **error) /* Run in a separate thread */ priv->thread = g_thread_create(glk_enter, glk_main, TRUE, error); - /* Close module */ -/* if( !g_module_close(module) ) - { - g_warning( "Error closing module: %s", g_module_error() ); - return FALSE; - }*/ - return !(priv->thread == NULL); } +/** + * 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. + */ void chimara_glk_stop(ChimaraGlk *glk) { g_return_if_fail(glk || CHIMARA_IS_GLK(glk)); - + /* TODO: check if glk is actually running a program */ signal_abort(); } +/** + * chimara_glk_wait: + * @glk: a #ChimaraGlk widget + * + * Holds up the main thread and waits for the Glk program running in @glk to + * finish. + */ void chimara_glk_wait(ChimaraGlk *glk) { + g_return_if_fail(glk || CHIMARA_IS_GLK(glk)); + ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk); g_thread_join(priv->thread); -} \ No newline at end of file +}