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