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