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