2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020-2022 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
18 #if defined(unix) || defined(__unix__)
22 #include <X11/keysym.h>
23 #include <X11/cursorfont.h>
27 #ifndef GLX_SAMPLE_BUFFERS_ARB
28 #define GLX_SAMPLE_BUFFERS_ARB 100000
29 #define GLX_SAMPLES_ARB 100001
31 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
32 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
36 static Window win, root;
40 static GLXContext ctx;
41 static Atom xa_wm_proto, xa_wm_del_win;
42 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
43 static Atom xa_motif_wm_hints;
44 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
45 static unsigned int evmask;
46 static Cursor blank_cursor;
48 static int have_netwm_fullscr(void);
55 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
57 static HINSTANCE hinst;
65 #error unsupported platform
71 #pragma warning (disable: 4244 4305)
76 int rsize, gsize, bsize, asize;
84 static void cleanup(void);
85 static void create_window(const char *title);
86 static void get_window_pos(int *x, int *y);
87 static void get_window_size(int *w, int *h);
88 static void get_screen_size(int *scrw, int *scrh);
90 static long get_msec(void);
91 static void panic(const char *msg);
92 static void sys_exit(int status);
93 static int sys_write(int fd, const void *buf, int count);
96 static int init_x = -1, init_y, init_width = 256, init_height = 256;
97 static unsigned int init_mode;
99 static struct ctx_info ctx_info;
100 static int cur_cursor = GLUT_CURSOR_INHERIT;
101 static int ignore_key_repeat;
103 static glut_cb cb_display;
104 static glut_cb cb_idle;
105 static glut_cb_reshape cb_reshape;
106 static glut_cb_state cb_vis, cb_entry;
107 static glut_cb_keyb cb_keydown, cb_keyup;
108 static glut_cb_special cb_skeydown, cb_skeyup;
109 static glut_cb_mouse cb_mouse;
110 static glut_cb_motion cb_motion, cb_passive;
111 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
112 static glut_cb_sbbutton cb_sball_button;
114 static int fullscreen;
115 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
117 static int win_width, win_height;
120 static int upd_pending;
123 void glutInit(int *argc, char **argv)
129 if(!(dpy = XOpenDisplay(0))) {
130 panic("Failed to connect to the X server\n");
132 scr = DefaultScreen(dpy);
133 root = RootWindow(dpy, scr);
134 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
135 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
136 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
137 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
138 if(have_netwm_fullscr()) {
139 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
142 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
143 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
144 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
145 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
147 evmask = ExposureMask | StructureNotifyMask;
149 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
150 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
151 XFreePixmap(dpy, blankpix);
158 hinst = GetModuleHandle(0);
160 wc.cbSize = sizeof wc;
161 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
162 wc.hCursor = LoadCursor(0, IDC_ARROW);
163 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
164 wc.hInstance = hinst;
165 wc.lpfnWndProc = handle_message;
166 wc.lpszClassName = "MiniGLUT";
167 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
168 if(!RegisterClassEx(&wc)) {
169 panic("Failed to register \"MiniGLUT\" window class\n");
173 get_screen_size(&init_x, &init_y);
180 void glutInitWindowPosition(int x, int y)
186 void glutInitWindowSize(int xsz, int ysz)
192 void glutInitDisplayMode(unsigned int mode)
197 void glutCreateWindow(const char *title)
199 create_window(title);
207 void glutMainLoop(void)
214 void glutPostRedisplay(void)
219 void glutIgnoreKeyRepeat(int ignore)
221 ignore_key_repeat = ignore;
224 #define UPD_EVMASK(x) \
231 if(win) XSelectInput(dpy, win, evmask); \
235 void glutIdleFunc(glut_cb func)
240 void glutDisplayFunc(glut_cb func)
245 void glutReshapeFunc(glut_cb_reshape func)
250 void glutVisibilityFunc(glut_cb_state func)
254 UPD_EVMASK(VisibilityChangeMask);
258 void glutEntryFunc(glut_cb_state func)
262 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
266 void glutKeyboardFunc(glut_cb_keyb func)
270 UPD_EVMASK(KeyPressMask);
274 void glutKeyboardUpFunc(glut_cb_keyb func)
278 UPD_EVMASK(KeyReleaseMask);
282 void glutSpecialFunc(glut_cb_special func)
286 UPD_EVMASK(KeyPressMask);
290 void glutSpecialUpFunc(glut_cb_special func)
294 UPD_EVMASK(KeyReleaseMask);
298 void glutMouseFunc(glut_cb_mouse func)
302 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
306 void glutMotionFunc(glut_cb_motion func)
310 UPD_EVMASK(ButtonMotionMask);
314 void glutPassiveMotionFunc(glut_cb_motion func)
318 UPD_EVMASK(PointerMotionMask);
322 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
324 cb_sball_motion = func;
327 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
329 cb_sball_rotate = func;
332 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
334 cb_sball_button = func;
337 int glutGet(unsigned int s)
342 get_window_pos(&x, &y);
345 get_window_pos(&x, &y);
347 case GLUT_WINDOW_WIDTH:
348 get_window_size(&x, &y);
350 case GLUT_WINDOW_HEIGHT:
351 get_window_size(&x, &y);
353 case GLUT_WINDOW_BUFFER_SIZE:
354 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
355 case GLUT_WINDOW_STENCIL_SIZE:
356 return ctx_info.ssize;
357 case GLUT_WINDOW_DEPTH_SIZE:
358 return ctx_info.zsize;
359 case GLUT_WINDOW_RED_SIZE:
360 return ctx_info.rsize;
361 case GLUT_WINDOW_GREEN_SIZE:
362 return ctx_info.gsize;
363 case GLUT_WINDOW_BLUE_SIZE:
364 return ctx_info.bsize;
365 case GLUT_WINDOW_ALPHA_SIZE:
366 return ctx_info.asize;
367 case GLUT_WINDOW_DOUBLEBUFFER:
368 return ctx_info.dblbuf;
369 case GLUT_WINDOW_RGBA:
371 case GLUT_WINDOW_NUM_SAMPLES:
372 return ctx_info.samples;
373 case GLUT_WINDOW_STEREO:
374 return ctx_info.stereo;
375 case GLUT_WINDOW_SRGB:
376 return ctx_info.srgb;
377 case GLUT_WINDOW_CURSOR:
379 case GLUT_WINDOW_COLORMAP_SIZE:
381 case GLUT_SCREEN_WIDTH:
382 get_screen_size(&x, &y);
384 case GLUT_SCREEN_HEIGHT:
385 get_screen_size(&x, &y);
387 case GLUT_INIT_DISPLAY_MODE:
389 case GLUT_INIT_WINDOW_X:
391 case GLUT_INIT_WINDOW_Y:
393 case GLUT_INIT_WINDOW_WIDTH:
395 case GLUT_INIT_WINDOW_HEIGHT:
397 case GLUT_ELAPSED_TIME:
405 int glutGetModifiers(void)
410 static int is_space(int c)
412 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
415 static const char *skip_space(const char *s)
417 while(*s && is_space(*s)) s++;
421 int glutExtensionSupported(char *ext)
423 const char *str, *eptr;
425 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
430 str = skip_space(str);
431 eptr = skip_space(ext);
432 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
436 if((!*str || is_space(*str)) && !*eptr) {
439 while(*str && !is_space(*str)) str++;
446 /* --------------- UNIX/X11 implementation ----------------- */
449 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
451 SPNAV_EVENT_BUTTON /* includes both press and release */
454 struct spnav_event_motion {
462 struct spnav_event_button {
470 struct spnav_event_motion motion;
471 struct spnav_event_button button;
475 static void handle_event(XEvent *ev);
477 static int spnav_window(Window win);
478 static int spnav_event(const XEvent *xev, union spnav_event *event);
479 static int spnav_remove_events(int type);
481 void *miniglut_x11_display(void)
486 int miniglut_x11_socket(void)
488 return ConnectionNumber(dpy);
491 unsigned int miniglut_x11_window(void)
496 void glutMainLoopEvent(void)
501 panic("display callback not set");
504 if(!upd_pending && !cb_idle) {
505 XNextEvent(dpy, &ev);
509 while(XPending(dpy)) {
510 XNextEvent(dpy, &ev);
519 if(upd_pending && mapped) {
530 static void cleanup(void)
534 glXMakeCurrent(dpy, 0, 0);
535 XDestroyWindow(dpy, win);
539 static KeySym translate_keysym(KeySym sym)
560 static void handle_event(XEvent *ev)
563 union spnav_event sev;
572 case ConfigureNotify:
573 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
574 win_width = ev->xconfigure.width;
575 win_height = ev->xconfigure.height;
576 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
581 if(ev->xclient.message_type == xa_wm_proto) {
582 if(ev->xclient.data.l[0] == xa_wm_del_win) {
586 if(spnav_event(ev, &sev)) {
588 case SPNAV_EVENT_MOTION:
589 if(cb_sball_motion) {
590 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
592 if(cb_sball_rotate) {
593 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
595 spnav_remove_events(SPNAV_EVENT_MOTION);
598 case SPNAV_EVENT_BUTTON:
599 if(cb_sball_button) {
600 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
617 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
619 XPeekEvent(dpy, &next);
621 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
622 next.xkey.time == ev->xkey.time) {
623 /* this is a key-repeat event, ignore the release and consume
624 * the following press
626 XNextEvent(dpy, &next);
631 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
632 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
635 sym = translate_keysym(sym);
637 if(ev->type == KeyPress) {
638 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
640 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
643 if(ev->type == KeyPress) {
644 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
646 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
653 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
655 int bn = ev->xbutton.button - Button1;
656 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
657 ev->xbutton.x, ev->xbutton.y);
662 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
663 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
665 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
669 case VisibilityNotify:
671 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
675 if(cb_entry) cb_entry(GLUT_ENTERED);
678 if(cb_entry) cb_entry(GLUT_LEFT);
683 void glutSwapBuffers(void)
685 glXSwapBuffers(dpy, win);
689 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
690 * needs to resize the window to make it fullscreen. The way it does this is by
691 * querying the size of the root window (see get_screen_size), which in the
692 * case of multi-monitor setups will be the combined size of all monitors.
693 * This is problematic; the way to solve it is to use the XRandR extension, or
694 * the Xinerama extension, to figure out the dimensions of the correct video
695 * output, which would add potentially two extension support libraries to our
697 * Moreover, any X installation modern enough to support XR&R will almost
698 * certainly be running a window manager supporting the EHWM
699 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
700 * on manual resizing, and is used in preference if available, making this
701 * whole endeavor pointless.
702 * So I'll just leave it with set_fullscreen_mwm covering the entire
703 * multi-monitor area for now.
708 unsigned long functions;
709 unsigned long decorations;
711 unsigned long status;
714 #define MWM_HINTS_DECORATIONS 2
715 #define MWM_DECOR_ALL 1
717 static void set_fullscreen_mwm(int fs)
719 struct mwm_hints hints;
720 int scr_width, scr_height;
723 get_window_pos(&prev_win_x, &prev_win_y);
724 get_window_size(&prev_win_width, &prev_win_height);
725 get_screen_size(&scr_width, &scr_height);
727 hints.decorations = 0;
728 hints.flags = MWM_HINTS_DECORATIONS;
729 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
730 PropModeReplace, (unsigned char*)&hints, 5);
732 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
734 XDeleteProperty(dpy, win, xa_motif_wm_hints);
735 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
739 static int have_netwm_fullscr(void)
743 unsigned long i, count, rem;
745 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
748 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
749 &type, &fmt, &count, &rem, (unsigned char**)&prop);
751 for(i=0; i<count; i++) {
752 if(prop[i] == xa_net_wm_state_fullscr) {
764 static void set_fullscreen_ewmh(int fs)
766 XClientMessageEvent msg = {0};
768 msg.type = ClientMessage;
770 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
772 msg.data.l[0] = fs ? 1 : 0;
773 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
775 msg.data.l[3] = 1; /* source regular application */
776 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
779 static void set_fullscreen(int fs)
781 if(fullscreen == fs) return;
783 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
784 set_fullscreen_ewmh(fs);
786 } else if(xa_motif_wm_hints) {
787 set_fullscreen_mwm(fs);
792 void glutPositionWindow(int x, int y)
795 XMoveWindow(dpy, win, x, y);
798 void glutReshapeWindow(int xsz, int ysz)
801 XResizeWindow(dpy, win, xsz, ysz);
804 void glutFullScreen(void)
809 void glutSetWindowTitle(const char *title)
812 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
815 XSetWMName(dpy, win, &tprop);
819 void glutSetIconTitle(const char *title)
822 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
825 XSetWMIconName(dpy, win, &tprop);
829 void glutSetCursor(int cidx)
834 case GLUT_CURSOR_LEFT_ARROW:
835 cur = XCreateFontCursor(dpy, XC_left_ptr);
837 case GLUT_CURSOR_INHERIT:
839 case GLUT_CURSOR_NONE:
846 XDefineCursor(dpy, win, cur);
850 void glutWarpPointer(int x, int y)
852 XWarpPointer(dpy, None, win, 0, 0, 0, 0, x, y);
855 void glutSetColor(int idx, float r, float g, float b)
859 if(idx >= 0 && idx < cmap_size) {
861 color.red = (unsigned short)(r * 65535.0f);
862 color.green = (unsigned short)(g * 65535.0f);
863 color.blue = (unsigned short)(b * 65535.0f);
864 color.flags = DoRed | DoGreen | DoBlue;
865 XStoreColor(dpy, cmap, &color);
869 float glutGetColor(int idx, int comp)
873 if(idx < 0 || idx >= cmap_size) {
878 XQueryColor(dpy, cmap, &color);
881 return color.red / 65535.0f;
883 return color.green / 65535.0f;
885 return color.blue / 65535.0f;
892 void glutSetKeyRepeat(int repmode)
901 static XVisualInfo *choose_visual(unsigned int mode)
908 if(mode & GLUT_DOUBLE) {
909 *aptr++ = GLX_DOUBLEBUFFER;
912 if(mode & GLUT_INDEX) {
913 *aptr++ = GLX_BUFFER_SIZE;
917 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
918 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
919 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
921 if(mode & GLUT_ALPHA) {
922 *aptr++ = GLX_ALPHA_SIZE;
925 if(mode & GLUT_DEPTH) {
926 *aptr++ = GLX_DEPTH_SIZE;
929 if(mode & GLUT_STENCIL) {
930 *aptr++ = GLX_STENCIL_SIZE;
933 if(mode & GLUT_ACCUM) {
934 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
935 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
936 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
938 if(mode & GLUT_STEREO) {
939 *aptr++ = GLX_STEREO;
941 if(mode & GLUT_SRGB) {
942 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
944 if(mode & GLUT_MULTISAMPLE) {
945 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
947 *aptr++ = GLX_SAMPLES_ARB;
954 return glXChooseVisual(dpy, scr, attr);
956 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
965 static void create_window(const char *title)
967 XSetWindowAttributes xattr = {0};
969 unsigned int xattr_mask;
970 unsigned int mode = init_mode;
972 if(!(vi = choose_visual(mode))) {
974 if(!(vi = choose_visual(mode))) {
975 panic("Failed to find compatible visual\n");
979 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
981 panic("Failed to create OpenGL context\n");
984 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
985 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
986 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
987 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
988 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
989 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
990 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
991 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
992 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
993 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
995 if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
997 glXDestroyContext(dpy, ctx);
998 panic("Failed to create colormap\n");
1000 cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
1002 xattr.background_pixel = BlackPixel(dpy, scr);
1003 xattr.colormap = cmap;
1004 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
1005 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
1006 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
1008 glXDestroyContext(dpy, ctx);
1009 XFreeColormap(dpy, cmap);
1010 panic("Failed to create window\n");
1014 XSelectInput(dpy, win, evmask);
1018 glutSetWindowTitle(title);
1019 glutSetIconTitle(title);
1020 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
1021 XMapWindow(dpy, win);
1023 glXMakeCurrent(dpy, win, ctx);
1026 static void get_window_pos(int *x, int *y)
1029 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1032 static void get_window_size(int *w, int *h)
1034 XWindowAttributes wattr;
1035 XGetWindowAttributes(dpy, win, &wattr);
1040 static void get_screen_size(int *scrw, int *scrh)
1042 XWindowAttributes wattr;
1043 XGetWindowAttributes(dpy, root, &wattr);
1044 *scrw = wattr.width;
1045 *scrh = wattr.height;
1051 CMD_APP_WINDOW = 27695,
1055 static Window get_daemon_window(Display *dpy);
1056 static int catch_badwin(Display *dpy, XErrorEvent *err);
1058 #define SPNAV_INITIALIZED (xa_motion_event)
1060 static int spnav_window(Window win)
1062 int (*prev_xerr_handler)(Display*, XErrorEvent*);
1066 if(!SPNAV_INITIALIZED) {
1070 if(!(daemon_win = get_daemon_window(dpy))) {
1074 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1076 xev.type = ClientMessage;
1077 xev.xclient.send_event = False;
1078 xev.xclient.display = dpy;
1079 xev.xclient.window = win;
1080 xev.xclient.message_type = xa_command_event;
1081 xev.xclient.format = 16;
1082 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1083 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1084 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1086 XSendEvent(dpy, daemon_win, False, 0, &xev);
1089 XSetErrorHandler(prev_xerr_handler);
1093 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1095 int evtype = *(int*)arg;
1097 if(xev->type != ClientMessage) {
1101 if(xev->xclient.message_type == xa_motion_event) {
1102 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1104 if(xev->xclient.message_type == xa_button_press_event ||
1105 xev->xclient.message_type == xa_button_release_event) {
1106 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1111 static int spnav_remove_events(int type)
1115 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1121 static int spnav_event(const XEvent *xev, union spnav_event *event)
1126 xmsg_type = xev->xclient.message_type;
1128 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1129 xmsg_type != xa_button_release_event) {
1133 if(xmsg_type == xa_motion_event) {
1134 event->type = SPNAV_EVENT_MOTION;
1135 event->motion.data = &event->motion.x;
1137 for(i=0; i<6; i++) {
1138 event->motion.data[i] = xev->xclient.data.s[i + 2];
1140 event->motion.period = xev->xclient.data.s[8];
1142 event->type = SPNAV_EVENT_BUTTON;
1143 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1144 event->button.bnum = xev->xclient.data.s[2];
1149 static int mglut_strcmp(const char *s1, const char *s2)
1151 while(*s1 && *s1 == *s2) {
1158 static Window get_daemon_window(Display *dpy)
1161 XTextProperty wname;
1164 unsigned long nitems, bytes_after;
1165 unsigned char *prop;
1167 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1168 &type, &fmt, &nitems, &bytes_after, &prop);
1173 win = *(Window*)prop;
1177 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1185 static int catch_badwin(Display *dpy, XErrorEvent *err)
1192 #endif /* BUILD_X11 */
1195 /* --------------- windows implementation ----------------- */
1197 static int reshape_pending;
1199 static void update_modkeys(void);
1200 static int translate_vkey(int vkey);
1201 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1203 #ifdef MINIGLUT_WINMAIN
1204 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1207 char *argv[] = { "miniglut.exe", 0 };
1208 return main(argc, argv);
1212 void glutMainLoopEvent(void)
1217 panic("display callback not set");
1220 if(reshape_pending && cb_reshape) {
1221 reshape_pending = 0;
1222 get_window_size(&win_width, &win_height);
1223 cb_reshape(win_width, win_height);
1226 if(!upd_pending && !cb_idle) {
1227 GetMessage(&msg, 0, 0, 0);
1228 TranslateMessage(&msg);
1229 DispatchMessage(&msg);
1232 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1233 TranslateMessage(&msg);
1234 DispatchMessage(&msg);
1242 if(upd_pending && mapped) {
1248 static void cleanup(void)
1251 wglMakeCurrent(dc, 0);
1252 wglDeleteContext(ctx);
1253 UnregisterClass("MiniGLUT", hinst);
1257 void glutSwapBuffers(void)
1262 void glutPositionWindow(int x, int y)
1265 unsigned int flags = SWP_SHOWWINDOW;
1268 rect.left = prev_win_x;
1269 rect.top = prev_win_y;
1270 rect.right = rect.left + prev_win_width;
1271 rect.bottom = rect.top + prev_win_height;
1272 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1274 flags |= SWP_FRAMECHANGED;
1276 GetWindowRect(win, &rect);
1278 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1281 static void calc_win_rect(RECT *rect, int x, int y, int w, int h)
1285 rect->right = x + w;
1286 rect->bottom = y + h;
1287 AdjustWindowRect(rect, WS_OVERLAPPEDWINDOW, 0);
1290 void glutReshapeWindow(int xsz, int ysz)
1293 unsigned int flags = SWP_SHOWWINDOW;
1296 calc_win_rect(&rect, prev_win_x, prev_win_y, xsz, ysz);
1297 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1299 flags |= SWP_FRAMECHANGED;
1301 GetWindowRect(win, &rect);
1302 calc_win_rect(&rect, rect.left, rect.top, xsz, ysz);
1305 xsz = rect.right - rect.left;
1306 ysz = rect.bottom - rect.top;
1307 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1310 void glutFullScreen(void)
1313 int scr_width, scr_height;
1315 if(fullscreen) return;
1317 GetWindowRect(win, &rect);
1318 prev_win_x = rect.left;
1319 prev_win_y = rect.top;
1320 prev_win_width = rect.right - rect.left;
1321 prev_win_height = rect.bottom - rect.top;
1323 get_screen_size(&scr_width, &scr_height);
1325 SetWindowLong(win, GWL_STYLE, 0);
1326 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1331 void glutSetWindowTitle(const char *title)
1333 SetWindowText(win, title);
1336 void glutSetIconTitle(const char *title)
1340 void glutSetCursor(int cidx)
1343 case GLUT_CURSOR_NONE:
1346 case GLUT_CURSOR_INHERIT:
1347 case GLUT_CURSOR_LEFT_ARROW:
1349 SetCursor(LoadCursor(0, IDC_ARROW));
1354 void glutWarpPointer(int x, int y)
1360 ClientToScreen(win, &pt);
1361 SetCursorPos(pt.x, pt.y);
1364 void glutSetColor(int idx, float r, float g, float b)
1368 if(idx < 0 || idx >= 256 || !cmap) {
1372 col.peRed = (int)(r * 255.0f);
1373 col.peGreen = (int)(g * 255.0f);
1374 col.peBlue = (int)(b * 255.0f);
1375 col.peFlags = PC_NOCOLLAPSE;
1377 SetPaletteEntries(cmap, idx, 1, &col);
1380 UnrealizeObject(cmap);
1381 SelectPalette(dc, cmap, 0);
1386 float glutGetColor(int idx, int comp)
1390 if(idx < 0 || idx >= 256 || !cmap) {
1394 if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1400 return col.peRed / 255.0f;
1402 return col.peGreen / 255.0f;
1404 return col.peBlue / 255.0f;
1411 void glutSetKeyRepeat(int repmode)
1415 #define WGL_DRAW_TO_WINDOW 0x2001
1416 #define WGL_ACCELERATION 0x2003
1417 #define WGL_SUPPORT_OPENGL 0x2010
1418 #define WGL_DOUBLE_BUFFER 0x2011
1419 #define WGL_STEREO 0x2012
1420 #define WGL_PIXEL_TYPE 0x2013
1421 #define WGL_COLOR_BITS 0x2014
1422 #define WGL_RED_BITS 0x2015
1423 #define WGL_GREEN_BITS 0x2017
1424 #define WGL_BLUE_BITS 0x2019
1425 #define WGL_ALPHA_BITS 0x201b
1426 #define WGL_ACCUM_BITS 0x201d
1427 #define WGL_DEPTH_BITS 0x2022
1428 #define WGL_STENCIL_BITS 0x2023
1429 #define WGL_FULL_ACCELERATION 0x2027
1431 #define WGL_TYPE_RGBA 0x202b
1432 #define WGL_TYPE_COLORINDEX 0x202c
1434 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1435 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1436 #define WGL_SAMPLES_ARB 0x2042
1438 static PROC wglChoosePixelFormat;
1439 static PROC wglGetPixelFormatAttribiv;
1441 #define ATTR(a, v) \
1442 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1444 static unsigned int choose_pixfmt(unsigned int mode)
1446 unsigned int num_pixfmt, pixfmt = 0;
1447 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1448 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1449 float fattr[2] = {0, 0};
1451 int *aptr = attr + 6;
1454 if(mode & GLUT_DOUBLE) {
1455 ATTR(WGL_DOUBLE_BUFFER, 1);
1458 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1459 ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1460 if(mode & GLUT_ALPHA) {
1461 ATTR(WGL_ALPHA_BITS, 4);
1463 if(mode & GLUT_DEPTH) {
1464 ATTR(WGL_DEPTH_BITS, 16);
1466 if(mode & GLUT_STENCIL) {
1467 ATTR(WGL_STENCIL_BITS, 1);
1469 if(mode & GLUT_ACCUM) {
1470 ATTR(WGL_ACCUM_BITS, 1);
1472 if(mode & GLUT_STEREO) {
1473 ATTR(WGL_STEREO, 1);
1475 if(mode & GLUT_SRGB) {
1476 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1478 if(mode & GLUT_MULTISAMPLE) {
1479 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1480 *aptr++ = WGL_SAMPLES_ARB;
1486 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1495 static PIXELFORMATDESCRIPTOR pfd;
1496 static PIXELFORMATDESCRIPTOR tmppfd = {
1497 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1498 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1499 PFD_MAIN_PLANE, 0, 0, 0, 0
1501 #define TMPCLASS "TempMiniGLUT"
1503 #define GETATTR(attr, vptr) \
1506 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1509 static int create_window_wglext(const char *title, int width, int height)
1511 WNDCLASSEX wc = {0};
1517 /* create a temporary window and GL context, just to query and retrieve
1518 * the wglChoosePixelFormatEXT function
1520 wc.cbSize = sizeof wc;
1521 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1522 wc.hCursor = LoadCursor(0, IDC_ARROW);
1523 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1524 wc.hInstance = hinst;
1525 wc.lpfnWndProc = DefWindowProc;
1526 wc.lpszClassName = TMPCLASS;
1527 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1528 if(!RegisterClassEx(&wc)) {
1531 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1532 width, height, 0, 0, hinst, 0))) {
1535 tmpdc = GetDC(tmpwin);
1537 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1538 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1539 !(tmpctx = wglCreateContext(tmpdc))) {
1542 wglMakeCurrent(tmpdc, tmpctx);
1544 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1545 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1548 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1552 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1556 wglMakeCurrent(0, 0);
1557 wglDeleteContext(tmpctx);
1558 DestroyWindow(tmpwin);
1559 UnregisterClass(TMPCLASS, hinst);
1561 /* create the real window and context */
1562 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1563 init_y, width, height, 0, 0, hinst, 0))) {
1564 panic("Failed to create window\n");
1568 if(!(pixfmt = choose_pixfmt(init_mode))) {
1569 panic("Failed to find suitable pixel format\n");
1571 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1572 panic("Failed to set the selected pixel format\n");
1574 if(!(ctx = wglCreateContext(dc))) {
1575 panic("Failed to create the OpenGL context\n");
1577 wglMakeCurrent(dc, ctx);
1579 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1580 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1581 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1582 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1583 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1584 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1585 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1586 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1587 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1593 wglMakeCurrent(0, 0);
1594 wglDeleteContext(tmpctx);
1597 DestroyWindow(tmpwin);
1599 UnregisterClass(TMPCLASS, hinst);
1603 static void create_window(const char *title)
1606 int i, pixfmt, width, height;
1607 char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1610 calc_win_rect(&rect, init_x, init_y, init_width, init_height);
1611 width = rect.right - rect.left;
1612 height = rect.bottom - rect.top;
1614 memset(&pfd, 0, sizeof pfd);
1615 pfd.nSize = sizeof pfd;
1617 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1618 if(init_mode & GLUT_STEREO) {
1619 pfd.dwFlags |= PFD_STEREO;
1621 if(init_mode & GLUT_INDEX) {
1622 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1625 pfd.iPixelType = PFD_TYPE_RGBA;
1626 pfd.cColorBits = 24;
1628 if(init_mode & GLUT_ALPHA) {
1631 if(init_mode & GLUT_ACCUM) {
1632 pfd.cAccumBits = 24;
1634 if(init_mode & GLUT_DEPTH) {
1635 pfd.cDepthBits = 24;
1637 if(init_mode & GLUT_STENCIL) {
1638 pfd.cStencilBits = 8;
1640 pfd.iLayerType = PFD_MAIN_PLANE;
1642 if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
1643 if(create_window_wglext(title, width, height) != -1) {
1648 /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
1649 * failed, just use the old-style ChoosePixelFormat method instead
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");
1657 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1658 panic("Failed to find suitable pixel format\n");
1660 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1661 panic("Failed to set the selected pixel format\n");
1663 if(!(ctx = wglCreateContext(dc))) {
1664 panic("Failed to create the OpenGL context\n");
1666 wglMakeCurrent(dc, ctx);
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;
1681 SetForegroundWindow(win);
1684 if(init_mode & GLUT_INDEX) {
1685 logpal = (LOGPALETTE*)palbuf;
1687 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1689 logpal->palVersion = 0x300;
1690 logpal->palNumEntries = 256;
1692 if(!(cmap = CreatePalette(logpal))) {
1693 panic("Failed to create palette in indexed mode");
1695 SelectPalette(dc, cmap, 0);
1700 if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
1701 /* for RGB mode in 8bpp displays we also need to set up a palette
1702 * with RGB 332 colors
1704 logpal = (LOGPALETTE*)palbuf;
1706 logpal->palVersion = 0x300;
1707 logpal->palNumEntries = 256;
1709 for(i=0; i<256; i++) {
1711 int g = (i >> 3) & 7;
1712 int b = (i >> 5) & 3;
1714 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1715 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1716 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1717 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1720 if((cmap = CreatePalette(logpal))) {
1721 SelectPalette(dc, cmap, 0);
1729 reshape_pending = 1;
1732 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1734 static int mouse_x, mouse_y;
1739 if(win) DestroyWindow(win);
1750 ValidateRect(win, 0);
1754 x = lparam & 0xffff;
1756 if(x != win_width && y != win_height) {
1760 reshape_pending = 0;
1761 cb_reshape(win_width, win_height);
1768 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1774 key = translate_vkey(wparam);
1777 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1781 cb_skeydown(key, mouse_x, mouse_y);
1789 key = translate_vkey(wparam);
1792 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1796 cb_skeyup(key, mouse_x, mouse_y);
1801 case WM_LBUTTONDOWN:
1802 handle_mbutton(0, 1, wparam, lparam);
1804 case WM_MBUTTONDOWN:
1805 handle_mbutton(1, 1, wparam, lparam);
1807 case WM_RBUTTONDOWN:
1808 handle_mbutton(2, 1, wparam, lparam);
1811 handle_mbutton(0, 0, wparam, lparam);
1814 handle_mbutton(1, 0, wparam, lparam);
1817 handle_mbutton(2, 0, wparam, lparam);
1821 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1822 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1824 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1830 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1834 return DefWindowProc(win, msg, wparam, lparam);
1840 static void update_modkeys(void)
1842 if(GetKeyState(VK_SHIFT) & 0x8000) {
1843 modstate |= GLUT_ACTIVE_SHIFT;
1845 modstate &= ~GLUT_ACTIVE_SHIFT;
1847 if(GetKeyState(VK_CONTROL) & 0x8000) {
1848 modstate |= GLUT_ACTIVE_CTRL;
1850 modstate &= ~GLUT_ACTIVE_CTRL;
1852 if(GetKeyState(VK_MENU) & 0x8000) {
1853 modstate |= GLUT_ACTIVE_ALT;
1855 modstate &= ~GLUT_ACTIVE_ALT;
1860 #define VK_OEM_1 0xba
1861 #define VK_OEM_2 0xbf
1862 #define VK_OEM_3 0xc0
1863 #define VK_OEM_4 0xdb
1864 #define VK_OEM_5 0xdc
1865 #define VK_OEM_6 0xdd
1866 #define VK_OEM_7 0xde
1869 static int translate_vkey(int vkey)
1872 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1873 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1874 case VK_END: return GLUT_KEY_END;
1875 case VK_HOME: return GLUT_KEY_HOME;
1876 case VK_LEFT: return GLUT_KEY_LEFT;
1877 case VK_UP: return GLUT_KEY_UP;
1878 case VK_RIGHT: return GLUT_KEY_RIGHT;
1879 case VK_DOWN: return GLUT_KEY_DOWN;
1880 case VK_OEM_1: return ';';
1881 case VK_OEM_2: return '/';
1882 case VK_OEM_3: return '`';
1883 case VK_OEM_4: return '[';
1884 case VK_OEM_5: return '\\';
1885 case VK_OEM_6: return ']';
1886 case VK_OEM_7: return '\'';
1891 if(vkey >= 'A' && vkey <= 'Z') {
1893 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1894 vkey -= VK_F1 + GLUT_KEY_F1;
1900 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1907 x = lparam & 0xffff;
1909 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1913 static void get_window_pos(int *x, int *y)
1916 GetWindowRect(win, &rect);
1921 static void get_window_size(int *w, int *h)
1924 GetClientRect(win, &rect);
1925 *w = rect.right - rect.left;
1926 *h = rect.bottom - rect.top;
1929 static void get_screen_size(int *scrw, int *scrh)
1931 *scrw = GetSystemMetrics(SM_CXSCREEN);
1932 *scrh = GetSystemMetrics(SM_CYSCREEN);
1934 #endif /* BUILD_WIN32 */
1936 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1937 #include <sys/time.h>
1939 #ifdef MINIGLUT_USE_LIBC
1940 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1942 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1945 static long get_msec(void)
1947 static struct timeval tv0;
1950 sys_gettimeofday(&tv, 0);
1951 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1955 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1959 static long get_msec(void)
1964 #ifdef MINIGLUT_NO_WINMM
1965 tm = GetTickCount();
1977 static void panic(const char *msg)
1979 const char *end = msg;
1981 sys_write(2, msg, end - msg);
1986 #ifdef MINIGLUT_USE_LIBC
1994 static void sys_exit(int status)
1999 static int sys_write(int fd, const void *buf, int count)
2001 return write(fd, buf, count);
2004 #else /* !MINIGLUT_USE_LIBC */
2008 static void sys_exit(int status)
2012 :: "a"(60), "D"(status));
2014 static int sys_write(int fd, const void *buf, int count)
2020 : "a"(1), "D"(fd), "S"(buf), "d"(count));
2023 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2029 : "a"(96), "D"(tv), "S"(tz));
2032 #endif /* __x86_64__ */
2034 static void sys_exit(int status)
2038 :: "a"(1), "b"(status));
2040 static int sys_write(int fd, const void *buf, int count)
2046 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2049 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2055 : "a"(78), "b"(tv), "c"(tz));
2058 #endif /* __i386__ */
2059 #endif /* __linux__ */
2062 static void sys_exit(int status)
2064 ExitProcess(status);
2066 static int sys_write(int fd, const void *buf, int count)
2068 unsigned long wrsz = 0;
2070 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2071 if(!WriteFile(out, buf, count, &wrsz, 0)) {
2077 #endif /* !MINIGLUT_USE_LIBC */
2080 /* ----------------- primitives ------------------ */
2081 #ifdef MINIGLUT_USE_LIBC
2085 void mglut_sincos(float angle, float *sptr, float *cptr)
2091 float mglut_atan(float x)
2096 #else /* !MINIGLUT_USE_LIBC */
2099 void mglut_sincos(float angle, float *sptr, float *cptr)
2106 : "=m"(*sptr), "=m"(*cptr)
2111 float mglut_atan(float x)
2127 void mglut_sincos(float angle, float *sptr, float *cptr)
2140 float mglut_atan(float x)
2154 #pragma aux mglut_sincos = \
2156 "fstp dword ptr [edx]" \
2157 "fstp dword ptr [eax]" \
2158 parm[8087][eax][edx] \
2161 #pragma aux mglut_atan = \
2167 #endif /* __WATCOMC__ */
2169 #endif /* !MINIGLUT_USE_LIBC */
2171 #define PI 3.1415926536f
2173 void glutSolidSphere(float rad, int slices, int stacks)
2176 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2177 float du = 1.0f / (float)slices;
2178 float dv = 1.0f / (float)stacks;
2181 for(i=0; i<stacks; i++) {
2183 for(j=0; j<slices; j++) {
2185 for(k=0; k<4; k++) {
2186 gray = k ^ (k >> 1);
2187 s = gray & 1 ? u + du : u;
2188 t = gray & 2 ? v + dv : v;
2189 theta = s * PI * 2.0f;
2191 mglut_sincos(theta, &sintheta, &costheta);
2192 mglut_sincos(phi, &sinphi, &cosphi);
2193 x = sintheta * sinphi;
2194 y = costheta * sinphi;
2199 glNormal3f(x, y, z);
2200 glVertex3f(x * rad, y * rad, z * rad);
2207 void glutWireSphere(float rad, int slices, int stacks)
2209 glPushAttrib(GL_POLYGON_BIT);
2210 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2211 glutSolidSphere(rad, slices, stacks);
2215 void glutSolidCube(float sz)
2217 int i, j, idx, gray, flip, rotx;
2218 float vpos[3], norm[3];
2219 float rad = sz * 0.5f;
2222 for(i=0; i<6; i++) {
2225 idx = (~i & 2) - rotx;
2226 norm[0] = norm[1] = norm[2] = 0.0f;
2227 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2229 vpos[idx] = norm[idx] * rad;
2230 for(j=0; j<4; j++) {
2231 gray = j ^ (j >> 1);
2232 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2233 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2234 glTexCoord2f(gray & 1, gray >> 1);
2241 void glutWireCube(float sz)
2243 glPushAttrib(GL_POLYGON_BIT);
2244 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2249 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2252 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2253 float du = 1.0f / (float)slices;
2254 float dv = 1.0f / (float)stacks;
2257 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2258 mglut_sincos(phi, &sinphi, &cosphi);
2261 for(i=0; i<stacks; i++) {
2263 for(j=0; j<slices; j++) {
2265 for(k=0; k<4; k++) {
2266 gray = k ^ (k >> 1);
2267 s = gray & 2 ? u + du : u;
2268 t = gray & 1 ? v + dv : v;
2269 rad = rbot + (rtop - rbot) * t;
2270 theta = s * PI * 2.0f;
2271 mglut_sincos(theta, &sintheta, &costheta);
2273 x = sintheta * cosphi;
2274 y = costheta * cosphi;
2279 glNormal3f(x, y, z);
2280 glVertex3f(sintheta * rad, costheta * rad, t * height);
2287 void glutSolidCone(float base, float height, int slices, int stacks)
2289 draw_cylinder(base, 0, height, slices, stacks);
2292 void glutWireCone(float base, float height, int slices, int stacks)
2294 glPushAttrib(GL_POLYGON_BIT);
2295 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2296 glutSolidCone(base, height, slices, stacks);
2300 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2302 draw_cylinder(rad, rad, height, slices, stacks);
2305 void glutWireCylinder(float rad, float height, int slices, int stacks)
2307 glPushAttrib(GL_POLYGON_BIT);
2308 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2309 glutSolidCylinder(rad, height, slices, stacks);
2313 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2316 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2317 float du = 1.0f / (float)rings;
2318 float dv = 1.0f / (float)sides;
2321 for(i=0; i<rings; i++) {
2323 for(j=0; j<sides; j++) {
2325 for(k=0; k<4; k++) {
2326 gray = k ^ (k >> 1);
2327 s = gray & 1 ? u + du : u;
2328 t = gray & 2 ? v + dv : v;
2329 theta = s * PI * 2.0f;
2330 phi = t * PI * 2.0f;
2331 mglut_sincos(theta, &sintheta, &costheta);
2332 mglut_sincos(phi, &sinphi, &cosphi);
2333 x = sintheta * sinphi;
2334 y = costheta * sinphi;
2339 glNormal3f(x, y, z);
2341 x = x * inner_rad + sintheta * outer_rad;
2342 y = y * inner_rad + costheta * outer_rad;
2344 glVertex3f(x, y, z);
2351 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2353 glPushAttrib(GL_POLYGON_BIT);
2354 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2355 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2359 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2360 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2362 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2364 #define PATCH_SUBDIV 7
2366 static float teapot_part_flip[] = {
2367 1, 1, 1, 1, /* rim flip */
2368 1, 1, 1, 1, /* body1 flip */
2369 1, 1, 1, 1, /* body2 flip */
2370 1, 1, 1, 1, /* lid patch 1 flip */
2371 1, 1, 1, 1, /* lid patch 2 flip */
2372 1, -1, /* handle 1 flip */
2373 1, -1, /* handle 2 flip */
2374 1, -1, /* spout 1 flip */
2375 1, -1, /* spout 2 flip */
2376 1, 1, 1, 1 /* bottom flip */
2379 static float teapot_part_rot[] = {
2380 0, 90, 180, 270, /* rim rotations */
2381 0, 90, 180, 270, /* body patch 1 rotations */
2382 0, 90, 180, 270, /* body patch 2 rotations */
2383 0, 90, 180, 270, /* lid patch 1 rotations */
2384 0, 90, 180, 270, /* lid patch 2 rotations */
2385 0, 0, /* handle 1 rotations */
2386 0, 0, /* handle 2 rotations */
2387 0, 0, /* spout 1 rotations */
2388 0, 0, /* spout 2 rotations */
2389 0, 90, 180, 270 /* bottom rotations */
2393 static int teapot_index[] = {
2395 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2396 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2397 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2398 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2400 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2401 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2402 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2403 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2405 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2406 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2407 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2408 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2410 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2411 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2412 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2413 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2415 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2416 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2417 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2418 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2420 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2421 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2423 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2424 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2426 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2427 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2429 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2430 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2432 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2433 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2434 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2435 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2439 static float teapot_verts[][3] = {
2440 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2441 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2442 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2443 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2444 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2445 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2446 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2447 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2448 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2449 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2450 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2451 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2452 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2453 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2454 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2455 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2456 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2457 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2458 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2459 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2460 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2461 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2462 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2463 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2464 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2465 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2466 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2467 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2468 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2469 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2470 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2471 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2472 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2473 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2474 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2475 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2476 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2477 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2478 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2479 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2480 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2481 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2482 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2483 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2484 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2485 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2486 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2487 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2488 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2489 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2490 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2491 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2492 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2493 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2494 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2495 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2496 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2497 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2498 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2499 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2500 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2501 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2502 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2503 { 0.8400, -1.5000, 0.07500 }
2506 static void draw_patch(int *index, int flip, float scale);
2507 static float bernstein(int i, float x);
2509 void glutSolidTeapot(float size)
2515 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2516 float flip = teapot_part_flip[i];
2517 float rot = teapot_part_rot[i];
2519 glMatrixMode(GL_MODELVIEW);
2521 glTranslatef(0, -3.15 * size * 0.5, 0);
2522 glRotatef(rot, 0, 1, 0);
2523 glScalef(1, 1, flip);
2524 glRotatef(-90, 1, 0, 0);
2526 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2532 void glutWireTeapot(float size)
2534 glPushAttrib(GL_POLYGON_BIT);
2535 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2536 glutSolidTeapot(size);
2541 static void bezier_patch(float *res, float *cp, float u, float v)
2545 res[0] = res[1] = res[2] = 0.0f;
2547 for(j=0; j<4; j++) {
2548 for(i=0; i<4; i++) {
2549 float bu = bernstein(i, u);
2550 float bv = bernstein(j, v);
2552 res[0] += cp[0] * bu * bv;
2553 res[1] += cp[1] * bu * bv;
2554 res[2] += cp[2] * bu * bv;
2561 static float rsqrt(float x)
2563 float xhalf = x * 0.5f;
2565 i = 0x5f3759df - (i >> 1);
2567 x = x * (1.5f - xhalf * x * x);
2572 #define CROSS(res, a, b) \
2574 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2575 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2576 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2579 #define NORMALIZE(v) \
2581 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2589 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2591 float tang[3], bitan[3], tmp[3];
2593 bezier_patch(tang, cp, u + DT, v);
2594 bezier_patch(tmp, cp, u - DT, v);
2599 bezier_patch(bitan, cp, u, v + DT);
2600 bezier_patch(tmp, cp, u, v - DT);
2605 CROSS(res, tang, bitan);
2611 static float bernstein(int i, float x)
2613 float invx = 1.0f - x;
2617 return invx * invx * invx;
2619 return 3.0f * x * invx * invx;
2621 return 3.0f * x * x * invx;
2630 static void draw_patch(int *index, int flip, float scale)
2632 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2633 static const float voffs[4] = {0, 1, 1, 0};
2639 float du = 1.0 / PATCH_SUBDIV;
2640 float dv = 1.0 / PATCH_SUBDIV;
2642 /* collect control points */
2643 for(i=0; i<16; i++) {
2644 cp[i * 3] = teapot_verts[index[i]][0];
2645 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2646 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2653 for(i=0; i<PATCH_SUBDIV; i++) {
2655 for(j=0; j<PATCH_SUBDIV; j++) {
2657 for(k=0; k<4; k++) {
2658 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2660 /* top/bottom normal hack */
2664 } else if(pt[2] < 0.00001) {
2668 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2673 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);