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