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