foo
[deeprace] / 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 glutWarpPointer(int x, int y)
836 {
837         XWarpPointer(dpy, None, win, 0, 0, 0, 0, x, y);
838 }
839
840 void glutSetColor(int idx, float r, float g, float b)
841 {
842         XColor color;
843
844         if(idx >= 0 && idx < cmap_size) {
845                 color.pixel = idx;
846                 color.red = (unsigned short)(r * 65535.0f);
847                 color.green = (unsigned short)(g * 65535.0f);
848                 color.blue = (unsigned short)(b * 65535.0f);
849                 color.flags = DoRed | DoGreen | DoBlue;
850                 XStoreColor(dpy, cmap, &color);
851         }
852 }
853
854 float glutGetColor(int idx, int comp)
855 {
856         XColor color;
857
858         if(idx < 0 || idx >= cmap_size) {
859                 return -1.0f;
860         }
861
862         color.pixel = idx;
863         XQueryColor(dpy, cmap, &color);
864         switch(comp) {
865         case GLUT_RED:
866                 return color.red / 65535.0f;
867         case GLUT_GREEN:
868                 return color.green / 65535.0f;
869         case GLUT_BLUE:
870                 return color.blue / 65535.0f;
871         default:
872                 break;
873         }
874         return -1.0f;
875 }
876
877 void glutSetKeyRepeat(int repmode)
878 {
879         if(repmode) {
880                 XAutoRepeatOn(dpy);
881         } else {
882                 XAutoRepeatOff(dpy);
883         }
884 }
885
886 static XVisualInfo *choose_visual(unsigned int mode)
887 {
888         XVisualInfo *vi;
889         int attr[32];
890         int *aptr = attr;
891         int *samples = 0;
892
893         if(mode & GLUT_DOUBLE) {
894                 *aptr++ = GLX_DOUBLEBUFFER;
895         }
896
897         if(mode & GLUT_INDEX) {
898                 *aptr++ = GLX_BUFFER_SIZE;
899                 *aptr++ = 1;
900         } else {
901                 *aptr++ = GLX_RGBA;
902                 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
903                 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
904                 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
905         }
906         if(mode & GLUT_ALPHA) {
907                 *aptr++ = GLX_ALPHA_SIZE;
908                 *aptr++ = 4;
909         }
910         if(mode & GLUT_DEPTH) {
911                 *aptr++ = GLX_DEPTH_SIZE;
912                 *aptr++ = 8;
913         }
914         if(mode & GLUT_STENCIL) {
915                 *aptr++ = GLX_STENCIL_SIZE;
916                 *aptr++ = 1;
917         }
918         if(mode & GLUT_ACCUM) {
919                 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
920                 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
921                 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
922         }
923         if(mode & GLUT_STEREO) {
924                 *aptr++ = GLX_STEREO;
925         }
926         if(mode & GLUT_SRGB) {
927                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
928         }
929         if(mode & GLUT_MULTISAMPLE) {
930                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
931                 *aptr++ = 1;
932                 *aptr++ = GLX_SAMPLES_ARB;
933                 samples = aptr;
934                 *aptr++ = 32;
935         }
936         *aptr++ = None;
937
938         if(!samples) {
939                 return glXChooseVisual(dpy, scr, attr);
940         }
941         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
942                 *samples >>= 1;
943                 if(!*samples) {
944                         aptr[-3] = None;
945                 }
946         }
947         return vi;
948 }
949
950 static void create_window(const char *title)
951 {
952         XSetWindowAttributes xattr = {0};
953         XVisualInfo *vi;
954         unsigned int xattr_mask;
955         unsigned int mode = init_mode;
956
957         if(!(vi = choose_visual(mode))) {
958                 mode &= ~GLUT_SRGB;
959                 if(!(vi = choose_visual(mode))) {
960                         panic("Failed to find compatible visual\n");
961                 }
962         }
963
964         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
965                 XFree(vi);
966                 panic("Failed to create OpenGL context\n");
967         }
968
969         glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
970         glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
971         glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
972         glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
973         glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
974         glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
975         glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
976         glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
977         glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
978         glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
979
980         if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
981                 XFree(vi);
982                 glXDestroyContext(dpy, ctx);
983                 panic("Failed to create colormap\n");
984         }
985         cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
986
987         xattr.background_pixel = BlackPixel(dpy, scr);
988         xattr.colormap = cmap;
989         xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
990         if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
991                         vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
992                 XFree(vi);
993                 glXDestroyContext(dpy, ctx);
994                 XFreeColormap(dpy, cmap);
995                 panic("Failed to create window\n");
996         }
997         XFree(vi);
998
999         XSelectInput(dpy, win, evmask);
1000
1001         spnav_window(win);
1002
1003         glutSetWindowTitle(title);
1004         glutSetIconTitle(title);
1005         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
1006         XMapWindow(dpy, win);
1007
1008         glXMakeCurrent(dpy, win, ctx);
1009 }
1010
1011 static void get_window_pos(int *x, int *y)
1012 {
1013         Window child;
1014         XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1015 }
1016
1017 static void get_window_size(int *w, int *h)
1018 {
1019         XWindowAttributes wattr;
1020         XGetWindowAttributes(dpy, win, &wattr);
1021         *w = wattr.width;
1022         *h = wattr.height;
1023 }
1024
1025 static void get_screen_size(int *scrw, int *scrh)
1026 {
1027         XWindowAttributes wattr;
1028         XGetWindowAttributes(dpy, root, &wattr);
1029         *scrw = wattr.width;
1030         *scrh = wattr.height;
1031 }
1032
1033
1034 /* spaceball */
1035 enum {
1036   CMD_APP_WINDOW = 27695,
1037   CMD_APP_SENS
1038 };
1039
1040 static Window get_daemon_window(Display *dpy);
1041 static int catch_badwin(Display *dpy, XErrorEvent *err);
1042
1043 #define SPNAV_INITIALIZED       (xa_motion_event)
1044
1045 static int spnav_window(Window win)
1046 {
1047         int (*prev_xerr_handler)(Display*, XErrorEvent*);
1048         XEvent xev;
1049         Window daemon_win;
1050
1051         if(!SPNAV_INITIALIZED) {
1052                 return -1;
1053         }
1054
1055         if(!(daemon_win = get_daemon_window(dpy))) {
1056                 return -1;
1057         }
1058
1059         prev_xerr_handler = XSetErrorHandler(catch_badwin);
1060
1061         xev.type = ClientMessage;
1062         xev.xclient.send_event = False;
1063         xev.xclient.display = dpy;
1064         xev.xclient.window = win;
1065         xev.xclient.message_type = xa_command_event;
1066         xev.xclient.format = 16;
1067         xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1068         xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1069         xev.xclient.data.s[2] = CMD_APP_WINDOW;
1070
1071         XSendEvent(dpy, daemon_win, False, 0, &xev);
1072         XSync(dpy, False);
1073
1074         XSetErrorHandler(prev_xerr_handler);
1075         return 0;
1076 }
1077
1078 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1079 {
1080         int evtype = *(int*)arg;
1081
1082         if(xev->type != ClientMessage) {
1083                 return False;
1084         }
1085
1086         if(xev->xclient.message_type == xa_motion_event) {
1087                 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1088         }
1089         if(xev->xclient.message_type == xa_button_press_event ||
1090                         xev->xclient.message_type == xa_button_release_event) {
1091                 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1092         }
1093         return False;
1094 }
1095
1096 static int spnav_remove_events(int type)
1097 {
1098         int rm_count = 0;
1099         XEvent xev;
1100         while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1101                 rm_count++;
1102         }
1103         return rm_count;
1104 }
1105
1106 static int spnav_event(const XEvent *xev, union spnav_event *event)
1107 {
1108         int i;
1109         int xmsg_type;
1110
1111         xmsg_type = xev->xclient.message_type;
1112
1113         if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1114                         xmsg_type != xa_button_release_event) {
1115                 return 0;
1116         }
1117
1118         if(xmsg_type == xa_motion_event) {
1119                 event->type = SPNAV_EVENT_MOTION;
1120                 event->motion.data = &event->motion.x;
1121
1122                 for(i=0; i<6; i++) {
1123                         event->motion.data[i] = xev->xclient.data.s[i + 2];
1124                 }
1125                 event->motion.period = xev->xclient.data.s[8];
1126         } else {
1127                 event->type = SPNAV_EVENT_BUTTON;
1128                 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1129                 event->button.bnum = xev->xclient.data.s[2];
1130         }
1131         return event->type;
1132 }
1133
1134 static int mglut_strcmp(const char *s1, const char *s2)
1135 {
1136         while(*s1 && *s1 == *s2) {
1137                 s1++;
1138                 s2++;
1139         }
1140         return *s1 - *s2;
1141 }
1142
1143 static Window get_daemon_window(Display *dpy)
1144 {
1145         Window win;
1146         XTextProperty wname;
1147         Atom type;
1148         int fmt;
1149         unsigned long nitems, bytes_after;
1150         unsigned char *prop;
1151
1152         XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1153                         &type, &fmt, &nitems, &bytes_after, &prop);
1154         if(!prop) {
1155                 return 0;
1156         }
1157
1158         win = *(Window*)prop;
1159         XFree(prop);
1160
1161         wname.value = 0;
1162         if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1163                 win = 0;
1164         }
1165         XFree(wname.value);
1166
1167         return win;
1168 }
1169
1170 static int catch_badwin(Display *dpy, XErrorEvent *err)
1171 {
1172         return 0;
1173 }
1174
1175
1176
1177 #endif  /* BUILD_X11 */
1178
1179
1180 /* --------------- windows implementation ----------------- */
1181 #ifdef BUILD_WIN32
1182 static int reshape_pending;
1183
1184 static void update_modkeys(void);
1185 static int translate_vkey(int vkey);
1186 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1187
1188 #ifdef MINIGLUT_WINMAIN
1189 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1190 {
1191         int argc = 1;
1192         char *argv[] = { "miniglut.exe", 0 };
1193         return main(argc, argv);
1194 }
1195 #endif
1196
1197 void glutMainLoopEvent(void)
1198 {
1199         MSG msg;
1200
1201         if(!cb_display) {
1202                 panic("display callback not set");
1203         }
1204
1205         if(reshape_pending && cb_reshape) {
1206                 reshape_pending = 0;
1207                 get_window_size(&win_width, &win_height);
1208                 cb_reshape(win_width, win_height);
1209         }
1210
1211         if(!upd_pending && !cb_idle) {
1212                 GetMessage(&msg, 0, 0, 0);
1213                 TranslateMessage(&msg);
1214                 DispatchMessage(&msg);
1215                 if(quit) return;
1216         }
1217         while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1218                 TranslateMessage(&msg);
1219                 DispatchMessage(&msg);
1220                 if(quit) return;
1221         }
1222
1223         if(cb_idle) {
1224                 cb_idle();
1225         }
1226
1227         if(upd_pending && mapped) {
1228                 upd_pending = 0;
1229                 cb_display();
1230         }
1231 }
1232
1233 static void cleanup(void)
1234 {
1235         if(win) {
1236                 wglMakeCurrent(dc, 0);
1237                 wglDeleteContext(ctx);
1238                 UnregisterClass("MiniGLUT", hinst);
1239         }
1240 }
1241
1242 void glutSwapBuffers(void)
1243 {
1244         SwapBuffers(dc);
1245 }
1246
1247 void glutPositionWindow(int x, int y)
1248 {
1249         RECT rect;
1250         unsigned int flags = SWP_SHOWWINDOW;
1251
1252         if(fullscreen) {
1253                 rect.left = prev_win_x;
1254                 rect.top = prev_win_y;
1255                 rect.right = rect.left + prev_win_width;
1256                 rect.bottom = rect.top + prev_win_height;
1257                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1258                 fullscreen = 0;
1259                 flags |= SWP_FRAMECHANGED;
1260         } else {
1261                 GetWindowRect(win, &rect);
1262         }
1263         SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1264 }
1265
1266 void glutReshapeWindow(int xsz, int ysz)
1267 {
1268         RECT rect;
1269         unsigned int flags = SWP_SHOWWINDOW;
1270
1271         if(fullscreen) {
1272                 rect.left = prev_win_x;
1273                 rect.top = prev_win_y;
1274                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1275                 fullscreen = 0;
1276                 flags |= SWP_FRAMECHANGED;
1277         } else {
1278                 GetWindowRect(win, &rect);
1279         }
1280         SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1281 }
1282
1283 void glutFullScreen(void)
1284 {
1285         RECT rect;
1286         int scr_width, scr_height;
1287
1288         if(fullscreen) return;
1289
1290         GetWindowRect(win, &rect);
1291         prev_win_x = rect.left;
1292         prev_win_y = rect.top;
1293         prev_win_width = rect.right - rect.left;
1294         prev_win_height = rect.bottom - rect.top;
1295
1296         get_screen_size(&scr_width, &scr_height);
1297
1298         SetWindowLong(win, GWL_STYLE, 0);
1299         SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1300
1301         fullscreen = 1;
1302 }
1303
1304 void glutSetWindowTitle(const char *title)
1305 {
1306         SetWindowText(win, title);
1307 }
1308
1309 void glutSetIconTitle(const char *title)
1310 {
1311 }
1312
1313 void glutSetCursor(int cidx)
1314 {
1315         switch(cidx) {
1316         case GLUT_CURSOR_NONE:
1317                 ShowCursor(0);
1318                 break;
1319         case GLUT_CURSOR_INHERIT:
1320         case GLUT_CURSOR_LEFT_ARROW:
1321         default:
1322                 SetCursor(LoadCursor(0, IDC_ARROW));
1323                 ShowCursor(1);
1324         }
1325 }
1326
1327 void glutWarpPointer(int x, int y)
1328 {
1329         SetCursorPos(x, y);
1330 }
1331
1332 void glutSetColor(int idx, float r, float g, float b)
1333 {
1334         PALETTEENTRY col;
1335
1336         if(idx < 0 || idx >= 256 || !cmap) {
1337                 return;
1338         }
1339
1340         col.peRed = (int)(r * 255.0f);
1341         col.peGreen = (int)(g * 255.0f);
1342         col.peBlue = (int)(b * 255.0f);
1343         col.peFlags = PC_NOCOLLAPSE;
1344
1345         SetPaletteEntries(cmap, idx, 1, &col);
1346
1347         if(dc) {
1348                 UnrealizeObject(cmap);
1349                 SelectPalette(dc, cmap, 0);
1350                 RealizePalette(dc);
1351         }
1352 }
1353
1354 float glutGetColor(int idx, int comp)
1355 {
1356         PALETTEENTRY col;
1357
1358         if(idx < 0 || idx >= 256 || !cmap) {
1359                 return -1.0f;
1360         }
1361
1362         if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1363                 return -1.0f;
1364         }
1365
1366         switch(comp) {
1367         case GLUT_RED:
1368                 return col.peRed / 255.0f;
1369         case GLUT_GREEN:
1370                 return col.peGreen / 255.0f;
1371         case GLUT_BLUE:
1372                 return col.peBlue / 255.0f;
1373         default:
1374                 break;
1375         }
1376         return -1.0f;
1377 }
1378
1379 void glutSetKeyRepeat(int repmode)
1380 {
1381 }
1382
1383 #define WGL_DRAW_TO_WINDOW      0x2001
1384 #define WGL_ACCELERATION        0x2003
1385 #define WGL_SUPPORT_OPENGL      0x2010
1386 #define WGL_DOUBLE_BUFFER       0x2011
1387 #define WGL_STEREO                      0x2012
1388 #define WGL_PIXEL_TYPE          0x2013
1389 #define WGL_COLOR_BITS          0x2014
1390 #define WGL_RED_BITS            0x2015
1391 #define WGL_GREEN_BITS          0x2017
1392 #define WGL_BLUE_BITS           0x2019
1393 #define WGL_ALPHA_BITS          0x201b
1394 #define WGL_ACCUM_BITS          0x201d
1395 #define WGL_DEPTH_BITS          0x2022
1396 #define WGL_STENCIL_BITS        0x2023
1397 #define WGL_FULL_ACCELERATION   0x2027
1398
1399 #define WGL_TYPE_RGBA           0x202b
1400 #define WGL_TYPE_COLORINDEX     0x202c
1401
1402 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20a9
1403 #define WGL_SAMPLE_BUFFERS_ARB                          0x2041
1404 #define WGL_SAMPLES_ARB                                         0x2042
1405
1406 static PROC wglChoosePixelFormat;
1407 static PROC wglGetPixelFormatAttribiv;
1408
1409 #define ATTR(a, v) \
1410         do { *aptr++ = (a); *aptr++ = (v); } while(0)
1411
1412 static unsigned int choose_pixfmt(unsigned int mode)
1413 {
1414         unsigned int num_pixfmt, pixfmt = 0;
1415         int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1416                 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1417         float fattr[2] = {0, 0};
1418
1419         int *aptr = attr + 6;
1420         int *samples = 0;
1421
1422         if(mode & GLUT_DOUBLE) {
1423                 ATTR(WGL_DOUBLE_BUFFER, 1);
1424         }
1425
1426         ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1427         ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1428         if(mode & GLUT_ALPHA) {
1429                 ATTR(WGL_ALPHA_BITS, 4);
1430         }
1431         if(mode & GLUT_DEPTH) {
1432                 ATTR(WGL_DEPTH_BITS, 16);
1433         }
1434         if(mode & GLUT_STENCIL) {
1435                 ATTR(WGL_STENCIL_BITS, 1);
1436         }
1437         if(mode & GLUT_ACCUM) {
1438                 ATTR(WGL_ACCUM_BITS, 1);
1439         }
1440         if(mode & GLUT_STEREO) {
1441                 ATTR(WGL_STEREO, 1);
1442         }
1443         if(mode & GLUT_SRGB) {
1444                 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1445         }
1446         if(mode & GLUT_MULTISAMPLE) {
1447                 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1448                 *aptr++ = WGL_SAMPLES_ARB;
1449                 samples = aptr;
1450                 *aptr++ = 32;
1451         }
1452         *aptr++ = 0;
1453
1454         while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1455                 *samples >>= 1;
1456                 if(!*samples) {
1457                         aptr[-3] = 0;
1458                 }
1459         }
1460         return pixfmt;
1461 }
1462
1463 static PIXELFORMATDESCRIPTOR pfd;
1464 static PIXELFORMATDESCRIPTOR tmppfd = {
1465         sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1466         PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1467         PFD_MAIN_PLANE, 0, 0, 0, 0
1468 };
1469 #define TMPCLASS        "TempMiniGLUT"
1470
1471 #define GETATTR(attr, vptr) \
1472         do { \
1473                 int gattr = attr; \
1474                 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1475         } while(0)
1476
1477 static int create_window_wglext(const char *title, int width, int height)
1478 {
1479         WNDCLASSEX wc = {0};
1480         HWND tmpwin = 0;
1481         HDC tmpdc = 0;
1482         HGLRC tmpctx = 0;
1483         int pixfmt;
1484
1485         /* create a temporary window and GL context, just to query and retrieve
1486          * the wglChoosePixelFormatEXT function
1487          */
1488         wc.cbSize = sizeof wc;
1489         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1490         wc.hCursor = LoadCursor(0, IDC_ARROW);
1491         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1492         wc.hInstance = hinst;
1493         wc.lpfnWndProc = DefWindowProc;
1494         wc.lpszClassName = TMPCLASS;
1495         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1496         if(!RegisterClassEx(&wc)) {
1497                 return 0;
1498         }
1499         if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1500                                         width, height, 0, 0, hinst, 0))) {
1501                 goto fail;
1502         }
1503         tmpdc = GetDC(tmpwin);
1504
1505         if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1506                         !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1507                         !(tmpctx = wglCreateContext(tmpdc))) {
1508                 goto fail;
1509         }
1510         wglMakeCurrent(tmpdc, tmpctx);
1511
1512         if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1513                 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1514                         goto fail;
1515                 }
1516                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1517                         goto fail;
1518                 }
1519         } else {
1520                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1521                         goto fail;
1522                 }
1523         }
1524         wglMakeCurrent(0, 0);
1525         wglDeleteContext(tmpctx);
1526         DestroyWindow(tmpwin);
1527         UnregisterClass(TMPCLASS, hinst);
1528
1529         /* create the real window and context */
1530         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1531                                         init_y, width, height, 0, 0, hinst, 0))) {
1532                 panic("Failed to create window\n");
1533         }
1534         dc = GetDC(win);
1535
1536         if(!(pixfmt = choose_pixfmt(init_mode))) {
1537                 panic("Failed to find suitable pixel format\n");
1538         }
1539         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1540                 panic("Failed to set the selected pixel format\n");
1541         }
1542         if(!(ctx = wglCreateContext(dc))) {
1543                 panic("Failed to create the OpenGL context\n");
1544         }
1545         wglMakeCurrent(dc, ctx);
1546
1547         GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1548         GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1549         GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1550         GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1551         GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1552         GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1553         GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1554         GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1555         GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1556
1557         return 0;
1558
1559 fail:
1560         if(tmpctx) {
1561                 wglMakeCurrent(0, 0);
1562                 wglDeleteContext(tmpctx);
1563         }
1564         if(tmpwin) {
1565                 DestroyWindow(tmpwin);
1566         }
1567         UnregisterClass(TMPCLASS, hinst);
1568         return -1;
1569 }
1570
1571
1572 static void create_window(const char *title)
1573 {
1574         RECT rect;
1575         int i, pixfmt, width, height;
1576         char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1577         LOGPALETTE *logpal;
1578
1579
1580         rect.left = init_x;
1581         rect.top = init_y;
1582         rect.right = init_x + init_width;
1583         rect.bottom = init_y + init_height;
1584         AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1585         width = rect.right - rect.left;
1586         height = rect.bottom - rect.top;
1587
1588         memset(&pfd, 0, sizeof pfd);
1589         pfd.nSize = sizeof pfd;
1590         pfd.nVersion = 1;
1591         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1592         if(init_mode & GLUT_STEREO) {
1593                 pfd.dwFlags |= PFD_STEREO;
1594         }
1595         if(init_mode & GLUT_INDEX) {
1596                 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1597                 pfd.cColorBits = 8;
1598         } else {
1599                 pfd.iPixelType = PFD_TYPE_RGBA;
1600                 pfd.cColorBits = 24;
1601         }
1602         if(init_mode & GLUT_ALPHA) {
1603                 pfd.cAlphaBits = 8;
1604         }
1605         if(init_mode & GLUT_ACCUM) {
1606                 pfd.cAccumBits = 24;
1607         }
1608         if(init_mode & GLUT_DEPTH) {
1609                 pfd.cDepthBits = 24;
1610         }
1611         if(init_mode & GLUT_STENCIL) {
1612                 pfd.cStencilBits = 8;
1613         }
1614         pfd.iLayerType = PFD_MAIN_PLANE;
1615
1616         if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
1617                 if(create_window_wglext(title, width, height) != -1) {
1618                         goto ctxdone;
1619                 }
1620         }
1621
1622         /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
1623          * failed, just use the old-style ChoosePixelFormat method instead
1624          */
1625         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1626                                 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1627                 panic("Failed to create window\n");
1628         }
1629         dc = GetDC(win);
1630
1631         if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1632                 panic("Failed to find suitable pixel format\n");
1633         }
1634         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1635                 panic("Failed to set the selected pixel format\n");
1636         }
1637         if(!(ctx = wglCreateContext(dc))) {
1638                 panic("Failed to create the OpenGL context\n");
1639         }
1640         wglMakeCurrent(dc, ctx);
1641
1642         DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1643         ctx_info.rsize = pfd.cRedBits;
1644         ctx_info.gsize = pfd.cGreenBits;
1645         ctx_info.bsize = pfd.cBlueBits;
1646         ctx_info.asize = pfd.cAlphaBits;
1647         ctx_info.zsize = pfd.cDepthBits;
1648         ctx_info.ssize = pfd.cStencilBits;
1649         ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1650         ctx_info.samples = 0;
1651         ctx_info.srgb = 0;
1652
1653 ctxdone:
1654         ShowWindow(win, 1);
1655         SetForegroundWindow(win);
1656         SetFocus(win);
1657
1658         if(init_mode & GLUT_INDEX) {
1659                 logpal = (LOGPALETTE*)palbuf;
1660
1661                 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1662
1663                 logpal->palVersion = 0x300;
1664                 logpal->palNumEntries = 256;
1665
1666                 if(!(cmap = CreatePalette(logpal))) {
1667                         panic("Failed to create palette in indexed mode");
1668                 }
1669                 SelectPalette(dc, cmap, 0);
1670                 RealizePalette(dc);
1671
1672                 cmap_size = 256;
1673         } else {
1674                 if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
1675                         /* for RGB mode in 8bpp displays we also need to set up a palette
1676                          * with RGB 332 colors
1677                          */
1678                         logpal = (LOGPALETTE*)palbuf;
1679
1680                         logpal->palVersion = 0x300;
1681                         logpal->palNumEntries = 256;
1682
1683                         for(i=0; i<256; i++) {
1684                                 int r = i & 7;
1685                                 int g = (i >> 3) & 7;
1686                                 int b = (i >> 5) & 3;
1687
1688                                 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1689                                 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1690                                 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1691                                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1692                         }
1693
1694                         if((cmap = CreatePalette(logpal))) {
1695                                 SelectPalette(dc, cmap, 0);
1696                                 RealizePalette(dc);
1697                                 cmap_size = 256;
1698                         }
1699                 }
1700         }
1701
1702         upd_pending = 1;
1703         reshape_pending = 1;
1704 }
1705
1706 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1707 {
1708         static int mouse_x, mouse_y;
1709         int x, y, key;
1710
1711         switch(msg) {
1712         case WM_CLOSE:
1713                 if(win) DestroyWindow(win);
1714                 break;
1715
1716         case WM_DESTROY:
1717                 cleanup();
1718                 quit = 1;
1719                 PostQuitMessage(0);
1720                 break;
1721
1722         case WM_PAINT:
1723                 upd_pending = 1;
1724                 ValidateRect(win, 0);
1725                 break;
1726
1727         case WM_SIZE:
1728                 x = lparam & 0xffff;
1729                 y = lparam >> 16;
1730                 if(x != win_width && y != win_height) {
1731                         win_width = x;
1732                         win_height = y;
1733                         if(cb_reshape) {
1734                                 reshape_pending = 0;
1735                                 cb_reshape(win_width, win_height);
1736                         }
1737                 }
1738                 break;
1739
1740         case WM_SHOWWINDOW:
1741                 mapped = wparam;
1742                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1743                 break;
1744
1745         case WM_KEYDOWN:
1746         case WM_SYSKEYDOWN:
1747                 update_modkeys();
1748                 key = translate_vkey(wparam);
1749                 if(key < 256) {
1750                         if(cb_keydown) {
1751                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1752                         }
1753                 } else {
1754                         if(cb_skeydown) {
1755                                 cb_skeydown(key, mouse_x, mouse_y);
1756                         }
1757                 }
1758                 break;
1759
1760         case WM_KEYUP:
1761         case WM_SYSKEYUP:
1762                 update_modkeys();
1763                 key = translate_vkey(wparam);
1764                 if(key < 256) {
1765                         if(cb_keyup) {
1766                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1767                         }
1768                 } else {
1769                         if(cb_skeyup) {
1770                                 cb_skeyup(key, mouse_x, mouse_y);
1771                         }
1772                 }
1773                 break;
1774
1775         case WM_LBUTTONDOWN:
1776                 handle_mbutton(0, 1, wparam, lparam);
1777                 break;
1778         case WM_MBUTTONDOWN:
1779                 handle_mbutton(1, 1, wparam, lparam);
1780                 break;
1781         case WM_RBUTTONDOWN:
1782                 handle_mbutton(2, 1, wparam, lparam);
1783                 break;
1784         case WM_LBUTTONUP:
1785                 handle_mbutton(0, 0, wparam, lparam);
1786                 break;
1787         case WM_MBUTTONUP:
1788                 handle_mbutton(1, 0, wparam, lparam);
1789                 break;
1790         case WM_RBUTTONUP:
1791                 handle_mbutton(2, 0, wparam, lparam);
1792                 break;
1793
1794         case WM_MOUSEMOVE:
1795                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1796                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1797                 } else {
1798                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1799                 }
1800                 break;
1801
1802         case WM_SYSCOMMAND:
1803                 wparam &= 0xfff0;
1804                 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1805                         return 0;
1806                 }
1807         default:
1808                 return DefWindowProc(win, msg, wparam, lparam);
1809         }
1810
1811         return 0;
1812 }
1813
1814 static void update_modkeys(void)
1815 {
1816         if(GetKeyState(VK_SHIFT) & 0x8000) {
1817                 modstate |= GLUT_ACTIVE_SHIFT;
1818         } else {
1819                 modstate &= ~GLUT_ACTIVE_SHIFT;
1820         }
1821         if(GetKeyState(VK_CONTROL) & 0x8000) {
1822                 modstate |= GLUT_ACTIVE_CTRL;
1823         } else {
1824                 modstate &= ~GLUT_ACTIVE_CTRL;
1825         }
1826         if(GetKeyState(VK_MENU) & 0x8000) {
1827                 modstate |= GLUT_ACTIVE_ALT;
1828         } else {
1829                 modstate &= ~GLUT_ACTIVE_ALT;
1830         }
1831 }
1832
1833 #ifndef VK_OEM_1
1834 #define VK_OEM_1        0xba
1835 #define VK_OEM_2        0xbf
1836 #define VK_OEM_3        0xc0
1837 #define VK_OEM_4        0xdb
1838 #define VK_OEM_5        0xdc
1839 #define VK_OEM_6        0xdd
1840 #define VK_OEM_7        0xde
1841 #endif
1842
1843 static int translate_vkey(int vkey)
1844 {
1845         switch(vkey) {
1846         case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1847         case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1848         case VK_END: return GLUT_KEY_END;
1849         case VK_HOME: return GLUT_KEY_HOME;
1850         case VK_LEFT: return GLUT_KEY_LEFT;
1851         case VK_UP: return GLUT_KEY_UP;
1852         case VK_RIGHT: return GLUT_KEY_RIGHT;
1853         case VK_DOWN: return GLUT_KEY_DOWN;
1854         case VK_OEM_1: return ';';
1855         case VK_OEM_2: return '/';
1856         case VK_OEM_3: return '`';
1857         case VK_OEM_4: return '[';
1858         case VK_OEM_5: return '\\';
1859         case VK_OEM_6: return ']';
1860         case VK_OEM_7: return '\'';
1861         default:
1862                 break;
1863         }
1864
1865         if(vkey >= 'A' && vkey <= 'Z') {
1866                 vkey += 32;
1867         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1868                 vkey -= VK_F1 + GLUT_KEY_F1;
1869         }
1870
1871         return vkey;
1872 }
1873
1874 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1875 {
1876         int x, y;
1877
1878         update_modkeys();
1879
1880         if(cb_mouse) {
1881                 x = lparam & 0xffff;
1882                 y = lparam >> 16;
1883                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1884         }
1885 }
1886
1887 static void get_window_pos(int *x, int *y)
1888 {
1889         RECT rect;
1890         GetWindowRect(win, &rect);
1891         *x = rect.left;
1892         *y = rect.top;
1893 }
1894
1895 static void get_window_size(int *w, int *h)
1896 {
1897         RECT rect;
1898         GetClientRect(win, &rect);
1899         *w = rect.right - rect.left;
1900         *h = rect.bottom - rect.top;
1901 }
1902
1903 static void get_screen_size(int *scrw, int *scrh)
1904 {
1905         *scrw = GetSystemMetrics(SM_CXSCREEN);
1906         *scrh = GetSystemMetrics(SM_CYSCREEN);
1907 }
1908 #endif  /* BUILD_WIN32 */
1909
1910 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1911 #include <sys/time.h>
1912
1913 #ifdef MINIGLUT_USE_LIBC
1914 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1915 #else
1916 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1917 #endif
1918
1919 static long get_msec(void)
1920 {
1921         static struct timeval tv0;
1922         struct timeval tv;
1923
1924         sys_gettimeofday(&tv, 0);
1925         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1926                 tv0 = tv;
1927                 return 0;
1928         }
1929         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1930 }
1931 #endif  /* UNIX */
1932 #ifdef _WIN32
1933 static long get_msec(void)
1934 {
1935         static long t0;
1936         long tm;
1937
1938 #ifdef MINIGLUT_NO_WINMM
1939         tm = GetTickCount();
1940 #else
1941         tm = timeGetTime();
1942 #endif
1943         if(!t0) {
1944                 t0 = tm;
1945                 return 0;
1946         }
1947         return tm - t0;
1948 }
1949 #endif
1950
1951 static void panic(const char *msg)
1952 {
1953         const char *end = msg;
1954         while(*end) end++;
1955         sys_write(2, msg, end - msg);
1956         sys_exit(1);
1957 }
1958
1959
1960 #ifdef MINIGLUT_USE_LIBC
1961 #include <stdlib.h>
1962 #ifdef _WIN32
1963 #include <io.h>
1964 #else
1965 #include <unistd.h>
1966 #endif
1967
1968 static void sys_exit(int status)
1969 {
1970         exit(status);
1971 }
1972
1973 static int sys_write(int fd, const void *buf, int count)
1974 {
1975         return write(fd, buf, count);
1976 }
1977
1978 #else   /* !MINIGLUT_USE_LIBC */
1979
1980 #ifdef __linux__
1981 #ifdef __x86_64__
1982 static void sys_exit(int status)
1983 {
1984         asm volatile(
1985                 "syscall\n\t"
1986                 :: "a"(60), "D"(status));
1987 }
1988 static int sys_write(int fd, const void *buf, int count)
1989 {
1990         long res;
1991         asm volatile(
1992                 "syscall\n\t"
1993                 : "=a"(res)
1994                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1995         return res;
1996 }
1997 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1998 {
1999         int res;
2000         asm volatile(
2001                 "syscall\n\t"
2002                 : "=a"(res)
2003                 : "a"(96), "D"(tv), "S"(tz));
2004         return res;
2005 }
2006 #endif  /* __x86_64__ */
2007 #ifdef __i386__
2008 static void sys_exit(int status)
2009 {
2010         asm volatile(
2011                 "int $0x80\n\t"
2012                 :: "a"(1), "b"(status));
2013 }
2014 static int sys_write(int fd, const void *buf, int count)
2015 {
2016         int res;
2017         asm volatile(
2018                 "int $0x80\n\t"
2019                 : "=a"(res)
2020                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2021         return res;
2022 }
2023 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2024 {
2025         int res;
2026         asm volatile(
2027                 "int $0x80\n\t"
2028                 : "=a"(res)
2029                 : "a"(78), "b"(tv), "c"(tz));
2030         return res;
2031 }
2032 #endif  /* __i386__ */
2033 #endif  /* __linux__ */
2034
2035 #ifdef _WIN32
2036 static void sys_exit(int status)
2037 {
2038         ExitProcess(status);
2039 }
2040 static int sys_write(int fd, const void *buf, int count)
2041 {
2042         unsigned long wrsz = 0;
2043
2044         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2045         if(!WriteFile(out, buf, count, &wrsz, 0)) {
2046                 return -1;
2047         }
2048         return wrsz;
2049 }
2050 #endif  /* _WIN32 */
2051 #endif  /* !MINIGLUT_USE_LIBC */
2052
2053
2054 /* ----------------- primitives ------------------ */
2055 #ifdef MINIGLUT_USE_LIBC
2056 #include <stdlib.h>
2057 #include <math.h>
2058
2059 void mglut_sincos(float angle, float *sptr, float *cptr)
2060 {
2061         *sptr = sin(angle);
2062         *cptr = cos(angle);
2063 }
2064
2065 float mglut_atan(float x)
2066 {
2067         return atan(x);
2068 }
2069
2070 #else   /* !MINIGLUT_USE_LIBC */
2071
2072 #ifdef __GNUC__
2073 void mglut_sincos(float angle, float *sptr, float *cptr)
2074 {
2075         asm volatile(
2076                 "flds %2\n\t"
2077                 "fsincos\n\t"
2078                 "fstps %1\n\t"
2079                 "fstps %0\n\t"
2080                 : "=m"(*sptr), "=m"(*cptr)
2081                 : "m"(angle)
2082         );
2083 }
2084
2085 float mglut_atan(float x)
2086 {
2087         float res;
2088         asm volatile(
2089                 "flds %1\n\t"
2090                 "fld1\n\t"
2091                 "fpatan\n\t"
2092                 "fstps %0\n\t"
2093                 : "=m"(res)
2094                 : "m"(x)
2095         );
2096         return res;
2097 }
2098 #endif
2099
2100 #ifdef _MSC_VER
2101 void mglut_sincos(float angle, float *sptr, float *cptr)
2102 {
2103         float s, c;
2104         __asm {
2105                 fld angle
2106                 fsincos
2107                 fstp c
2108                 fstp s
2109         }
2110         *sptr = s;
2111         *cptr = c;
2112 }
2113
2114 float mglut_atan(float x)
2115 {
2116         float res;
2117         __asm {
2118                 fld x
2119                 fld1
2120                 fpatan
2121                 fstp res
2122         }
2123         return res;
2124 }
2125 #endif
2126
2127 #ifdef __WATCOMC__
2128 #pragma aux mglut_sincos = \
2129         "fsincos" \
2130         "fstp dword ptr [edx]" \
2131         "fstp dword ptr [eax]" \
2132         parm[8087][eax][edx]    \
2133         modify[8087];
2134
2135 #pragma aux mglut_atan = \
2136         "fld1" \
2137         "fpatan" \
2138         parm[8087] \
2139         value[8087] \
2140         modify [8087];
2141 #endif  /* __WATCOMC__ */
2142
2143 #endif  /* !MINIGLUT_USE_LIBC */
2144
2145 #define PI      3.1415926536f
2146
2147 void glutSolidSphere(float rad, int slices, int stacks)
2148 {
2149         int i, j, k, gray;
2150         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2151         float du = 1.0f / (float)slices;
2152         float dv = 1.0f / (float)stacks;
2153
2154         glBegin(GL_QUADS);
2155         for(i=0; i<stacks; i++) {
2156                 v = i * dv;
2157                 for(j=0; j<slices; j++) {
2158                         u = j * du;
2159                         for(k=0; k<4; k++) {
2160                                 gray = k ^ (k >> 1);
2161                                 s = gray & 1 ? u + du : u;
2162                                 t = gray & 2 ? v + dv : v;
2163                                 theta = s * PI * 2.0f;
2164                                 phi = t * PI;
2165                                 mglut_sincos(theta, &sintheta, &costheta);
2166                                 mglut_sincos(phi, &sinphi, &cosphi);
2167                                 x = sintheta * sinphi;
2168                                 y = costheta * sinphi;
2169                                 z = cosphi;
2170
2171                                 glColor3f(s, t, 1);
2172                                 glTexCoord2f(s, t);
2173                                 glNormal3f(x, y, z);
2174                                 glVertex3f(x * rad, y * rad, z * rad);
2175                         }
2176                 }
2177         }
2178         glEnd();
2179 }
2180
2181 void glutWireSphere(float rad, int slices, int stacks)
2182 {
2183         glPushAttrib(GL_POLYGON_BIT);
2184         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2185         glutSolidSphere(rad, slices, stacks);
2186         glPopAttrib();
2187 }
2188
2189 void glutSolidCube(float sz)
2190 {
2191         int i, j, idx, gray, flip, rotx;
2192         float vpos[3], norm[3];
2193         float rad = sz * 0.5f;
2194
2195         glBegin(GL_QUADS);
2196         for(i=0; i<6; i++) {
2197                 flip = i & 1;
2198                 rotx = i >> 2;
2199                 idx = (~i & 2) - rotx;
2200                 norm[0] = norm[1] = norm[2] = 0.0f;
2201                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2202                 glNormal3fv(norm);
2203                 vpos[idx] = norm[idx] * rad;
2204                 for(j=0; j<4; j++) {
2205                         gray = j ^ (j >> 1);
2206                         vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2207                         vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2208                         glTexCoord2f(gray & 1, gray >> 1);
2209                         glVertex3fv(vpos);
2210                 }
2211         }
2212         glEnd();
2213 }
2214
2215 void glutWireCube(float sz)
2216 {
2217         glPushAttrib(GL_POLYGON_BIT);
2218         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2219         glutSolidCube(sz);
2220         glPopAttrib();
2221 }
2222
2223 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2224 {
2225         int i, j, k, gray;
2226         float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2227         float du = 1.0f / (float)slices;
2228         float dv = 1.0f / (float)stacks;
2229
2230         rad = rbot - rtop;
2231         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2232         mglut_sincos(phi, &sinphi, &cosphi);
2233
2234         glBegin(GL_QUADS);
2235         for(i=0; i<stacks; i++) {
2236                 v = i * dv;
2237                 for(j=0; j<slices; j++) {
2238                         u = j * du;
2239                         for(k=0; k<4; k++) {
2240                                 gray = k ^ (k >> 1);
2241                                 s = gray & 2 ? u + du : u;
2242                                 t = gray & 1 ? v + dv : v;
2243                                 rad = rbot + (rtop - rbot) * t;
2244                                 theta = s * PI * 2.0f;
2245                                 mglut_sincos(theta, &sintheta, &costheta);
2246
2247                                 x = sintheta * cosphi;
2248                                 y = costheta * cosphi;
2249                                 z = sinphi;
2250
2251                                 glColor3f(s, t, 1);
2252                                 glTexCoord2f(s, t);
2253                                 glNormal3f(x, y, z);
2254                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
2255                         }
2256                 }
2257         }
2258         glEnd();
2259 }
2260
2261 void glutSolidCone(float base, float height, int slices, int stacks)
2262 {
2263         draw_cylinder(base, 0, height, slices, stacks);
2264 }
2265
2266 void glutWireCone(float base, float height, int slices, int stacks)
2267 {
2268         glPushAttrib(GL_POLYGON_BIT);
2269         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2270         glutSolidCone(base, height, slices, stacks);
2271         glPopAttrib();
2272 }
2273
2274 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2275 {
2276         draw_cylinder(rad, rad, height, slices, stacks);
2277 }
2278
2279 void glutWireCylinder(float rad, float height, int slices, int stacks)
2280 {
2281         glPushAttrib(GL_POLYGON_BIT);
2282         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2283         glutSolidCylinder(rad, height, slices, stacks);
2284         glPopAttrib();
2285 }
2286
2287 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2288 {
2289         int i, j, k, gray;
2290         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2291         float du = 1.0f / (float)rings;
2292         float dv = 1.0f / (float)sides;
2293
2294         glBegin(GL_QUADS);
2295         for(i=0; i<rings; i++) {
2296                 u = i * du;
2297                 for(j=0; j<sides; j++) {
2298                         v = j * dv;
2299                         for(k=0; k<4; k++) {
2300                                 gray = k ^ (k >> 1);
2301                                 s = gray & 1 ? u + du : u;
2302                                 t = gray & 2 ? v + dv : v;
2303                                 theta = s * PI * 2.0f;
2304                                 phi = t * PI * 2.0f;
2305                                 mglut_sincos(theta, &sintheta, &costheta);
2306                                 mglut_sincos(phi, &sinphi, &cosphi);
2307                                 x = sintheta * sinphi;
2308                                 y = costheta * sinphi;
2309                                 z = cosphi;
2310
2311                                 glColor3f(s, t, 1);
2312                                 glTexCoord2f(s, t);
2313                                 glNormal3f(x, y, z);
2314
2315                                 x = x * inner_rad + sintheta * outer_rad;
2316                                 y = y * inner_rad + costheta * outer_rad;
2317                                 z *= inner_rad;
2318                                 glVertex3f(x, y, z);
2319                         }
2320                 }
2321         }
2322         glEnd();
2323 }
2324
2325 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2326 {
2327         glPushAttrib(GL_POLYGON_BIT);
2328         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2329         glutSolidTorus(inner_rad, outer_rad, sides, rings);
2330         glPopAttrib();
2331 }
2332
2333 #define NUM_TEAPOT_INDICES      (sizeof teapot_index / sizeof *teapot_index)
2334 #define NUM_TEAPOT_VERTS        (sizeof teapot_verts / sizeof *teapot_verts)
2335
2336 #define NUM_TEAPOT_PATCHES      (NUM_TEAPOT_INDICES / 16)
2337
2338 #define PATCH_SUBDIV    7
2339
2340 static float teapot_part_flip[] = {
2341         1, 1, 1, 1,                     /* rim flip */
2342         1, 1, 1, 1,                     /* body1 flip */
2343         1, 1, 1, 1,                     /* body2 flip */
2344         1, 1, 1, 1,                     /* lid patch 1 flip */
2345         1, 1, 1, 1,                     /* lid patch 2 flip */
2346         1, -1,                          /* handle 1 flip */
2347         1, -1,                          /* handle 2 flip */
2348         1, -1,                          /* spout 1 flip */
2349         1, -1,                          /* spout 2 flip */
2350         1, 1, 1, 1                      /* bottom flip */
2351 };
2352
2353 static float teapot_part_rot[] = {
2354         0, 90, 180, 270,        /* rim rotations */
2355         0, 90, 180, 270,        /* body patch 1 rotations */
2356         0, 90, 180, 270,        /* body patch 2 rotations */
2357         0, 90, 180, 270,        /* lid patch 1 rotations */
2358         0, 90, 180, 270,        /* lid patch 2 rotations */
2359         0, 0,                           /* handle 1 rotations */
2360         0, 0,                           /* handle 2 rotations */
2361         0, 0,                           /* spout 1 rotations */
2362         0, 0,                           /* spout 2 rotations */
2363         0, 90, 180, 270         /* bottom rotations */
2364 };
2365
2366
2367 static int teapot_index[] = {
2368         /* rim */
2369         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2370         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2371         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2372         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2373         /* body1 */
2374         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2375         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2376         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2377         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2378         /* body 2 */
2379         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2380         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2381         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2382         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2383         /* lid 1 */
2384         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2385         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2386         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2387         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2388         /* lid 2 */
2389         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2390         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2391         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2392         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2393         /* handle 1 */
2394         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2395         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2396         /* handle 2 */
2397         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2398         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2399         /* spout 1 */
2400         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2401         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2402         /* spout 2 */
2403         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2404         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2405         /* bottom */
2406         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2407         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2408         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2409         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2410 };
2411
2412
2413 static float teapot_verts[][3] = {
2414         {  0.2000,  0.0000, 2.70000 }, {  0.2000, -0.1120, 2.70000 },
2415         {  0.1120, -0.2000, 2.70000 }, {  0.0000, -0.2000, 2.70000 },
2416         {  1.3375,  0.0000, 2.53125 }, {  1.3375, -0.7490, 2.53125 },
2417         {  0.7490, -1.3375, 2.53125 }, {  0.0000, -1.3375, 2.53125 },
2418         {  1.4375,  0.0000, 2.53125 }, {  1.4375, -0.8050, 2.53125 },
2419         {  0.8050, -1.4375, 2.53125 }, {  0.0000, -1.4375, 2.53125 },
2420         {  1.5000,  0.0000, 2.40000 }, {  1.5000, -0.8400, 2.40000 },
2421         {  0.8400, -1.5000, 2.40000 }, {  0.0000, -1.5000, 2.40000 },
2422         {  1.7500,  0.0000, 1.87500 }, {  1.7500, -0.9800, 1.87500 },
2423         {  0.9800, -1.7500, 1.87500 }, {  0.0000, -1.7500, 1.87500 },
2424         {  2.0000,  0.0000, 1.35000 }, {  2.0000, -1.1200, 1.35000 },
2425         {  1.1200, -2.0000, 1.35000 }, {  0.0000, -2.0000, 1.35000 },
2426         {  2.0000,  0.0000, 0.90000 }, {  2.0000, -1.1200, 0.90000 },
2427         {  1.1200, -2.0000, 0.90000 }, {  0.0000, -2.0000, 0.90000 },
2428         { -2.0000,  0.0000, 0.90000 }, {  2.0000,  0.0000, 0.45000 },
2429         {  2.0000, -1.1200, 0.45000 }, {  1.1200, -2.0000, 0.45000 },
2430         {  0.0000, -2.0000, 0.45000 }, {  1.5000,  0.0000, 0.22500 },
2431         {  1.5000, -0.8400, 0.22500 }, {  0.8400, -1.5000, 0.22500 },
2432         {  0.0000, -1.5000, 0.22500 }, {  1.5000,  0.0000, 0.15000 },
2433         {  1.5000, -0.8400, 0.15000 }, {  0.8400, -1.5000, 0.15000 },
2434         {  0.0000, -1.5000, 0.15000 }, { -1.6000,  0.0000, 2.02500 },
2435         { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2436         { -1.5000,  0.0000, 2.25000 }, { -2.3000,  0.0000, 2.02500 },
2437         { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2438         { -2.5000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 2.02500 },
2439         { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2440         { -3.0000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 1.80000 },
2441         { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2442         { -3.0000,  0.0000, 1.80000 }, { -2.7000,  0.0000, 1.57500 },
2443         { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2444         { -3.0000,  0.0000, 1.35000 }, { -2.5000,  0.0000, 1.12500 },
2445         { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2446         { -2.6500,  0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2447         { -1.9000, -0.3000, 0.60000 }, { -1.9000,  0.0000, 0.60000 },
2448         {  1.7000,  0.0000, 1.42500 }, {  1.7000, -0.6600, 1.42500 },
2449         {  1.7000, -0.6600, 0.60000 }, {  1.7000,  0.0000, 0.60000 },
2450         {  2.6000,  0.0000, 1.42500 }, {  2.6000, -0.6600, 1.42500 },
2451         {  3.1000, -0.6600, 0.82500 }, {  3.1000,  0.0000, 0.82500 },
2452         {  2.3000,  0.0000, 2.10000 }, {  2.3000, -0.2500, 2.10000 },
2453         {  2.4000, -0.2500, 2.02500 }, {  2.4000,  0.0000, 2.02500 },
2454         {  2.7000,  0.0000, 2.40000 }, {  2.7000, -0.2500, 2.40000 },
2455         {  3.3000, -0.2500, 2.40000 }, {  3.3000,  0.0000, 2.40000 },
2456         {  2.8000,  0.0000, 2.47500 }, {  2.8000, -0.2500, 2.47500 },
2457         {  3.5250, -0.2500, 2.49375 }, {  3.5250,  0.0000, 2.49375 },
2458         {  2.9000,  0.0000, 2.47500 }, {  2.9000, -0.1500, 2.47500 },
2459         {  3.4500, -0.1500, 2.51250 }, {  3.4500,  0.0000, 2.51250 },
2460         {  2.8000,  0.0000, 2.40000 }, {  2.8000, -0.1500, 2.40000 },
2461         {  3.2000, -0.1500, 2.40000 }, {  3.2000,  0.0000, 2.40000 },
2462         {  0.0000,  0.0000, 3.15000 }, {  0.8000,  0.0000, 3.15000 },
2463         {  0.8000, -0.4500, 3.15000 }, {  0.4500, -0.8000, 3.15000 },
2464         {  0.0000, -0.8000, 3.15000 }, {  0.0000,  0.0000, 2.85000 },
2465         {  1.4000,  0.0000, 2.40000 }, {  1.4000, -0.7840, 2.40000 },
2466         {  0.7840, -1.4000, 2.40000 }, {  0.0000, -1.4000, 2.40000 },
2467         {  0.4000,  0.0000, 2.55000 }, {  0.4000, -0.2240, 2.55000 },
2468         {  0.2240, -0.4000, 2.55000 }, {  0.0000, -0.4000, 2.55000 },
2469         {  1.3000,  0.0000, 2.55000 }, {  1.3000, -0.7280, 2.55000 },
2470         {  0.7280, -1.3000, 2.55000 }, {  0.0000, -1.3000, 2.55000 },
2471         {  1.3000,  0.0000, 2.40000 }, {  1.3000, -0.7280, 2.40000 },
2472         {  0.7280, -1.3000, 2.40000 }, {  0.0000, -1.3000, 2.40000 },
2473         {  0.0000,  0.0000, 0.00000 }, {  1.4250, -0.7980, 0.00000 },
2474         {  1.5000,  0.0000, 0.07500 }, {  1.4250,  0.0000, 0.00000 },
2475         {  0.7980, -1.4250, 0.00000 }, {  0.0000, -1.5000, 0.07500 },
2476         {  0.0000, -1.4250, 0.00000 }, {  1.5000, -0.8400, 0.07500 },
2477         {  0.8400, -1.5000, 0.07500 }
2478 };
2479
2480 static void draw_patch(int *index, int flip, float scale);
2481 static float bernstein(int i, float x);
2482
2483 void glutSolidTeapot(float size)
2484 {
2485         int i;
2486
2487         size /= 2.0;
2488
2489         for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2490                 float flip = teapot_part_flip[i];
2491                 float rot = teapot_part_rot[i];
2492
2493                 glMatrixMode(GL_MODELVIEW);
2494                 glPushMatrix();
2495                 glTranslatef(0, -3.15 * size * 0.5, 0);
2496                 glRotatef(rot, 0, 1, 0);
2497                 glScalef(1, 1, flip);
2498                 glRotatef(-90, 1, 0, 0);
2499
2500                 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2501
2502                 glPopMatrix();
2503         }
2504 }
2505
2506 void glutWireTeapot(float size)
2507 {
2508         glPushAttrib(GL_POLYGON_BIT);
2509         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2510         glutSolidTeapot(size);
2511         glPopAttrib();
2512 }
2513
2514
2515 static void bezier_patch(float *res, float *cp, float u, float v)
2516 {
2517         int i, j;
2518
2519         res[0] = res[1] = res[2] = 0.0f;
2520
2521         for(j=0; j<4; j++) {
2522                 for(i=0; i<4; i++) {
2523                         float bu = bernstein(i, u);
2524                         float bv = bernstein(j, v);
2525
2526                         res[0] += cp[0] * bu * bv;
2527                         res[1] += cp[1] * bu * bv;
2528                         res[2] += cp[2] * bu * bv;
2529
2530                         cp += 3;
2531                 }
2532         }
2533 }
2534
2535 static float rsqrt(float x)
2536 {
2537         float xhalf = x * 0.5f;
2538         int i = *(int*)&x;
2539         i = 0x5f3759df - (i >> 1);
2540         x = *(float*)&i;
2541         x = x * (1.5f - xhalf * x * x);
2542         return x;
2543 }
2544
2545
2546 #define CROSS(res, a, b) \
2547         do { \
2548                 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2549                 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2550                 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2551         } while(0)
2552
2553 #define NORMALIZE(v) \
2554         do { \
2555                 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2556                 (v)[0] *= s; \
2557                 (v)[1] *= s; \
2558                 (v)[2] *= s; \
2559         } while(0)
2560
2561 #define DT      0.001
2562
2563 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2564 {
2565         float tang[3], bitan[3], tmp[3];
2566
2567         bezier_patch(tang, cp, u + DT, v);
2568         bezier_patch(tmp, cp, u - DT, v);
2569         tang[0] -= tmp[0];
2570         tang[1] -= tmp[1];
2571         tang[2] -= tmp[2];
2572
2573         bezier_patch(bitan, cp, u, v + DT);
2574         bezier_patch(tmp, cp, u, v - DT);
2575         bitan[0] -= tmp[0];
2576         bitan[1] -= tmp[1];
2577         bitan[2] -= tmp[2];
2578
2579         CROSS(res, tang, bitan);
2580         NORMALIZE(res);
2581 }
2582
2583
2584
2585 static float bernstein(int i, float x)
2586 {
2587         float invx = 1.0f - x;
2588
2589         switch(i) {
2590         case 0:
2591                 return invx * invx * invx;
2592         case 1:
2593                 return 3.0f * x * invx * invx;
2594         case 2:
2595                 return 3.0f * x * x * invx;
2596         case 3:
2597                 return x * x * x;
2598         default:
2599                 break;
2600         }
2601         return 0.0f;
2602 }
2603
2604 static void draw_patch(int *index, int flip, float scale)
2605 {
2606         static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2607         static const float voffs[4] = {0, 1, 1, 0};
2608
2609         int i, j, k;
2610         float cp[16 * 3];
2611         float pt[3], n[3];
2612         float u, v;
2613         float du = 1.0 / PATCH_SUBDIV;
2614         float dv = 1.0 / PATCH_SUBDIV;
2615
2616         /* collect control points */
2617         for(i=0; i<16; i++) {
2618                 cp[i * 3] = teapot_verts[index[i]][0];
2619                 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2620                 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2621         }
2622
2623         glBegin(GL_QUADS);
2624         glColor3f(1, 1, 1);
2625
2626         u = 0;
2627         for(i=0; i<PATCH_SUBDIV; i++) {
2628                 v = 0;
2629                 for(j=0; j<PATCH_SUBDIV; j++) {
2630
2631                         for(k=0; k<4; k++) {
2632                                 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2633
2634                                 /* top/bottom normal hack */
2635                                 if(pt[2] > 3.14) {
2636                                         n[0] = n[1] = 0.0f;
2637                                         n[2] = 1.0f;
2638                                 } else if(pt[2] < 0.00001) {
2639                                         n[0] = n[1] = 0.0f;
2640                                         n[2] = -1.0f;
2641                                 } else {
2642                                         bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2643                                 }
2644
2645                                 glTexCoord2f(u, v);
2646                                 glNormal3fv(n);
2647                                 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
2648                         }
2649
2650                         v += dv;
2651                 }
2652                 u += du;
2653         }
2654
2655         glEnd();
2656 }