added scr_lvled, a bunch of libraries, and improved framework code
[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 '\r';
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 static void calc_win_rect(RECT *rect, int x, int y, int w, int h)
1267 {
1268         rect->left = x;
1269         rect->top = y;
1270         rect->right = x + w;
1271         rect->bottom = y + h;
1272         AdjustWindowRect(rect, WS_OVERLAPPEDWINDOW, 0);
1273 }
1274
1275 void glutReshapeWindow(int xsz, int ysz)
1276 {
1277         RECT rect;
1278         unsigned int flags = SWP_SHOWWINDOW;
1279
1280         if(fullscreen) {
1281                 calc_win_rect(&rect, prev_win_x, prev_win_y, xsz, ysz);
1282                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1283                 fullscreen = 0;
1284                 flags |= SWP_FRAMECHANGED;
1285         } else {
1286                 GetWindowRect(win, &rect);
1287                 calc_win_rect(&rect, rect.left, rect.top, xsz, ysz);
1288         }
1289
1290         xsz = rect.right - rect.left;
1291         ysz = rect.bottom - rect.top;
1292         SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1293 }
1294
1295 void glutFullScreen(void)
1296 {
1297         RECT rect;
1298         int scr_width, scr_height;
1299
1300         if(fullscreen) return;
1301
1302         GetWindowRect(win, &rect);
1303         prev_win_x = rect.left;
1304         prev_win_y = rect.top;
1305         prev_win_width = rect.right - rect.left;
1306         prev_win_height = rect.bottom - rect.top;
1307
1308         get_screen_size(&scr_width, &scr_height);
1309
1310         SetWindowLong(win, GWL_STYLE, 0);
1311         SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1312
1313         fullscreen = 1;
1314 }
1315
1316 void glutSetWindowTitle(const char *title)
1317 {
1318         SetWindowText(win, title);
1319 }
1320
1321 void glutSetIconTitle(const char *title)
1322 {
1323 }
1324
1325 void glutSetCursor(int cidx)
1326 {
1327         switch(cidx) {
1328         case GLUT_CURSOR_NONE:
1329                 ShowCursor(0);
1330                 break;
1331         case GLUT_CURSOR_INHERIT:
1332         case GLUT_CURSOR_LEFT_ARROW:
1333         default:
1334                 SetCursor(LoadCursor(0, IDC_ARROW));
1335                 ShowCursor(1);
1336         }
1337 }
1338
1339 void glutWarpPointer(int x, int y)
1340 {
1341         POINT pt;
1342         pt.x = x;
1343         pt.y = y;
1344
1345         ClientToScreen(win, &pt);
1346         SetCursorPos(pt.x, pt.y);
1347 }
1348
1349 void glutSetColor(int idx, float r, float g, float b)
1350 {
1351         PALETTEENTRY col;
1352
1353         if(idx < 0 || idx >= 256 || !cmap) {
1354                 return;
1355         }
1356
1357         col.peRed = (int)(r * 255.0f);
1358         col.peGreen = (int)(g * 255.0f);
1359         col.peBlue = (int)(b * 255.0f);
1360         col.peFlags = PC_NOCOLLAPSE;
1361
1362         SetPaletteEntries(cmap, idx, 1, &col);
1363
1364         if(dc) {
1365                 UnrealizeObject(cmap);
1366                 SelectPalette(dc, cmap, 0);
1367                 RealizePalette(dc);
1368         }
1369 }
1370
1371 float glutGetColor(int idx, int comp)
1372 {
1373         PALETTEENTRY col;
1374
1375         if(idx < 0 || idx >= 256 || !cmap) {
1376                 return -1.0f;
1377         }
1378
1379         if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1380                 return -1.0f;
1381         }
1382
1383         switch(comp) {
1384         case GLUT_RED:
1385                 return col.peRed / 255.0f;
1386         case GLUT_GREEN:
1387                 return col.peGreen / 255.0f;
1388         case GLUT_BLUE:
1389                 return col.peBlue / 255.0f;
1390         default:
1391                 break;
1392         }
1393         return -1.0f;
1394 }
1395
1396 void glutSetKeyRepeat(int repmode)
1397 {
1398 }
1399
1400 #define WGL_DRAW_TO_WINDOW      0x2001
1401 #define WGL_ACCELERATION        0x2003
1402 #define WGL_SUPPORT_OPENGL      0x2010
1403 #define WGL_DOUBLE_BUFFER       0x2011
1404 #define WGL_STEREO                      0x2012
1405 #define WGL_PIXEL_TYPE          0x2013
1406 #define WGL_COLOR_BITS          0x2014
1407 #define WGL_RED_BITS            0x2015
1408 #define WGL_GREEN_BITS          0x2017
1409 #define WGL_BLUE_BITS           0x2019
1410 #define WGL_ALPHA_BITS          0x201b
1411 #define WGL_ACCUM_BITS          0x201d
1412 #define WGL_DEPTH_BITS          0x2022
1413 #define WGL_STENCIL_BITS        0x2023
1414 #define WGL_FULL_ACCELERATION   0x2027
1415
1416 #define WGL_TYPE_RGBA           0x202b
1417 #define WGL_TYPE_COLORINDEX     0x202c
1418
1419 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20a9
1420 #define WGL_SAMPLE_BUFFERS_ARB                          0x2041
1421 #define WGL_SAMPLES_ARB                                         0x2042
1422
1423 static PROC wglChoosePixelFormat;
1424 static PROC wglGetPixelFormatAttribiv;
1425
1426 #define ATTR(a, v) \
1427         do { *aptr++ = (a); *aptr++ = (v); } while(0)
1428
1429 static unsigned int choose_pixfmt(unsigned int mode)
1430 {
1431         unsigned int num_pixfmt, pixfmt = 0;
1432         int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1433                 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1434         float fattr[2] = {0, 0};
1435
1436         int *aptr = attr + 6;
1437         int *samples = 0;
1438
1439         if(mode & GLUT_DOUBLE) {
1440                 ATTR(WGL_DOUBLE_BUFFER, 1);
1441         }
1442
1443         ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1444         ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1445         if(mode & GLUT_ALPHA) {
1446                 ATTR(WGL_ALPHA_BITS, 4);
1447         }
1448         if(mode & GLUT_DEPTH) {
1449                 ATTR(WGL_DEPTH_BITS, 16);
1450         }
1451         if(mode & GLUT_STENCIL) {
1452                 ATTR(WGL_STENCIL_BITS, 1);
1453         }
1454         if(mode & GLUT_ACCUM) {
1455                 ATTR(WGL_ACCUM_BITS, 1);
1456         }
1457         if(mode & GLUT_STEREO) {
1458                 ATTR(WGL_STEREO, 1);
1459         }
1460         if(mode & GLUT_SRGB) {
1461                 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1462         }
1463         if(mode & GLUT_MULTISAMPLE) {
1464                 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1465                 *aptr++ = WGL_SAMPLES_ARB;
1466                 samples = aptr;
1467                 *aptr++ = 32;
1468         }
1469         *aptr++ = 0;
1470
1471         while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1472                 *samples >>= 1;
1473                 if(!*samples) {
1474                         aptr[-3] = 0;
1475                 }
1476         }
1477         return pixfmt;
1478 }
1479
1480 static PIXELFORMATDESCRIPTOR pfd;
1481 static PIXELFORMATDESCRIPTOR tmppfd = {
1482         sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1483         PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1484         PFD_MAIN_PLANE, 0, 0, 0, 0
1485 };
1486 #define TMPCLASS        "TempMiniGLUT"
1487
1488 #define GETATTR(attr, vptr) \
1489         do { \
1490                 int gattr = attr; \
1491                 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1492         } while(0)
1493
1494 static int create_window_wglext(const char *title, int width, int height)
1495 {
1496         WNDCLASSEX wc = {0};
1497         HWND tmpwin = 0;
1498         HDC tmpdc = 0;
1499         HGLRC tmpctx = 0;
1500         int pixfmt;
1501
1502         /* create a temporary window and GL context, just to query and retrieve
1503          * the wglChoosePixelFormatEXT function
1504          */
1505         wc.cbSize = sizeof wc;
1506         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1507         wc.hCursor = LoadCursor(0, IDC_ARROW);
1508         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1509         wc.hInstance = hinst;
1510         wc.lpfnWndProc = DefWindowProc;
1511         wc.lpszClassName = TMPCLASS;
1512         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1513         if(!RegisterClassEx(&wc)) {
1514                 return 0;
1515         }
1516         if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1517                                         width, height, 0, 0, hinst, 0))) {
1518                 goto fail;
1519         }
1520         tmpdc = GetDC(tmpwin);
1521
1522         if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1523                         !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1524                         !(tmpctx = wglCreateContext(tmpdc))) {
1525                 goto fail;
1526         }
1527         wglMakeCurrent(tmpdc, tmpctx);
1528
1529         if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1530                 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1531                         goto fail;
1532                 }
1533                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1534                         goto fail;
1535                 }
1536         } else {
1537                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1538                         goto fail;
1539                 }
1540         }
1541         wglMakeCurrent(0, 0);
1542         wglDeleteContext(tmpctx);
1543         DestroyWindow(tmpwin);
1544         UnregisterClass(TMPCLASS, hinst);
1545
1546         /* create the real window and context */
1547         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1548                                         init_y, width, height, 0, 0, hinst, 0))) {
1549                 panic("Failed to create window\n");
1550         }
1551         dc = GetDC(win);
1552
1553         if(!(pixfmt = choose_pixfmt(init_mode))) {
1554                 panic("Failed to find suitable pixel format\n");
1555         }
1556         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1557                 panic("Failed to set the selected pixel format\n");
1558         }
1559         if(!(ctx = wglCreateContext(dc))) {
1560                 panic("Failed to create the OpenGL context\n");
1561         }
1562         wglMakeCurrent(dc, ctx);
1563
1564         GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1565         GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1566         GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1567         GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1568         GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1569         GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1570         GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1571         GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1572         GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1573
1574         return 0;
1575
1576 fail:
1577         if(tmpctx) {
1578                 wglMakeCurrent(0, 0);
1579                 wglDeleteContext(tmpctx);
1580         }
1581         if(tmpwin) {
1582                 DestroyWindow(tmpwin);
1583         }
1584         UnregisterClass(TMPCLASS, hinst);
1585         return -1;
1586 }
1587
1588 static void create_window(const char *title)
1589 {
1590         RECT rect;
1591         int i, pixfmt, width, height;
1592         char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1593         LOGPALETTE *logpal;
1594
1595         calc_win_rect(&rect, init_x, init_y, init_width, init_height);
1596         width = rect.right - rect.left;
1597         height = rect.bottom - rect.top;
1598
1599         memset(&pfd, 0, sizeof pfd);
1600         pfd.nSize = sizeof pfd;
1601         pfd.nVersion = 1;
1602         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1603         if(init_mode & GLUT_STEREO) {
1604                 pfd.dwFlags |= PFD_STEREO;
1605         }
1606         if(init_mode & GLUT_INDEX) {
1607                 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1608                 pfd.cColorBits = 8;
1609         } else {
1610                 pfd.iPixelType = PFD_TYPE_RGBA;
1611                 pfd.cColorBits = 24;
1612         }
1613         if(init_mode & GLUT_ALPHA) {
1614                 pfd.cAlphaBits = 8;
1615         }
1616         if(init_mode & GLUT_ACCUM) {
1617                 pfd.cAccumBits = 24;
1618         }
1619         if(init_mode & GLUT_DEPTH) {
1620                 pfd.cDepthBits = 24;
1621         }
1622         if(init_mode & GLUT_STENCIL) {
1623                 pfd.cStencilBits = 8;
1624         }
1625         pfd.iLayerType = PFD_MAIN_PLANE;
1626
1627         if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
1628                 if(create_window_wglext(title, width, height) != -1) {
1629                         goto ctxdone;
1630                 }
1631         }
1632
1633         /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
1634          * failed, just use the old-style ChoosePixelFormat method instead
1635          */
1636         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1637                                 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1638                 panic("Failed to create window\n");
1639         }
1640         dc = GetDC(win);
1641
1642         if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1643                 panic("Failed to find suitable pixel format\n");
1644         }
1645         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1646                 panic("Failed to set the selected pixel format\n");
1647         }
1648         if(!(ctx = wglCreateContext(dc))) {
1649                 panic("Failed to create the OpenGL context\n");
1650         }
1651         wglMakeCurrent(dc, ctx);
1652
1653         DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1654         ctx_info.rsize = pfd.cRedBits;
1655         ctx_info.gsize = pfd.cGreenBits;
1656         ctx_info.bsize = pfd.cBlueBits;
1657         ctx_info.asize = pfd.cAlphaBits;
1658         ctx_info.zsize = pfd.cDepthBits;
1659         ctx_info.ssize = pfd.cStencilBits;
1660         ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1661         ctx_info.samples = 0;
1662         ctx_info.srgb = 0;
1663
1664 ctxdone:
1665         ShowWindow(win, 1);
1666         SetForegroundWindow(win);
1667         SetFocus(win);
1668
1669         if(init_mode & GLUT_INDEX) {
1670                 logpal = (LOGPALETTE*)palbuf;
1671
1672                 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1673
1674                 logpal->palVersion = 0x300;
1675                 logpal->palNumEntries = 256;
1676
1677                 if(!(cmap = CreatePalette(logpal))) {
1678                         panic("Failed to create palette in indexed mode");
1679                 }
1680                 SelectPalette(dc, cmap, 0);
1681                 RealizePalette(dc);
1682
1683                 cmap_size = 256;
1684         } else {
1685                 if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
1686                         /* for RGB mode in 8bpp displays we also need to set up a palette
1687                          * with RGB 332 colors
1688                          */
1689                         logpal = (LOGPALETTE*)palbuf;
1690
1691                         logpal->palVersion = 0x300;
1692                         logpal->palNumEntries = 256;
1693
1694                         for(i=0; i<256; i++) {
1695                                 int r = i & 7;
1696                                 int g = (i >> 3) & 7;
1697                                 int b = (i >> 5) & 3;
1698
1699                                 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1700                                 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1701                                 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1702                                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1703                         }
1704
1705                         if((cmap = CreatePalette(logpal))) {
1706                                 SelectPalette(dc, cmap, 0);
1707                                 RealizePalette(dc);
1708                                 cmap_size = 256;
1709                         }
1710                 }
1711         }
1712
1713         upd_pending = 1;
1714         reshape_pending = 1;
1715 }
1716
1717 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1718 {
1719         static int mouse_x, mouse_y;
1720         int x, y, key;
1721
1722         switch(msg) {
1723         case WM_CLOSE:
1724                 if(win) DestroyWindow(win);
1725                 break;
1726
1727         case WM_DESTROY:
1728                 cleanup();
1729                 quit = 1;
1730                 PostQuitMessage(0);
1731                 break;
1732
1733         case WM_PAINT:
1734                 upd_pending = 1;
1735                 ValidateRect(win, 0);
1736                 break;
1737
1738         case WM_SIZE:
1739                 x = lparam & 0xffff;
1740                 y = lparam >> 16;
1741                 if(x != win_width && y != win_height) {
1742                         win_width = x;
1743                         win_height = y;
1744                         if(cb_reshape) {
1745                                 reshape_pending = 0;
1746                                 cb_reshape(win_width, win_height);
1747                         }
1748                 }
1749                 break;
1750
1751         case WM_SHOWWINDOW:
1752                 mapped = wparam;
1753                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1754                 break;
1755
1756         case WM_KEYDOWN:
1757         case WM_SYSKEYDOWN:
1758                 update_modkeys();
1759                 key = translate_vkey(wparam);
1760                 if(key < 256) {
1761                         if(cb_keydown) {
1762                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1763                         }
1764                 } else {
1765                         if(cb_skeydown) {
1766                                 cb_skeydown(key, mouse_x, mouse_y);
1767                         }
1768                 }
1769                 break;
1770
1771         case WM_KEYUP:
1772         case WM_SYSKEYUP:
1773                 update_modkeys();
1774                 key = translate_vkey(wparam);
1775                 if(key < 256) {
1776                         if(cb_keyup) {
1777                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1778                         }
1779                 } else {
1780                         if(cb_skeyup) {
1781                                 cb_skeyup(key, mouse_x, mouse_y);
1782                         }
1783                 }
1784                 break;
1785
1786         case WM_LBUTTONDOWN:
1787                 handle_mbutton(0, 1, wparam, lparam);
1788                 break;
1789         case WM_MBUTTONDOWN:
1790                 handle_mbutton(1, 1, wparam, lparam);
1791                 break;
1792         case WM_RBUTTONDOWN:
1793                 handle_mbutton(2, 1, wparam, lparam);
1794                 break;
1795         case WM_LBUTTONUP:
1796                 handle_mbutton(0, 0, wparam, lparam);
1797                 break;
1798         case WM_MBUTTONUP:
1799                 handle_mbutton(1, 0, wparam, lparam);
1800                 break;
1801         case WM_RBUTTONUP:
1802                 handle_mbutton(2, 0, wparam, lparam);
1803                 break;
1804
1805         case WM_MOUSEMOVE:
1806                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1807                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1808                 } else {
1809                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1810                 }
1811                 break;
1812
1813         case WM_SYSCOMMAND:
1814                 wparam &= 0xfff0;
1815                 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1816                         return 0;
1817                 }
1818         default:
1819                 return DefWindowProc(win, msg, wparam, lparam);
1820         }
1821
1822         return 0;
1823 }
1824
1825 static void update_modkeys(void)
1826 {
1827         if(GetKeyState(VK_SHIFT) & 0x8000) {
1828                 modstate |= GLUT_ACTIVE_SHIFT;
1829         } else {
1830                 modstate &= ~GLUT_ACTIVE_SHIFT;
1831         }
1832         if(GetKeyState(VK_CONTROL) & 0x8000) {
1833                 modstate |= GLUT_ACTIVE_CTRL;
1834         } else {
1835                 modstate &= ~GLUT_ACTIVE_CTRL;
1836         }
1837         if(GetKeyState(VK_MENU) & 0x8000) {
1838                 modstate |= GLUT_ACTIVE_ALT;
1839         } else {
1840                 modstate &= ~GLUT_ACTIVE_ALT;
1841         }
1842 }
1843
1844 #ifndef VK_OEM_1
1845 #define VK_OEM_1        0xba
1846 #define VK_OEM_2        0xbf
1847 #define VK_OEM_3        0xc0
1848 #define VK_OEM_4        0xdb
1849 #define VK_OEM_5        0xdc
1850 #define VK_OEM_6        0xdd
1851 #define VK_OEM_7        0xde
1852 #endif
1853
1854 static int translate_vkey(int vkey)
1855 {
1856         switch(vkey) {
1857         case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1858         case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1859         case VK_END: return GLUT_KEY_END;
1860         case VK_HOME: return GLUT_KEY_HOME;
1861         case VK_LEFT: return GLUT_KEY_LEFT;
1862         case VK_UP: return GLUT_KEY_UP;
1863         case VK_RIGHT: return GLUT_KEY_RIGHT;
1864         case VK_DOWN: return GLUT_KEY_DOWN;
1865         case VK_OEM_1: return ';';
1866         case VK_OEM_2: return '/';
1867         case VK_OEM_3: return '`';
1868         case VK_OEM_4: return '[';
1869         case VK_OEM_5: return '\\';
1870         case VK_OEM_6: return ']';
1871         case VK_OEM_7: return '\'';
1872         default:
1873                 break;
1874         }
1875
1876         if(vkey >= 'A' && vkey <= 'Z') {
1877                 vkey += 32;
1878         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1879                 vkey -= VK_F1 + GLUT_KEY_F1;
1880         }
1881
1882         return vkey;
1883 }
1884
1885 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1886 {
1887         int x, y;
1888
1889         update_modkeys();
1890
1891         if(cb_mouse) {
1892                 x = lparam & 0xffff;
1893                 y = lparam >> 16;
1894                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1895         }
1896 }
1897
1898 static void get_window_pos(int *x, int *y)
1899 {
1900         RECT rect;
1901         GetWindowRect(win, &rect);
1902         *x = rect.left;
1903         *y = rect.top;
1904 }
1905
1906 static void get_window_size(int *w, int *h)
1907 {
1908         RECT rect;
1909         GetClientRect(win, &rect);
1910         *w = rect.right - rect.left;
1911         *h = rect.bottom - rect.top;
1912 }
1913
1914 static void get_screen_size(int *scrw, int *scrh)
1915 {
1916         *scrw = GetSystemMetrics(SM_CXSCREEN);
1917         *scrh = GetSystemMetrics(SM_CYSCREEN);
1918 }
1919 #endif  /* BUILD_WIN32 */
1920
1921 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1922 #include <sys/time.h>
1923
1924 #ifdef MINIGLUT_USE_LIBC
1925 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1926 #else
1927 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1928 #endif
1929
1930 static long get_msec(void)
1931 {
1932         static struct timeval tv0;
1933         struct timeval tv;
1934
1935         sys_gettimeofday(&tv, 0);
1936         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1937                 tv0 = tv;
1938                 return 0;
1939         }
1940         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1941 }
1942 #endif  /* UNIX */
1943 #ifdef _WIN32
1944 static long get_msec(void)
1945 {
1946         static long t0;
1947         long tm;
1948
1949 #ifdef MINIGLUT_NO_WINMM
1950         tm = GetTickCount();
1951 #else
1952         tm = timeGetTime();
1953 #endif
1954         if(!t0) {
1955                 t0 = tm;
1956                 return 0;
1957         }
1958         return tm - t0;
1959 }
1960 #endif
1961
1962 static void panic(const char *msg)
1963 {
1964         const char *end = msg;
1965         while(*end) end++;
1966         sys_write(2, msg, end - msg);
1967         sys_exit(1);
1968 }
1969
1970
1971 #ifdef MINIGLUT_USE_LIBC
1972 #include <stdlib.h>
1973 #ifdef _WIN32
1974 #include <io.h>
1975 #else
1976 #include <unistd.h>
1977 #endif
1978
1979 static void sys_exit(int status)
1980 {
1981         exit(status);
1982 }
1983
1984 static int sys_write(int fd, const void *buf, int count)
1985 {
1986         return write(fd, buf, count);
1987 }
1988
1989 #else   /* !MINIGLUT_USE_LIBC */
1990
1991 #ifdef __linux__
1992 #ifdef __x86_64__
1993 static void sys_exit(int status)
1994 {
1995         asm volatile(
1996                 "syscall\n\t"
1997                 :: "a"(60), "D"(status));
1998 }
1999 static int sys_write(int fd, const void *buf, int count)
2000 {
2001         long res;
2002         asm volatile(
2003                 "syscall\n\t"
2004                 : "=a"(res)
2005                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
2006         return res;
2007 }
2008 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2009 {
2010         int res;
2011         asm volatile(
2012                 "syscall\n\t"
2013                 : "=a"(res)
2014                 : "a"(96), "D"(tv), "S"(tz));
2015         return res;
2016 }
2017 #endif  /* __x86_64__ */
2018 #ifdef __i386__
2019 static void sys_exit(int status)
2020 {
2021         asm volatile(
2022                 "int $0x80\n\t"
2023                 :: "a"(1), "b"(status));
2024 }
2025 static int sys_write(int fd, const void *buf, int count)
2026 {
2027         int res;
2028         asm volatile(
2029                 "int $0x80\n\t"
2030                 : "=a"(res)
2031                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2032         return res;
2033 }
2034 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2035 {
2036         int res;
2037         asm volatile(
2038                 "int $0x80\n\t"
2039                 : "=a"(res)
2040                 : "a"(78), "b"(tv), "c"(tz));
2041         return res;
2042 }
2043 #endif  /* __i386__ */
2044 #endif  /* __linux__ */
2045
2046 #ifdef _WIN32
2047 static void sys_exit(int status)
2048 {
2049         ExitProcess(status);
2050 }
2051 static int sys_write(int fd, const void *buf, int count)
2052 {
2053         unsigned long wrsz = 0;
2054
2055         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2056         if(!WriteFile(out, buf, count, &wrsz, 0)) {
2057                 return -1;
2058         }
2059         return wrsz;
2060 }
2061 #endif  /* _WIN32 */
2062 #endif  /* !MINIGLUT_USE_LIBC */
2063
2064
2065 /* ----------------- primitives ------------------ */
2066 #ifdef MINIGLUT_USE_LIBC
2067 #include <stdlib.h>
2068 #include <math.h>
2069
2070 void mglut_sincos(float angle, float *sptr, float *cptr)
2071 {
2072         *sptr = sin(angle);
2073         *cptr = cos(angle);
2074 }
2075
2076 float mglut_atan(float x)
2077 {
2078         return atan(x);
2079 }
2080
2081 #else   /* !MINIGLUT_USE_LIBC */
2082
2083 #ifdef __GNUC__
2084 void mglut_sincos(float angle, float *sptr, float *cptr)
2085 {
2086         asm volatile(
2087                 "flds %2\n\t"
2088                 "fsincos\n\t"
2089                 "fstps %1\n\t"
2090                 "fstps %0\n\t"
2091                 : "=m"(*sptr), "=m"(*cptr)
2092                 : "m"(angle)
2093         );
2094 }
2095
2096 float mglut_atan(float x)
2097 {
2098         float res;
2099         asm volatile(
2100                 "flds %1\n\t"
2101                 "fld1\n\t"
2102                 "fpatan\n\t"
2103                 "fstps %0\n\t"
2104                 : "=m"(res)
2105                 : "m"(x)
2106         );
2107         return res;
2108 }
2109 #endif
2110
2111 #ifdef _MSC_VER
2112 void mglut_sincos(float angle, float *sptr, float *cptr)
2113 {
2114         float s, c;
2115         __asm {
2116                 fld angle
2117                 fsincos
2118                 fstp c
2119                 fstp s
2120         }
2121         *sptr = s;
2122         *cptr = c;
2123 }
2124
2125 float mglut_atan(float x)
2126 {
2127         float res;
2128         __asm {
2129                 fld x
2130                 fld1
2131                 fpatan
2132                 fstp res
2133         }
2134         return res;
2135 }
2136 #endif
2137
2138 #ifdef __WATCOMC__
2139 #pragma aux mglut_sincos = \
2140         "fsincos" \
2141         "fstp dword ptr [edx]" \
2142         "fstp dword ptr [eax]" \
2143         parm[8087][eax][edx]    \
2144         modify[8087];
2145
2146 #pragma aux mglut_atan = \
2147         "fld1" \
2148         "fpatan" \
2149         parm[8087] \
2150         value[8087] \
2151         modify [8087];
2152 #endif  /* __WATCOMC__ */
2153
2154 #endif  /* !MINIGLUT_USE_LIBC */
2155
2156 #define PI      3.1415926536f
2157
2158 void glutSolidSphere(float rad, int slices, int stacks)
2159 {
2160         int i, j, k, gray;
2161         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2162         float du = 1.0f / (float)slices;
2163         float dv = 1.0f / (float)stacks;
2164
2165         glBegin(GL_QUADS);
2166         for(i=0; i<stacks; i++) {
2167                 v = i * dv;
2168                 for(j=0; j<slices; j++) {
2169                         u = j * du;
2170                         for(k=0; k<4; k++) {
2171                                 gray = k ^ (k >> 1);
2172                                 s = gray & 1 ? u + du : u;
2173                                 t = gray & 2 ? v + dv : v;
2174                                 theta = s * PI * 2.0f;
2175                                 phi = t * PI;
2176                                 mglut_sincos(theta, &sintheta, &costheta);
2177                                 mglut_sincos(phi, &sinphi, &cosphi);
2178                                 x = sintheta * sinphi;
2179                                 y = costheta * sinphi;
2180                                 z = cosphi;
2181
2182                                 glColor3f(s, t, 1);
2183                                 glTexCoord2f(s, t);
2184                                 glNormal3f(x, y, z);
2185                                 glVertex3f(x * rad, y * rad, z * rad);
2186                         }
2187                 }
2188         }
2189         glEnd();
2190 }
2191
2192 void glutWireSphere(float rad, int slices, int stacks)
2193 {
2194         glPushAttrib(GL_POLYGON_BIT);
2195         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2196         glutSolidSphere(rad, slices, stacks);
2197         glPopAttrib();
2198 }
2199
2200 void glutSolidCube(float sz)
2201 {
2202         int i, j, idx, gray, flip, rotx;
2203         float vpos[3], norm[3];
2204         float rad = sz * 0.5f;
2205
2206         glBegin(GL_QUADS);
2207         for(i=0; i<6; i++) {
2208                 flip = i & 1;
2209                 rotx = i >> 2;
2210                 idx = (~i & 2) - rotx;
2211                 norm[0] = norm[1] = norm[2] = 0.0f;
2212                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2213                 glNormal3fv(norm);
2214                 vpos[idx] = norm[idx] * rad;
2215                 for(j=0; j<4; j++) {
2216                         gray = j ^ (j >> 1);
2217                         vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2218                         vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2219                         glTexCoord2f(gray & 1, gray >> 1);
2220                         glVertex3fv(vpos);
2221                 }
2222         }
2223         glEnd();
2224 }
2225
2226 void glutWireCube(float sz)
2227 {
2228         glPushAttrib(GL_POLYGON_BIT);
2229         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2230         glutSolidCube(sz);
2231         glPopAttrib();
2232 }
2233
2234 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2235 {
2236         int i, j, k, gray;
2237         float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2238         float du = 1.0f / (float)slices;
2239         float dv = 1.0f / (float)stacks;
2240
2241         rad = rbot - rtop;
2242         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2243         mglut_sincos(phi, &sinphi, &cosphi);
2244
2245         glBegin(GL_QUADS);
2246         for(i=0; i<stacks; i++) {
2247                 v = i * dv;
2248                 for(j=0; j<slices; j++) {
2249                         u = j * du;
2250                         for(k=0; k<4; k++) {
2251                                 gray = k ^ (k >> 1);
2252                                 s = gray & 2 ? u + du : u;
2253                                 t = gray & 1 ? v + dv : v;
2254                                 rad = rbot + (rtop - rbot) * t;
2255                                 theta = s * PI * 2.0f;
2256                                 mglut_sincos(theta, &sintheta, &costheta);
2257
2258                                 x = sintheta * cosphi;
2259                                 y = costheta * cosphi;
2260                                 z = sinphi;
2261
2262                                 glColor3f(s, t, 1);
2263                                 glTexCoord2f(s, t);
2264                                 glNormal3f(x, y, z);
2265                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
2266                         }
2267                 }
2268         }
2269         glEnd();
2270 }
2271
2272 void glutSolidCone(float base, float height, int slices, int stacks)
2273 {
2274         draw_cylinder(base, 0, height, slices, stacks);
2275 }
2276
2277 void glutWireCone(float base, float height, int slices, int stacks)
2278 {
2279         glPushAttrib(GL_POLYGON_BIT);
2280         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2281         glutSolidCone(base, height, slices, stacks);
2282         glPopAttrib();
2283 }
2284
2285 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2286 {
2287         draw_cylinder(rad, rad, height, slices, stacks);
2288 }
2289
2290 void glutWireCylinder(float rad, float height, int slices, int stacks)
2291 {
2292         glPushAttrib(GL_POLYGON_BIT);
2293         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2294         glutSolidCylinder(rad, height, slices, stacks);
2295         glPopAttrib();
2296 }
2297
2298 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2299 {
2300         int i, j, k, gray;
2301         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2302         float du = 1.0f / (float)rings;
2303         float dv = 1.0f / (float)sides;
2304
2305         glBegin(GL_QUADS);
2306         for(i=0; i<rings; i++) {
2307                 u = i * du;
2308                 for(j=0; j<sides; j++) {
2309                         v = j * dv;
2310                         for(k=0; k<4; k++) {
2311                                 gray = k ^ (k >> 1);
2312                                 s = gray & 1 ? u + du : u;
2313                                 t = gray & 2 ? v + dv : v;
2314                                 theta = s * PI * 2.0f;
2315                                 phi = t * PI * 2.0f;
2316                                 mglut_sincos(theta, &sintheta, &costheta);
2317                                 mglut_sincos(phi, &sinphi, &cosphi);
2318                                 x = sintheta * sinphi;
2319                                 y = costheta * sinphi;
2320                                 z = cosphi;
2321
2322                                 glColor3f(s, t, 1);
2323                                 glTexCoord2f(s, t);
2324                                 glNormal3f(x, y, z);
2325
2326                                 x = x * inner_rad + sintheta * outer_rad;
2327                                 y = y * inner_rad + costheta * outer_rad;
2328                                 z *= inner_rad;
2329                                 glVertex3f(x, y, z);
2330                         }
2331                 }
2332         }
2333         glEnd();
2334 }
2335
2336 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2337 {
2338         glPushAttrib(GL_POLYGON_BIT);
2339         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2340         glutSolidTorus(inner_rad, outer_rad, sides, rings);
2341         glPopAttrib();
2342 }
2343
2344 #define NUM_TEAPOT_INDICES      (sizeof teapot_index / sizeof *teapot_index)
2345 #define NUM_TEAPOT_VERTS        (sizeof teapot_verts / sizeof *teapot_verts)
2346
2347 #define NUM_TEAPOT_PATCHES      (NUM_TEAPOT_INDICES / 16)
2348
2349 #define PATCH_SUBDIV    7
2350
2351 static float teapot_part_flip[] = {
2352         1, 1, 1, 1,                     /* rim flip */
2353         1, 1, 1, 1,                     /* body1 flip */
2354         1, 1, 1, 1,                     /* body2 flip */
2355         1, 1, 1, 1,                     /* lid patch 1 flip */
2356         1, 1, 1, 1,                     /* lid patch 2 flip */
2357         1, -1,                          /* handle 1 flip */
2358         1, -1,                          /* handle 2 flip */
2359         1, -1,                          /* spout 1 flip */
2360         1, -1,                          /* spout 2 flip */
2361         1, 1, 1, 1                      /* bottom flip */
2362 };
2363
2364 static float teapot_part_rot[] = {
2365         0, 90, 180, 270,        /* rim rotations */
2366         0, 90, 180, 270,        /* body patch 1 rotations */
2367         0, 90, 180, 270,        /* body patch 2 rotations */
2368         0, 90, 180, 270,        /* lid patch 1 rotations */
2369         0, 90, 180, 270,        /* lid patch 2 rotations */
2370         0, 0,                           /* handle 1 rotations */
2371         0, 0,                           /* handle 2 rotations */
2372         0, 0,                           /* spout 1 rotations */
2373         0, 0,                           /* spout 2 rotations */
2374         0, 90, 180, 270         /* bottom rotations */
2375 };
2376
2377
2378 static int teapot_index[] = {
2379         /* rim */
2380         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2381         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2382         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2383         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2384         /* body1 */
2385         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2386         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2387         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2388         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2389         /* body 2 */
2390         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2391         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2392         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2393         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2394         /* lid 1 */
2395         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2396         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2397         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2398         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2399         /* lid 2 */
2400         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2401         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2402         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2403         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2404         /* handle 1 */
2405         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2406         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2407         /* handle 2 */
2408         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2409         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2410         /* spout 1 */
2411         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2412         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2413         /* spout 2 */
2414         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2415         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2416         /* bottom */
2417         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2418         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2419         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2420         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2421 };
2422
2423
2424 static float teapot_verts[][3] = {
2425         {  0.2000,  0.0000, 2.70000 }, {  0.2000, -0.1120, 2.70000 },
2426         {  0.1120, -0.2000, 2.70000 }, {  0.0000, -0.2000, 2.70000 },
2427         {  1.3375,  0.0000, 2.53125 }, {  1.3375, -0.7490, 2.53125 },
2428         {  0.7490, -1.3375, 2.53125 }, {  0.0000, -1.3375, 2.53125 },
2429         {  1.4375,  0.0000, 2.53125 }, {  1.4375, -0.8050, 2.53125 },
2430         {  0.8050, -1.4375, 2.53125 }, {  0.0000, -1.4375, 2.53125 },
2431         {  1.5000,  0.0000, 2.40000 }, {  1.5000, -0.8400, 2.40000 },
2432         {  0.8400, -1.5000, 2.40000 }, {  0.0000, -1.5000, 2.40000 },
2433         {  1.7500,  0.0000, 1.87500 }, {  1.7500, -0.9800, 1.87500 },
2434         {  0.9800, -1.7500, 1.87500 }, {  0.0000, -1.7500, 1.87500 },
2435         {  2.0000,  0.0000, 1.35000 }, {  2.0000, -1.1200, 1.35000 },
2436         {  1.1200, -2.0000, 1.35000 }, {  0.0000, -2.0000, 1.35000 },
2437         {  2.0000,  0.0000, 0.90000 }, {  2.0000, -1.1200, 0.90000 },
2438         {  1.1200, -2.0000, 0.90000 }, {  0.0000, -2.0000, 0.90000 },
2439         { -2.0000,  0.0000, 0.90000 }, {  2.0000,  0.0000, 0.45000 },
2440         {  2.0000, -1.1200, 0.45000 }, {  1.1200, -2.0000, 0.45000 },
2441         {  0.0000, -2.0000, 0.45000 }, {  1.5000,  0.0000, 0.22500 },
2442         {  1.5000, -0.8400, 0.22500 }, {  0.8400, -1.5000, 0.22500 },
2443         {  0.0000, -1.5000, 0.22500 }, {  1.5000,  0.0000, 0.15000 },
2444         {  1.5000, -0.8400, 0.15000 }, {  0.8400, -1.5000, 0.15000 },
2445         {  0.0000, -1.5000, 0.15000 }, { -1.6000,  0.0000, 2.02500 },
2446         { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2447         { -1.5000,  0.0000, 2.25000 }, { -2.3000,  0.0000, 2.02500 },
2448         { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2449         { -2.5000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 2.02500 },
2450         { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2451         { -3.0000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 1.80000 },
2452         { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2453         { -3.0000,  0.0000, 1.80000 }, { -2.7000,  0.0000, 1.57500 },
2454         { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2455         { -3.0000,  0.0000, 1.35000 }, { -2.5000,  0.0000, 1.12500 },
2456         { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2457         { -2.6500,  0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2458         { -1.9000, -0.3000, 0.60000 }, { -1.9000,  0.0000, 0.60000 },
2459         {  1.7000,  0.0000, 1.42500 }, {  1.7000, -0.6600, 1.42500 },
2460         {  1.7000, -0.6600, 0.60000 }, {  1.7000,  0.0000, 0.60000 },
2461         {  2.6000,  0.0000, 1.42500 }, {  2.6000, -0.6600, 1.42500 },
2462         {  3.1000, -0.6600, 0.82500 }, {  3.1000,  0.0000, 0.82500 },
2463         {  2.3000,  0.0000, 2.10000 }, {  2.3000, -0.2500, 2.10000 },
2464         {  2.4000, -0.2500, 2.02500 }, {  2.4000,  0.0000, 2.02500 },
2465         {  2.7000,  0.0000, 2.40000 }, {  2.7000, -0.2500, 2.40000 },
2466         {  3.3000, -0.2500, 2.40000 }, {  3.3000,  0.0000, 2.40000 },
2467         {  2.8000,  0.0000, 2.47500 }, {  2.8000, -0.2500, 2.47500 },
2468         {  3.5250, -0.2500, 2.49375 }, {  3.5250,  0.0000, 2.49375 },
2469         {  2.9000,  0.0000, 2.47500 }, {  2.9000, -0.1500, 2.47500 },
2470         {  3.4500, -0.1500, 2.51250 }, {  3.4500,  0.0000, 2.51250 },
2471         {  2.8000,  0.0000, 2.40000 }, {  2.8000, -0.1500, 2.40000 },
2472         {  3.2000, -0.1500, 2.40000 }, {  3.2000,  0.0000, 2.40000 },
2473         {  0.0000,  0.0000, 3.15000 }, {  0.8000,  0.0000, 3.15000 },
2474         {  0.8000, -0.4500, 3.15000 }, {  0.4500, -0.8000, 3.15000 },
2475         {  0.0000, -0.8000, 3.15000 }, {  0.0000,  0.0000, 2.85000 },
2476         {  1.4000,  0.0000, 2.40000 }, {  1.4000, -0.7840, 2.40000 },
2477         {  0.7840, -1.4000, 2.40000 }, {  0.0000, -1.4000, 2.40000 },
2478         {  0.4000,  0.0000, 2.55000 }, {  0.4000, -0.2240, 2.55000 },
2479         {  0.2240, -0.4000, 2.55000 }, {  0.0000, -0.4000, 2.55000 },
2480         {  1.3000,  0.0000, 2.55000 }, {  1.3000, -0.7280, 2.55000 },
2481         {  0.7280, -1.3000, 2.55000 }, {  0.0000, -1.3000, 2.55000 },
2482         {  1.3000,  0.0000, 2.40000 }, {  1.3000, -0.7280, 2.40000 },
2483         {  0.7280, -1.3000, 2.40000 }, {  0.0000, -1.3000, 2.40000 },
2484         {  0.0000,  0.0000, 0.00000 }, {  1.4250, -0.7980, 0.00000 },
2485         {  1.5000,  0.0000, 0.07500 }, {  1.4250,  0.0000, 0.00000 },
2486         {  0.7980, -1.4250, 0.00000 }, {  0.0000, -1.5000, 0.07500 },
2487         {  0.0000, -1.4250, 0.00000 }, {  1.5000, -0.8400, 0.07500 },
2488         {  0.8400, -1.5000, 0.07500 }
2489 };
2490
2491 static void draw_patch(int *index, int flip, float scale);
2492 static float bernstein(int i, float x);
2493
2494 void glutSolidTeapot(float size)
2495 {
2496         int i;
2497
2498         size /= 2.0;
2499
2500         for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2501                 float flip = teapot_part_flip[i];
2502                 float rot = teapot_part_rot[i];
2503
2504                 glMatrixMode(GL_MODELVIEW);
2505                 glPushMatrix();
2506                 glTranslatef(0, -3.15 * size * 0.5, 0);
2507                 glRotatef(rot, 0, 1, 0);
2508                 glScalef(1, 1, flip);
2509                 glRotatef(-90, 1, 0, 0);
2510
2511                 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2512
2513                 glPopMatrix();
2514         }
2515 }
2516
2517 void glutWireTeapot(float size)
2518 {
2519         glPushAttrib(GL_POLYGON_BIT);
2520         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2521         glutSolidTeapot(size);
2522         glPopAttrib();
2523 }
2524
2525
2526 static void bezier_patch(float *res, float *cp, float u, float v)
2527 {
2528         int i, j;
2529
2530         res[0] = res[1] = res[2] = 0.0f;
2531
2532         for(j=0; j<4; j++) {
2533                 for(i=0; i<4; i++) {
2534                         float bu = bernstein(i, u);
2535                         float bv = bernstein(j, v);
2536
2537                         res[0] += cp[0] * bu * bv;
2538                         res[1] += cp[1] * bu * bv;
2539                         res[2] += cp[2] * bu * bv;
2540
2541                         cp += 3;
2542                 }
2543         }
2544 }
2545
2546 static float rsqrt(float x)
2547 {
2548         float xhalf = x * 0.5f;
2549         int i = *(int*)&x;
2550         i = 0x5f3759df - (i >> 1);
2551         x = *(float*)&i;
2552         x = x * (1.5f - xhalf * x * x);
2553         return x;
2554 }
2555
2556
2557 #define CROSS(res, a, b) \
2558         do { \
2559                 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2560                 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2561                 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2562         } while(0)
2563
2564 #define NORMALIZE(v) \
2565         do { \
2566                 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2567                 (v)[0] *= s; \
2568                 (v)[1] *= s; \
2569                 (v)[2] *= s; \
2570         } while(0)
2571
2572 #define DT      0.001
2573
2574 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2575 {
2576         float tang[3], bitan[3], tmp[3];
2577
2578         bezier_patch(tang, cp, u + DT, v);
2579         bezier_patch(tmp, cp, u - DT, v);
2580         tang[0] -= tmp[0];
2581         tang[1] -= tmp[1];
2582         tang[2] -= tmp[2];
2583
2584         bezier_patch(bitan, cp, u, v + DT);
2585         bezier_patch(tmp, cp, u, v - DT);
2586         bitan[0] -= tmp[0];
2587         bitan[1] -= tmp[1];
2588         bitan[2] -= tmp[2];
2589
2590         CROSS(res, tang, bitan);
2591         NORMALIZE(res);
2592 }
2593
2594
2595
2596 static float bernstein(int i, float x)
2597 {
2598         float invx = 1.0f - x;
2599
2600         switch(i) {
2601         case 0:
2602                 return invx * invx * invx;
2603         case 1:
2604                 return 3.0f * x * invx * invx;
2605         case 2:
2606                 return 3.0f * x * x * invx;
2607         case 3:
2608                 return x * x * x;
2609         default:
2610                 break;
2611         }
2612         return 0.0f;
2613 }
2614
2615 static void draw_patch(int *index, int flip, float scale)
2616 {
2617         static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2618         static const float voffs[4] = {0, 1, 1, 0};
2619
2620         int i, j, k;
2621         float cp[16 * 3];
2622         float pt[3], n[3];
2623         float u, v;
2624         float du = 1.0 / PATCH_SUBDIV;
2625         float dv = 1.0 / PATCH_SUBDIV;
2626
2627         /* collect control points */
2628         for(i=0; i<16; i++) {
2629                 cp[i * 3] = teapot_verts[index[i]][0];
2630                 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2631                 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2632         }
2633
2634         glBegin(GL_QUADS);
2635         glColor3f(1, 1, 1);
2636
2637         u = 0;
2638         for(i=0; i<PATCH_SUBDIV; i++) {
2639                 v = 0;
2640                 for(j=0; j<PATCH_SUBDIV; j++) {
2641
2642                         for(k=0; k<4; k++) {
2643                                 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2644
2645                                 /* top/bottom normal hack */
2646                                 if(pt[2] > 3.14) {
2647                                         n[0] = n[1] = 0.0f;
2648                                         n[2] = 1.0f;
2649                                 } else if(pt[2] < 0.00001) {
2650                                         n[0] = n[1] = 0.0f;
2651                                         n[2] = -1.0f;
2652                                 } else {
2653                                         bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2654                                 }
2655
2656                                 glTexCoord2f(u, v);
2657                                 glNormal3fv(n);
2658                                 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
2659                         }
2660
2661                         v += dv;
2662                 }
2663                 u += du;
2664         }
2665
2666         glEnd();
2667 }