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