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