Changed build system to Automake. Split Glk code off into a GTK widget.
[projects/chimara/chimara.git] / src / chimara-glk.c
diff --git a/src/chimara-glk.c b/src/chimara-glk.c
new file mode 100644 (file)
index 0000000..63e9a38
--- /dev/null
@@ -0,0 +1,316 @@
+/* licensing and copyright information here */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include "chimara-glk.h"
+#include "chimara-glk-private.h"
+#include "glk.h"
+#include "window.h"
+
+#define CHIMARA_GLK_MIN_WIDTH 0
+#define CHIMARA_GLK_MIN_HEIGHT 0
+
+enum {
+    PROP_0,
+    PROP_INTERACTIVE,
+    PROP_PROTECT
+};
+
+enum {
+       STOPPED,
+       STARTED,
+
+       LAST_SIGNAL
+};
+
+static guint chimara_glk_signals[LAST_SIGNAL] = { 0 };
+
+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);
+
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
+    
+    priv->self = self;
+    priv->interactive = TRUE;
+    priv->protect = FALSE;
+    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->abort_signalled = FALSE;
+    priv->interrupt_handler = NULL;
+    priv->root_window = NULL;
+    priv->fileref_list = NULL;
+    priv->current_stream = NULL;
+    priv->stream_list = NULL;
+}
+
+static void
+chimara_glk_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+    ChimaraGlk *glk = CHIMARA_GLK(object);
+    
+    switch(prop_id) 
+    {
+        case PROP_INTERACTIVE:
+            chimara_glk_set_interactive(glk, g_value_get_boolean(value));
+            break;
+        case PROP_PROTECT:
+            chimara_glk_set_protect(glk, g_value_get_boolean(value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+chimara_glk_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(object);
+    
+    switch(prop_id)
+    {
+        case PROP_INTERACTIVE:
+            g_value_set_boolean(value, priv->interactive);
+            break;
+        case PROP_PROTECT:
+            g_value_set_boolean(value, priv->protect);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+chimara_glk_finalize(GObject *object)
+{
+    ChimaraGlk *self = CHIMARA_GLK(object);
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
+    
+    /* Free the event queue */
+    g_mutex_lock(priv->event_lock);
+       g_queue_foreach(priv->event_queue, (GFunc)g_free, NULL);
+       g_queue_free(priv->event_queue);
+       g_cond_free(priv->event_queue_not_empty);
+       g_cond_free(priv->event_queue_not_full);
+       priv->event_queue = NULL;
+       g_mutex_unlock(priv->event_lock);
+       g_mutex_free(priv->event_lock);
+       
+       /* Free the abort signalling mechanism */
+       g_mutex_lock(priv->abort_lock);
+       /* Make sure no other thread is busy with this */
+       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);
+}
+
+static void
+chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+    g_return_if_fail(widget);
+    g_return_if_fail(requisition);
+    g_return_if_fail(CHIMARA_IS_GLK(widget));
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget);
+    
+    /* 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))
+            gtk_widget_size_request(child, requisition);
+    } else {
+        requisition->width = CHIMARA_GLK_MIN_WIDTH;
+        requisition->height = CHIMARA_GLK_MIN_HEIGHT;
+    }
+}
+
+static void
+chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+    g_return_if_fail(widget);
+    g_return_if_fail(allocation);
+    g_return_if_fail(CHIMARA_IS_GLK(widget));
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget);
+    
+    widget->allocation = *allocation;
+            
+    if(priv->root_window) {
+        GtkWidget *child = ((winid_t)(priv->root_window->data))->frame;
+        if(GTK_WIDGET_VISIBLE(child))
+            gtk_widget_size_allocate(child, allocation);
+    }
+}
+
+static void
+chimara_glk_forall(GtkContainer *container, gboolean include_internals,
+    GtkCallback callback, gpointer callback_data)
+{
+    g_return_if_fail(container);
+    g_return_if_fail(CHIMARA_IS_GLK(container));
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(container);
+    
+    if(priv->root_window) {
+        GtkWidget *child = ((winid_t)(priv->root_window->data))->frame;
+        (*callback)(child, callback_data);
+    }
+}
+
+static void
+chimara_glk_stopped(ChimaraGlk *self)
+{
+       /* TODO: Add default signal handler implementation here */
+}
+
+static void
+chimara_glk_started(ChimaraGlk *self)
+{
+       /* TODO: Add default signal handler implementation here */
+}
+
+static void
+chimara_glk_class_init(ChimaraGlkClass *klass)
+{
+    /* Override methods of parent classes */
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    object_class->set_property = chimara_glk_set_property;
+    object_class->get_property = chimara_glk_get_property;
+    object_class->finalize = chimara_glk_finalize;
+    
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+    widget_class->size_request = chimara_glk_size_request;
+    widget_class->size_allocate = chimara_glk_size_allocate;
+
+    GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
+    container_class->forall = chimara_glk_forall;
+
+    /* Signals */
+    klass->stopped = chimara_glk_stopped;
+    klass->started = chimara_glk_started;
+    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);
+       chimara_glk_signals[STARTED] = g_signal_new ("started",
+               G_OBJECT_CLASS_TYPE (klass), 0,
+               G_STRUCT_OFFSET(ChimaraGlkClass, started), NULL, NULL,
+               g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    /* Properties */
+    GParamSpec *pspec;
+    pspec = g_param_spec_boolean("interactive", _("Interactive"),
+        _("Whether user input is expected in the Glk program"),
+        TRUE,
+        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_LAX_VALIDATION |
+        G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+    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);
+    g_object_class_install_property(object_class, PROP_PROTECT, pspec);
+    
+    /* Private data */
+    g_type_class_add_private(klass, sizeof(ChimaraGlkPrivate));
+}
+
+/* PUBLIC FUNCTIONS */
+
+GtkWidget *
+chimara_glk_new(void)
+{
+    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();
+    
+    return GTK_WIDGET(self);
+}
+
+void 
+chimara_glk_set_interactive(ChimaraGlk *glk, gboolean interactive)
+{
+    g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    priv->interactive = interactive;
+}
+
+gboolean 
+chimara_glk_get_interactive(ChimaraGlk *glk)
+{
+    g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    return priv->interactive;
+}
+
+void 
+chimara_glk_set_protect(ChimaraGlk *glk, gboolean protect)
+{
+    g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    priv->protect = protect;
+}
+
+gboolean 
+chimara_glk_get_protect(ChimaraGlk *glk)
+{
+    g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
+    
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    return priv->protect;
+}
+
+/* glk_enter() is called to create a new thread in which glk_main() runs.  */
+static gpointer
+glk_enter(gpointer glk)
+{
+    extern ChimaraGlkPrivate *glk_data;
+    
+    glk_data = CHIMARA_GLK_PRIVATE((ChimaraGlk *)glk);
+       glk_main();
+       return NULL;
+}
+
+gboolean
+chimara_glk_run(ChimaraGlk *glk, GError **error)
+{
+    g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
+
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    /* Run in a separate thread */
+       priv->thread = g_thread_create(glk_enter, glk, TRUE, error);
+       return !(priv->thread == NULL);
+}
+
+void
+chimara_glk_stop(ChimaraGlk *glk)
+{
+    g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
+    
+    /* TODO */
+}
+
+void
+chimara_glk_wait(ChimaraGlk *glk)
+{
+    ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
+    g_thread_join(priv->thread);
+}
\ No newline at end of file