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