Changed build system to Automake. Split Glk code off into a GTK widget.
[rodin/chimara.git] / src / chimara-glk.c
1 /* licensing and copyright information here */
2
3 #include <gtk/gtk.h>
4 #include <glib/gi18n.h>
5 #include "chimara-glk.h"
6 #include "chimara-glk-private.h"
7 #include "glk.h"
8 #include "window.h"
9
10 #define CHIMARA_GLK_MIN_WIDTH 0
11 #define CHIMARA_GLK_MIN_HEIGHT 0
12
13 enum {
14     PROP_0,
15     PROP_INTERACTIVE,
16     PROP_PROTECT
17 };
18
19 enum {
20         STOPPED,
21         STARTED,
22
23         LAST_SIGNAL
24 };
25
26 static guint chimara_glk_signals[LAST_SIGNAL] = { 0 };
27
28 G_DEFINE_TYPE(ChimaraGlk, chimara_glk, GTK_TYPE_CONTAINER);
29
30 static void
31 chimara_glk_init(ChimaraGlk *self)
32 {
33     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(self), GTK_NO_WINDOW);
34
35     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
36     
37     priv->self = self;
38     priv->interactive = TRUE;
39     priv->protect = FALSE;
40     priv->thread = NULL;
41     priv->event_queue = NULL;
42     priv->event_lock = NULL;
43     priv->event_queue_not_empty = NULL;
44     priv->event_queue_not_full = NULL;
45     priv->abort_lock = NULL;
46     priv->abort_signalled = FALSE;
47     priv->interrupt_handler = NULL;
48     priv->root_window = NULL;
49     priv->fileref_list = NULL;
50     priv->current_stream = NULL;
51     priv->stream_list = NULL;
52 }
53
54 static void
55 chimara_glk_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
56 {
57     ChimaraGlk *glk = CHIMARA_GLK(object);
58     
59     switch(prop_id) 
60     {
61         case PROP_INTERACTIVE:
62             chimara_glk_set_interactive(glk, g_value_get_boolean(value));
63             break;
64         case PROP_PROTECT:
65             chimara_glk_set_protect(glk, g_value_get_boolean(value));
66             break;
67         default:
68             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
69     }
70 }
71
72 static void
73 chimara_glk_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
74 {
75     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(object);
76     
77     switch(prop_id)
78     {
79         case PROP_INTERACTIVE:
80             g_value_set_boolean(value, priv->interactive);
81             break;
82         case PROP_PROTECT:
83             g_value_set_boolean(value, priv->protect);
84             break;
85         default:
86             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
87     }
88 }
89
90 static void
91 chimara_glk_finalize(GObject *object)
92 {
93     ChimaraGlk *self = CHIMARA_GLK(object);
94     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
95     
96     /* Free the event queue */
97     g_mutex_lock(priv->event_lock);
98         g_queue_foreach(priv->event_queue, (GFunc)g_free, NULL);
99         g_queue_free(priv->event_queue);
100         g_cond_free(priv->event_queue_not_empty);
101         g_cond_free(priv->event_queue_not_full);
102         priv->event_queue = NULL;
103         g_mutex_unlock(priv->event_lock);
104         g_mutex_free(priv->event_lock);
105         
106         /* Free the abort signalling mechanism */
107         g_mutex_lock(priv->abort_lock);
108         /* Make sure no other thread is busy with this */
109         g_mutex_unlock(priv->abort_lock);
110         g_mutex_free(priv->abort_lock);
111         priv->abort_lock = NULL;
112     
113     G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object);
114 }
115
116 static void
117 chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition)
118 {
119     g_return_if_fail(widget);
120     g_return_if_fail(requisition);
121     g_return_if_fail(CHIMARA_IS_GLK(widget));
122     
123     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget);
124     
125     /* For now, just pass the size request on to the root Glk window */
126     if(priv->root_window) { 
127         GtkWidget *child = ((winid_t)(priv->root_window->data))->frame;
128         if(GTK_WIDGET_VISIBLE(child))
129             gtk_widget_size_request(child, requisition);
130     } else {
131         requisition->width = CHIMARA_GLK_MIN_WIDTH;
132         requisition->height = CHIMARA_GLK_MIN_HEIGHT;
133     }
134 }
135
136 static void
137 chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
138 {
139     g_return_if_fail(widget);
140     g_return_if_fail(allocation);
141     g_return_if_fail(CHIMARA_IS_GLK(widget));
142     
143     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget);
144     
145     widget->allocation = *allocation;
146             
147     if(priv->root_window) {
148         GtkWidget *child = ((winid_t)(priv->root_window->data))->frame;
149         if(GTK_WIDGET_VISIBLE(child))
150             gtk_widget_size_allocate(child, allocation);
151     }
152 }
153
154 static void
155 chimara_glk_forall(GtkContainer *container, gboolean include_internals,
156     GtkCallback callback, gpointer callback_data)
157 {
158     g_return_if_fail(container);
159     g_return_if_fail(CHIMARA_IS_GLK(container));
160     
161     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(container);
162     
163     if(priv->root_window) {
164         GtkWidget *child = ((winid_t)(priv->root_window->data))->frame;
165         (*callback)(child, callback_data);
166     }
167 }
168
169 static void
170 chimara_glk_stopped(ChimaraGlk *self)
171 {
172         /* TODO: Add default signal handler implementation here */
173 }
174
175 static void
176 chimara_glk_started(ChimaraGlk *self)
177 {
178         /* TODO: Add default signal handler implementation here */
179 }
180
181 static void
182 chimara_glk_class_init(ChimaraGlkClass *klass)
183 {
184     /* Override methods of parent classes */
185     GObjectClass *object_class = G_OBJECT_CLASS(klass);
186     object_class->set_property = chimara_glk_set_property;
187     object_class->get_property = chimara_glk_get_property;
188     object_class->finalize = chimara_glk_finalize;
189     
190     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
191     widget_class->size_request = chimara_glk_size_request;
192     widget_class->size_allocate = chimara_glk_size_allocate;
193
194     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
195     container_class->forall = chimara_glk_forall;
196
197     /* Signals */
198     klass->stopped = chimara_glk_stopped;
199     klass->started = chimara_glk_started;
200     chimara_glk_signals[STOPPED] = g_signal_new("stopped", 
201         G_OBJECT_CLASS_TYPE(klass), 0, 
202         G_STRUCT_OFFSET(ChimaraGlkClass, stopped), NULL, NULL,
203                 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
204         chimara_glk_signals[STARTED] = g_signal_new ("started",
205                 G_OBJECT_CLASS_TYPE (klass), 0,
206                 G_STRUCT_OFFSET(ChimaraGlkClass, started), NULL, NULL,
207                 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
208
209     /* Properties */
210     GParamSpec *pspec;
211     pspec = g_param_spec_boolean("interactive", _("Interactive"),
212         _("Whether user input is expected in the Glk program"),
213         TRUE,
214         G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_LAX_VALIDATION |
215         G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
216     g_object_class_install_property(object_class, PROP_INTERACTIVE, pspec);
217     pspec = g_param_spec_boolean("protect", _("Protected"),
218         _("Whether the Glk program is barred from doing file operations"),
219         FALSE,
220         G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_LAX_VALIDATION |
221         G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
222     g_object_class_install_property(object_class, PROP_PROTECT, pspec);
223     
224     /* Private data */
225     g_type_class_add_private(klass, sizeof(ChimaraGlkPrivate));
226 }
227
228 /* PUBLIC FUNCTIONS */
229
230 GtkWidget *
231 chimara_glk_new(void)
232 {
233     ChimaraGlk *self = CHIMARA_GLK(g_object_new(CHIMARA_TYPE_GLK, NULL));
234     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(self);
235     
236     priv->event_queue = g_queue_new();
237     priv->event_lock = g_mutex_new();
238     priv->event_queue_not_empty = g_cond_new();
239     priv->event_queue_not_full = g_cond_new();
240     priv->abort_lock = g_mutex_new();
241     
242     return GTK_WIDGET(self);
243 }
244
245 void 
246 chimara_glk_set_interactive(ChimaraGlk *glk, gboolean interactive)
247 {
248     g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
249     
250     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
251     priv->interactive = interactive;
252 }
253
254 gboolean 
255 chimara_glk_get_interactive(ChimaraGlk *glk)
256 {
257     g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
258     
259     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
260     return priv->interactive;
261 }
262
263 void 
264 chimara_glk_set_protect(ChimaraGlk *glk, gboolean protect)
265 {
266     g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
267     
268     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
269     priv->protect = protect;
270 }
271
272 gboolean 
273 chimara_glk_get_protect(ChimaraGlk *glk)
274 {
275     g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
276     
277     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
278     return priv->protect;
279 }
280
281 /* glk_enter() is called to create a new thread in which glk_main() runs.  */
282 static gpointer
283 glk_enter(gpointer glk)
284 {
285     extern ChimaraGlkPrivate *glk_data;
286     
287     glk_data = CHIMARA_GLK_PRIVATE((ChimaraGlk *)glk);
288         glk_main();
289         return NULL;
290 }
291
292 gboolean
293 chimara_glk_run(ChimaraGlk *glk, GError **error)
294 {
295     g_return_val_if_fail(glk || CHIMARA_IS_GLK(glk), FALSE);
296
297     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
298     /* Run in a separate thread */
299         priv->thread = g_thread_create(glk_enter, glk, TRUE, error);
300         return !(priv->thread == NULL);
301 }
302
303 void
304 chimara_glk_stop(ChimaraGlk *glk)
305 {
306     g_return_if_fail(glk || CHIMARA_IS_GLK(glk));
307     
308     /* TODO */
309 }
310
311 void
312 chimara_glk_wait(ChimaraGlk *glk)
313 {
314     ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
315     g_thread_join(priv->thread);
316 }