Use init and clear for GMutex and GCond
[projects/chimara/chimara.git] / libchimara / glk.c
1 #include <gtk/gtk.h>
2
3 #include "glk.h"
4 #include "abort.h"
5 #include "chimara-glk.h"
6 #include "chimara-glk-private.h"
7 #include "gi_blorb.h"
8 #include "window.h"
9
10 G_GNUC_INTERNAL GPrivate *glk_data_key = NULL;
11
12 /**
13  * glk_exit:
14  * 
15  * If you want to shut down your program in the middle of your <function>
16  * glk_main()</function> function, you can call glk_exit().
17  *
18  * This function does not return.
19  *
20  * If you print some text to a window and then shut down your program, you can
21  * assume that the player will be able to read it. Most likely the Glk library
22  * will give a <quote><computeroutput>Hit any key to 
23  * exit</computeroutput></quote> prompt. (There are other possiblities, however.
24  * A terminal-window version of Glk might simply exit and leave the last screen
25  * state visible in the terminal window.)
26  *
27  * <note><para>
28  * You should only shut down your program with glk_exit() or by returning from
29  * your <function>glk_main()</function> function. If you call the ANSI 
30  * <function>exit()</function> function, bad things may happen. Some versions of
31  * the Glk library may be designed for multiple sessions, for example, and you
32  * would be cutting off all the sessions instead of just yours. You would 
33  * probably also prevent final text from being visible to the player.
34  * </para></note>
35  * <note><title>Chimara</title>
36  * <para>
37  * If there are any windows open at the time glk_exit() is called, then Chimara
38  * will leave them open. This way, the final text remains visible. Note that bad  
39  * things most definitely <emphasis>will</emphasis> happen if you use the ANSI
40  * <function>exit()</function>.
41  * </para></note>
42  */
43 void
44 glk_exit(void)
45 {
46         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
47         
48         shutdown_glk_pre();
49         
50         /* Find the biggest text buffer window */
51         winid_t win, largewin = NULL;
52         glui32 largearea = 0;
53         for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) {
54                 if(win->type == wintype_TextBuffer) {
55                         glui32 w, h;
56                         if(!largewin) {
57                                 largewin = win;
58                                 glk_window_get_size(largewin, &w, &h);
59                                 largearea = w * h;
60                         } else {
61                                 glk_window_get_size(win, &w, &h);
62                                 if(w * h > largearea) {
63                                         largewin = win;
64                                         largearea = w * h;
65                                 }
66                         }
67                 }
68         }
69         if(largewin) {
70                 glk_set_window(largewin);
71                 glk_set_style(style_Alert);
72                 glk_put_string("\n");
73                 glk_put_string(glk_data->final_message);
74                 glk_put_string("\n");
75                 flush_window_buffer(largewin);
76         }
77
78         g_mutex_lock(&glk_data->shutdown_lock);
79         for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) {
80                 if(win->type == wintype_TextGrid || win->type == wintype_TextBuffer)
81                         g_signal_handler_unblock(win->widget, win->shutdown_keypress_handler);
82         }
83         g_cond_wait(&glk_data->shutdown_key_pressed, &glk_data->shutdown_lock);
84         g_mutex_unlock(&glk_data->shutdown_lock);
85
86         shutdown_glk_post();
87
88         g_signal_emit_by_name(glk_data->self, "stopped");
89         
90         g_thread_exit(NULL);
91 }
92
93 /**
94  * glk_tick:
95  *
96  * Carries out platform-dependent actions such as yielding time to the operating
97  * system and checking for interrupts. glk_tick() should be called every so 
98  * often when there is a long interval between calls of glk_select() or 
99  * glk_select_poll(). This call is fast; in fact, on average, it does nothing at
100  * all. So you can call it often.
101  *
102  * <note><para>
103  *   In a virtual machine interpreter, once per opcode is appropriate. A more
104  *   parsimonious approach would be once per branch and function call opcode;
105  *   this guarantees it will be called inside loops. In a program with lots of
106  *   computation, pick a comparable rate.
107  * </para></note>
108  * 
109  * glk_tick() does not try to update the screen, or check for player input, or
110  * any other interface task. For that, you should call glk_select() or 
111  * glk_select_poll(). See <link linkend="chimara-Events">Events</link>.
112  * 
113  * <note>
114  *   <para>Captious critics have pointed out that in the sample program
115  *   <filename>model.c</filename>, I do not call glk_tick() at all. This is
116  *   because <filename>model.c</filename> has no heavy loops. It does a bit of
117  *   work for each command, and then cycles back to the top of the event loop.
118  *   The glk_select() call, of course, blocks waiting for input, so it does all
119  *   the yielding and interrupt-checking one could imagine.
120  *   </para>
121  *   <para>Basically, you must ensure there's some fixed upper bound on the
122  *   amount of computation that can occur before a glk_tick() (or glk_select())
123  *   occurs. In a VM interpreter, where the VM code might contain an infinite
124  *   loop, this is critical. In a C program, you can often eyeball it.
125  *   </para>
126  *   <para>But the next version of <filename>model.c</filename> will have a
127  *   glk_tick() in the ornate printing loop of 
128  *   <function>verb_yada&lpar;&rpar;</function>. Just to make the point.
129  *   </para>
130  * </note>
131  */
132 void
133 glk_tick()
134 {
135         check_for_abort();
136         
137         /* Do one iteration of the main loop if there are any events */
138         gdk_threads_enter();
139         if(gtk_events_pending())
140                 gtk_main_iteration();
141         gdk_threads_leave();
142 }
143