win32: fixed missing VKEY translations for VK_OEM_*
[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         case VK_OEM_1: return ';';
1673         case VK_OEM_2: return '/';
1674         case VK_OEM_3: return '`';
1675         case VK_OEM_4: return '[';
1676         case VK_OEM_5: return '\\';
1677         case VK_OEM_6: return ']';
1678         case VK_OEM_7: return '\'';
1679         default:
1680                 break;
1681         }
1682
1683         if(vkey >= 'A' && vkey <= 'Z') {
1684                 vkey += 32;
1685         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1686                 vkey -= VK_F1 + GLUT_KEY_F1;
1687         }
1688
1689         return vkey;
1690 }
1691
1692 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1693 {
1694         int x, y;
1695
1696         update_modkeys();
1697
1698         if(cb_mouse) {
1699                 x = lparam & 0xffff;
1700                 y = lparam >> 16;
1701                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1702         }
1703 }
1704
1705 static void get_window_pos(int *x, int *y)
1706 {
1707         RECT rect;
1708         GetWindowRect(win, &rect);
1709         *x = rect.left;
1710         *y = rect.top;
1711 }
1712
1713 static void get_window_size(int *w, int *h)
1714 {
1715         RECT rect;
1716         GetClientRect(win, &rect);
1717         *w = rect.right - rect.left;
1718         *h = rect.bottom - rect.top;
1719 }
1720
1721 static void get_screen_size(int *scrw, int *scrh)
1722 {
1723         *scrw = GetSystemMetrics(SM_CXSCREEN);
1724         *scrh = GetSystemMetrics(SM_CYSCREEN);
1725 }
1726 #endif  /* BUILD_WIN32 */
1727
1728 #if defined(__unix__) || defined(__APPLE__)
1729 #include <sys/time.h>
1730
1731 #ifdef MINIGLUT_USE_LIBC
1732 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1733 #else
1734 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1735 #endif
1736
1737 static long get_msec(void)
1738 {
1739         static struct timeval tv0;
1740         struct timeval tv;
1741
1742         sys_gettimeofday(&tv, 0);
1743         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1744                 tv0 = tv;
1745                 return 0;
1746         }
1747         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1748 }
1749 #endif  /* UNIX */
1750 #ifdef _WIN32
1751 static long get_msec(void)
1752 {
1753         static long t0;
1754         long tm;
1755
1756 #ifdef MINIGLUT_NO_WINMM
1757         tm = GetTickCount();
1758 #else
1759         tm = timeGetTime();
1760 #endif
1761         if(!t0) {
1762                 t0 = tm;
1763                 return 0;
1764         }
1765         return tm - t0;
1766 }
1767 #endif
1768
1769 static void panic(const char *msg)
1770 {
1771         const char *end = msg;
1772         while(*end) end++;
1773         sys_write(2, msg, end - msg);
1774         sys_exit(1);
1775 }
1776
1777
1778 #ifdef MINIGLUT_USE_LIBC
1779 #include <stdlib.h>
1780 #ifdef _WIN32
1781 #include <io.h>
1782 #else
1783 #include <unistd.h>
1784 #endif
1785
1786 static void sys_exit(int status)
1787 {
1788         exit(status);
1789 }
1790
1791 static int sys_write(int fd, const void *buf, int count)
1792 {
1793         return write(fd, buf, count);
1794 }
1795
1796 #else   /* !MINIGLUT_USE_LIBC */
1797
1798 #ifdef __linux__
1799 #ifdef __x86_64__
1800 static void sys_exit(int status)
1801 {
1802         asm volatile(
1803                 "syscall\n\t"
1804                 :: "a"(60), "D"(status));
1805 }
1806 static int sys_write(int fd, const void *buf, int count)
1807 {
1808         long res;
1809         asm volatile(
1810                 "syscall\n\t"
1811                 : "=a"(res)
1812                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1813         return res;
1814 }
1815 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1816 {
1817         int res;
1818         asm volatile(
1819                 "syscall\n\t"
1820                 : "=a"(res)
1821                 : "a"(96), "D"(tv), "S"(tz));
1822         return res;
1823 }
1824 #endif  /* __x86_64__ */
1825 #ifdef __i386__
1826 static void sys_exit(int status)
1827 {
1828         asm volatile(
1829                 "int $0x80\n\t"
1830                 :: "a"(1), "b"(status));
1831 }
1832 static int sys_write(int fd, const void *buf, int count)
1833 {
1834         int res;
1835         asm volatile(
1836                 "int $0x80\n\t"
1837                 : "=a"(res)
1838                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1839         return res;
1840 }
1841 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1842 {
1843         int res;
1844         asm volatile(
1845                 "int $0x80\n\t"
1846                 : "=a"(res)
1847                 : "a"(78), "b"(tv), "c"(tz));
1848         return res;
1849 }
1850 #endif  /* __i386__ */
1851 #endif  /* __linux__ */
1852
1853 #ifdef _WIN32
1854 static void sys_exit(int status)
1855 {
1856         ExitProcess(status);
1857 }
1858 static int sys_write(int fd, const void *buf, int count)
1859 {
1860         unsigned long wrsz = 0;
1861
1862         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1863         if(!WriteFile(out, buf, count, &wrsz, 0)) {
1864                 return -1;
1865         }
1866         return wrsz;
1867 }
1868 #endif  /* _WIN32 */
1869 #endif  /* !MINIGLUT_USE_LIBC */
1870
1871
1872 /* ----------------- primitives ------------------ */
1873 #ifdef MINIGLUT_USE_LIBC
1874 #include <stdlib.h>
1875 #include <math.h>
1876
1877 void mglut_sincos(float angle, float *sptr, float *cptr)
1878 {
1879         *sptr = sin(angle);
1880         *cptr = cos(angle);
1881 }
1882
1883 float mglut_atan(float x)
1884 {
1885         return atan(x);
1886 }
1887
1888 #else   /* !MINIGLUT_USE_LIBC */
1889
1890 #ifdef __GNUC__
1891 void mglut_sincos(float angle, float *sptr, float *cptr)
1892 {
1893         asm volatile(
1894                 "flds %2\n\t"
1895                 "fsincos\n\t"
1896                 "fstps %1\n\t"
1897                 "fstps %0\n\t"
1898                 : "=m"(*sptr), "=m"(*cptr)
1899                 : "m"(angle)
1900         );
1901 }
1902
1903 float mglut_atan(float x)
1904 {
1905         float res;
1906         asm volatile(
1907                 "flds %1\n\t"
1908                 "fld1\n\t"
1909                 "fpatan\n\t"
1910                 "fstps %0\n\t"
1911                 : "=m"(res)
1912                 : "m"(x)
1913         );
1914         return res;
1915 }
1916 #endif
1917
1918 #ifdef _MSC_VER
1919 void mglut_sincos(float angle, float *sptr, float *cptr)
1920 {
1921         float s, c;
1922         __asm {
1923                 fld angle
1924                 fsincos
1925                 fstp c
1926                 fstp s
1927         }
1928         *sptr = s;
1929         *cptr = c;
1930 }
1931
1932 float mglut_atan(float x)
1933 {
1934         float res;
1935         __asm {
1936                 fld x
1937                 fld1
1938                 fpatan
1939                 fstp res
1940         }
1941         return res;
1942 }
1943 #endif
1944
1945 #ifdef __WATCOMC__
1946 #pragma aux mglut_sincos = \
1947         "fsincos" \
1948         "fstp dword ptr [edx]" \
1949         "fstp dword ptr [eax]" \
1950         parm[8087][eax][edx]    \
1951         modify[8087];
1952
1953 #pragma aux mglut_atan = \
1954         "fld1" \
1955         "fpatan" \
1956         parm[8087] \
1957         value[8087] \
1958         modify [8087];
1959 #endif  /* __WATCOMC__ */
1960
1961 #endif  /* !MINIGLUT_USE_LIBC */
1962
1963 #define PI      3.1415926536f
1964
1965 void glutSolidSphere(float rad, int slices, int stacks)
1966 {
1967         int i, j, k, gray;
1968         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1969         float du = 1.0f / (float)slices;
1970         float dv = 1.0f / (float)stacks;
1971
1972         glBegin(GL_QUADS);
1973         for(i=0; i<stacks; i++) {
1974                 v = i * dv;
1975                 for(j=0; j<slices; j++) {
1976                         u = j * du;
1977                         for(k=0; k<4; k++) {
1978                                 gray = k ^ (k >> 1);
1979                                 s = gray & 1 ? u + du : u;
1980                                 t = gray & 2 ? v + dv : v;
1981                                 theta = s * PI * 2.0f;
1982                                 phi = t * PI;
1983                                 mglut_sincos(theta, &sintheta, &costheta);
1984                                 mglut_sincos(phi, &sinphi, &cosphi);
1985                                 x = sintheta * sinphi;
1986                                 y = costheta * sinphi;
1987                                 z = cosphi;
1988
1989                                 glColor3f(s, t, 1);
1990                                 glTexCoord2f(s, t);
1991                                 glNormal3f(x, y, z);
1992                                 glVertex3f(x * rad, y * rad, z * rad);
1993                         }
1994                 }
1995         }
1996         glEnd();
1997 }
1998
1999 void glutWireSphere(float rad, int slices, int stacks)
2000 {
2001         glPushAttrib(GL_POLYGON_BIT);
2002         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2003         glutSolidSphere(rad, slices, stacks);
2004         glPopAttrib();
2005 }
2006
2007 void glutSolidCube(float sz)
2008 {
2009         int i, j, idx, gray, flip, rotx;
2010         float vpos[3], norm[3];
2011         float rad = sz * 0.5f;
2012
2013         glBegin(GL_QUADS);
2014         for(i=0; i<6; i++) {
2015                 flip = i & 1;
2016                 rotx = i >> 2;
2017                 idx = (~i & 2) - rotx;
2018                 norm[0] = norm[1] = norm[2] = 0.0f;
2019                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2020                 glNormal3fv(norm);
2021                 vpos[idx] = norm[idx] * rad;
2022                 for(j=0; j<4; j++) {
2023                         gray = j ^ (j >> 1);
2024                         vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2025                         vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2026                         glTexCoord2f(gray & 1, gray >> 1);
2027                         glVertex3fv(vpos);
2028                 }
2029         }
2030         glEnd();
2031 }
2032
2033 void glutWireCube(float sz)
2034 {
2035         glPushAttrib(GL_POLYGON_BIT);
2036         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2037         glutSolidCube(sz);
2038         glPopAttrib();
2039 }
2040
2041 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2042 {
2043         int i, j, k, gray;
2044         float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2045         float du = 1.0f / (float)slices;
2046         float dv = 1.0f / (float)stacks;
2047
2048         rad = rbot - rtop;
2049         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2050         mglut_sincos(phi, &sinphi, &cosphi);
2051
2052         glBegin(GL_QUADS);
2053         for(i=0; i<stacks; i++) {
2054                 v = i * dv;
2055                 for(j=0; j<slices; j++) {
2056                         u = j * du;
2057                         for(k=0; k<4; k++) {
2058                                 gray = k ^ (k >> 1);
2059                                 s = gray & 2 ? u + du : u;
2060                                 t = gray & 1 ? v + dv : v;
2061                                 rad = rbot + (rtop - rbot) * t;
2062                                 theta = s * PI * 2.0f;
2063                                 mglut_sincos(theta, &sintheta, &costheta);
2064
2065                                 x = sintheta * cosphi;
2066                                 y = costheta * cosphi;
2067                                 z = sinphi;
2068
2069                                 glColor3f(s, t, 1);
2070                                 glTexCoord2f(s, t);
2071                                 glNormal3f(x, y, z);
2072                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
2073                         }
2074                 }
2075         }
2076         glEnd();
2077 }
2078
2079 void glutSolidCone(float base, float height, int slices, int stacks)
2080 {
2081         draw_cylinder(base, 0, height, slices, stacks);
2082 }
2083
2084 void glutWireCone(float base, float height, int slices, int stacks)
2085 {
2086         glPushAttrib(GL_POLYGON_BIT);
2087         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2088         glutSolidCone(base, height, slices, stacks);
2089         glPopAttrib();
2090 }
2091
2092 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2093 {
2094         draw_cylinder(rad, rad, height, slices, stacks);
2095 }
2096
2097 void glutWireCylinder(float rad, float height, int slices, int stacks)
2098 {
2099         glPushAttrib(GL_POLYGON_BIT);
2100         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2101         glutSolidCylinder(rad, height, slices, stacks);
2102         glPopAttrib();
2103 }
2104
2105 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2106 {
2107         int i, j, k, gray;
2108         float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2109         float du = 1.0f / (float)rings;
2110         float dv = 1.0f / (float)sides;
2111
2112         glBegin(GL_QUADS);
2113         for(i=0; i<rings; i++) {
2114                 u = i * du;
2115                 for(j=0; j<sides; j++) {
2116                         v = j * dv;
2117                         for(k=0; k<4; k++) {
2118                                 gray = k ^ (k >> 1);
2119                                 s = gray & 1 ? u + du : u;
2120                                 t = gray & 2 ? v + dv : v;
2121                                 theta = s * PI * 2.0f;
2122                                 phi = t * PI * 2.0f;
2123                                 mglut_sincos(theta, &sintheta, &costheta);
2124                                 mglut_sincos(phi, &sinphi, &cosphi);
2125                                 x = sintheta * sinphi;
2126                                 y = costheta * sinphi;
2127                                 z = cosphi;
2128
2129                                 glColor3f(s, t, 1);
2130                                 glTexCoord2f(s, t);
2131                                 glNormal3f(x, y, z);
2132
2133                                 x = x * inner_rad + sintheta * outer_rad;
2134                                 y = y * inner_rad + costheta * outer_rad;
2135                                 z *= inner_rad;
2136                                 glVertex3f(x, y, z);
2137                         }
2138                 }
2139         }
2140         glEnd();
2141 }
2142
2143 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2144 {
2145         glPushAttrib(GL_POLYGON_BIT);
2146         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2147         glutSolidTorus(inner_rad, outer_rad, sides, rings);
2148         glPopAttrib();
2149 }
2150
2151 #define NUM_TEAPOT_INDICES      (sizeof teapot_index / sizeof *teapot_index)
2152 #define NUM_TEAPOT_VERTS        (sizeof teapot_verts / sizeof *teapot_verts)
2153
2154 #define NUM_TEAPOT_PATCHES      (NUM_TEAPOT_INDICES / 16)
2155
2156 #define PATCH_SUBDIV    7
2157
2158 static float teapot_part_flip[] = {
2159         1, 1, 1, 1,                     /* rim flip */
2160         1, 1, 1, 1,                     /* body1 flip */
2161         1, 1, 1, 1,                     /* body2 flip */
2162         1, 1, 1, 1,                     /* lid patch 1 flip */
2163         1, 1, 1, 1,                     /* lid patch 2 flip */
2164         1, -1,                          /* handle 1 flip */
2165         1, -1,                          /* handle 2 flip */
2166         1, -1,                          /* spout 1 flip */
2167         1, -1,                          /* spout 2 flip */
2168         1, 1, 1, 1                      /* bottom flip */
2169 };
2170
2171 static float teapot_part_rot[] = {
2172         0, 90, 180, 270,        /* rim rotations */
2173         0, 90, 180, 270,        /* body patch 1 rotations */
2174         0, 90, 180, 270,        /* body patch 2 rotations */
2175         0, 90, 180, 270,        /* lid patch 1 rotations */
2176         0, 90, 180, 270,        /* lid patch 2 rotations */
2177         0, 0,                           /* handle 1 rotations */
2178         0, 0,                           /* handle 2 rotations */
2179         0, 0,                           /* spout 1 rotations */
2180         0, 0,                           /* spout 2 rotations */
2181         0, 90, 180, 270         /* bottom rotations */
2182 };
2183
2184
2185 static int teapot_index[] = {
2186         /* rim */
2187         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2188         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2189         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2190         102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2191         /* body1 */
2192         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2193         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2194         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2195         12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2196         /* body 2 */
2197         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2198         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2199         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2200         24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2201         /* lid 1 */
2202         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2203         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2204         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2205         96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0,  1,  2, 3,
2206         /* lid 2 */
2207         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2208         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2209         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2210         0,  1,  2,  3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2211         /* handle 1 */
2212         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2213         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2214         /* handle 2 */
2215         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2216         53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2217         /* spout 1 */
2218         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2219         68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2220         /* spout 2 */
2221         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2222         80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2223         /* bottom */
2224         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2225         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2226         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2227         118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2228 };
2229
2230
2231 static float teapot_verts[][3] = {
2232         {  0.2000,  0.0000, 2.70000 }, {  0.2000, -0.1120, 2.70000 },
2233         {  0.1120, -0.2000, 2.70000 }, {  0.0000, -0.2000, 2.70000 },
2234         {  1.3375,  0.0000, 2.53125 }, {  1.3375, -0.7490, 2.53125 },
2235         {  0.7490, -1.3375, 2.53125 }, {  0.0000, -1.3375, 2.53125 },
2236         {  1.4375,  0.0000, 2.53125 }, {  1.4375, -0.8050, 2.53125 },
2237         {  0.8050, -1.4375, 2.53125 }, {  0.0000, -1.4375, 2.53125 },
2238         {  1.5000,  0.0000, 2.40000 }, {  1.5000, -0.8400, 2.40000 },
2239         {  0.8400, -1.5000, 2.40000 }, {  0.0000, -1.5000, 2.40000 },
2240         {  1.7500,  0.0000, 1.87500 }, {  1.7500, -0.9800, 1.87500 },
2241         {  0.9800, -1.7500, 1.87500 }, {  0.0000, -1.7500, 1.87500 },
2242         {  2.0000,  0.0000, 1.35000 }, {  2.0000, -1.1200, 1.35000 },
2243         {  1.1200, -2.0000, 1.35000 }, {  0.0000, -2.0000, 1.35000 },
2244         {  2.0000,  0.0000, 0.90000 }, {  2.0000, -1.1200, 0.90000 },
2245         {  1.1200, -2.0000, 0.90000 }, {  0.0000, -2.0000, 0.90000 },
2246         { -2.0000,  0.0000, 0.90000 }, {  2.0000,  0.0000, 0.45000 },
2247         {  2.0000, -1.1200, 0.45000 }, {  1.1200, -2.0000, 0.45000 },
2248         {  0.0000, -2.0000, 0.45000 }, {  1.5000,  0.0000, 0.22500 },
2249         {  1.5000, -0.8400, 0.22500 }, {  0.8400, -1.5000, 0.22500 },
2250         {  0.0000, -1.5000, 0.22500 }, {  1.5000,  0.0000, 0.15000 },
2251         {  1.5000, -0.8400, 0.15000 }, {  0.8400, -1.5000, 0.15000 },
2252         {  0.0000, -1.5000, 0.15000 }, { -1.6000,  0.0000, 2.02500 },
2253         { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2254         { -1.5000,  0.0000, 2.25000 }, { -2.3000,  0.0000, 2.02500 },
2255         { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2256         { -2.5000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 2.02500 },
2257         { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2258         { -3.0000,  0.0000, 2.25000 }, { -2.7000,  0.0000, 1.80000 },
2259         { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2260         { -3.0000,  0.0000, 1.80000 }, { -2.7000,  0.0000, 1.57500 },
2261         { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2262         { -3.0000,  0.0000, 1.35000 }, { -2.5000,  0.0000, 1.12500 },
2263         { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2264         { -2.6500,  0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2265         { -1.9000, -0.3000, 0.60000 }, { -1.9000,  0.0000, 0.60000 },
2266         {  1.7000,  0.0000, 1.42500 }, {  1.7000, -0.6600, 1.42500 },
2267         {  1.7000, -0.6600, 0.60000 }, {  1.7000,  0.0000, 0.60000 },
2268         {  2.6000,  0.0000, 1.42500 }, {  2.6000, -0.6600, 1.42500 },
2269         {  3.1000, -0.6600, 0.82500 }, {  3.1000,  0.0000, 0.82500 },
2270         {  2.3000,  0.0000, 2.10000 }, {  2.3000, -0.2500, 2.10000 },
2271         {  2.4000, -0.2500, 2.02500 }, {  2.4000,  0.0000, 2.02500 },
2272         {  2.7000,  0.0000, 2.40000 }, {  2.7000, -0.2500, 2.40000 },
2273         {  3.3000, -0.2500, 2.40000 }, {  3.3000,  0.0000, 2.40000 },
2274         {  2.8000,  0.0000, 2.47500 }, {  2.8000, -0.2500, 2.47500 },
2275         {  3.5250, -0.2500, 2.49375 }, {  3.5250,  0.0000, 2.49375 },
2276         {  2.9000,  0.0000, 2.47500 }, {  2.9000, -0.1500, 2.47500 },
2277         {  3.4500, -0.1500, 2.51250 }, {  3.4500,  0.0000, 2.51250 },
2278         {  2.8000,  0.0000, 2.40000 }, {  2.8000, -0.1500, 2.40000 },
2279         {  3.2000, -0.1500, 2.40000 }, {  3.2000,  0.0000, 2.40000 },
2280         {  0.0000,  0.0000, 3.15000 }, {  0.8000,  0.0000, 3.15000 },
2281         {  0.8000, -0.4500, 3.15000 }, {  0.4500, -0.8000, 3.15000 },
2282         {  0.0000, -0.8000, 3.15000 }, {  0.0000,  0.0000, 2.85000 },
2283         {  1.4000,  0.0000, 2.40000 }, {  1.4000, -0.7840, 2.40000 },
2284         {  0.7840, -1.4000, 2.40000 }, {  0.0000, -1.4000, 2.40000 },
2285         {  0.4000,  0.0000, 2.55000 }, {  0.4000, -0.2240, 2.55000 },
2286         {  0.2240, -0.4000, 2.55000 }, {  0.0000, -0.4000, 2.55000 },
2287         {  1.3000,  0.0000, 2.55000 }, {  1.3000, -0.7280, 2.55000 },
2288         {  0.7280, -1.3000, 2.55000 }, {  0.0000, -1.3000, 2.55000 },
2289         {  1.3000,  0.0000, 2.40000 }, {  1.3000, -0.7280, 2.40000 },
2290         {  0.7280, -1.3000, 2.40000 }, {  0.0000, -1.3000, 2.40000 },
2291         {  0.0000,  0.0000, 0.00000 }, {  1.4250, -0.7980, 0.00000 },
2292         {  1.5000,  0.0000, 0.07500 }, {  1.4250,  0.0000, 0.00000 },
2293         {  0.7980, -1.4250, 0.00000 }, {  0.0000, -1.5000, 0.07500 },
2294         {  0.0000, -1.4250, 0.00000 }, {  1.5000, -0.8400, 0.07500 },
2295         {  0.8400, -1.5000, 0.07500 }
2296 };
2297
2298 static void draw_patch(int *index, int flip, float scale);
2299 static float bernstein(int i, float x);
2300
2301 void glutSolidTeapot(float size)
2302 {
2303         int i;
2304
2305         size /= 2.0;
2306
2307         for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2308                 float flip = teapot_part_flip[i];
2309                 float rot = teapot_part_rot[i];
2310
2311                 glMatrixMode(GL_MODELVIEW);
2312                 glPushMatrix();
2313                 glTranslatef(0, -3.15 * size * 0.5, 0);
2314                 glRotatef(rot, 0, 1, 0);
2315                 glScalef(1, 1, flip);
2316                 glRotatef(-90, 1, 0, 0);
2317
2318                 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2319
2320                 glPopMatrix();
2321         }
2322 }
2323
2324 void glutWireTeapot(float size)
2325 {
2326         glPushAttrib(GL_POLYGON_BIT);
2327         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2328         glutSolidTeapot(size);
2329         glPopAttrib();
2330 }
2331
2332
2333 static void bezier_patch(float *res, float *cp, float u, float v)
2334 {
2335         int i, j;
2336
2337         res[0] = res[1] = res[2] = 0.0f;
2338
2339         for(j=0; j<4; j++) {
2340                 for(i=0; i<4; i++) {
2341                         float bu = bernstein(i, u);
2342                         float bv = bernstein(j, v);
2343
2344                         res[0] += cp[0] * bu * bv;
2345                         res[1] += cp[1] * bu * bv;
2346                         res[2] += cp[2] * bu * bv;
2347
2348                         cp += 3;
2349                 }
2350         }
2351 }
2352
2353 static float rsqrt(float x)
2354 {
2355         float xhalf = x * 0.5f;
2356         int i = *(int*)&x;
2357         i = 0x5f3759df - (i >> 1);
2358         x = *(float*)&i;
2359         x = x * (1.5f - xhalf * x * x);
2360         return x;
2361 }
2362
2363
2364 #define CROSS(res, a, b) \
2365         do { \
2366                 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2367                 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2368                 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2369         } while(0)
2370
2371 #define NORMALIZE(v) \
2372         do { \
2373                 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2374                 (v)[0] *= s; \
2375                 (v)[1] *= s; \
2376                 (v)[2] *= s; \
2377         } while(0)
2378
2379 #define DT      0.001
2380
2381 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2382 {
2383         float tang[3], bitan[3], tmp[3];
2384
2385         bezier_patch(tang, cp, u + DT, v);
2386         bezier_patch(tmp, cp, u - DT, v);
2387         tang[0] -= tmp[0];
2388         tang[1] -= tmp[1];
2389         tang[2] -= tmp[2];
2390
2391         bezier_patch(bitan, cp, u, v + DT);
2392         bezier_patch(tmp, cp, u, v - DT);
2393         bitan[0] -= tmp[0];
2394         bitan[1] -= tmp[1];
2395         bitan[2] -= tmp[2];
2396
2397         CROSS(res, tang, bitan);
2398         NORMALIZE(res);
2399 }
2400
2401
2402
2403 static float bernstein(int i, float x)
2404 {
2405         float invx = 1.0f - x;
2406
2407         switch(i) {
2408         case 0:
2409                 return invx * invx * invx;
2410         case 1:
2411                 return 3.0f * x * invx * invx;
2412         case 2:
2413                 return 3.0f * x * x * invx;
2414         case 3:
2415                 return x * x * x;
2416         default:
2417                 break;
2418         }
2419         return 0.0f;
2420 }
2421
2422 static void draw_patch(int *index, int flip, float scale)
2423 {
2424         static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2425         static const float voffs[4] = {0, 1, 1, 0};
2426
2427         int i, j, k;
2428         float cp[16 * 3];
2429         float pt[3], n[3];
2430         float u, v;
2431         float du = 1.0 / PATCH_SUBDIV;
2432         float dv = 1.0 / PATCH_SUBDIV;
2433
2434         /* collect control points */
2435         for(i=0; i<16; i++) {
2436                 cp[i * 3] = teapot_verts[index[i]][0];
2437                 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2438                 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2439         }
2440
2441         glBegin(GL_QUADS);
2442         glColor3f(1, 1, 1);
2443
2444         u = 0;
2445         for(i=0; i<PATCH_SUBDIV; i++) {
2446                 v = 0;
2447                 for(j=0; j<PATCH_SUBDIV; j++) {
2448
2449                         for(k=0; k<4; k++) {
2450                                 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2451
2452                                 /* top/bottom normal hack */
2453                                 if(pt[2] > 3.14) {
2454                                         n[0] = n[1] = 0.0f;
2455                                         n[2] = 1.0f;
2456                                 } else if(pt[2] < 0.00001) {
2457                                         n[0] = n[1] = 0.0f;
2458                                         n[2] = -1.0f;
2459                                 } else {
2460                                         bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2461                                 }
2462
2463                                 glTexCoord2f(u, v);
2464                                 glNormal3fv(n);
2465                                 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);
2466                         }
2467
2468                         v += dv;
2469                 }
2470                 u += du;
2471         }
2472
2473         glEnd();
2474 }