X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fchimara-glk.c;h=5162046fee0470530782c1f09fa28f2b35287bd6;hb=4bb7f43aca27bca1d04a7420b432b6189af06259;hp=f5617703c8c169c2349904fe65a0abe1d5adfa6f;hpb=c6e78c57fc1b323ec055bfe48c7430515be27d1c;p=rodin%2Fchimara.git diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index f561770..5162046 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -30,7 +30,7 @@ * SECTION:chimara-glk * @short_description: Widget which executes a Glk program * @stability: Unstable - * @include: chimara/chimara-glk.h + * @include: libchimara/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() @@ -49,6 +49,72 @@ * * Libtool manual). + * + * You need to initialize multithreading in any program you use a #ChimaraGlk + * widget in. This means including the following incantation at the beginning + * of your program: + * |[ + * if(!g_thread_supported()) + * g_thread_init(NULL); + * gdk_threads_init(); + * ]| + * This initialization must take place before the call to + * gtk_init(). In addition to this, you must also protect your call to + * gtk_main() by calling gdk_threads_enter() right before it, and + * gdk_threads_leave() right after it. + * + * The following sample program shows how to initialize and construct a simple + * GTK window that runs a Glk program: + * |[ + * #include + * #include + * #include + * + * static gboolean + * quit(void) + * { + * gtk_main_quit(); + * return TRUE; + * } + * + * int + * main(int argc, char *argv[]) + * { + * GtkWidget *window, *glk; + * GError *error = NULL; + * gchar *plugin_argv[] = { "plugin.so", "-option" }; + * + * /* Initialize threads and GTK */ + * if(!g_thread_supported()) + * g_thread_init(NULL); + * gdk_threads_init(); + * gtk_init(&argc, &argv); + * + * /* Construct the window and its contents. We quit the GTK main loop + * * when the window's close button is clicked. */ + * window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + * g_signal_connect(window, "delete-event", G_CALLBACK(quit), NULL); + * glk = chimara_glk_new(); + * gtk_container_add(GTK_CONTAINER(window), glk); + * gtk_widget_show_all(window); + * + * /* Start the Glk program in a separate thread */ + * if(!chimara_glk_run(CHIMARA_GLK(glk), "./plugin.so", 2, plugin_argv, &error)) + * g_error("Error starting Glk library: %s\n", error->message); + * + * /* Start the GTK main loop */ + * gdk_threads_enter(); + * gtk_main(); + * gdk_threads_leave(); + * + * /* After the GTK main loop exits, signal the Glk program to shut down if + * * it is still running, and wait for it to exit. */ + * chimara_glk_stop(CHIMARA_GLK(glk)); + * chimara_glk_wait(CHIMARA_GLK(glk)); + * + * return 0; + * } + * ]| */ typedef void (* glk_main_t) (void); @@ -59,6 +125,9 @@ enum { PROP_INTERACTIVE, PROP_PROTECT, PROP_SPACING, + PROP_PROGRAM_NAME, + PROP_PROGRAM_INFO, + PROP_STORY_NAME }; enum { @@ -112,6 +181,9 @@ chimara_glk_init(ChimaraGlk *self) priv->resource_loaded = g_cond_new(); priv->resource_info_available = g_cond_new(); priv->image_cache = NULL; + priv->program_name = NULL; + priv->program_info = NULL; + priv->story_name = NULL; priv->interrupt_handler = NULL; priv->root_window = NULL; priv->fileref_list = NULL; @@ -161,7 +233,16 @@ chimara_glk_get_property(GObject *object, guint prop_id, GValue *value, GParamSp case PROP_SPACING: g_value_set_uint(value, priv->spacing); break; - default: + case PROP_PROGRAM_NAME: + g_value_set_string(value, priv->program_name); + break; + case PROP_PROGRAM_INFO: + g_value_set_string(value, priv->program_info); + break; + case PROP_STORY_NAME: + g_value_set_string(value, priv->story_name); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } @@ -220,6 +301,9 @@ chimara_glk_finalize(GObject *object) /* Free other stuff */ g_free(priv->current_dir); + g_free(priv->program_name); + g_free(priv->program_info); + g_free(priv->story_name); /* Chain up to parent */ G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object); @@ -564,6 +648,12 @@ chimara_glk_stopped(ChimaraGlk *self) { CHIMARA_GLK_USE_PRIVATE(self, priv); priv->running = FALSE; + priv->program_name = NULL; + g_object_notify(G_OBJECT(self), "program-name"); + priv->program_info = NULL; + g_object_notify(G_OBJECT(self), "program-info"); + priv->story_name = NULL; + g_object_notify(G_OBJECT(self), "story-name"); } static void @@ -678,7 +768,7 @@ chimara_glk_class_init(ChimaraGlkClass *klass) chimara_glk_signals[CHAR_INPUT] = g_signal_new("char-input", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, char_input), NULL, NULL, - chimara_marshal_VOID__UINT_UINT, + _chimara_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); /** * ChimaraGlk::line-input: @@ -692,7 +782,7 @@ chimara_glk_class_init(ChimaraGlkClass *klass) chimara_glk_signals[LINE_INPUT] = g_signal_new("line-input", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, line_input), NULL, NULL, - chimara_marshal_VOID__UINT_STRING, + _chimara_marshal_VOID__UINT_STRING, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); /** * ChimaraGlk::text-buffer-output: @@ -705,7 +795,7 @@ chimara_glk_class_init(ChimaraGlkClass *klass) chimara_glk_signals[TEXT_BUFFER_OUTPUT] = g_signal_new("text-buffer-output", G_OBJECT_CLASS_TYPE(klass), 0, G_STRUCT_OFFSET(ChimaraGlkClass, text_buffer_output), NULL, NULL, - chimara_marshal_VOID__UINT_STRING, + _chimara_marshal_VOID__UINT_STRING, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); /* Properties */ @@ -749,7 +839,54 @@ chimara_glk_class_init(ChimaraGlkClass *klass) 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS) ); - /* Private data */ + /** + * ChimaraGlk:program-name: + * + * The name of the currently running Glk program. You cannot set this + * property yourself. It is set to the filename of the plugin when you call + * chimara_glk_run(), but the plugin can change it by calling + * garglk_set_program_name(). To find out when this information changes, + * for example to put the program name in the title bar of a window, connect + * to the ::notify::program-name signal. + */ + g_object_class_install_property(object_class, PROP_PROGRAM_NAME, + g_param_spec_string("program-name", _("Program name"), + _("Name of the currently running program"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS) ); + + /** + * ChimaraGlk:program-info: + * + * Information about the currently running Glk program. You cannot set this + * property yourself. The plugin can change it by calling + * garglk_set_program_info(). See also #ChimaraGlk:program-name. + */ + g_object_class_install_property(object_class, PROP_PROGRAM_INFO, + g_param_spec_string("program-info", _("Program info"), + _("Information about the currently running program"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS) ); + + /** + * ChimaraGlk:story-name: + * + * The name of the story currently running in the Glk interpreter. You + * cannot set this property yourself. It is set to the story filename when + * you call chimara_if_run_game(), but the plugin can change it by calling + * garglk_set_story_name(). + * + * Strictly speaking, this should be a property of #ChimaraIF, but it is + * legal for any Glk program to call garglk_set_story_name(), even if it is + * not an interpreter and does not load story files. + */ + g_object_class_install_property(object_class, PROP_STORY_NAME, + g_param_spec_string("story-name", _("Story name"), + _("Name of the story currently loaded in the interpreter"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS) ); + + /* Private data */ g_type_class_add_private(klass, sizeof(ChimaraGlkPrivate)); } @@ -1093,11 +1230,15 @@ chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *argv[], GE startup->args.argv = g_new0(gchar *, 1); } - /* Set the program name */ + /* Set the program invocation name */ startup->args.argv[0] = g_strdup(plugin); } startup->glk_data = priv; + /* Set the program name */ + priv->program_name = g_path_get_basename(plugin); + g_object_notify(G_OBJECT(glk), "program-name"); + /* Run in a separate thread */ priv->thread = g_thread_create((GThreadFunc)glk_enter, startup, TRUE, error);