fixed bugs in windows context creation, which would result in some times
[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__)
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 LRESULT 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_ACCELERATION        0x2003
1272 #define WGL_SUPPORT_OPENGL      0x2010
1273 #define WGL_DOUBLE_BUFFER       0x2011
1274 #define WGL_STEREO                      0x2012
1275 #define WGL_PIXEL_TYPE          0x2013
1276 #define WGL_COLOR_BITS          0x2014
1277 #define WGL_RED_BITS            0x2015
1278 #define WGL_GREEN_BITS          0x2017
1279 #define WGL_BLUE_BITS           0x2019
1280 #define WGL_ALPHA_BITS          0x201b
1281 #define WGL_ACCUM_BITS          0x201d
1282 #define WGL_DEPTH_BITS          0x2022
1283 #define WGL_STENCIL_BITS        0x2023
1284 #define WGL_FULL_ACCELERATION   0x2027
1285
1286 #define WGL_TYPE_RGBA           0x202b
1287 #define WGL_TYPE_COLORINDEX     0x202c
1288
1289 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20a9
1290 #define WGL_SAMPLE_BUFFERS_ARB                          0x2041
1291 #define WGL_SAMPLES_ARB                                         0x2042
1292
1293 static PROC wglChoosePixelFormat;
1294 static PROC wglGetPixelFormatAttribiv;
1295
1296 #define ATTR(a, v) \
1297         do { *aptr++ = (a); *aptr++ = (v); } while(0)
1298
1299 static unsigned int choose_pixfmt(unsigned int mode)
1300 {
1301         unsigned int num_pixfmt, pixfmt = 0;
1302         int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1303                 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1304         float fattr[2] = {0, 0};
1305
1306         int *aptr = attr + 6;
1307         int *samples = 0;
1308
1309         if(mode & GLUT_DOUBLE) {
1310                 ATTR(WGL_DOUBLE_BUFFER, 1);
1311         }
1312
1313         ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1314         ATTR(WGL_COLOR_BITS, 8);
1315         if(mode & GLUT_ALPHA) {
1316                 ATTR(WGL_ALPHA_BITS, 4);
1317         }
1318         if(mode & GLUT_DEPTH) {
1319                 ATTR(WGL_DEPTH_BITS, 16);
1320         }
1321         if(mode & GLUT_STENCIL) {
1322                 ATTR(WGL_STENCIL_BITS, 1);
1323         }
1324         if(mode & GLUT_ACCUM) {
1325                 ATTR(WGL_ACCUM_BITS, 1);
1326         }
1327         if(mode & GLUT_STEREO) {
1328                 ATTR(WGL_STEREO, 1);
1329         }
1330         if(mode & GLUT_SRGB) {
1331                 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1332         }
1333         if(mode & GLUT_MULTISAMPLE) {
1334                 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1335                 *aptr++ = WGL_SAMPLES_ARB;
1336                 samples = aptr;
1337                 *aptr++ = 32;
1338         }
1339         *aptr++ = 0;
1340
1341         while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1342                 *samples >>= 1;
1343                 if(!*samples) {
1344                         aptr[-3] = 0;
1345                 }
1346         }
1347         return pixfmt;
1348 }
1349
1350 static PIXELFORMATDESCRIPTOR pfd;
1351 static PIXELFORMATDESCRIPTOR tmppfd = {
1352         sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1353         PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1354         PFD_MAIN_PLANE, 0, 0, 0, 0
1355 };
1356 #define TMPCLASS        "TempMiniGLUT"
1357
1358 #define GETATTR(attr, vptr) \
1359         do { \
1360                 int gattr = attr; \
1361                 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1362         } while(0)
1363
1364 static int create_window_wglext(const char *title, int width, int height)
1365 {
1366         WNDCLASSEX wc = {0};
1367         HWND tmpwin = 0;
1368         HDC tmpdc = 0;
1369         HGLRC tmpctx = 0;
1370         int pixfmt;
1371
1372         /* create a temporary window and GL context, just to query and retrieve
1373          * the wglChoosePixelFormatEXT function
1374          */
1375         wc.cbSize = sizeof wc;
1376         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1377         wc.hCursor = LoadCursor(0, IDC_ARROW);
1378         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1379         wc.hInstance = hinst;
1380         wc.lpfnWndProc = DefWindowProc;
1381         wc.lpszClassName = TMPCLASS;
1382         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1383         if(!RegisterClassEx(&wc)) {
1384                 return 0;
1385         }
1386         if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1387                                         width, height, 0, 0, hinst, 0))) {
1388                 goto fail;
1389         }
1390         tmpdc = GetDC(tmpwin);
1391
1392         if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1393                         !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1394                         !(tmpctx = wglCreateContext(tmpdc))) {
1395                 goto fail;
1396         }
1397         wglMakeCurrent(tmpdc, tmpctx);
1398
1399         if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1400                 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1401                         goto fail;
1402                 }
1403                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1404                         goto fail;
1405                 }
1406         } else {
1407                 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1408                         goto fail;
1409                 }
1410         }
1411         wglMakeCurrent(0, 0);
1412         wglDeleteContext(tmpctx);
1413         DestroyWindow(tmpwin);
1414         UnregisterClass(TMPCLASS, hinst);
1415
1416         /* create the real window and context */
1417         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1418                                         init_y, width, height, 0, 0, hinst, 0))) {
1419                 panic("Failed to create window\n");
1420         }
1421         dc = GetDC(win);
1422
1423         if(!(pixfmt = choose_pixfmt(init_mode))) {
1424                 panic("Failed to find suitable pixel format\n");
1425         }
1426         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1427                 panic("Failed to set the selected pixel format\n");
1428         }
1429         if(!(ctx = wglCreateContext(dc))) {
1430                 panic("Failed to create the OpenGL context\n");
1431         }
1432         wglMakeCurrent(dc, ctx);
1433
1434         GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1435         GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1436         GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1437         GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1438         GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1439         GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1440         GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1441         GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1442         GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1443         return 0;
1444
1445 fail:
1446         if(tmpctx) {
1447                 wglMakeCurrent(0, 0);
1448                 wglDeleteContext(tmpctx);
1449         }
1450         if(tmpwin) {
1451                 DestroyWindow(tmpwin);
1452         }
1453         UnregisterClass(TMPCLASS, hinst);
1454         return -1;
1455 }
1456
1457
1458 static void create_window(const char *title)
1459 {
1460         int pixfmt;
1461         RECT rect;
1462         int width, height;
1463
1464         rect.left = init_x;
1465         rect.top = init_y;
1466         rect.right = init_x + init_width;
1467         rect.bottom = init_y + init_height;
1468         AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1469         width = rect.right - rect.left;
1470         height = rect.bottom - rect.top;
1471
1472         memset(&pfd, 0, sizeof pfd);
1473         pfd.nSize = sizeof pfd;
1474         pfd.nVersion = 1;
1475         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1476         if(init_mode & GLUT_STEREO) {
1477                 pfd.dwFlags |= PFD_STEREO;
1478         }
1479         pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1480         pfd.cColorBits = 24;
1481         if(init_mode & GLUT_ALPHA) {
1482                 pfd.cAlphaBits = 8;
1483         }
1484         if(init_mode & GLUT_ACCUM) {
1485                 pfd.cAccumBits = 24;
1486         }
1487         if(init_mode & GLUT_DEPTH) {
1488                 pfd.cDepthBits = 24;
1489         }
1490         if(init_mode & GLUT_STENCIL) {
1491                 pfd.cStencilBits = 8;
1492         }
1493         pfd.iLayerType = PFD_MAIN_PLANE;
1494
1495
1496         if(create_window_wglext(title, width, height) == -1) {
1497
1498                 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1499                                         rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1500                         panic("Failed to create window\n");
1501                 }
1502                 dc = GetDC(win);
1503
1504                 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1505                         panic("Failed to find suitable pixel format\n");
1506                 }
1507                 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1508                         panic("Failed to set the selected pixel format\n");
1509                 }
1510                 if(!(ctx = wglCreateContext(dc))) {
1511                         panic("Failed to create the OpenGL context\n");
1512                 }
1513                 wglMakeCurrent(dc, ctx);
1514
1515                 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1516                 ctx_info.rsize = pfd.cRedBits;
1517                 ctx_info.gsize = pfd.cGreenBits;
1518                 ctx_info.bsize = pfd.cBlueBits;
1519                 ctx_info.asize = pfd.cAlphaBits;
1520                 ctx_info.zsize = pfd.cDepthBits;
1521                 ctx_info.ssize = pfd.cStencilBits;
1522                 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1523                 ctx_info.samples = 0;
1524                 ctx_info.srgb = 0;
1525         }
1526
1527         ShowWindow(win, 1);
1528         SetForegroundWindow(win);
1529         SetFocus(win);
1530         upd_pending = 1;
1531         reshape_pending = 1;
1532 }
1533
1534 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1535 {
1536         static int mouse_x, mouse_y;
1537         int x, y, key;
1538
1539         switch(msg) {
1540         case WM_CLOSE:
1541                 if(win) DestroyWindow(win);
1542                 break;
1543
1544         case WM_DESTROY:
1545                 cleanup();
1546                 quit = 1;
1547                 PostQuitMessage(0);
1548                 break;
1549
1550         case WM_PAINT:
1551                 upd_pending = 1;
1552                 ValidateRect(win, 0);
1553                 break;
1554
1555         case WM_SIZE:
1556                 x = lparam & 0xffff;
1557                 y = lparam >> 16;
1558                 if(x != win_width && y != win_height) {
1559                         win_width = x;
1560                         win_height = y;
1561                         if(cb_reshape) {
1562                                 reshape_pending = 0;
1563                                 cb_reshape(win_width, win_height);
1564                         }
1565                 }
1566                 break;
1567
1568         case WM_SHOWWINDOW:
1569                 mapped = wparam;
1570                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1571                 break;
1572
1573         case WM_KEYDOWN:
1574         case WM_SYSKEYDOWN:
1575                 update_modkeys();
1576                 key = translate_vkey(wparam);
1577                 if(key < 256) {
1578                         if(cb_keydown) {
1579                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1580                         }
1581                 } else {
1582                         if(cb_skeydown) {
1583                                 cb_skeydown(key, mouse_x, mouse_y);
1584                         }
1585                 }
1586                 break;
1587
1588         case WM_KEYUP:
1589         case WM_SYSKEYUP:
1590                 update_modkeys();
1591                 key = translate_vkey(wparam);
1592                 if(key < 256) {
1593                         if(cb_keyup) {
1594                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1595                         }
1596                 } else {
1597                         if(cb_skeyup) {
1598                                 cb_skeyup(key, mouse_x, mouse_y);
1599                         }
1600                 }
1601                 break;
1602
1603         case WM_LBUTTONDOWN:
1604                 handle_mbutton(0, 1, wparam, lparam);
1605                 break;
1606         case WM_MBUTTONDOWN:
1607                 handle_mbutton(1, 1, wparam, lparam);
1608                 break;
1609         case WM_RBUTTONDOWN:
1610                 handle_mbutton(2, 1, wparam, lparam);
1611                 break;
1612         case WM_LBUTTONUP:
1613                 handle_mbutton(0, 0, wparam, lparam);
1614                 break;
1615         case WM_MBUTTONUP:
1616                 handle_mbutton(1, 0, wparam, lparam);
1617                 break;
1618         case WM_RBUTTONUP:
1619                 handle_mbutton(2, 0, wparam, lparam);
1620                 break;
1621
1622         case WM_MOUSEMOVE:
1623                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1624                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1625                 } else {
1626                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1627                 }
1628                 break;
1629
1630         case WM_SYSCOMMAND:
1631                 wparam &= 0xfff0;
1632                 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1633                         return 0;
1634                 }
1635         default:
1636                 return DefWindowProc(win, msg, wparam, lparam);
1637         }
1638
1639         return 0;
1640 }
1641
1642 static void update_modkeys(void)
1643 {
1644         if(GetKeyState(VK_SHIFT) & 0x8000) {
1645                 modstate |= GLUT_ACTIVE_SHIFT;
1646         } else {
1647                 modstate &= ~GLUT_ACTIVE_SHIFT;
1648         }
1649         if(GetKeyState(VK_CONTROL) & 0x8000) {
1650                 modstate |= GLUT_ACTIVE_CTRL;
1651         } else {
1652                 modstate &= ~GLUT_ACTIVE_CTRL;
1653         }
1654         if(GetKeyState(VK_MENU) & 0x8000) {
1655                 modstate |= GLUT_ACTIVE_ALT;
1656         } else {
1657                 modstate &= ~GLUT_ACTIVE_ALT;
1658         }
1659 }
1660
1661 static int translate_vkey(int vkey)
1662 {
1663         switch(vkey) {
1664         case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1665         case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1666         case VK_END: return GLUT_KEY_END;
1667         case VK_HOME: return GLUT_KEY_HOME;
1668         case VK_LEFT: return GLUT_KEY_LEFT;
1669         case VK_UP: return GLUT_KEY_UP;
1670         case VK_RIGHT: return GLUT_KEY_RIGHT;
1671         case VK_DOWN: return GLUT_KEY_DOWN;
1672         default:
1673                 break;
1674         }
1675
1676         if(vkey >= 'A' && vkey <= 'Z') {
1677                 vkey += 32;
1678         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1679                 vkey -= VK_F1 + GLUT_KEY_F1;
1680         }
1681
1682         return vkey;
1683 }
1684
1685 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1686 {
1687         int x, y;
1688
1689         update_modkeys();
1690
1691         if(cb_mouse) {
1692                 x = lparam & 0xffff;
1693                 y = lparam >> 16;
1694                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1695         }
1696 }
1697
1698 static void get_window_pos(int *x, int *y)
1699 {
1700         RECT rect;
1701         GetWindowRect(win, &rect);
1702         *x = rect.left;
1703         *y = rect.top;
1704 }
1705
1706 static void get_window_size(int *w, int *h)
1707 {
1708         RECT rect;
1709         GetClientRect(win, &rect);
1710         *w = rect.right - rect.left;
1711         *h = rect.bottom - rect.top;
1712 }
1713
1714 static void get_screen_size(int *scrw, int *scrh)
1715 {
1716         *scrw = GetSystemMetrics(SM_CXSCREEN);
1717         *scrh = GetSystemMetrics(SM_CYSCREEN);
1718 }
1719 #endif  /* BUILD_WIN32 */
1720
1721 #if defined(__unix__) || defined(__APPLE__)
1722 #include <sys/time.h>
1723
1724 #ifdef MINIGLUT_USE_LIBC
1725 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1726 #else
1727 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1728 #endif
1729
1730 static long get_msec(void)
1731 {
1732         static struct timeval tv0;
1733         struct timeval tv;
1734
1735         sys_gettimeofday(&tv, 0);
1736         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1737                 tv0 = tv;
1738                 return 0;
1739         }
1740         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1741 }
1742 #endif  /* UNIX */
1743 #ifdef _WIN32
1744 static long get_msec(void)
1745 {
1746         static long t0;
1747         long tm;
1748
1749 #ifdef MINIGLUT_NO_WINMM
1750         tm = GetTickCount();
1751 #else
1752         tm = timeGetTime();
1753 #endif
1754         if(!t0) {
1755                 t0 = tm;
1756                 return 0;
1757         }
1758         return tm - t0;
1759 }
1760 #endif
1761
1762 static void panic(const char *msg)
1763 {
1764         const char *end = msg;
1765         while(*end) end++;
1766         sys_write(2, msg, end - msg);
1767         sys_exit(1);
1768 }
1769
1770
1771 #ifdef MINIGLUT_USE_LIBC
1772 #include <stdlib.h>
1773 #ifdef _WIN32
1774 #include <io.h>
1775 #else
1776 #include <unistd.h>
1777 #endif
1778
1779 static void sys_exit(int status)
1780 {
1781         exit(status);
1782 }
1783
1784 static int sys_write(int fd, const void *buf, int count)
1785 {
1786         return write(fd, buf, count);
1787 }
1788
1789 #else   /* !MINIGLUT_USE_LIBC */
1790
1791 #ifdef __linux__
1792 #ifdef __x86_64__
1793 static void sys_exit(int status)
1794 {
1795         asm volatile(
1796                 "syscall\n\t"
1797                 :: "a"(60), "D"(status));
1798 }
1799 static int sys_write(int fd, const void *buf, int count)
1800 {
1801         long res;
1802         asm volatile(
1803                 "syscall\n\t"
1804                 : "=a"(res)
1805                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1806         return res;
1807 }
1808 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1809 {
1810         int res;
1811         asm volatile(
1812                 "syscall\n\t"
1813                 : "=a"(res)
1814                 : "a"(96), "D"(tv), "S"(tz));
1815         return res;
1816 }
1817 #endif  /* __x86_64__ */
1818 #ifdef __i386__
1819 static void sys_exit(int status)
1820 {
1821         asm volatile(
1822                 "int $0x80\n\t"
1823                 :: "a"(1), "b"(status));
1824 }
1825 static int sys_write(int fd, const void *buf, int count)
1826 {
1827         int res;
1828         asm volatile(
1829                 "int $0x80\n\t"
1830                 : "=a"(res)
1831                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1832         return res;
1833 }
1834 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1835 {
1836         int res;
1837         asm volatile(
1838                 "int $0x80\n\t"
1839                 : "=a"(res)
1840                 : "a"(78), "b"(tv), "c"(tz));
1841         return res;
1842 }
1843 #endif  /* __i386__ */
1844 #endif  /* __linux__ */
1845
1846 #ifdef _WIN32
1847 static void sys_exit(int status)
1848 {
1849         ExitProcess(status);
1850 }
1851 static int sys_write(int fd, const void *buf, int count)
1852 {
1853         unsigned long wrsz = 0;
1854
1855         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1856         if(!WriteFile(out, buf, count, &wrsz, 0)) {
1857                 return -1;
1858         }
1859         return wrsz;
1860 }
1861 #endif  /* _WIN32 */
1862 #endif  /* !MINIGLUT_USE_LIBC */
1863
1864
1865 /* ----------------- primitives ------------------ */
1866 #ifdef MINIGLUT_USE_LIBC
1867 #include <stdlib.h>
1868 #include <math.h>
1869
1870 void mglut_sincos(float angle, float *sptr, float *cptr)
1871 {
1872         *sptr = sin(angle);
1873         *cptr = cos(angle);
1874 }
1875
1876 float mglut_atan(float x)
1877 {
1878         return atan(x);
1879 }
1880
1881 #else   /* !MINIGLUT_USE_LIBC */
1882
1883 #ifdef __GNUC__
1884 void mglut_sincos(float angle, float *sptr, float *cptr)
1885 {
1886         asm volatile(
1887                 "flds %2\n\t"
1888                 "fsincos\n\t"
1889                 "fstps %1\n\t"
1890                 "fstps %0\n\t"
1891                 : "=m"(*sptr), "=m"(*cptr)
1892                 : "m"(angle)
1893         );
1894 }
1895
1896 float mglut_atan(float x)
1897 {
1898         float res;
1899         asm volatile(
1900                 "flds %1\n\t"
1901                 "fld1\n\t"
1902                 "fpatan\n\t"
1903                 "fstps %0\n\t"
1904                 : "=m"(res)
1905                 : "m"(x)
1906         );
1907         return res;
1908 }
1909 #endif
1910
1911 #ifdef _MSC_VER
1912 void mglut_sincos(float angle, float *sptr, float *cptr)
1913 {
1914         float s, c;
1915         __asm {
1916                 fld angle
1917                 fsincos
1918                 fstp c
1919                 fstp s
1920         }
1921         *sptr = s;
1922         *cptr = c;
1923 }
1924
1925 float mglut_atan(float x)
1926 {
1927         float res;
1928         __asm {
1929                 fld x
1930                 fld1
1931                 fpatan
1932                 fstp res
1933         }
1934         return res;
1935 }
1936 #endif
1937
1938 #ifdef __WATCOMC__
1939 #pragma aux mglut_sincos = \
1940         "fsincos" \
1941         "fstp dword ptr [edx]" \
1942         "fstp dword ptr [eax]" \
1943         parm[8087][eax][edx]    \
1944         modify[8087];
1945
1946 #pragma aux mglut_atan = \
1947         "fld1" \
1948         "fpatan" \
1949         parm[8087] \
1950         value[8087] \
1951         modify [8087];
1952 #endif  /* __WATCOMC__ */
1953
1954 #endif  /* !MINIGLUT_USE_LIBC */
1955
1956 #define PI      3.1415926536f
1957
1958 void glutSolidSphere(float rad, int slices, int stacks)
1959 {
1960         int i, j, k, gray;
1961         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1962         float du = 1.0f / (float)slices;
1963         float dv = 1.0f / (float)stacks;
1964
1965         glBegin(GL_QUADS);
1966         for(i=0; i<stacks; i++) {
1967                 v = i * dv;
1968                 for(j=0; j<slices; j++) {
1969                         u = j * du;
1970                         for(k=0; k<4; k++) {
1971                                 gray = k ^ (k >> 1);
1972                                 s = gray & 1 ? u + du : u;
1973                                 t = gray & 2 ? v + dv : v;
1974                                 theta = s * PI * 2.0f;
1975                                 phi = t * PI;
1976                                 mglut_sincos(theta, &sintheta, &costheta);
1977                                 mglut_sincos(phi, &sinphi, &cosphi);
1978                                 x = sintheta * sinphi;
1979                                 y = costheta * sinphi;
1980                                 z = cosphi;
1981
1982                                 glColor3f(s, t, 1);
1983                                 glTexCoord2f(s, t);
1984                                 glNormal3f(x, y, z);
1985                                 glVertex3f(x * rad, y * rad, z * rad);
1986                         }
1987                 }
1988         }
1989         glEnd();
1990 }
1991
1992 void glutWireSphere(float rad, int slices, int stacks)
1993 {
1994         glPushAttrib(GL_POLYGON_BIT);
1995         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1996         glutSolidSphere(rad, slices, stacks);
1997         glPopAttrib();
1998 }
1999
2000 void glutSolidCube(float sz)
2001 {
2002         int i, j, idx, gray, flip, rotx;
2003         float vpos[3], norm[3];
2004         float rad = sz * 0.5f;
2005
2006         glBegin(GL_QUADS);
2007         for(i=0; i<6; i++) {
2008                 flip = i & 1;
2009                 rotx = i >> 2;
2010                 idx = (~i & 2) - rotx;
2011                 norm[0] = norm[1] = norm[2] = 0.0f;
2012                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2013                 glNormal3fv(norm);
2014                 vpos[idx] = norm[idx] * rad;
2015                 for(j=0; j<4; j++) {
2016                         gray = j ^ (j >> 1);
2017                         vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2018                         vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2019                         glTexCoord2f(gray & 1, gray >> 1);
2020                         glVertex3fv(vpos);
2021                 }
2022         }
2023         glEnd();
2024 }
2025
2026 void glutWireCube(float sz)
2027 {
2028         glPushAttrib(GL_POLYGON_BIT);
2029         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2030         glutSolidCube(sz);
2031         glPopAttrib();
2032 }
2033
2034 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2035 {
2036         int i, j, k, gray;
2037         float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2038         float du = 1.0f / (float)slices;
2039         float dv = 1.0f / (float)stacks;
2040
2041         rad = rbot - rtop;
2042         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2043         mglut_sincos(phi, &sinphi, &cosphi);
2044
2045         glBegin(GL_QUADS);
2046         for(i=0; i<stacks; i++) {
2047                 v = i * dv;
2048                 for(j=0; j<slices; j++) {
2049                         u = j * du;
2050                         for(k=0; k<4; k++) {
2051                                 gray = k ^ (k >> 1);
2052                                 s = gray & 2 ? u + du : u;
2053                                 t = gray & 1 ? v + dv : v;
2054                                 rad = rbot + (rtop - rbot) * t;
2055                                 theta = s * PI * 2.0f;
2056                                 mglut_sincos(theta, &sintheta, &costheta);
2057
2058                                 x = sintheta * cosphi;
2059                                 y = costheta * cosphi;
2060                                 z = sinphi;
2061
2062                                 glColor3f(s, t, 1);
2063                                 glTexCoord2f(s, t);
2064                                 glNormal3f(x, y, z);
2065                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
2066                         }
2067                 }
2068         }
2069         glEnd();
2070 }
2071
2072 void glutSolidCone(float base, float height, int slices, int stacks)
2073 {
2074         draw_cylinder(base, 0, height, slices, stacks);
2075 }
2076
2077 void glutWireCone(float base, float height, int slices, int stacks)
2078 {
2079         glPushAttrib(GL_POLYGON_BIT);
2080         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2081         glutSolidCone(base, height, slices, stacks);
2082         glPopAttrib();
2083 }
2084
2085 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2086 {
2087         draw_cylinder(rad, rad, height, slices, stacks);
2088 }
2089
2090 void glutWireCylinder(float rad, float height, int slices, int stacks)
2091 {
2092         glPushAttrib(GL_POLYGON_BIT);
2093         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2094         glutSolidCylinder(rad, height, slices, stacks);
2095         glPopAttrib();
2096 }
2097
2098 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2099 {
2100         int i, j, k, gray;
2101         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2102         float du = 1.0f / (float)rings;
2103         float dv = 1.0f / (float)sides;
2104
2105         glBegin(GL_QUADS);
2106         for(i=0; i<rings; i++) {
2107                 u = i * du;
2108                 for(j=0; j<sides; j++) {
2109                         v = j * dv;
2110                         for(k=0; k<4; k++) {
2111                                 gray = k ^ (k >> 1);
2112                                 s = gray & 1 ? u + du : u;
2113                                 t = gray & 2 ? v + dv : v;
2114                                 theta = s * PI * 2.0f;
2115                                 phi = t * PI * 2.0f;
2116                                 mglut_sincos(theta, &sintheta, &costheta);
2117                                 mglut_sincos(phi, &sinphi, &cosphi);
2118                                 x = sintheta * sinphi;
2119                                 y = costheta * sinphi;
2120                                 z = cosphi;
2121
2122                                 glColor3f(s, t, 1);
2123                                 glTexCoord2f(s, t);
2124                                 glNormal3f(x, y, z);
2125
2126                                 x = x * inner_rad + sintheta * outer_rad;
2127                                 y = y * inner_rad + costheta * outer_rad;
2128                                 z *= inner_rad;
2129                                 glVertex3f(x, y, z);
2130                         }
2131                 }
2132         }
2133         glEnd();
2134 }
2135
2136 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2137 {
2138         glPushAttrib(GL_POLYGON_BIT);
2139         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2140         glutSolidTorus(inner_rad, outer_rad, sides, rings);
2141         glPopAttrib();
2142 }
2143
2144 #define NUM_TEAPOT_INDICES      (sizeof teapot_index / sizeof *teapot_index)
2145 #define NUM_TEAPOT_VERTS        (sizeof teapot_verts / sizeof *teapot_verts)
2146
2147 #define NUM_TEAPOT_PATCHES      (NUM_TEAPOT_INDICES / 16)
2148
2149 #define PATCH_SUBDIV    7
2150
2151 static float teapot_part_flip[] = {
2152         1, 1, 1, 1,                     /* rim flip */
2153         1, 1, 1, 1,                     /* body1 flip */
2154         1, 1, 1, 1,                     /* body2 flip */
2155         1, 1, 1, 1,                     /* lid patch 1 flip */
2156         1, 1, 1, 1,                     /* lid patch 2 flip */
2157         1, -1,                          /* handle 1 flip */
2158         1, -1,                          /* handle 2 flip */
2159         1, -1,                          /* spout 1 flip */
2160         1, -1,                          /* spout 2 flip */
2161         1, 1, 1, 1                      /* bottom flip */
2162 };
2163
2164 static float teapot_part_rot[] = {
2165         0, 90, 180, 270,        /* rim rotations */
2166         0, 90, 180, 270,        /* body patch 1 rotations */
2167         0, 90, 180, 270,        /* body patch 2 rotations */
2168         0, 90, 180, 270,        /* lid patch 1 rotations */
2169         0, 90, 180, 270,        /* lid patch 2 rotations */
2170         0, 0,                           /* handle 1 rotations */
2171         0, 0,                           /* handle 2 rotations */
2172         0, 0,                           /* spout 1 rotations */
2173         0, 0,                           /* spout 2 rotations */
2174         0, 90, 180, 270         /* bottom rotations */
2175 };
2176
2177
2178 static int teapot_index[] = {
2179         /* rim */
2180         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2181         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2182         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2183         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2184         /* body1 */
2185         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2186         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2187         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2188         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2189         /* body 2 */
2190         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2191         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2192         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2193         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2194         /* lid 1 */
2195         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2196         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2197         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2198         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2199         /* lid 2 */
2200         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2201         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2202         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2203         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2204         /* handle 1 */
2205         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2206         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2207         /* handle 2 */
2208         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2209         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2210         /* spout 1 */
2211         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2212         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2213         /* spout 2 */
2214         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2215         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2216         /* bottom */
2217         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2218         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2219         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2220         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2221 };
2222
2223
2224 static float teapot_verts[][3] = {
2225         {  0.2000,  0.0000, 2.70000 }, {  0.2000, -0.1120, 2.70000 },
2226         {  0.1120, -0.2000, 2.70000 }, {  0.0000, -0.2000, 2.70000 },
2227         {  1.3375,  0.0000, 2.53125 }, {  1.3375, -0.7490, 2.53125 },
2228         {  0.7490, -1.3375, 2.53125 }, {  0.0000, -1.3375, 2.53125 },
2229         {  1.4375,  0.0000, 2.53125 }, {  1.4375, -0.8050, 2.53125 },
2230         {  0.8050, -1.4375, 2.53125 }, {  0.0000, -1.4375, 2.53125 },
2231         {  1.5000,  0.0000, 2.40000 }, {  1.5000, -0.8400, 2.40000 },
2232         {  0.8400, -1.5000, 2.40000 }, {  0.0000, -1.5000, 2.40000 },
2233         {  1.7500,  0.0000, 1.87500 }, {  1.7500, -0.9800, 1.87500 },
2234         {  0.9800, -1.7500, 1.87500 }, {  0.0000, -1.7500, 1.87500 },
2235         {  2.0000,  0.0000, 1.35000 }, {  2.0000, -1.1200, 1.35000 },
2236         {  1.1200, -2.0000, 1.35000 }, {  0.0000, -2.0000, 1.35000 },
2237         {  2.0000,  0.0000, 0.90000 }, {  2.0000, -1.1200, 0.90000 },
2238         {  1.1200, -2.0000, 0.90000 }, {  0.0000, -2.0000, 0.90000 },
2239         { -2.0000,  0.0000, 0.90000 }, {  2.0000,  0.0000, 0.45000 },
2240         {  2.0000, -1.1200, 0.45000 }, {  1.1200, -2.0000, 0.45000 },
2241         {  0.0000, -2.0000, 0.45000 }, {  1.5000,  0.0000, 0.22500 },
2242         {  1.5000, -0.8400, 0.22500 }, {  0.8400, -1.5000, 0.22500 },
2243         {  0.0000, -1.5000, 0.22500 }, {  1.5000,  0.0000, 0.15000 },
2244         {  1.5000, -0.8400, 0.15000 }, {  0.8400, -1.5000, 0.15000 },
2245         {  0.0000, -1.5000, 0.15000 }, { -1.6000,  0.0000, 2.02500 },
2246         { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2247         { -1.5000,  0.0000, 2.25000 }, { -2.3000,  0.0000, 2.02500 },
2248         { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2249         { -2.5000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 2.02500 },
2250         { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2251         { -3.0000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 1.80000 },
2252         { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2253         { -3.0000,  0.0000, 1.80000 }, { -2.7000,  0.0000, 1.57500 },
2254         { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2255         { -3.0000,  0.0000, 1.35000 }, { -2.5000,  0.0000, 1.12500 },
2256         { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2257         { -2.6500,  0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2258         { -1.9000, -0.3000, 0.60000 }, { -1.9000,  0.0000, 0.60000 },
2259         {  1.7000,  0.0000, 1.42500 }, {  1.7000, -0.6600, 1.42500 },
2260         {  1.7000, -0.6600, 0.60000 }, {  1.7000,  0.0000, 0.60000 },
2261         {  2.6000,  0.0000, 1.42500 }, {  2.6000, -0.6600, 1.42500 },
2262         {  3.1000, -0.6600, 0.82500 }, {  3.1000,  0.0000, 0.82500 },
2263         {  2.3000,  0.0000, 2.10000 }, {  2.3000, -0.2500, 2.10000 },
2264         {  2.4000, -0.2500, 2.02500 }, {  2.4000,  0.0000, 2.02500 },
2265         {  2.7000,  0.0000, 2.40000 }, {  2.7000, -0.2500, 2.40000 },
2266         {  3.3000, -0.2500, 2.40000 }, {  3.3000,  0.0000, 2.40000 },
2267         {  2.8000,  0.0000, 2.47500 }, {  2.8000, -0.2500, 2.47500 },
2268         {  3.5250, -0.2500, 2.49375 }, {  3.5250,  0.0000, 2.49375 },
2269         {  2.9000,  0.0000, 2.47500 }, {  2.9000, -0.1500, 2.47500 },
2270         {  3.4500, -0.1500, 2.51250 }, {  3.4500,  0.0000, 2.51250 },
2271         {  2.8000,  0.0000, 2.40000 }, {  2.8000, -0.1500, 2.40000 },
2272         {  3.2000, -0.1500, 2.40000 }, {  3.2000,  0.0000, 2.40000 },
2273         {  0.0000,  0.0000, 3.15000 }, {  0.8000,  0.0000, 3.15000 },
2274         {  0.8000, -0.4500, 3.15000 }, {  0.4500, -0.8000, 3.15000 },
2275         {  0.0000, -0.8000, 3.15000 }, {  0.0000,  0.0000, 2.85000 },
2276         {  1.4000,  0.0000, 2.40000 }, {  1.4000, -0.7840, 2.40000 },
2277         {  0.7840, -1.4000, 2.40000 }, {  0.0000, -1.4000, 2.40000 },
2278         {  0.4000,  0.0000, 2.55000 }, {  0.4000, -0.2240, 2.55000 },
2279         {  0.2240, -0.4000, 2.55000 }, {  0.0000, -0.4000, 2.55000 },
2280         {  1.3000,  0.0000, 2.55000 }, {  1.3000, -0.7280, 2.55000 },
2281         {  0.7280, -1.3000, 2.55000 }, {  0.0000, -1.3000, 2.55000 },
2282         {  1.3000,  0.0000, 2.40000 }, {  1.3000, -0.7280, 2.40000 },
2283         {  0.7280, -1.3000, 2.40000 }, {  0.0000, -1.3000, 2.40000 },
2284         {  0.0000,  0.0000, 0.00000 }, {  1.4250, -0.7980, 0.00000 },
2285         {  1.5000,  0.0000, 0.07500 }, {  1.4250,  0.0000, 0.00000 },
2286         {  0.7980, -1.4250, 0.00000 }, {  0.0000, -1.5000, 0.07500 },
2287         {  0.0000, -1.4250, 0.00000 }, {  1.5000, -0.8400, 0.07500 },
2288         {  0.8400, -1.5000, 0.07500 }
2289 };
2290
2291 static void draw_patch(int *index, int flip, float scale);
2292 static float bernstein(int i, float x);
2293
2294 void glutSolidTeapot(float size)
2295 {
2296         int i;
2297
2298         size /= 2.0;
2299
2300         for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2301                 float flip = teapot_part_flip[i];
2302                 float rot = teapot_part_rot[i];
2303
2304                 glMatrixMode(GL_MODELVIEW);
2305                 glPushMatrix();
2306                 glTranslatef(0, -3.15 * size * 0.5, 0);
2307                 glRotatef(rot, 0, 1, 0);
2308                 glScalef(1, 1, flip);
2309                 glRotatef(-90, 1, 0, 0);
2310
2311                 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2312
2313                 glPopMatrix();
2314         }
2315 }
2316
2317 void glutWireTeapot(float size)
2318 {
2319         glPushAttrib(GL_POLYGON_BIT);
2320         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2321         glutSolidTeapot(size);
2322         glPopAttrib();
2323 }
2324
2325
2326 static void bezier_patch(float *res, float *cp, float u, float v)
2327 {
2328         int i, j;
2329
2330         res[0] = res[1] = res[2] = 0.0f;
2331
2332         for(j=0; j<4; j++) {
2333                 for(i=0; i<4; i++) {
2334                         float bu = bernstein(i, u);
2335                         float bv = bernstein(j, v);
2336
2337                         res[0] += cp[0] * bu * bv;
2338                         res[1] += cp[1] * bu * bv;
2339                         res[2] += cp[2] * bu * bv;
2340
2341                         cp += 3;
2342                 }
2343         }
2344 }
2345
2346 static float rsqrt(float x)
2347 {
2348         float xhalf = x * 0.5f;
2349         int i = *(int*)&x;
2350         i = 0x5f3759df - (i >> 1);
2351         x = *(float*)&i;
2352         x = x * (1.5f - xhalf * x * x);
2353         return x;
2354 }
2355
2356
2357 #define CROSS(res, a, b) \
2358         do { \
2359                 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2360                 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2361                 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2362         } while(0)
2363
2364 #define NORMALIZE(v) \
2365         do { \
2366                 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2367                 (v)[0] *= s; \
2368                 (v)[1] *= s; \
2369                 (v)[2] *= s; \
2370         } while(0)
2371
2372 #define DT      0.001
2373
2374 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2375 {
2376         float tang[3], bitan[3], tmp[3];
2377
2378         bezier_patch(tang, cp, u + DT, v);
2379         bezier_patch(tmp, cp, u - DT, v);
2380         tang[0] -= tmp[0];
2381         tang[1] -= tmp[1];
2382         tang[2] -= tmp[2];
2383
2384         bezier_patch(bitan, cp, u, v + DT);
2385         bezier_patch(tmp, cp, u, v - DT);
2386         bitan[0] -= tmp[0];
2387         bitan[1] -= tmp[1];
2388         bitan[2] -= tmp[2];
2389
2390         CROSS(res, tang, bitan);
2391         NORMALIZE(res);
2392 }
2393
2394
2395
2396 static float bernstein(int i, float x)
2397 {
2398         float invx = 1.0f - x;
2399
2400         switch(i) {
2401         case 0:
2402                 return invx * invx * invx;
2403         case 1:
2404                 return 3.0f * x * invx * invx;
2405         case 2:
2406                 return 3.0f * x * x * invx;
2407         case 3:
2408                 return x * x * x;
2409         default:
2410                 break;
2411         }
2412         return 0.0f;
2413 }
2414
2415 static void draw_patch(int *index, int flip, float scale)
2416 {
2417         static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2418         static const float voffs[4] = {0, 1, 1, 0};
2419
2420         int i, j, k;
2421         float cp[16 * 3];
2422         float pt[3], n[3];
2423         float u, v;
2424         float du = 1.0 / PATCH_SUBDIV;
2425         float dv = 1.0 / PATCH_SUBDIV;
2426
2427         /* collect control points */
2428         for(i=0; i<16; i++) {
2429                 cp[i * 3] = teapot_verts[index[i]][0];
2430                 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2431                 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2432         }
2433
2434         glBegin(GL_QUADS);
2435         glColor3f(1, 1, 1);
2436
2437         u = 0;
2438         for(i=0; i<PATCH_SUBDIV; i++) {
2439                 v = 0;
2440                 for(j=0; j<PATCH_SUBDIV; j++) {
2441
2442                         for(k=0; k<4; k++) {
2443                                 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2444
2445                                 /* top/bottom normal hack */
2446                                 if(pt[2] > 3.14) {
2447                                         n[0] = n[1] = 0.0f;
2448                                         n[2] = 1.0f;
2449                                 } else if(pt[2] < 0.00001) {
2450                                         n[0] = n[1] = 0.0f;
2451                                         n[2] = -1.0f;
2452                                 } else {
2453                                         bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2454                                 }
2455
2456                                 glTexCoord2f(u, v);
2457                                 glNormal3fv(n);
2458                                 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
2459                         }
2460
2461                         v += dv;
2462                 }
2463                 u += du;
2464         }
2465
2466         glEnd();
2467 }