1 /*
2  * gauche-glut.c - Gauche GLUT binding
3  *
4  *   Copyright (c) 2001-2014  Shiro Kawai  <shiro@acm.org>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   3. Neither the name of the authors nor the names of its contributors
18  *      may be used to endorse or promote products derived from this
19  *      software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <gauche.h>
35 #include <gauche/extend.h>
36 
37 #if MacOSX
38 #include <GLUT/glut.h>
39 #else
40 #include <GL/glut.h>
41 #endif
42 #include "gauche-glut.h"
43 
44 extern void Scm_Init_glut_lib(ScmModule *mod);
45 
46 /*================================================================
47  * Glut font
48  */
49 
50 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_GlutFontClass, NULL);
51 
makeGlutFont(void * ptr)52 static ScmObj makeGlutFont(void *ptr)
53 {
54     ScmGlutFont *gf = SCM_NEW(ScmGlutFont);
55     SCM_SET_CLASS(gf, SCM_CLASS_GLUT_FONT);
56     gf->font = ptr;
57     return SCM_OBJ(gf);
58 }
59 
60 /*================================================================
61  * Callback support.
62  *
63  * Glut callbacks are associated to the "current window".
64  * unfortunately the callback interface doesn't allow us
65  * to pass extra data pointer, so our C callback routine
66  * doesn't know which Scheme closure to be called.  We maintain
67  * that information in our table.
68  *
69  * TODO: We don't want to use Scm_ApplyRec, for we need to cons
70  * the arguments (display is ok, but motion and passiveMotion generates
71  * garbages which will eventurally trigger GC.)  Rewrite *_cb functions
72  * after we implement Scm_ApplyRec0, Scm_ApplyRec1, .., etc. in
73  * Gauche core.
74  */
75 
76 static ScmObj ScmGlutCallbackTable = SCM_UNDEFINED; /* set by init routine */
77 
get_callback(int type)78 static ScmObj get_callback(int type)
79 {
80     int win = glutGetWindow();
81     ScmObj entry = Scm_HashTableRef(SCM_HASH_TABLE(ScmGlutCallbackTable),
82                                     SCM_MAKE_INT(win), SCM_FALSE);
83     SCM_ASSERT(type >= 0 && type < SCM_GLUT_NUM_WINDOW_CBS);
84     if (SCM_VECTORP(entry)) {
85         return SCM_VECTOR_ELEMENT(entry, type);
86     } else {
87         return SCM_FALSE;
88     }
89 }
90 
91 #define define_callback(name, num, arglist, args)                       \
92     static void SCM_CPP_CAT(name, _cb) arglist                          \
93     {                                                                   \
94         ScmObj cb = get_callback(SCM_CPP_CAT(SCM_GLUT_CB_, num));       \
95         if (!SCM_FALSEP(cb)) {                                          \
96             Scm_ApplyRec(cb, args);                                     \
97         }                                                               \
98     }
99 
100 define_callback(display, DISPLAY, (void), SCM_NIL)
101 
102 
103 define_callback(overlay_display, OVERLAY_DISPLAY, (void), SCM_NIL)
104 define_callback(reshape, RESHAPE, (int w, int h),
105                 SCM_LIST2(SCM_MAKE_INT(w), SCM_MAKE_INT(h)));
106 define_callback(keyboard, KEYBOARD, (unsigned char key, int w, int h),
107                 SCM_LIST3(SCM_MAKE_INT(key), SCM_MAKE_INT(w), SCM_MAKE_INT(h)))
108 define_callback(keyboard_up, KEYBOARD_UP, (unsigned char key, int w, int h),
109                 SCM_LIST3(SCM_MAKE_INT(key), SCM_MAKE_INT(w), SCM_MAKE_INT(h)))
110 define_callback(mouse, MOUSE, (int button, int state, int x, int y),
111                 SCM_LIST4(SCM_MAKE_INT(button), SCM_MAKE_INT(state),
112                           SCM_MAKE_INT(x), SCM_MAKE_INT(y)))
113 define_callback(motion, MOTION, (int x, int y),
114                 SCM_LIST2(SCM_MAKE_INT(x), SCM_MAKE_INT(y)))
115 define_callback(passive_motion, PASSIVE_MOTION, (int x, int y),
116                 SCM_LIST2(SCM_MAKE_INT(x), SCM_MAKE_INT(y)))
117 define_callback(visibility, VISIBILITY, (int state),
118                 SCM_LIST1(SCM_MAKE_INT(state)))
119 define_callback(entry, ENTRY, (int state),
120                 SCM_LIST1(SCM_MAKE_INT(state)))
121 define_callback(special, SPECIAL, (int key, int w, int h),
122                 SCM_LIST3(SCM_MAKE_INT(key), SCM_MAKE_INT(w), SCM_MAKE_INT(h)))
123 define_callback(special_up, SPECIAL_UP, (int key, int w, int h),
124                 SCM_LIST3(SCM_MAKE_INT(key), SCM_MAKE_INT(w), SCM_MAKE_INT(h)))
125 define_callback(spaceball_motion, SPACEBALL_MOTION, (int x, int y, int z),
126                 SCM_LIST3(SCM_MAKE_INT(x), SCM_MAKE_INT(y), SCM_MAKE_INT(z)))
127 define_callback(spaceball_rotate, SPACEBALL_ROTATE, (int x, int y, int z),
128                 SCM_LIST3(SCM_MAKE_INT(x), SCM_MAKE_INT(y), SCM_MAKE_INT(z)))
129 define_callback(spaceball_button, SPACEBALL_BUTTON, (int button, int state),
130                 SCM_LIST2(SCM_MAKE_INT(button), SCM_MAKE_INT(state)))
131 define_callback(button_box, BUTTON_BOX, (int button, int state),
132                 SCM_LIST2(SCM_MAKE_INT(button), SCM_MAKE_INT(state)))
133 define_callback(dials, DIALS, (int dial, int value),
134                 SCM_LIST2(SCM_MAKE_INT(dial), SCM_MAKE_INT(value)))
135 define_callback(tablet_motion, TABLET_MOTION, (int x, int y),
136                 SCM_LIST2(SCM_MAKE_INT(x), SCM_MAKE_INT(y)))
137 define_callback(tablet_button, TABLET_BUTTON,
138                 (int button, int state, int x, int y),
139                 SCM_LIST4(SCM_MAKE_INT(button),
140                           SCM_MAKE_INT(state),
141                           SCM_MAKE_INT(x),
142                           SCM_MAKE_INT(y)))
143 define_callback(menu_status, MENU_STATUS, (int status, int x, int y),
144                 SCM_LIST3(SCM_MAKE_INT(status),
145                           SCM_MAKE_INT(x), SCM_MAKE_INT(y)))
146 define_callback(window_status, WINDOW_STATUS, (int status),
147                 SCM_LIST1(SCM_MAKE_INT(status)))
148 define_callback(joystick, JOYSTICK, (unsigned int mask, int x, int y, int z),
149                 SCM_LIST4(SCM_MAKE_INT(mask),
150                           SCM_MAKE_INT(x), SCM_MAKE_INT(y), SCM_MAKE_INT(z)))
151 
152 /* global callbacks */
153 static ScmObj idle_closure = SCM_FALSE;
154 
idle_cb(void)155 static void idle_cb(void)
156 {
157     if (!SCM_FALSEP(idle_closure)) {
158         Scm_ApplyRec(idle_closure, SCM_NIL);
159     }
160 }
161 
162 static ScmObj timer_closure = SCM_FALSE;
163 
timer_cb(int value)164 static void timer_cb(int value)
165 {
166     if (!SCM_FALSEP(timer_closure)) {
167         Scm_ApplyRec(timer_closure, SCM_LIST1(Scm_MakeInteger(value)));
168     }
169 }
170 
171 
172 /* NB: these functions are new addition by freeglut.  we provide
173    dummy functions for older versions. */
174 #if !(GLUT_API_VERSION >= 4 || GLUT_XLIB_IMPLEMENTATION >= 13)
glutKeyboardUpFunc(void (* fn)(unsigned char,int,int))175 static void glutKeyboardUpFunc(void (*fn)(unsigned char, int, int))
176 {
177     Scm_Warn("glutKeyboardUpFunc unsupported in this version of GLUT");
178 }
glutSpecialUpFunc(void (* fn)(int,int,int))179 static void glutSpecialUpFunc(void (*fn)(int, int, int))
180 {
181     Scm_Warn("glutSpecialUpFunc unsupported in this version of GLUT");
182 }
glutJoystickFunc(void (* fn)(unsigned int,int,int,int),int interval)183 static void glutJoystickFunc(void (*fn)(unsigned int, int, int, int),
184                              int interval)
185 {
186     Scm_Warn("glutJoystickFunc unsupported in this version of GLUT");
187 }
glutWindowStatusFunc(void (* fn)(unsigned int,int,int,int))188 static void glutWindowStatusFunc(void (*fn)(unsigned int, int, int, int))
189 {
190     Scm_Warn("glutWindowStatusFunc unsupported in this version of GLUT");
191 }
192 #endif
193 
194 
195 #define define_registrar(glutfn, cbname)                                \
196     static void SCM_CPP_CAT(register_, cbname)(int flag, int xtra)      \
197     {                                                                   \
198         if (flag) {                                                     \
199             glutfn(SCM_CPP_CAT(cbname, _cb));                           \
200         } else {                                                        \
201             glutfn(NULL);                                               \
202         }                                                               \
203     }
204 
define_registrar(glutDisplayFunc,display)205 define_registrar(glutDisplayFunc, display)
206 define_registrar(glutOverlayDisplayFunc, overlay_display)
207 define_registrar(glutReshapeFunc, reshape)
208 define_registrar(glutKeyboardFunc, keyboard)
209 define_registrar(glutKeyboardUpFunc, keyboard_up)
210 define_registrar(glutMouseFunc, mouse)
211 define_registrar(glutMotionFunc, motion)
212 define_registrar(glutPassiveMotionFunc, passive_motion)
213 define_registrar(glutVisibilityFunc, visibility)
214 define_registrar(glutEntryFunc, entry)
215 define_registrar(glutSpecialFunc, special)
216 define_registrar(glutSpecialUpFunc, special_up)
217 define_registrar(glutSpaceballMotionFunc, spaceball_motion)
218 define_registrar(glutSpaceballRotateFunc, spaceball_rotate)
219 define_registrar(glutSpaceballButtonFunc, spaceball_button)
220 define_registrar(glutButtonBoxFunc, button_box)
221 define_registrar(glutDialsFunc, dials)
222 define_registrar(glutTabletMotionFunc, tablet_motion)
223 define_registrar(glutTabletButtonFunc, tablet_button)
224 define_registrar(glutMenuStatusFunc, menu_status)
225 define_registrar(glutWindowStatusFunc, window_status)
226 
227 /* joystick fn is a bit different */
228 static void register_joystick(int flag, int interval)
229 {
230     if (flag) {
231         glutJoystickFunc(joystick_cb, interval);
232     } else {
233         glutJoystickFunc(NULL, interval);
234     }
235 }
236 
237 
238 /* NB: order must match SCM_GLUT_CB_* enums */
239 static void (*registrars[])(int flag, int xtra) = {
240     register_display,
241     register_overlay_display,
242     register_reshape,
243     register_keyboard,
244     register_mouse,
245     register_motion,
246     register_passive_motion,
247     register_visibility,
248     register_entry,
249     register_special,
250     register_spaceball_motion,
251     register_spaceball_rotate,
252     register_spaceball_button,
253     register_button_box,
254     register_dials,
255     register_tablet_motion,
256     register_tablet_button,
257     register_menu_status,
258     register_window_status,
259     register_keyboard_up,
260     register_special_up,
261     register_joystick,
262 };
263 
264 /*
265  * External entry to manage registering callbacks
266  * 'xtra1' and 'xtra2' are ignored by most callbacks; only the two callbacks
267  * use them:
268  *   glutTimerFunc: xtra1 for millliseconds, xtra2 for value
269  *   glutJoystickFunc: xtra1 for interval
270  */
Scm_GlutRegisterCallback(int type,ScmObj closure,int xtra1,int xtra2)271 void Scm_GlutRegisterCallback(int type, ScmObj closure, int xtra1, int xtra2)
272 {
273     SCM_ASSERT(type >= 0 && type < SCM_GLUT_NUM_CBS);
274     if (type < SCM_GLUT_NUM_WINDOW_CBS) {
275         int win = glutGetWindow();
276         ScmObj entry = Scm_HashTableRef(SCM_HASH_TABLE(ScmGlutCallbackTable),
277                                         SCM_MAKE_INT(win), SCM_FALSE);
278 
279         if (SCM_EQ(entry, SCM_FALSE)) {
280             entry = Scm_MakeVector(SCM_GLUT_NUM_WINDOW_CBS, SCM_FALSE);
281             Scm_HashTableSet(SCM_HASH_TABLE(ScmGlutCallbackTable),
282                              SCM_MAKE_INT(win), entry, 0);
283         }
284         SCM_VECTOR_ELEMENT(entry, type) = closure;
285         registrars[type](!SCM_FALSEP(closure), xtra1);
286     } else if (type == SCM_GLUT_CB_IDLE) {
287         idle_closure = closure;
288         if (SCM_FALSEP(closure)) {
289             glutIdleFunc(NULL);
290         } else {
291             glutIdleFunc(idle_cb);
292         }
293     } else {
294         timer_closure = closure;
295         if (!SCM_FALSEP(closure)) {
296             glutTimerFunc(xtra1, timer_cb, xtra2);
297         }
298     }
299 }
300 
301 /*================================================================
302  * Initialization
303  */
Scm_Init_libgauche_glut(void)304 void Scm_Init_libgauche_glut(void)
305 {
306     ScmModule *mod;
307     SCM_INIT_EXTENSION(libgauche_glut);
308     mod = SCM_MODULE(SCM_FIND_MODULE("gl.glut", TRUE));
309     Scm_Init_glut_lib(mod);
310 
311     /* Callback table */
312     ScmGlutCallbackTable = Scm_MakeHashTableSimple(SCM_HASH_EQV, 0);
313 
314     /* Glut built-in fonts */
315 #define DEFFONT(name) Scm_DefineConst(mod, SCM_SYMBOL(SCM_INTERN(#name)), makeGlutFont(name))
316     /* Stroke font constants (use these in GLUT program). */
317     DEFFONT(GLUT_STROKE_ROMAN);
318     DEFFONT(GLUT_STROKE_MONO_ROMAN);
319 
320     /* Bitmap font constants (use these in GLUT program). */
321     DEFFONT(GLUT_BITMAP_9_BY_15);
322     DEFFONT(GLUT_BITMAP_8_BY_13);
323     DEFFONT(GLUT_BITMAP_TIMES_ROMAN_10);
324     DEFFONT(GLUT_BITMAP_TIMES_ROMAN_24);
325 #if (GLUT_API_VERSION >= 3)
326     DEFFONT(GLUT_BITMAP_HELVETICA_10);
327     DEFFONT(GLUT_BITMAP_HELVETICA_12);
328     DEFFONT(GLUT_BITMAP_HELVETICA_18);
329 #endif
330 }
331