added assfile
[raydungeon] / src / miniglut.c
1 /*
2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020-2022  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18 #if defined(unix) || defined(__unix__)
19
20 #include <X11/Xlib.h>
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
23 #include <GL/glx.h>
24 #define BUILD_X11
25
26 #ifndef GLX_SAMPLE_BUFFERS_ARB
27 #define GLX_SAMPLE_BUFFERS_ARB  100000
28 #define GLX_SAMPLES_ARB                 100001
29 #endif
30 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
31 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20b2
32 #endif
33
34 static Display *dpy;
35 static Window win, root;
36 static Colormap cmap;
37 static int cmap_size;
38 static int scr;
39 static GLXContext ctx;
40 static Atom xa_wm_proto, xa_wm_del_win;
41 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
42 static Atom xa_motif_wm_hints;
43 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
44 static unsigned int evmask;
45 static Cursor blank_cursor;
46
47 static int have_netwm_fullscr(void);
48
49 #elif defined(_WIN32)
50
51 #include <windows.h>
52 #define BUILD_WIN32
53
54 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
55
56 static HINSTANCE hinst;
57 static HWND win;
58 static HDC dc;
59 static HGLRC ctx;
60 static HPALETTE cmap;
61 static int cmap_size;
62
63 #else
64 #error unsupported platform
65 #endif
66 #include <GL/gl.h>
67 #include "miniglut.h"
68
69 #ifdef _MSC_VER
70 #pragma warning (disable: 4244 4305)
71 #endif
72
73
74 struct ctx_info {
75         int rsize, gsize, bsize, asize;
76         int zsize, ssize;
77         int dblbuf;
78         int samples;
79         int stereo;
80         int srgb;
81 };
82
83 static void cleanup(void);
84 static void create_window(const char *title);
85 static void get_window_pos(int *x, int *y);
86 static void get_window_size(int *w, int *h);
87 static void get_screen_size(int *scrw, int *scrh);
88
89 static long get_msec(void);
90 static void panic(const char *msg);
91 static void sys_exit(int status);
92 static int sys_write(int fd, const void *buf, int count);
93
94
95 static int init_x = -1, init_y, init_width = 256, init_height = 256;
96 static unsigned int init_mode;
97
98 static struct ctx_info ctx_info;
99 static int cur_cursor = GLUT_CURSOR_INHERIT;
100 static int ignore_key_repeat;
101
102 static glut_cb cb_display;
103 static glut_cb cb_idle;
104 static glut_cb_reshape cb_reshape;
105 static glut_cb_state cb_vis, cb_entry;
106 static glut_cb_keyb cb_keydown, cb_keyup;
107 static glut_cb_special cb_skeydown, cb_skeyup;
108 static glut_cb_mouse cb_mouse;
109 static glut_cb_motion cb_motion, cb_passive;
110 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
111 static glut_cb_sbbutton cb_sball_button;
112
113 static int fullscreen;
114 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
115
116 static int win_width, win_height;
117 static int mapped;
118 static int quit;
119 static int upd_pending;
120 static int modstate;
121
122 void glutInit(int *argc, char **argv)
123 {
124 #ifdef BUILD_X11
125         Pixmap blankpix = 0;
126         XColor xcol;
127
128         if(!(dpy = XOpenDisplay(0))) {
129                 panic("Failed to connect to the X server\n");
130         }
131         scr = DefaultScreen(dpy);
132         root = RootWindow(dpy, scr);
133         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
134         xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
135         xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
136         xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
137         if(have_netwm_fullscr()) {
138                 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
139         }
140
141         xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
142         xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
143         xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
144         xa_command_event = XInternAtom(dpy, "CommandEvent", True);
145
146         evmask = ExposureMask | StructureNotifyMask;
147
148         if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
149                 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
150                 XFreePixmap(dpy, blankpix);
151         }
152
153 #endif
154 #ifdef BUILD_WIN32
155         WNDCLASSEX wc = {0};
156
157         hinst = GetModuleHandle(0);
158
159         wc.cbSize = sizeof wc;
160         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
161         wc.hCursor = LoadCursor(0, IDC_ARROW);
162         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
163         wc.hInstance = hinst;
164         wc.lpfnWndProc = handle_message;
165         wc.lpszClassName = "MiniGLUT";
166         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
167         if(!RegisterClassEx(&wc)) {
168                 panic("Failed to register \"MiniGLUT\" window class\n");
169         }
170
171         if(init_x == -1) {
172                 get_screen_size(&init_x, &init_y);
173                 init_x >>= 3;
174                 init_y >>= 3;
175         }
176 #endif
177 }
178
179 void glutInitWindowPosition(int x, int y)
180 {
181         init_x = x;
182         init_y = y;
183 }
184
185 void glutInitWindowSize(int xsz, int ysz)
186 {
187         init_width = xsz;
188         init_height = ysz;
189 }
190
191 void glutInitDisplayMode(unsigned int mode)
192 {
193         init_mode = mode;
194 }
195
196 void glutCreateWindow(const char *title)
197 {
198         create_window(title);
199 }
200
201 void glutExit(void)
202 {
203         quit = 1;
204 }
205
206 void glutMainLoop(void)
207 {
208         while(!quit) {
209                 glutMainLoopEvent();
210         }
211 }
212
213 void glutPostRedisplay(void)
214 {
215         upd_pending = 1;
216 }
217
218 void glutIgnoreKeyRepeat(int ignore)
219 {
220         ignore_key_repeat = ignore;
221 }
222
223 #define UPD_EVMASK(x) \
224         do { \
225                 if(func) { \
226                         evmask |= x; \
227                 } else { \
228                         evmask &= ~(x); \
229                 } \
230                 if(win) XSelectInput(dpy, win, evmask); \
231         } while(0)
232
233
234 void glutIdleFunc(glut_cb func)
235 {
236         cb_idle = func;
237 }
238
239 void glutDisplayFunc(glut_cb func)
240 {
241         cb_display = func;
242 }
243
244 void glutReshapeFunc(glut_cb_reshape func)
245 {
246         cb_reshape = func;
247 }
248
249 void glutVisibilityFunc(glut_cb_state func)
250 {
251         cb_vis = func;
252 #ifdef BUILD_X11
253         UPD_EVMASK(VisibilityChangeMask);
254 #endif
255 }
256
257 void glutEntryFunc(glut_cb_state func)
258 {
259         cb_entry = func;
260 #ifdef BUILD_X11
261         UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
262 #endif
263 }
264
265 void glutKeyboardFunc(glut_cb_keyb func)
266 {
267         cb_keydown = func;
268 #ifdef BUILD_X11
269         UPD_EVMASK(KeyPressMask);
270 #endif
271 }
272
273 void glutKeyboardUpFunc(glut_cb_keyb func)
274 {
275         cb_keyup = func;
276 #ifdef BUILD_X11
277         UPD_EVMASK(KeyReleaseMask);
278 #endif
279 }
280
281 void glutSpecialFunc(glut_cb_special func)
282 {
283         cb_skeydown = func;
284 #ifdef BUILD_X11
285         UPD_EVMASK(KeyPressMask);
286 #endif
287 }
288
289 void glutSpecialUpFunc(glut_cb_special func)
290 {
291         cb_skeyup = func;
292 #ifdef BUILD_X11
293         UPD_EVMASK(KeyReleaseMask);
294 #endif
295 }
296
297 void glutMouseFunc(glut_cb_mouse func)
298 {
299         cb_mouse = func;
300 #ifdef BUILD_X11
301         UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
302 #endif
303 }
304
305 void glutMotionFunc(glut_cb_motion func)
306 {
307         cb_motion = func;
308 #ifdef BUILD_X11
309         UPD_EVMASK(ButtonMotionMask);
310 #endif
311 }
312
313 void glutPassiveMotionFunc(glut_cb_motion func)
314 {
315         cb_passive = func;
316 #ifdef BUILD_X11
317         UPD_EVMASK(PointerMotionMask);
318 #endif
319 }
320
321 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
322 {
323         cb_sball_motion = func;
324 }
325
326 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
327 {
328         cb_sball_rotate = func;
329 }
330
331 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
332 {
333         cb_sball_button = func;
334 }
335
336 int glutGet(unsigned int s)
337 {
338         int x, y;
339         switch(s) {
340         case GLUT_WINDOW_X:
341                 get_window_pos(&x, &y);
342                 return x;
343         case GLUT_WINDOW_Y:
344                 get_window_pos(&x, &y);
345                 return y;
346         case GLUT_WINDOW_WIDTH:
347                 get_window_size(&x, &y);
348                 return x;
349         case GLUT_WINDOW_HEIGHT:
350                 get_window_size(&x, &y);
351                 return y;
352         case GLUT_WINDOW_BUFFER_SIZE:
353                 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
354         case GLUT_WINDOW_STENCIL_SIZE:
355                 return ctx_info.ssize;
356         case GLUT_WINDOW_DEPTH_SIZE:
357                 return ctx_info.zsize;
358         case GLUT_WINDOW_RED_SIZE:
359                 return ctx_info.rsize;
360         case GLUT_WINDOW_GREEN_SIZE:
361                 return ctx_info.gsize;
362         case GLUT_WINDOW_BLUE_SIZE:
363                 return ctx_info.bsize;
364         case GLUT_WINDOW_ALPHA_SIZE:
365                 return ctx_info.asize;
366         case GLUT_WINDOW_DOUBLEBUFFER:
367                 return ctx_info.dblbuf;
368         case GLUT_WINDOW_RGBA:
369                 return 1;
370         case GLUT_WINDOW_NUM_SAMPLES:
371                 return ctx_info.samples;
372         case GLUT_WINDOW_STEREO:
373                 return ctx_info.stereo;
374         case GLUT_WINDOW_SRGB:
375                 return ctx_info.srgb;
376         case GLUT_WINDOW_CURSOR:
377                 return cur_cursor;
378         case GLUT_WINDOW_COLORMAP_SIZE:
379                 return cmap_size;
380         case GLUT_SCREEN_WIDTH:
381                 get_screen_size(&x, &y);
382                 return x;
383         case GLUT_SCREEN_HEIGHT:
384                 get_screen_size(&x, &y);
385                 return y;
386         case GLUT_INIT_DISPLAY_MODE:
387                 return init_mode;
388         case GLUT_INIT_WINDOW_X:
389                 return init_x;
390         case GLUT_INIT_WINDOW_Y:
391                 return init_y;
392         case GLUT_INIT_WINDOW_WIDTH:
393                 return init_width;
394         case GLUT_INIT_WINDOW_HEIGHT:
395                 return init_height;
396         case GLUT_ELAPSED_TIME:
397                 return get_msec();
398         default:
399                 break;
400         }
401         return 0;
402 }
403
404 int glutGetModifiers(void)
405 {
406         return modstate;
407 }
408
409 static int is_space(int c)
410 {
411         return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
412 }
413
414 static const char *skip_space(const char *s)
415 {
416         while(*s && is_space(*s)) s++;
417         return s;
418 }
419
420 int glutExtensionSupported(char *ext)
421 {
422         const char *str, *eptr;
423
424         if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
425                 return 0;
426         }
427
428         while(*str) {
429                 str = skip_space(str);
430                 eptr = skip_space(ext);
431                 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
432                         str++;
433                         eptr++;
434                 }
435                 if((!*str || is_space(*str)) && !*eptr) {
436                         return 1;
437                 }
438                 while(*str && !is_space(*str)) str++;
439         }
440
441         return 0;
442 }
443
444
445 /* --------------- UNIX/X11 implementation ----------------- */
446 #ifdef BUILD_X11
447 enum {
448     SPNAV_EVENT_ANY,  /* used by spnav_remove_events() */
449     SPNAV_EVENT_MOTION,
450     SPNAV_EVENT_BUTTON  /* includes both press and release */
451 };
452
453 struct spnav_event_motion {
454     int type;
455     int x, y, z;
456     int rx, ry, rz;
457     unsigned int period;
458     int *data;
459 };
460
461 struct spnav_event_button {
462     int type;
463     int press;
464     int bnum;
465 };
466
467 union spnav_event {
468     int type;
469     struct spnav_event_motion motion;
470     struct spnav_event_button button;
471 };
472
473
474 static void handle_event(XEvent *ev);
475
476 static int spnav_window(Window win);
477 static int spnav_event(const XEvent *xev, union spnav_event *event);
478 static int spnav_remove_events(int type);
479
480
481 void glutMainLoopEvent(void)
482 {
483         XEvent ev;
484
485         if(!cb_display) {
486                 panic("display callback not set");
487         }
488
489         if(!upd_pending && !cb_idle) {
490                 XNextEvent(dpy, &ev);
491                 handle_event(&ev);
492                 if(quit) goto end;
493         }
494         while(XPending(dpy)) {
495                 XNextEvent(dpy, &ev);
496                 handle_event(&ev);
497                 if(quit) goto end;
498         }
499
500         if(cb_idle) {
501                 cb_idle();
502         }
503
504         if(upd_pending && mapped) {
505                 upd_pending = 0;
506                 cb_display();
507         }
508
509 end:
510         if(quit) {
511                 cleanup();
512         }
513 }
514
515 static void cleanup(void)
516 {
517         if(win) {
518                 spnav_window(root);
519                 glXMakeCurrent(dpy, 0, 0);
520                 XDestroyWindow(dpy, win);
521         }
522 }
523
524 static KeySym translate_keysym(KeySym sym)
525 {
526         switch(sym) {
527         case XK_Escape:
528                 return 27;
529         case XK_BackSpace:
530                 return '\b';
531         case XK_Linefeed:
532                 return '\r';
533         case XK_Return:
534                 return '\n';
535         case XK_Delete:
536                 return 127;
537         case XK_Tab:
538                 return '\t';
539         default:
540                 break;
541         }
542         return sym;
543 }
544
545 static void handle_event(XEvent *ev)
546 {
547         KeySym sym;
548         union spnav_event sev;
549
550         switch(ev->type) {
551         case MapNotify:
552                 mapped = 1;
553                 break;
554         case UnmapNotify:
555                 mapped = 0;
556                 break;
557         case ConfigureNotify:
558                 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
559                         win_width = ev->xconfigure.width;
560                         win_height = ev->xconfigure.height;
561                         cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
562                 }
563                 break;
564
565         case ClientMessage:
566                 if(ev->xclient.message_type == xa_wm_proto) {
567                         if(ev->xclient.data.l[0] == xa_wm_del_win) {
568                                 quit = 1;
569                         }
570                 }
571                 if(spnav_event(ev, &sev)) {
572                         switch(sev.type) {
573                         case SPNAV_EVENT_MOTION:
574                                 if(cb_sball_motion) {
575                                         cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
576                                 }
577                                 if(cb_sball_rotate) {
578                                         cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
579                                 }
580                                 spnav_remove_events(SPNAV_EVENT_MOTION);
581                                 break;
582
583                         case SPNAV_EVENT_BUTTON:
584                                 if(cb_sball_button) {
585                                         cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
586                                 }
587                                 break;
588
589                         default:
590                                 break;
591                         }
592                 }
593                 break;
594
595         case Expose:
596                 upd_pending = 1;
597                 break;
598
599         case KeyPress:
600                 if(0) {
601         case KeyRelease:
602                         if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
603                                 XEvent next;
604                                 XPeekEvent(dpy, &next);
605
606                                 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
607                                                 next.xkey.time == ev->xkey.time) {
608                                         /* this is a key-repeat event, ignore the release and consume
609                                          * the following press
610                                          */
611                                         XNextEvent(dpy, &next);
612                                         break;
613                                 }
614                         }
615                 }
616                 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
617                 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
618                         break;
619                 }
620                 sym = translate_keysym(sym);
621                 if(sym < 256) {
622                         if(ev->type == KeyPress) {
623                                 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
624                         } else {
625                                 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
626                         }
627                 } else {
628                         if(ev->type == KeyPress) {
629                                 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
630                         } else {
631                                 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
632                         }
633                 }
634                 break;
635
636         case ButtonPress:
637         case ButtonRelease:
638                 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
639                 if(cb_mouse) {
640                         int bn = ev->xbutton.button - Button1;
641                         cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
642                                         ev->xbutton.x, ev->xbutton.y);
643                 }
644                 break;
645
646         case MotionNotify:
647                 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
648                         if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
649                 } else {
650                         if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
651                 }
652                 break;
653
654         case VisibilityNotify:
655                 if(cb_vis) {
656                         cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
657                 }
658                 break;
659         case EnterNotify:
660                 if(cb_entry) cb_entry(GLUT_ENTERED);
661                 break;
662         case LeaveNotify:
663                 if(cb_entry) cb_entry(GLUT_LEFT);
664                 break;
665         }
666 }
667
668 void glutSwapBuffers(void)
669 {
670         glXSwapBuffers(dpy, win);
671 }
672
673 /* BUG:
674  * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
675  * needs to resize the window to make it fullscreen. The way it does this is by
676  * querying the size of the root window (see get_screen_size), which in the
677  * case of multi-monitor setups will be the combined size of all monitors.
678  * This is problematic; the way to solve it is to use the XRandR extension, or
679  * the Xinerama extension, to figure out the dimensions of the correct video
680  * output, which would add potentially two extension support libraries to our
681  * dependencies list.
682  * Moreover, any X installation modern enough to support XR&R will almost
683  * certainly be running a window manager supporting the EHWM
684  * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
685  * on manual resizing, and is used in preference if available, making this
686  * whole endeavor pointless.
687  * So I'll just leave it with set_fullscreen_mwm covering the entire
688  * multi-monitor area for now.
689  */
690
691 struct mwm_hints {
692         unsigned long flags;
693         unsigned long functions;
694         unsigned long decorations;
695         long input_mode;
696         unsigned long status;
697 };
698
699 #define MWM_HINTS_DECORATIONS   2
700 #define MWM_DECOR_ALL                   1
701
702 static void set_fullscreen_mwm(int fs)
703 {
704         struct mwm_hints hints;
705         int scr_width, scr_height;
706
707         if(fs) {
708                 get_window_pos(&prev_win_x, &prev_win_y);
709                 get_window_size(&prev_win_width, &prev_win_height);
710                 get_screen_size(&scr_width, &scr_height);
711
712                 hints.decorations = 0;
713                 hints.flags = MWM_HINTS_DECORATIONS;
714                 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
715                                 PropModeReplace, (unsigned char*)&hints, 5);
716
717                 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
718         } else {
719                 XDeleteProperty(dpy, win, xa_motif_wm_hints);
720                 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
721         }
722 }
723
724 static int have_netwm_fullscr(void)
725 {
726         int fmt;
727         long offs = 0;
728         unsigned long i, count, rem;
729         Atom *prop, type;
730         Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
731
732         do {
733                 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
734                                 &type, &fmt, &count, &rem, (unsigned char**)&prop);
735
736                 for(i=0; i<count; i++) {
737                         if(prop[i] == xa_net_wm_state_fullscr) {
738                                 XFree(prop);
739                                 return 1;
740                         }
741                 }
742                 XFree(prop);
743                 offs += count;
744         } while(rem > 0);
745
746         return 0;
747 }
748
749 static void set_fullscreen_ewmh(int fs)
750 {
751         XClientMessageEvent msg = {0};
752
753         msg.type = ClientMessage;
754         msg.window = win;
755         msg.message_type = xa_net_wm_state;     /* _NET_WM_STATE */
756         msg.format = 32;
757         msg.data.l[0] = fs ? 1 : 0;
758         msg.data.l[1] = xa_net_wm_state_fullscr;        /* _NET_WM_STATE_FULLSCREEN */
759         msg.data.l[2] = 0;
760         msg.data.l[3] = 1;      /* source regular application */
761         XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
762 }
763
764 static void set_fullscreen(int fs)
765 {
766         if(fullscreen == fs) return;
767
768         if(xa_net_wm_state && xa_net_wm_state_fullscr) {
769                 set_fullscreen_ewmh(fs);
770                 fullscreen = fs;
771         } else if(xa_motif_wm_hints) {
772                 set_fullscreen_mwm(fs);
773                 fullscreen = fs;
774         }
775 }
776
777 void glutPositionWindow(int x, int y)
778 {
779         set_fullscreen(0);
780         XMoveWindow(dpy, win, x, y);
781 }
782
783 void glutReshapeWindow(int xsz, int ysz)
784 {
785         set_fullscreen(0);
786         XResizeWindow(dpy, win, xsz, ysz);
787 }
788
789 void glutFullScreen(void)
790 {
791         set_fullscreen(1);
792 }
793
794 void glutSetWindowTitle(const char *title)
795 {
796         XTextProperty tprop;
797         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
798                 return;
799         }
800         XSetWMName(dpy, win, &tprop);
801         XFree(tprop.value);
802 }
803
804 void glutSetIconTitle(const char *title)
805 {
806         XTextProperty tprop;
807         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
808                 return;
809         }
810         XSetWMIconName(dpy, win, &tprop);
811         XFree(tprop.value);
812 }
813
814 void glutSetCursor(int cidx)
815 {
816         Cursor cur = None;
817
818         switch(cidx) {
819         case GLUT_CURSOR_LEFT_ARROW:
820                 cur = XCreateFontCursor(dpy, XC_left_ptr);
821                 break;
822         case GLUT_CURSOR_INHERIT:
823                 break;
824         case GLUT_CURSOR_NONE:
825                 cur = blank_cursor;
826                 break;
827         default:
828                 return;
829         }
830
831         XDefineCursor(dpy, win, cur);
832         cur_cursor = cidx;
833 }
834
835 void glutSetColor(int idx, float r, float g, float b)
836 {
837         XColor color;
838
839         if(idx >= 0 && idx < cmap_size) {
840                 color.pixel = idx;
841                 color.red = (unsigned short)(r * 65535.0f);
842                 color.green = (unsigned short)(g * 65535.0f);
843                 color.blue = (unsigned short)(b * 65535.0f);
844                 color.flags = DoRed | DoGreen | DoBlue;
845                 XStoreColor(dpy, cmap, &color);
846         }
847 }
848
849 float glutGetColor(int idx, int comp)
850 {
851         XColor color;
852
853         if(idx < 0 || idx >= cmap_size) {
854                 return -1.0f;
855         }
856
857         color.pixel = idx;
858         XQueryColor(dpy, cmap, &color);
859         switch(comp) {
860         case GLUT_RED:
861                 return color.red / 65535.0f;
862         case GLUT_GREEN:
863                 return color.green / 65535.0f;
864         case GLUT_BLUE:
865                 return color.blue / 65535.0f;
866         default:
867                 break;
868         }
869         return -1.0f;
870 }
871
872 void glutSetKeyRepeat(int repmode)
873 {
874         if(repmode) {
875                 XAutoRepeatOn(dpy);
876         } else {
877                 XAutoRepeatOff(dpy);
878         }
879 }
880
881 static XVisualInfo *choose_visual(unsigned int mode)
882 {
883         XVisualInfo *vi;
884         int attr[32];
885         int *aptr = attr;
886         int *samples = 0;
887
888         if(mode & GLUT_DOUBLE) {
889                 *aptr++ = GLX_DOUBLEBUFFER;
890         }
891
892         if(mode & GLUT_INDEX) {
893                 *aptr++ = GLX_BUFFER_SIZE;
894                 *aptr++ = 1;
895         } else {
896                 *aptr++ = GLX_RGBA;
897                 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
898                 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
899                 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
900         }
901         if(mode & GLUT_ALPHA) {
902                 *aptr++ = GLX_ALPHA_SIZE;
903                 *aptr++ = 4;
904         }
905         if(mode & GLUT_DEPTH) {
906                 *aptr++ = GLX_DEPTH_SIZE;
907                 *aptr++ = 8;
908         }
909         if(mode & GLUT_STENCIL) {
910                 *aptr++ = GLX_STENCIL_SIZE;
911                 *aptr++ = 1;
912         }
913         if(mode & GLUT_ACCUM) {
914                 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
915                 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
916                 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
917         }
918         if(mode & GLUT_STEREO) {
919                 *aptr++ = GLX_STEREO;
920         }
921         if(mode & GLUT_SRGB) {
922                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
923         }
924         if(mode & GLUT_MULTISAMPLE) {
925                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
926                 *aptr++ = 1;
927                 *aptr++ = GLX_SAMPLES_ARB;
928                 samples = aptr;
929                 *aptr++ = 32;
930         }
931         *aptr++ = None;
932
933         if(!samples) {
934                 return glXChooseVisual(dpy, scr, attr);
935         }
936         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
937                 *samples >>= 1;
938                 if(!*samples) {
939                         aptr[-3] = None;
940                 }
941         }
942         return vi;
943 }
944
945 static void create_window(const char *title)
946 {
947         XSetWindowAttributes xattr = {0};
948         XVisualInfo *vi;
949         unsigned int xattr_mask;
950         unsigned int mode = init_mode;
951
952         if(!(vi = choose_visual(mode))) {
953                 mode &= ~GLUT_SRGB;
954                 if(!(vi = choose_visual(mode))) {
955                         panic("Failed to find compatible visual\n");
956                 }
957         }
958
959         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
960                 XFree(vi);
961                 panic("Failed to create OpenGL context\n");
962         }
963
964         glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
965         glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
966         glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
967         glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
968         glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
969         glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
970         glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
971         glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
972         glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
973         glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
974
975         if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
976                 XFree(vi);
977                 glXDestroyContext(dpy, ctx);
978                 panic("Failed to create colormap\n");
979         }
980         cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
981
982         xattr.background_pixel = BlackPixel(dpy, scr);
983         xattr.colormap = cmap;
984         xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
985         if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
986                         vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
987                 XFree(vi);
988                 glXDestroyContext(dpy, ctx);
989                 XFreeColormap(dpy, cmap);
990                 panic("Failed to create window\n");
991         }
992         XFree(vi);
993
994         XSelectInput(dpy, win, evmask);
995
996         spnav_window(win);
997
998         glutSetWindowTitle(title);
999         glutSetIconTitle(title);
1000         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
1001         XMapWindow(dpy, win);
1002
1003         glXMakeCurrent(dpy, win, ctx);
1004 }
1005
1006 static void get_window_pos(int *x, int *y)
1007 {
1008         Window child;
1009         XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1010 }
1011
1012 static void get_window_size(int *w, int *h)
1013 {
1014         XWindowAttributes wattr;
1015         XGetWindowAttributes(dpy, win, &wattr);
1016         *w = wattr.width;
1017         *h = wattr.height;
1018 }
1019
1020 static void get_screen_size(int *scrw, int *scrh)
1021 {
1022         XWindowAttributes wattr;
1023         XGetWindowAttributes(dpy, root, &wattr);
1024         *scrw = wattr.width;
1025         *scrh = wattr.height;
1026 }
1027
1028
1029 /* spaceball */
1030 enum {
1031   CMD_APP_WINDOW = 27695,
1032   CMD_APP_SENS
1033 };
1034
1035 static Window get_daemon_window(Display *dpy);
1036 static int catch_badwin(Display *dpy, XErrorEvent *err);
1037
1038 #define SPNAV_INITIALIZED       (xa_motion_event)
1039
1040 static int spnav_window(Window win)
1041 {
1042         int (*prev_xerr_handler)(Display*, XErrorEvent*);
1043         XEvent xev;
1044         Window daemon_win;
1045
1046         if(!SPNAV_INITIALIZED) {
1047                 return -1;
1048         }
1049
1050         if(!(daemon_win = get_daemon_window(dpy))) {
1051                 return -1;
1052         }
1053
1054         prev_xerr_handler = XSetErrorHandler(catch_badwin);
1055
1056         xev.type = ClientMessage;
1057         xev.xclient.send_event = False;
1058         xev.xclient.display = dpy;
1059         xev.xclient.window = win;
1060         xev.xclient.message_type = xa_command_event;
1061         xev.xclient.format = 16;
1062         xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1063         xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1064         xev.xclient.data.s[2] = CMD_APP_WINDOW;
1065
1066         XSendEvent(dpy, daemon_win, False, 0, &xev);
1067         XSync(dpy, False);
1068
1069         XSetErrorHandler(prev_xerr_handler);
1070         return 0;
1071 }
1072
1073 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1074 {
1075         int evtype = *(int*)arg;
1076
1077         if(xev->type != ClientMessage) {
1078                 return False;
1079         }
1080
1081         if(xev->xclient.message_type == xa_motion_event) {
1082                 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1083         }
1084         if(xev->xclient.message_type == xa_button_press_event ||
1085                         xev->xclient.message_type == xa_button_release_event) {
1086                 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1087         }
1088         return False;
1089 }
1090
1091 static int spnav_remove_events(int type)
1092 {
1093         int rm_count = 0;
1094         XEvent xev;
1095         while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1096                 rm_count++;
1097         }
1098         return rm_count;
1099 }
1100
1101 static int spnav_event(const XEvent *xev, union spnav_event *event)
1102 {
1103         int i;
1104         int xmsg_type;
1105
1106         xmsg_type = xev->xclient.message_type;
1107
1108         if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1109                         xmsg_type != xa_button_release_event) {
1110                 return 0;
1111         }
1112
1113         if(xmsg_type == xa_motion_event) {
1114                 event->type = SPNAV_EVENT_MOTION;
1115                 event->motion.data = &event->motion.x;
1116
1117                 for(i=0; i<6; i++) {
1118                         event->motion.data[i] = xev->xclient.data.s[i + 2];
1119                 }
1120                 event->motion.period = xev->xclient.data.s[8];
1121         } else {
1122                 event->type = SPNAV_EVENT_BUTTON;
1123                 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1124                 event->button.bnum = xev->xclient.data.s[2];
1125         }
1126         return event->type;
1127 }
1128
1129 static int mglut_strcmp(const char *s1, const char *s2)
1130 {
1131         while(*s1 && *s1 == *s2) {
1132                 s1++;
1133                 s2++;
1134         }
1135         return *s1 - *s2;
1136 }
1137
1138 static Window get_daemon_window(Display *dpy)
1139 {
1140         Window win;
1141         XTextProperty wname;
1142         Atom type;
1143         int fmt;
1144         unsigned long nitems, bytes_after;
1145         unsigned char *prop;
1146
1147         XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1148                         &type, &fmt, &nitems, &bytes_after, &prop);
1149         if(!prop) {
1150                 return 0;
1151         }
1152
1153         win = *(Window*)prop;
1154         XFree(prop);
1155
1156         wname.value = 0;
1157         if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1158                 win = 0;
1159         }
1160         XFree(wname.value);
1161
1162         return win;
1163 }
1164
1165 static int catch_badwin(Display *dpy, XErrorEvent *err)
1166 {
1167         return 0;
1168 }
1169
1170
1171
1172 #endif  /* BUILD_X11 */
1173
1174
1175 /* --------------- windows implementation ----------------- */
1176 #ifdef BUILD_WIN32
1177 static int reshape_pending;
1178
1179 static void update_modkeys(void);
1180 static int translate_vkey(int vkey);
1181 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1182
1183 #ifdef MINIGLUT_WINMAIN
1184 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1185 {
1186         int argc = 1;
1187         char *argv[] = { "miniglut.exe", 0 };
1188         return main(argc, argv);
1189 }
1190 #endif
1191
1192 void glutMainLoopEvent(void)
1193 {
1194         MSG msg;
1195
1196         if(!cb_display) {
1197                 panic("display callback not set");
1198         }
1199
1200         if(reshape_pending && cb_reshape) {
1201                 reshape_pending = 0;
1202                 get_window_size(&win_width, &win_height);
1203                 cb_reshape(win_width, win_height);
1204         }
1205
1206         if(!upd_pending && !cb_idle) {
1207                 GetMessage(&msg, 0, 0, 0);
1208                 TranslateMessage(&msg);
1209                 DispatchMessage(&msg);
1210                 if(quit) return;
1211         }
1212         while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1213                 TranslateMessage(&msg);
1214                 DispatchMessage(&msg);
1215                 if(quit) return;
1216         }
1217
1218         if(cb_idle) {
1219                 cb_idle();
1220         }
1221
1222         if(upd_pending && mapped) {
1223                 upd_pending = 0;
1224                 cb_display();
1225         }
1226 }
1227
1228 static void cleanup(void)
1229 {
1230         if(win) {
1231                 wglMakeCurrent(dc, 0);
1232                 wglDeleteContext(ctx);
1233                 UnregisterClass("MiniGLUT", hinst);
1234         }
1235 }
1236
1237 void glutSwapBuffers(void)
1238 {
1239         SwapBuffers(dc);
1240 }
1241
1242 void glutPositionWindow(int x, int y)
1243 {
1244         RECT rect;
1245         unsigned int flags = SWP_SHOWWINDOW;
1246
1247         if(fullscreen) {
1248                 rect.left = prev_win_x;
1249                 rect.top = prev_win_y;
1250                 rect.right = rect.left + prev_win_width;
1251                 rect.bottom = rect.top + prev_win_height;
1252                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1253                 fullscreen = 0;
1254                 flags |= SWP_FRAMECHANGED;
1255         } else {
1256                 GetWindowRect(win, &rect);
1257         }
1258         SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1259 }
1260
1261 void glutReshapeWindow(int xsz, int ysz)
1262 {
1263         RECT rect;
1264         unsigned int flags = SWP_SHOWWINDOW;
1265
1266         if(fullscreen) {
1267                 rect.left = prev_win_x;
1268                 rect.top = prev_win_y;
1269                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1270                 fullscreen = 0;
1271                 flags |= SWP_FRAMECHANGED;
1272         } else {
1273                 GetWindowRect(win, &rect);
1274         }
1275         SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1276 }
1277
1278 void glutFullScreen(void)
1279 {
1280         RECT rect;
1281         int scr_width, scr_height;
1282
1283         if(fullscreen) return;
1284
1285         GetWindowRect(win, &rect);
1286         prev_win_x = rect.left;
1287         prev_win_y = rect.top;
1288         prev_win_width = rect.right - rect.left;
1289         prev_win_height = rect.bottom - rect.top;
1290
1291         get_screen_size(&scr_width, &scr_height);
1292
1293         SetWindowLong(win, GWL_STYLE, 0);
1294         SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1295
1296         fullscreen = 1;
1297 }
1298
1299 void glutSetWindowTitle(const char *title)
1300 {
1301         SetWindowText(win, title);
1302 }
1303
1304 void glutSetIconTitle(const char *title)
1305 {
1306 }
1307
1308 void glutSetCursor(int cidx)
1309 {
1310         switch(cidx) {
1311         case GLUT_CURSOR_NONE:
1312                 ShowCursor(0);
1313                 break;
1314         case GLUT_CURSOR_INHERIT:
1315         case GLUT_CURSOR_LEFT_ARROW:
1316         default:
1317                 SetCursor(LoadCursor(0, IDC_ARROW));
1318                 ShowCursor(1);
1319         }
1320 }
1321
1322 void glutSetColor(int idx, float r, float g, float b)
1323 {
1324         PALETTEENTRY col;
1325
1326         if(idx < 0 || idx >= 256 || !cmap) {
1327                 return;
1328         }
1329
1330         col.peRed = (int)(r * 255.0f);
1331         col.peGreen = (int)(g * 255.0f);
1332         col.peBlue = (int)(b * 255.0f);
1333         col.peFlags = PC_NOCOLLAPSE;
1334
1335         SetPaletteEntries(cmap, idx, 1, &col);
1336
1337         if(dc) {
1338                 UnrealizeObject(cmap);
1339                 SelectPalette(dc, cmap, 0);
1340                 RealizePalette(dc);
1341         }
1342 }
1343
1344 float glutGetColor(int idx, int comp)
1345 {
1346         PALETTEENTRY col;
1347
1348         if(idx < 0 || idx >= 256 || !cmap) {
1349                 return -1.0f;
1350         }
1351
1352         if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1353                 return -1.0f;
1354         }
1355
1356         switch(comp) {
1357         case GLUT_RED:
1358                 return col.peRed / 255.0f;
1359         case GLUT_GREEN:
1360                 return col.peGreen / 255.0f;
1361         case GLUT_BLUE:
1362                 return col.peBlue / 255.0f;
1363         default:
1364                 break;
1365         }
1366         return -1.0f;
1367 }
1368
1369 void glutSetKeyRepeat(int repmode)
1370 {
1371 }
1372
1373 #define WGL_DRAW_TO_WINDOW      0x2001
1374 #define WGL_ACCELERATION        0x2003
1375 #define WGL_SUPPORT_OPENGL      0x2010
1376 #define WGL_DOUBLE_BUFFER       0x2011
1377 #define WGL_STEREO                      0x2012
1378 #define WGL_PIXEL_TYPE          0x2013
1379 #define WGL_COLOR_BITS          0x2014
1380 #define WGL_RED_BITS            0x2015
1381 #define WGL_GREEN_BITS          0x2017
1382 #define WGL_BLUE_BITS           0x2019
1383 #define WGL_ALPHA_BITS          0x201b
1384 #define WGL_ACCUM_BITS          0x201d
1385 #define WGL_DEPTH_BITS          0x2022
1386 #define WGL_STENCIL_BITS        0x2023
1387 #define WGL_FULL_ACCELERATION   0x2027
1388
1389 #define WGL_TYPE_RGBA           0x202b
1390 #define WGL_TYPE_COLORINDEX     0x202c
1391
1392 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20a9
1393 #define WGL_SAMPLE_BUFFERS_ARB                          0x2041
1394 #define WGL_SAMPLES_ARB                                         0x2042
1395
1396 static PROC wglChoosePixelFormat;
1397 static PROC wglGetPixelFormatAttribiv;
1398
1399 #define ATTR(a, v) \
1400         do { *aptr++ = (a); *aptr++ = (v); } while(0)
1401
1402 static unsigned int choose_pixfmt(unsigned int mode)
1403 {
1404         unsigned int num_pixfmt, pixfmt = 0;
1405         int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1406                 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1407         float fattr[2] = {0, 0};
1408
1409         int *aptr = attr + 6;
1410         int *samples = 0;
1411
1412         if(mode & GLUT_DOUBLE) {
1413                 ATTR(WGL_DOUBLE_BUFFER, 1);
1414         }
1415
1416         ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1417         ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1418         if(mode & GLUT_ALPHA) {
1419                 ATTR(WGL_ALPHA_BITS, 4);
1420         }
1421         if(mode & GLUT_DEPTH) {
1422                 ATTR(WGL_DEPTH_BITS, 16);
1423         }
1424         if(mode & GLUT_STENCIL) {
1425                 ATTR(WGL_STENCIL_BITS, 1);
1426         }
1427         if(mode & GLUT_ACCUM) {
1428                 ATTR(WGL_ACCUM_BITS, 1);
1429         }
1430         if(mode & GLUT_STEREO) {
1431                 ATTR(WGL_STEREO, 1);
1432         }
1433         if(mode & GLUT_SRGB) {
1434                 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1435         }
1436         if(mode & GLUT_MULTISAMPLE) {
1437                 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1438                 *aptr++ = WGL_SAMPLES_ARB;
1439                 samples = aptr;
1440                 *aptr++ = 32;
1441         }
1442         *aptr++ = 0;
1443
1444         while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1445                 *samples >>= 1;
1446                 if(!*samples) {
1447                         aptr[-3] = 0;
1448                 }
1449         }
1450         return pixfmt;
1451 }
1452
1453 static PIXELFORMATDESCRIPTOR pfd;
1454 static PIXELFORMATDESCRIPTOR tmppfd = {
1455         sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1456         PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1457         PFD_MAIN_PLANE, 0, 0, 0, 0
1458 };
1459 #define TMPCLASS        "TempMiniGLUT"
1460
1461 #define GETATTR(attr, vptr) \
1462         do { \
1463                 int gattr = attr; \
1464                 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1465         } while(0)
1466
1467 static int create_window_wglext(const char *title, int width, int height)
1468 {
1469         WNDCLASSEX wc = {0};
1470         HWND tmpwin = 0;
1471         HDC tmpdc = 0;
1472         HGLRC tmpctx = 0;
1473         int pixfmt;
1474
1475         /* create a temporary window and GL context, just to query and retrieve
1476          * the wglChoosePixelFormatEXT function
1477          */
1478         wc.cbSize = sizeof wc;
1479         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1480         wc.hCursor = LoadCursor(0, IDC_ARROW);
1481         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1482         wc.hInstance = hinst;
1483         wc.lpfnWndProc = DefWindowProc;
1484         wc.lpszClassName = TMPCLASS;
1485         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1486         if(!RegisterClassEx(&wc)) {
1487                 return 0;
1488         }
1489         if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1490                                         width, height, 0, 0, hinst, 0))) {
1491                 goto fail;
1492         }
1493         tmpdc = GetDC(tmpwin);
1494
1495         if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1496                         !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1497                         !(tmpctx = wglCreateContext(tmpdc))) {
1498                 goto fail;
1499         }
1500         wglMakeCurrent(tmpdc, tmpctx);
1501
1502         if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1503                 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1504                         goto fail;
1505                 }
1506                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1507                         goto fail;
1508                 }
1509         } else {
1510                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1511                         goto fail;
1512                 }
1513         }
1514         wglMakeCurrent(0, 0);
1515         wglDeleteContext(tmpctx);
1516         DestroyWindow(tmpwin);
1517         UnregisterClass(TMPCLASS, hinst);
1518
1519         /* create the real window and context */
1520         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1521                                         init_y, width, height, 0, 0, hinst, 0))) {
1522                 panic("Failed to create window\n");
1523         }
1524         dc = GetDC(win);
1525
1526         if(!(pixfmt = choose_pixfmt(init_mode))) {
1527                 panic("Failed to find suitable pixel format\n");
1528         }
1529         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1530                 panic("Failed to set the selected pixel format\n");
1531         }
1532         if(!(ctx = wglCreateContext(dc))) {
1533                 panic("Failed to create the OpenGL context\n");
1534         }
1535         wglMakeCurrent(dc, ctx);
1536
1537         GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1538         GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1539         GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1540         GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1541         GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1542         GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1543         GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1544         GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1545         GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1546
1547         return 0;
1548
1549 fail:
1550         if(tmpctx) {
1551                 wglMakeCurrent(0, 0);
1552                 wglDeleteContext(tmpctx);
1553         }
1554         if(tmpwin) {
1555                 DestroyWindow(tmpwin);
1556         }
1557         UnregisterClass(TMPCLASS, hinst);
1558         return -1;
1559 }
1560
1561
1562 static void create_window(const char *title)
1563 {
1564         RECT rect;
1565         int i, pixfmt, width, height;
1566         char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1567         LOGPALETTE *logpal;
1568
1569
1570         rect.left = init_x;
1571         rect.top = init_y;
1572         rect.right = init_x + init_width;
1573         rect.bottom = init_y + init_height;
1574         AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1575         width = rect.right - rect.left;
1576         height = rect.bottom - rect.top;
1577
1578         memset(&pfd, 0, sizeof pfd);
1579         pfd.nSize = sizeof pfd;
1580         pfd.nVersion = 1;
1581         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1582         if(init_mode & GLUT_STEREO) {
1583                 pfd.dwFlags |= PFD_STEREO;
1584         }
1585         if(init_mode & GLUT_INDEX) {
1586                 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1587                 pfd.cColorBits = 8;
1588         } else {
1589                 pfd.iPixelType = PFD_TYPE_RGBA;
1590                 pfd.cColorBits = 24;
1591         }
1592         if(init_mode & GLUT_ALPHA) {
1593                 pfd.cAlphaBits = 8;
1594         }
1595         if(init_mode & GLUT_ACCUM) {
1596                 pfd.cAccumBits = 24;
1597         }
1598         if(init_mode & GLUT_DEPTH) {
1599                 pfd.cDepthBits = 24;
1600         }
1601         if(init_mode & GLUT_STENCIL) {
1602                 pfd.cStencilBits = 8;
1603         }
1604         pfd.iLayerType = PFD_MAIN_PLANE;
1605
1606         if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
1607                 if(create_window_wglext(title, width, height) != -1) {
1608                         goto ctxdone;
1609                 }
1610         }
1611
1612         /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
1613          * failed, just use the old-style ChoosePixelFormat method instead
1614          */
1615         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1616                                 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1617                 panic("Failed to create window\n");
1618         }
1619         dc = GetDC(win);
1620
1621         if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1622                 panic("Failed to find suitable pixel format\n");
1623         }
1624         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1625                 panic("Failed to set the selected pixel format\n");
1626         }
1627         if(!(ctx = wglCreateContext(dc))) {
1628                 panic("Failed to create the OpenGL context\n");
1629         }
1630         wglMakeCurrent(dc, ctx);
1631
1632         DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1633         ctx_info.rsize = pfd.cRedBits;
1634         ctx_info.gsize = pfd.cGreenBits;
1635         ctx_info.bsize = pfd.cBlueBits;
1636         ctx_info.asize = pfd.cAlphaBits;
1637         ctx_info.zsize = pfd.cDepthBits;
1638         ctx_info.ssize = pfd.cStencilBits;
1639         ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1640         ctx_info.samples = 0;
1641         ctx_info.srgb = 0;
1642
1643 ctxdone:
1644         ShowWindow(win, 1);
1645         SetForegroundWindow(win);
1646         SetFocus(win);
1647
1648         if(init_mode & GLUT_INDEX) {
1649                 logpal = (LOGPALETTE*)palbuf;
1650
1651                 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1652
1653                 logpal->palVersion = 0x300;
1654                 logpal->palNumEntries = 256;
1655
1656                 if(!(cmap = CreatePalette(logpal))) {
1657                         panic("Failed to create palette in indexed mode");
1658                 }
1659                 SelectPalette(dc, cmap, 0);
1660                 RealizePalette(dc);
1661
1662                 cmap_size = 256;
1663         } else {
1664                 if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
1665                         /* for RGB mode in 8bpp displays we also need to set up a palette
1666                          * with RGB 332 colors
1667                          */
1668                         logpal = (LOGPALETTE*)palbuf;
1669
1670                         logpal->palVersion = 0x300;
1671                         logpal->palNumEntries = 256;
1672
1673                         for(i=0; i<256; i++) {
1674                                 int r = i & 7;
1675                                 int g = (i >> 3) & 7;
1676                                 int b = (i >> 5) & 3;
1677
1678                                 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1679                                 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1680                                 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1681                                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1682                         }
1683
1684                         if((cmap = CreatePalette(logpal))) {
1685                                 SelectPalette(dc, cmap, 0);
1686                                 RealizePalette(dc);
1687                                 cmap_size = 256;
1688                         }
1689                 }
1690         }
1691
1692         upd_pending = 1;
1693         reshape_pending = 1;
1694 }
1695
1696 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1697 {
1698         static int mouse_x, mouse_y;
1699         int x, y, key;
1700
1701         switch(msg) {
1702         case WM_CLOSE:
1703                 if(win) DestroyWindow(win);
1704                 break;
1705
1706         case WM_DESTROY:
1707                 cleanup();
1708                 quit = 1;
1709                 PostQuitMessage(0);
1710                 break;
1711
1712         case WM_PAINT:
1713                 upd_pending = 1;
1714                 ValidateRect(win, 0);
1715                 break;
1716
1717         case WM_SIZE:
1718                 x = lparam & 0xffff;
1719                 y = lparam >> 16;
1720                 if(x != win_width && y != win_height) {
1721                         win_width = x;
1722                         win_height = y;
1723                         if(cb_reshape) {
1724                                 reshape_pending = 0;
1725                                 cb_reshape(win_width, win_height);
1726                         }
1727                 }
1728                 break;
1729
1730         case WM_SHOWWINDOW:
1731                 mapped = wparam;
1732                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1733                 break;
1734
1735         case WM_KEYDOWN:
1736         case WM_SYSKEYDOWN:
1737                 update_modkeys();
1738                 key = translate_vkey(wparam);
1739                 if(key < 256) {
1740                         if(cb_keydown) {
1741                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1742                         }
1743                 } else {
1744                         if(cb_skeydown) {
1745                                 cb_skeydown(key, mouse_x, mouse_y);
1746                         }
1747                 }
1748                 break;
1749
1750         case WM_KEYUP:
1751         case WM_SYSKEYUP:
1752                 update_modkeys();
1753                 key = translate_vkey(wparam);
1754                 if(key < 256) {
1755                         if(cb_keyup) {
1756                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1757                         }
1758                 } else {
1759                         if(cb_skeyup) {
1760                                 cb_skeyup(key, mouse_x, mouse_y);
1761                         }
1762                 }
1763                 break;
1764
1765         case WM_LBUTTONDOWN:
1766                 handle_mbutton(0, 1, wparam, lparam);
1767                 break;
1768         case WM_MBUTTONDOWN:
1769                 handle_mbutton(1, 1, wparam, lparam);
1770                 break;
1771         case WM_RBUTTONDOWN:
1772                 handle_mbutton(2, 1, wparam, lparam);
1773                 break;
1774         case WM_LBUTTONUP:
1775                 handle_mbutton(0, 0, wparam, lparam);
1776                 break;
1777         case WM_MBUTTONUP:
1778                 handle_mbutton(1, 0, wparam, lparam);
1779                 break;
1780         case WM_RBUTTONUP:
1781                 handle_mbutton(2, 0, wparam, lparam);
1782                 break;
1783
1784         case WM_MOUSEMOVE:
1785                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1786                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1787                 } else {
1788                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1789                 }
1790                 break;
1791
1792         case WM_SYSCOMMAND:
1793                 wparam &= 0xfff0;
1794                 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1795                         return 0;
1796                 }
1797         default:
1798                 return DefWindowProc(win, msg, wparam, lparam);
1799         }
1800
1801         return 0;
1802 }
1803
1804 static void update_modkeys(void)
1805 {
1806         if(GetKeyState(VK_SHIFT) & 0x8000) {
1807                 modstate |= GLUT_ACTIVE_SHIFT;
1808         } else {
1809                 modstate &= ~GLUT_ACTIVE_SHIFT;
1810         }
1811         if(GetKeyState(VK_CONTROL) & 0x8000) {
1812                 modstate |= GLUT_ACTIVE_CTRL;
1813         } else {
1814                 modstate &= ~GLUT_ACTIVE_CTRL;
1815         }
1816         if(GetKeyState(VK_MENU) & 0x8000) {
1817                 modstate |= GLUT_ACTIVE_ALT;
1818         } else {
1819                 modstate &= ~GLUT_ACTIVE_ALT;
1820         }
1821 }
1822
1823 #ifndef VK_OEM_1
1824 #define VK_OEM_1        0xba
1825 #define VK_OEM_2        0xbf
1826 #define VK_OEM_3        0xc0
1827 #define VK_OEM_4        0xdb
1828 #define VK_OEM_5        0xdc
1829 #define VK_OEM_6        0xdd
1830 #define VK_OEM_7        0xde
1831 #endif
1832
1833 static int translate_vkey(int vkey)
1834 {
1835         switch(vkey) {
1836         case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1837         case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1838         case VK_END: return GLUT_KEY_END;
1839         case VK_HOME: return GLUT_KEY_HOME;
1840         case VK_LEFT: return GLUT_KEY_LEFT;
1841         case VK_UP: return GLUT_KEY_UP;
1842         case VK_RIGHT: return GLUT_KEY_RIGHT;
1843         case VK_DOWN: return GLUT_KEY_DOWN;
1844         case VK_OEM_1: return ';';
1845         case VK_OEM_2: return '/';
1846         case VK_OEM_3: return '`';
1847         case VK_OEM_4: return '[';
1848         case VK_OEM_5: return '\\';
1849         case VK_OEM_6: return ']';
1850         case VK_OEM_7: return '\'';
1851         default:
1852                 break;
1853         }
1854
1855         if(vkey >= 'A' && vkey <= 'Z') {
1856                 vkey += 32;
1857         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1858                 vkey -= VK_F1 + GLUT_KEY_F1;
1859         }
1860
1861         return vkey;
1862 }
1863
1864 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1865 {
1866         int x, y;
1867
1868         update_modkeys();
1869
1870         if(cb_mouse) {
1871                 x = lparam & 0xffff;
1872                 y = lparam >> 16;
1873                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1874         }
1875 }
1876
1877 static void get_window_pos(int *x, int *y)
1878 {
1879         RECT rect;
1880         GetWindowRect(win, &rect);
1881         *x = rect.left;
1882         *y = rect.top;
1883 }
1884
1885 static void get_window_size(int *w, int *h)
1886 {
1887         RECT rect;
1888         GetClientRect(win, &rect);
1889         *w = rect.right - rect.left;
1890         *h = rect.bottom - rect.top;
1891 }
1892
1893 static void get_screen_size(int *scrw, int *scrh)
1894 {
1895         *scrw = GetSystemMetrics(SM_CXSCREEN);
1896         *scrh = GetSystemMetrics(SM_CYSCREEN);
1897 }
1898 #endif  /* BUILD_WIN32 */
1899
1900 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1901 #include <sys/time.h>
1902
1903 #ifdef MINIGLUT_USE_LIBC
1904 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1905 #else
1906 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1907 #endif
1908
1909 static long get_msec(void)
1910 {
1911         static struct timeval tv0;
1912         struct timeval tv;
1913
1914         sys_gettimeofday(&tv, 0);
1915         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1916                 tv0 = tv;
1917                 return 0;
1918         }
1919         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1920 }
1921 #endif  /* UNIX */
1922 #ifdef _WIN32
1923 static long get_msec(void)
1924 {
1925         static long t0;
1926         long tm;
1927
1928 #ifdef MINIGLUT_NO_WINMM
1929         tm = GetTickCount();
1930 #else
1931         tm = timeGetTime();
1932 #endif
1933         if(!t0) {
1934                 t0 = tm;
1935                 return 0;
1936         }
1937         return tm - t0;
1938 }
1939 #endif
1940
1941 static void panic(const char *msg)
1942 {
1943         const char *end = msg;
1944         while(*end) end++;
1945         sys_write(2, msg, end - msg);
1946         sys_exit(1);
1947 }
1948
1949
1950 #ifdef MINIGLUT_USE_LIBC
1951 #include <stdlib.h>
1952 #ifdef _WIN32
1953 #include <io.h>
1954 #else
1955 #include <unistd.h>
1956 #endif
1957
1958 static void sys_exit(int status)
1959 {
1960         exit(status);
1961 }
1962
1963 static int sys_write(int fd, const void *buf, int count)
1964 {
1965         return write(fd, buf, count);
1966 }
1967
1968 #else   /* !MINIGLUT_USE_LIBC */
1969
1970 #ifdef __linux__
1971 #ifdef __x86_64__
1972 static void sys_exit(int status)
1973 {
1974         asm volatile(
1975                 "syscall\n\t"
1976                 :: "a"(60), "D"(status));
1977 }
1978 static int sys_write(int fd, const void *buf, int count)
1979 {
1980         long res;
1981         asm volatile(
1982                 "syscall\n\t"
1983                 : "=a"(res)
1984                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1985         return res;
1986 }
1987 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1988 {
1989         int res;
1990         asm volatile(
1991                 "syscall\n\t"
1992                 : "=a"(res)
1993                 : "a"(96), "D"(tv), "S"(tz));
1994         return res;
1995 }
1996 #endif  /* __x86_64__ */
1997 #ifdef __i386__
1998 static void sys_exit(int status)
1999 {
2000         asm volatile(
2001                 "int $0x80\n\t"
2002                 :: "a"(1), "b"(status));
2003 }
2004 static int sys_write(int fd, const void *buf, int count)
2005 {
2006         int res;
2007         asm volatile(
2008                 "int $0x80\n\t"
2009                 : "=a"(res)
2010                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2011         return res;
2012 }
2013 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2014 {
2015         int res;
2016         asm volatile(
2017                 "int $0x80\n\t"
2018                 : "=a"(res)
2019                 : "a"(78), "b"(tv), "c"(tz));
2020         return res;
2021 }
2022 #endif  /* __i386__ */
2023 #endif  /* __linux__ */
2024
2025 #ifdef _WIN32
2026 static void sys_exit(int status)
2027 {
2028         ExitProcess(status);
2029 }
2030 static int sys_write(int fd, const void *buf, int count)
2031 {
2032         unsigned long wrsz = 0;
2033
2034         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2035         if(!WriteFile(out, buf, count, &wrsz, 0)) {
2036                 return -1;
2037         }
2038         return wrsz;
2039 }
2040 #endif  /* _WIN32 */
2041 #endif  /* !MINIGLUT_USE_LIBC */
2042
2043
2044 /* ----------------- primitives ------------------ */
2045 #ifdef MINIGLUT_USE_LIBC
2046 #include <stdlib.h>
2047 #include <math.h>
2048
2049 void mglut_sincos(float angle, float *sptr, float *cptr)
2050 {
2051         *sptr = sin(angle);
2052         *cptr = cos(angle);
2053 }
2054
2055 float mglut_atan(float x)
2056 {
2057         return atan(x);
2058 }
2059
2060 #else   /* !MINIGLUT_USE_LIBC */
2061
2062 #ifdef __GNUC__
2063 void mglut_sincos(float angle, float *sptr, float *cptr)
2064 {
2065         asm volatile(
2066                 "flds %2\n\t"
2067                 "fsincos\n\t"
2068                 "fstps %1\n\t"
2069                 "fstps %0\n\t"
2070                 : "=m"(*sptr), "=m"(*cptr)
2071                 : "m"(angle)
2072         );
2073 }
2074
2075 float mglut_atan(float x)
2076 {
2077         float res;
2078         asm volatile(
2079                 "flds %1\n\t"
2080                 "fld1\n\t"
2081                 "fpatan\n\t"
2082                 "fstps %0\n\t"
2083                 : "=m"(res)
2084                 : "m"(x)
2085         );
2086         return res;
2087 }
2088 #endif
2089
2090 #ifdef _MSC_VER
2091 void mglut_sincos(float angle, float *sptr, float *cptr)
2092 {
2093         float s, c;
2094         __asm {
2095                 fld angle
2096                 fsincos
2097                 fstp c
2098                 fstp s
2099         }
2100         *sptr = s;
2101         *cptr = c;
2102 }
2103
2104 float mglut_atan(float x)
2105 {
2106         float res;
2107         __asm {
2108                 fld x
2109                 fld1
2110                 fpatan
2111                 fstp res
2112         }
2113         return res;
2114 }
2115 #endif
2116
2117 #ifdef __WATCOMC__
2118 #pragma aux mglut_sincos = \
2119         "fsincos" \
2120         "fstp dword ptr [edx]" \
2121         "fstp dword ptr [eax]" \
2122         parm[8087][eax][edx]    \
2123         modify[8087];
2124
2125 #pragma aux mglut_atan = \
2126         "fld1" \
2127         "fpatan" \
2128         parm[8087] \
2129         value[8087] \
2130         modify [8087];
2131 #endif  /* __WATCOMC__ */
2132
2133 #endif  /* !MINIGLUT_USE_LIBC */
2134
2135 #define PI      3.1415926536f
2136
2137 void glutSolidSphere(float rad, int slices, int stacks)
2138 {
2139         int i, j, k, gray;
2140         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2141         float du = 1.0f / (float)slices;
2142         float dv = 1.0f / (float)stacks;
2143
2144         glBegin(GL_QUADS);
2145         for(i=0; i<stacks; i++) {
2146                 v = i * dv;
2147                 for(j=0; j<slices; j++) {
2148                         u = j * du;
2149                         for(k=0; k<4; k++) {
2150                                 gray = k ^ (k >> 1);
2151                                 s = gray & 1 ? u + du : u;
2152                                 t = gray & 2 ? v + dv : v;
2153                                 theta = s * PI * 2.0f;
2154                                 phi = t * PI;
2155                                 mglut_sincos(theta, &sintheta, &costheta);
2156                                 mglut_sincos(phi, &sinphi, &cosphi);
2157                                 x = sintheta * sinphi;
2158                                 y = costheta * sinphi;
2159                                 z = cosphi;
2160
2161                                 glColor3f(s, t, 1);
2162                                 glTexCoord2f(s, t);
2163                                 glNormal3f(x, y, z);
2164                                 glVertex3f(x * rad, y * rad, z * rad);
2165                         }
2166                 }
2167         }
2168         glEnd();
2169 }
2170
2171 void glutWireSphere(float rad, int slices, int stacks)
2172 {
2173         glPushAttrib(GL_POLYGON_BIT);
2174         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2175         glutSolidSphere(rad, slices, stacks);
2176         glPopAttrib();
2177 }
2178
2179 void glutSolidCube(float sz)
2180 {
2181         int i, j, idx, gray, flip, rotx;
2182         float vpos[3], norm[3];
2183         float rad = sz * 0.5f;
2184
2185         glBegin(GL_QUADS);
2186         for(i=0; i<6; i++) {
2187                 flip = i & 1;
2188                 rotx = i >> 2;
2189                 idx = (~i & 2) - rotx;
2190                 norm[0] = norm[1] = norm[2] = 0.0f;
2191                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2192                 glNormal3fv(norm);
2193                 vpos[idx] = norm[idx] * rad;
2194                 for(j=0; j<4; j++) {
2195                         gray = j ^ (j >> 1);
2196                         vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2197                         vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2198                         glTexCoord2f(gray & 1, gray >> 1);
2199                         glVertex3fv(vpos);
2200                 }
2201         }
2202         glEnd();
2203 }
2204
2205 void glutWireCube(float sz)
2206 {
2207         glPushAttrib(GL_POLYGON_BIT);
2208         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2209         glutSolidCube(sz);
2210         glPopAttrib();
2211 }
2212
2213 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2214 {
2215         int i, j, k, gray;
2216         float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2217         float du = 1.0f / (float)slices;
2218         float dv = 1.0f / (float)stacks;
2219
2220         rad = rbot - rtop;
2221         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2222         mglut_sincos(phi, &sinphi, &cosphi);
2223
2224         glBegin(GL_QUADS);
2225         for(i=0; i<stacks; i++) {
2226                 v = i * dv;
2227                 for(j=0; j<slices; j++) {
2228                         u = j * du;
2229                         for(k=0; k<4; k++) {
2230                                 gray = k ^ (k >> 1);
2231                                 s = gray & 2 ? u + du : u;
2232                                 t = gray & 1 ? v + dv : v;
2233                                 rad = rbot + (rtop - rbot) * t;
2234                                 theta = s * PI * 2.0f;
2235                                 mglut_sincos(theta, &sintheta, &costheta);
2236
2237                                 x = sintheta * cosphi;
2238                                 y = costheta * cosphi;
2239                                 z = sinphi;
2240
2241                                 glColor3f(s, t, 1);
2242                                 glTexCoord2f(s, t);
2243                                 glNormal3f(x, y, z);
2244                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
2245                         }
2246                 }
2247         }
2248         glEnd();
2249 }
2250
2251 void glutSolidCone(float base, float height, int slices, int stacks)
2252 {
2253         draw_cylinder(base, 0, height, slices, stacks);
2254 }
2255
2256 void glutWireCone(float base, float height, int slices, int stacks)
2257 {
2258         glPushAttrib(GL_POLYGON_BIT);
2259         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2260         glutSolidCone(base, height, slices, stacks);
2261         glPopAttrib();
2262 }
2263
2264 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2265 {
2266         draw_cylinder(rad, rad, height, slices, stacks);
2267 }
2268
2269 void glutWireCylinder(float rad, float height, int slices, int stacks)
2270 {
2271         glPushAttrib(GL_POLYGON_BIT);
2272         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2273         glutSolidCylinder(rad, height, slices, stacks);
2274         glPopAttrib();
2275 }
2276
2277 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2278 {
2279         int i, j, k, gray;
2280         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2281         float du = 1.0f / (float)rings;
2282         float dv = 1.0f / (float)sides;
2283
2284         glBegin(GL_QUADS);
2285         for(i=0; i<rings; i++) {
2286                 u = i * du;
2287                 for(j=0; j<sides; j++) {
2288                         v = j * dv;
2289                         for(k=0; k<4; k++) {
2290                                 gray = k ^ (k >> 1);
2291                                 s = gray & 1 ? u + du : u;
2292                                 t = gray & 2 ? v + dv : v;
2293                                 theta = s * PI * 2.0f;
2294                                 phi = t * PI * 2.0f;
2295                                 mglut_sincos(theta, &sintheta, &costheta);
2296                                 mglut_sincos(phi, &sinphi, &cosphi);
2297                                 x = sintheta * sinphi;
2298                                 y = costheta * sinphi;
2299                                 z = cosphi;
2300
2301                                 glColor3f(s, t, 1);
2302                                 glTexCoord2f(s, t);
2303                                 glNormal3f(x, y, z);
2304
2305                                 x = x * inner_rad + sintheta * outer_rad;
2306                                 y = y * inner_rad + costheta * outer_rad;
2307                                 z *= inner_rad;
2308                                 glVertex3f(x, y, z);
2309                         }
2310                 }
2311         }
2312         glEnd();
2313 }
2314
2315 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2316 {
2317         glPushAttrib(GL_POLYGON_BIT);
2318         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2319         glutSolidTorus(inner_rad, outer_rad, sides, rings);
2320         glPopAttrib();
2321 }
2322
2323 #define NUM_TEAPOT_INDICES      (sizeof teapot_index / sizeof *teapot_index)
2324 #define NUM_TEAPOT_VERTS        (sizeof teapot_verts / sizeof *teapot_verts)
2325
2326 #define NUM_TEAPOT_PATCHES      (NUM_TEAPOT_INDICES / 16)
2327
2328 #define PATCH_SUBDIV    7
2329
2330 static float teapot_part_flip[] = {
2331         1, 1, 1, 1,                     /* rim flip */
2332         1, 1, 1, 1,                     /* body1 flip */
2333         1, 1, 1, 1,                     /* body2 flip */
2334         1, 1, 1, 1,                     /* lid patch 1 flip */
2335         1, 1, 1, 1,                     /* lid patch 2 flip */
2336         1, -1,                          /* handle 1 flip */
2337         1, -1,                          /* handle 2 flip */
2338         1, -1,                          /* spout 1 flip */
2339         1, -1,                          /* spout 2 flip */
2340         1, 1, 1, 1                      /* bottom flip */
2341 };
2342
2343 static float teapot_part_rot[] = {
2344         0, 90, 180, 270,        /* rim rotations */
2345         0, 90, 180, 270,        /* body patch 1 rotations */
2346         0, 90, 180, 270,        /* body patch 2 rotations */
2347         0, 90, 180, 270,        /* lid patch 1 rotations */
2348         0, 90, 180, 270,        /* lid patch 2 rotations */
2349         0, 0,                           /* handle 1 rotations */
2350         0, 0,                           /* handle 2 rotations */
2351         0, 0,                           /* spout 1 rotations */
2352         0, 0,                           /* spout 2 rotations */
2353         0, 90, 180, 270         /* bottom rotations */
2354 };
2355
2356
2357 static int teapot_index[] = {
2358         /* rim */
2359         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2360         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2361         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2362         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2363         /* body1 */
2364         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2365         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2366         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2367         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2368         /* body 2 */
2369         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2370         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2371         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2372         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2373         /* lid 1 */
2374         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2375         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2376         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2377         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2378         /* lid 2 */
2379         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2380         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2381         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2382         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2383         /* handle 1 */
2384         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2385         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2386         /* handle 2 */
2387         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2388         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2389         /* spout 1 */
2390         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2391         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2392         /* spout 2 */
2393         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2394         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2395         /* bottom */
2396         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2397         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2398         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2399         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2400 };
2401
2402
2403 static float teapot_verts[][3] = {
2404         {  0.2000,  0.0000, 2.70000 }, {  0.2000, -0.1120, 2.70000 },
2405         {  0.1120, -0.2000, 2.70000 }, {  0.0000, -0.2000, 2.70000 },
2406         {  1.3375,  0.0000, 2.53125 }, {  1.3375, -0.7490, 2.53125 },
2407         {  0.7490, -1.3375, 2.53125 }, {  0.0000, -1.3375, 2.53125 },
2408         {  1.4375,  0.0000, 2.53125 }, {  1.4375, -0.8050, 2.53125 },
2409         {  0.8050, -1.4375, 2.53125 }, {  0.0000, -1.4375, 2.53125 },
2410         {  1.5000,  0.0000, 2.40000 }, {  1.5000, -0.8400, 2.40000 },
2411         {  0.8400, -1.5000, 2.40000 }, {  0.0000, -1.5000, 2.40000 },
2412         {  1.7500,  0.0000, 1.87500 }, {  1.7500, -0.9800, 1.87500 },
2413         {  0.9800, -1.7500, 1.87500 }, {  0.0000, -1.7500, 1.87500 },
2414         {  2.0000,  0.0000, 1.35000 }, {  2.0000, -1.1200, 1.35000 },
2415         {  1.1200, -2.0000, 1.35000 }, {  0.0000, -2.0000, 1.35000 },
2416         {  2.0000,  0.0000, 0.90000 }, {  2.0000, -1.1200, 0.90000 },
2417         {  1.1200, -2.0000, 0.90000 }, {  0.0000, -2.0000, 0.90000 },
2418         { -2.0000,  0.0000, 0.90000 }, {  2.0000,  0.0000, 0.45000 },
2419         {  2.0000, -1.1200, 0.45000 }, {  1.1200, -2.0000, 0.45000 },
2420         {  0.0000, -2.0000, 0.45000 }, {  1.5000,  0.0000, 0.22500 },
2421         {  1.5000, -0.8400, 0.22500 }, {  0.8400, -1.5000, 0.22500 },
2422         {  0.0000, -1.5000, 0.22500 }, {  1.5000,  0.0000, 0.15000 },
2423         {  1.5000, -0.8400, 0.15000 }, {  0.8400, -1.5000, 0.15000 },
2424         {  0.0000, -1.5000, 0.15000 }, { -1.6000,  0.0000, 2.02500 },
2425         { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2426         { -1.5000,  0.0000, 2.25000 }, { -2.3000,  0.0000, 2.02500 },
2427         { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2428         { -2.5000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 2.02500 },
2429         { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2430         { -3.0000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 1.80000 },
2431         { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2432         { -3.0000,  0.0000, 1.80000 }, { -2.7000,  0.0000, 1.57500 },
2433         { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2434         { -3.0000,  0.0000, 1.35000 }, { -2.5000,  0.0000, 1.12500 },
2435         { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2436         { -2.6500,  0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2437         { -1.9000, -0.3000, 0.60000 }, { -1.9000,  0.0000, 0.60000 },
2438         {  1.7000,  0.0000, 1.42500 }, {  1.7000, -0.6600, 1.42500 },
2439         {  1.7000, -0.6600, 0.60000 }, {  1.7000,  0.0000, 0.60000 },
2440         {  2.6000,  0.0000, 1.42500 }, {  2.6000, -0.6600, 1.42500 },
2441         {  3.1000, -0.6600, 0.82500 }, {  3.1000,  0.0000, 0.82500 },
2442         {  2.3000,  0.0000, 2.10000 }, {  2.3000, -0.2500, 2.10000 },
2443         {  2.4000, -0.2500, 2.02500 }, {  2.4000,  0.0000, 2.02500 },
2444         {  2.7000,  0.0000, 2.40000 }, {  2.7000, -0.2500, 2.40000 },
2445         {  3.3000, -0.2500, 2.40000 }, {  3.3000,  0.0000, 2.40000 },
2446         {  2.8000,  0.0000, 2.47500 }, {  2.8000, -0.2500, 2.47500 },
2447         {  3.5250, -0.2500, 2.49375 }, {  3.5250,  0.0000, 2.49375 },
2448         {  2.9000,  0.0000, 2.47500 }, {  2.9000, -0.1500, 2.47500 },
2449         {  3.4500, -0.1500, 2.51250 }, {  3.4500,  0.0000, 2.51250 },
2450         {  2.8000,  0.0000, 2.40000 }, {  2.8000, -0.1500, 2.40000 },
2451         {  3.2000, -0.1500, 2.40000 }, {  3.2000,  0.0000, 2.40000 },
2452         {  0.0000,  0.0000, 3.15000 }, {  0.8000,  0.0000, 3.15000 },
2453         {  0.8000, -0.4500, 3.15000 }, {  0.4500, -0.8000, 3.15000 },
2454         {  0.0000, -0.8000, 3.15000 }, {  0.0000,  0.0000, 2.85000 },
2455         {  1.4000,  0.0000, 2.40000 }, {  1.4000, -0.7840, 2.40000 },
2456         {  0.7840, -1.4000, 2.40000 }, {  0.0000, -1.4000, 2.40000 },
2457         {  0.4000,  0.0000, 2.55000 }, {  0.4000, -0.2240, 2.55000 },
2458         {  0.2240, -0.4000, 2.55000 }, {  0.0000, -0.4000, 2.55000 },
2459         {  1.3000,  0.0000, 2.55000 }, {  1.3000, -0.7280, 2.55000 },
2460         {  0.7280, -1.3000, 2.55000 }, {  0.0000, -1.3000, 2.55000 },
2461         {  1.3000,  0.0000, 2.40000 }, {  1.3000, -0.7280, 2.40000 },
2462         {  0.7280, -1.3000, 2.40000 }, {  0.0000, -1.3000, 2.40000 },
2463         {  0.0000,  0.0000, 0.00000 }, {  1.4250, -0.7980, 0.00000 },
2464         {  1.5000,  0.0000, 0.07500 }, {  1.4250,  0.0000, 0.00000 },
2465         {  0.7980, -1.4250, 0.00000 }, {  0.0000, -1.5000, 0.07500 },
2466         {  0.0000, -1.4250, 0.00000 }, {  1.5000, -0.8400, 0.07500 },
2467         {  0.8400, -1.5000, 0.07500 }
2468 };
2469
2470 static void draw_patch(int *index, int flip, float scale);
2471 static float bernstein(int i, float x);
2472
2473 void glutSolidTeapot(float size)
2474 {
2475         int i;
2476
2477         size /= 2.0;
2478
2479         for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2480                 float flip = teapot_part_flip[i];
2481                 float rot = teapot_part_rot[i];
2482
2483                 glMatrixMode(GL_MODELVIEW);
2484                 glPushMatrix();
2485                 glTranslatef(0, -3.15 * size * 0.5, 0);
2486                 glRotatef(rot, 0, 1, 0);
2487                 glScalef(1, 1, flip);
2488                 glRotatef(-90, 1, 0, 0);
2489
2490                 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2491
2492                 glPopMatrix();
2493         }
2494 }
2495
2496 void glutWireTeapot(float size)
2497 {
2498         glPushAttrib(GL_POLYGON_BIT);
2499         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2500         glutSolidTeapot(size);
2501         glPopAttrib();
2502 }
2503
2504
2505 static void bezier_patch(float *res, float *cp, float u, float v)
2506 {
2507         int i, j;
2508
2509         res[0] = res[1] = res[2] = 0.0f;
2510
2511         for(j=0; j<4; j++) {
2512                 for(i=0; i<4; i++) {
2513                         float bu = bernstein(i, u);
2514                         float bv = bernstein(j, v);
2515
2516                         res[0] += cp[0] * bu * bv;
2517                         res[1] += cp[1] * bu * bv;
2518                         res[2] += cp[2] * bu * bv;
2519
2520                         cp += 3;
2521                 }
2522         }
2523 }
2524
2525 static float rsqrt(float x)
2526 {
2527         float xhalf = x * 0.5f;
2528         int i = *(int*)&x;
2529         i = 0x5f3759df - (i >> 1);
2530         x = *(float*)&i;
2531         x = x * (1.5f - xhalf * x * x);
2532         return x;
2533 }
2534
2535
2536 #define CROSS(res, a, b) \
2537         do { \
2538                 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2539                 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2540                 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2541         } while(0)
2542
2543 #define NORMALIZE(v) \
2544         do { \
2545                 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2546                 (v)[0] *= s; \
2547                 (v)[1] *= s; \
2548                 (v)[2] *= s; \
2549         } while(0)
2550
2551 #define DT      0.001
2552
2553 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2554 {
2555         float tang[3], bitan[3], tmp[3];
2556
2557         bezier_patch(tang, cp, u + DT, v);
2558         bezier_patch(tmp, cp, u - DT, v);
2559         tang[0] -= tmp[0];
2560         tang[1] -= tmp[1];
2561         tang[2] -= tmp[2];
2562
2563         bezier_patch(bitan, cp, u, v + DT);
2564         bezier_patch(tmp, cp, u, v - DT);
2565         bitan[0] -= tmp[0];
2566         bitan[1] -= tmp[1];
2567         bitan[2] -= tmp[2];
2568
2569         CROSS(res, tang, bitan);
2570         NORMALIZE(res);
2571 }
2572
2573
2574
2575 static float bernstein(int i, float x)
2576 {
2577         float invx = 1.0f - x;
2578
2579         switch(i) {
2580         case 0:
2581                 return invx * invx * invx;
2582         case 1:
2583                 return 3.0f * x * invx * invx;
2584         case 2:
2585                 return 3.0f * x * x * invx;
2586         case 3:
2587                 return x * x * x;
2588         default:
2589                 break;
2590         }
2591         return 0.0f;
2592 }
2593
2594 static void draw_patch(int *index, int flip, float scale)
2595 {
2596         static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2597         static const float voffs[4] = {0, 1, 1, 0};
2598
2599         int i, j, k;
2600         float cp[16 * 3];
2601         float pt[3], n[3];
2602         float u, v;
2603         float du = 1.0 / PATCH_SUBDIV;
2604         float dv = 1.0 / PATCH_SUBDIV;
2605
2606         /* collect control points */
2607         for(i=0; i<16; i++) {
2608                 cp[i * 3] = teapot_verts[index[i]][0];
2609                 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2610                 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2611         }
2612
2613         glBegin(GL_QUADS);
2614         glColor3f(1, 1, 1);
2615
2616         u = 0;
2617         for(i=0; i<PATCH_SUBDIV; i++) {
2618                 v = 0;
2619                 for(j=0; j<PATCH_SUBDIV; j++) {
2620
2621                         for(k=0; k<4; k++) {
2622                                 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2623
2624                                 /* top/bottom normal hack */
2625                                 if(pt[2] > 3.14) {
2626                                         n[0] = n[1] = 0.0f;
2627                                         n[2] = 1.0f;
2628                                 } else if(pt[2] < 0.00001) {
2629                                         n[0] = n[1] = 0.0f;
2630                                         n[2] = -1.0f;
2631                                 } else {
2632                                         bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2633                                 }
2634
2635                                 glTexCoord2f(u, v);
2636                                 glNormal3fv(n);
2637                                 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
2638                         }
2639
2640                         v += dv;
2641                 }
2642                 u += du;
2643         }
2644
2645         glEnd();
2646 }