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