Use statically-allocated thread private data
[projects/chimara/chimara.git] / libchimara / dispatch.c
1 #include <libchimara/glk.h>
2 #include "chimara-glk-private.h"
3 #include "window.h"
4 #include "stream.h"
5 #include "fileref.h"
6 #include "schannel.h"
7
8 extern GPrivate glk_data_key;
9
10 /**
11  * gidispatch_set_object_registry:
12  * @regi: Function to call whenever an opaque object is created.
13  * @unregi: Function to call whenever an opaque object is destroyed.
14  *
15  * The Glk API refers to opaque objects by pointer; but a VM probably cannot 
16  * store pointers to native memory. Therefore, a VM program will want to keep a
17  * VM-accessible collection of opaque objects.
18  * 
19  * <note><para>
20  *   For example, it might keep a hash table for each opaque object class,
21  *   mapping integer identifiers to object pointers.
22  * </para></note>
23  * 
24  * To make this possible, a Glk library must implement 
25  * gidispatch_set_object_registry().
26  * 
27  * Your program calls gidispatch_set_object_registry() early (before it begins
28  * actually executing VM code.) You pass in two function pointers, matching the
29  * following prototypes:
30  * |[
31  * gidispatch_rock_t my_vm_reg_object(void *obj, glui32 objclass);
32  * void my_vm_unreg_object(void *obj, glui32 objclass, gidispatch_rock_t objrock);
33  * ]|
34  * 
35  * Whenever the Glk library creates an object, it will call 
36  * <function>my_vm_reg_object&lpar;&rpar;</function>. It will pass the object
37  * pointer and the class number (from 0 to <inlineequation><mathphrase>N - 
38  * 1</mathphrase><alt>N - 1</alt></inlineequation>, where N is the value 
39  * returned by gidispatch_count_classes().)
40  * 
41  * You can return any value in the #gidispatch_rock_t object; the library will
42  * stash this away inside the object.
43  * 
44  * <note><para>
45  *   Note that this is entirely separate from the regular Glk rock, which is
46  *   always a #glui32 and can be set independently.
47  * </para></note>
48  * 
49  * Whenever the Glk library destroys an object, it will call
50  * <function>my_vm_unreg_object&lpar;&rpar;</function>. It passes you the object
51  * pointer, class number, and the object rock.
52  *
53  * One significant detail: It is possible that some Glk objects will already
54  * exist when your glk_main() function is called.
55  * 
56  * <note><para>
57  *   For example, MacGlk can open a stream when the user double-clicks a file;
58  *   this occurs before glk_main().
59  * </para></note>
60  * 
61  * So when you call gidispatch_set_object_registry(), it may immediately call
62  * your <function>my_vm_reg_object&lpar;&rpar;</function> callback, notifying 
63  * you of the existing objects. You must be prepared for this possibility.
64  * 
65  * <note><para>
66  *   If you are keeping hash tables, for example, create them before you call
67  *   gidispatch_set_object_registry().
68  * </para></note>
69  */
70 void 
71 gidispatch_set_object_registry(gidispatch_rock_t (*regi)(void *obj, glui32 objclass), void (*unregi)(void *obj, glui32 objclass, gidispatch_rock_t objrock))
72 {
73         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
74         winid_t win;
75     strid_t str;
76     frefid_t fref;
77     
78     glk_data->register_obj = regi;
79     glk_data->unregister_obj = unregi;
80     
81     if(glk_data->register_obj) 
82         {
83         /* It's now necessary to go through all existing objects, and register them. */
84         for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL))
85             win->disprock = (*glk_data->register_obj)(win, gidisp_Class_Window);
86         for(str = glk_stream_iterate(NULL, NULL); str; str = glk_stream_iterate(str, NULL))
87             str->disprock = (*glk_data->register_obj)(str, gidisp_Class_Stream);
88         for(fref = glk_fileref_iterate(NULL, NULL); fref; fref = glk_fileref_iterate(fref, NULL))
89             fref->disprock = (*glk_data->register_obj)(fref, gidisp_Class_Fileref);
90     }
91 }
92
93 /**
94  * gidispatch_get_objrock:
95  * @obj: An opaque object.
96  * @objclass: One of #gidisp_Class_Window, #gidisp_Class_Stream,
97  * #gidisp_Class_Fileref, or #gidisp_Class_Schannel.
98  *
99  * You can, at any time, get the object rock of an object. The library
100  * implements this function.
101  * 
102  * With this and your two callbacks, you can maintain (say) a hash table for
103  * each object class, and easily convert back and forth between hash table keys
104  * and Glk object pointers. A more sophisticated run-time system (such as Java)
105  * could create a typed VM object for every Glk object, thus allowing VM code to
106  * manipulate Glk objects intelligently.
107  */
108 gidispatch_rock_t 
109 gidispatch_get_objrock(void *obj, glui32 objclass)
110 {
111         g_return_val_if_fail(obj, (gidispatch_rock_t)NULL);
112
113         
114         switch(objclass) 
115         {
116                 case gidisp_Class_Window:
117                         return ((winid_t)obj)->disprock;
118                 case gidisp_Class_Stream:
119                         return ((strid_t)obj)->disprock;
120                 case gidisp_Class_Fileref:
121                         return ((frefid_t)obj)->disprock;
122                 case gidisp_Class_Schannel:
123                         return ((schanid_t)obj)->disprock;
124                 default: 
125                 {
126                         gidispatch_rock_t dummy;
127                         dummy.num = 0;
128                         return dummy;
129                 }
130         }
131 }
132
133 /**
134  * gidispatch_set_retained_registry:
135  * @regi: Function to call whenever the Glk library assumes ownership of an
136  * array.
137  * @unregi: Function to call whenever the Glk library releases ownership of an
138  * array.
139  *
140  * A few Glk functions take an array and hold onto it. The memory is 
141  * <quote>owned</quote> by the library until some future Glk call releases it.
142  * While the library retains the array, your program should not read, write,
143  * move, or deallocate it. When the library releases it, the contents are in
144  * their final form, and you can copy them out (if appropriate) and dispose of
145  * the memory as you wish.
146  * 
147  * To allow this, the library implements gidispatch_set_retained_registry().
148  * 
149  * Again, you pass in two function pointers:
150  * |[
151  * gidispatch_rock_t my_vm_reg_array(void *array, glui32 len, char *typecode);
152  * void my_vm_unreg_array(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock);
153  * ]|
154  *
155  * Whenever a Glk function retains an array, it will call 
156  * <function>my_vm_reg_array&lpar;&rpar;</function>. This occurs only if you 
157  * pass an array to an argument with the <code>"#!"</code> prefix.
158  *
159  * <note><para>
160  *   But not in every such case. Wait for the
161  *   <function>my_vm_reg_array&lpar;&rpar;</function> call to confirm it.
162  * </para></note>
163  *
164  * The library passes the array and its length, exactly as you put them in the
165  * #gluniversal_t array. It also passes the string which describes the argument.
166  *
167  * <note><para>
168  *   Currently, the only calls that retain arrays are glk_request_line_event(),
169  *   glk_stream_open_memory(), glk_request_line_event_uni(), and
170  *   glk_stream_open_memory_uni(). The first two of these use arrays of
171  *   characters, so the string is <code>"&+#!Cn"</code>. The latter two use
172  *   arrays of #glui32, so the string is <code>"&+#!Iu"</code>.
173  * </para></note>
174  * 
175  * You can return any value in the #gidispatch_rock_t object; the library will
176  * stash this away with the array.
177  * 
178  * When a Glk function releases a retained array, it will call
179  * <function>my_vm_unreg_array&lpar;&rpar;</function>. It passes back the same
180  * @array, @len, and @typecode parameters, as well as the #gidispatch_rock_t you
181  * returned from <function>my_vm_reg_array&lpar;&rpar;</function>.
182  * 
183  * With these callbacks, you can maintain a collection of retained arrays. You
184  * can use this to copy data from C arrays to your own data structures, or keep
185  * relocatable memory locked, or prevent a garbage-collection system from
186  * deallocating an array while Glk is writing to it.
187  */
188 void 
189 gidispatch_set_retained_registry(gidispatch_rock_t (*regi)(void *array, glui32 len, char *typecode), void (*unregi)(void *array, glui32 len, char *typecode, gidispatch_rock_t objrock))
190 {
191         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
192         glk_data->register_arr = regi;
193         glk_data->unregister_arr = unregi;
194 }