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