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/>.
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
26 #ifndef GLX_SAMPLE_BUFFERS_ARB
27 #define GLX_SAMPLE_BUFFERS_ARB 100000
28 #define GLX_SAMPLES_ARB 100001
30 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
31 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
35 static Window win, root;
37 static GLXContext ctx;
38 static Atom xa_wm_proto, xa_wm_del_win;
39 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
40 static Atom xa_motif_wm_hints;
41 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
42 static unsigned int evmask;
43 static Cursor blank_cursor;
45 static int have_netwm_fullscr(void);
52 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
54 static HINSTANCE hinst;
60 #error unsupported platform
66 int rsize, gsize, bsize, asize;
74 static void cleanup(void);
75 static void create_window(const char *title);
76 static void get_window_pos(int *x, int *y);
77 static void get_window_size(int *w, int *h);
78 static void get_screen_size(int *scrw, int *scrh);
80 static long get_msec(void);
81 static void panic(const char *msg);
82 static void sys_exit(int status);
83 static int sys_write(int fd, const void *buf, int count);
86 static int init_x = -1, init_y, init_width = 256, init_height = 256;
87 static unsigned int init_mode;
89 static struct ctx_info ctx_info;
90 static int cur_cursor = GLUT_CURSOR_INHERIT;
91 static int ignore_key_repeat;
93 static glut_cb cb_display;
94 static glut_cb cb_idle;
95 static glut_cb_reshape cb_reshape;
96 static glut_cb_state cb_vis, cb_entry;
97 static glut_cb_keyb cb_keydown, cb_keyup;
98 static glut_cb_special cb_skeydown, cb_skeyup;
99 static glut_cb_mouse cb_mouse;
100 static glut_cb_motion cb_motion, cb_passive;
101 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
102 static glut_cb_sbbutton cb_sball_button;
104 static int fullscreen;
105 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
107 static int win_width, win_height;
110 static int upd_pending;
113 void glutInit(int *argc, char **argv)
119 if(!(dpy = XOpenDisplay(0))) {
120 panic("Failed to connect to the X server\n");
122 scr = DefaultScreen(dpy);
123 root = RootWindow(dpy, scr);
124 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
125 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
126 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
127 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
128 if(have_netwm_fullscr()) {
129 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
132 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
133 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
134 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
135 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
137 evmask = ExposureMask | StructureNotifyMask;
139 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
140 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
141 XFreePixmap(dpy, blankpix);
148 hinst = GetModuleHandle(0);
150 wc.cbSize = sizeof wc;
151 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
152 wc.hCursor = LoadCursor(0, IDC_ARROW);
153 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
154 wc.hInstance = hinst;
155 wc.lpfnWndProc = handle_message;
156 wc.lpszClassName = "MiniGLUT";
157 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
158 if(!RegisterClassEx(&wc)) {
159 panic("Failed to register \"MiniGLUT\" window class\n");
163 get_screen_size(&init_x, &init_y);
170 void glutInitWindowPosition(int x, int y)
176 void glutInitWindowSize(int xsz, int ysz)
182 void glutInitDisplayMode(unsigned int mode)
187 void glutCreateWindow(const char *title)
189 create_window(title);
197 void glutMainLoop(void)
204 void glutPostRedisplay(void)
209 void glutIgnoreKeyRepeat(int ignore)
211 ignore_key_repeat = ignore;
214 #define UPD_EVMASK(x) \
221 if(win) XSelectInput(dpy, win, evmask); \
225 void glutIdleFunc(glut_cb func)
230 void glutDisplayFunc(glut_cb func)
235 void glutReshapeFunc(glut_cb_reshape func)
240 void glutVisibilityFunc(glut_cb_state func)
244 UPD_EVMASK(VisibilityChangeMask);
248 void glutEntryFunc(glut_cb_state func)
252 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
256 void glutKeyboardFunc(glut_cb_keyb func)
260 UPD_EVMASK(KeyPressMask);
264 void glutKeyboardUpFunc(glut_cb_keyb func)
268 UPD_EVMASK(KeyReleaseMask);
272 void glutSpecialFunc(glut_cb_special func)
276 UPD_EVMASK(KeyPressMask);
280 void glutSpecialUpFunc(glut_cb_special func)
284 UPD_EVMASK(KeyReleaseMask);
288 void glutMouseFunc(glut_cb_mouse func)
292 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
296 void glutMotionFunc(glut_cb_motion func)
300 UPD_EVMASK(ButtonMotionMask);
304 void glutPassiveMotionFunc(glut_cb_motion func)
308 UPD_EVMASK(PointerMotionMask);
312 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
314 cb_sball_motion = func;
317 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
319 cb_sball_rotate = func;
322 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
324 cb_sball_button = func;
327 int glutGet(unsigned int s)
332 get_window_pos(&x, &y);
335 get_window_pos(&x, &y);
337 case GLUT_WINDOW_WIDTH:
338 get_window_size(&x, &y);
340 case GLUT_WINDOW_HEIGHT:
341 get_window_size(&x, &y);
343 case GLUT_WINDOW_BUFFER_SIZE:
344 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
345 case GLUT_WINDOW_STENCIL_SIZE:
346 return ctx_info.ssize;
347 case GLUT_WINDOW_DEPTH_SIZE:
348 return ctx_info.zsize;
349 case GLUT_WINDOW_RED_SIZE:
350 return ctx_info.rsize;
351 case GLUT_WINDOW_GREEN_SIZE:
352 return ctx_info.gsize;
353 case GLUT_WINDOW_BLUE_SIZE:
354 return ctx_info.bsize;
355 case GLUT_WINDOW_ALPHA_SIZE:
356 return ctx_info.asize;
357 case GLUT_WINDOW_DOUBLEBUFFER:
358 return ctx_info.dblbuf;
359 case GLUT_WINDOW_RGBA:
361 case GLUT_WINDOW_NUM_SAMPLES:
362 return ctx_info.samples;
363 case GLUT_WINDOW_STEREO:
364 return ctx_info.stereo;
365 case GLUT_WINDOW_SRGB:
366 return ctx_info.srgb;
367 case GLUT_WINDOW_CURSOR:
369 case GLUT_SCREEN_WIDTH:
370 get_screen_size(&x, &y);
372 case GLUT_SCREEN_HEIGHT:
373 get_screen_size(&x, &y);
375 case GLUT_INIT_DISPLAY_MODE:
377 case GLUT_INIT_WINDOW_X:
379 case GLUT_INIT_WINDOW_Y:
381 case GLUT_INIT_WINDOW_WIDTH:
383 case GLUT_INIT_WINDOW_HEIGHT:
385 case GLUT_ELAPSED_TIME:
393 int glutGetModifiers(void)
398 static int is_space(int c)
400 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
403 static const char *skip_space(const char *s)
405 while(*s && is_space(*s)) s++;
409 int glutExtensionSupported(char *ext)
411 const char *str, *eptr;
413 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
418 str = skip_space(str);
419 eptr = skip_space(ext);
420 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
424 if((!*str || is_space(*str)) && !*eptr) {
427 while(*str && !is_space(*str)) str++;
434 /* --------------- UNIX/X11 implementation ----------------- */
437 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
439 SPNAV_EVENT_BUTTON /* includes both press and release */
442 struct spnav_event_motion {
450 struct spnav_event_button {
458 struct spnav_event_motion motion;
459 struct spnav_event_button button;
463 static void handle_event(XEvent *ev);
465 static int spnav_window(Window win);
466 static int spnav_event(const XEvent *xev, union spnav_event *event);
467 static int spnav_remove_events(int type);
470 void glutMainLoopEvent(void)
475 panic("display callback not set");
478 if(!upd_pending && !cb_idle) {
479 XNextEvent(dpy, &ev);
483 while(XPending(dpy)) {
484 XNextEvent(dpy, &ev);
493 if(upd_pending && mapped) {
504 static void cleanup(void)
508 glXMakeCurrent(dpy, 0, 0);
509 XDestroyWindow(dpy, win);
513 static KeySym translate_keysym(KeySym sym)
534 static void handle_event(XEvent *ev)
537 union spnav_event sev;
546 case ConfigureNotify:
547 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
548 win_width = ev->xconfigure.width;
549 win_height = ev->xconfigure.height;
550 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
555 if(ev->xclient.message_type == xa_wm_proto) {
556 if(ev->xclient.data.l[0] == xa_wm_del_win) {
560 if(spnav_event(ev, &sev)) {
562 case SPNAV_EVENT_MOTION:
563 if(cb_sball_motion) {
564 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
566 if(cb_sball_rotate) {
567 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
569 spnav_remove_events(SPNAV_EVENT_MOTION);
572 case SPNAV_EVENT_BUTTON:
573 if(cb_sball_button) {
574 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
591 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
593 XPeekEvent(dpy, &next);
595 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
596 next.xkey.time == ev->xkey.time) {
597 /* this is a key-repeat event, ignore the release and consume
598 * the following press
600 XNextEvent(dpy, &next);
605 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
606 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
609 sym = translate_keysym(sym);
611 if(ev->type == KeyPress) {
612 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
614 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
617 if(ev->type == KeyPress) {
618 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
620 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
627 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
629 int bn = ev->xbutton.button - Button1;
630 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
631 ev->xbutton.x, ev->xbutton.y);
636 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
637 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
639 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
643 case VisibilityNotify:
645 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
649 if(cb_entry) cb_entry(GLUT_ENTERED);
652 if(cb_entry) cb_entry(GLUT_LEFT);
657 void glutSwapBuffers(void)
659 glXSwapBuffers(dpy, win);
663 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
664 * needs to resize the window to make it fullscreen. The way it does this is by
665 * querying the size of the root window (see get_screen_size), which in the
666 * case of multi-monitor setups will be the combined size of all monitors.
667 * This is problematic; the way to solve it is to use the XRandR extension, or
668 * the Xinerama extension, to figure out the dimensions of the correct video
669 * output, which would add potentially two extension support libraries to our
671 * Moreover, any X installation modern enough to support XR&R will almost
672 * certainly be running a window manager supporting the EHWM
673 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
674 * on manual resizing, and is used in preference if available, making this
675 * whole endeavor pointless.
676 * So I'll just leave it with set_fullscreen_mwm covering the entire
677 * multi-monitor area for now.
682 unsigned long functions;
683 unsigned long decorations;
685 unsigned long status;
688 #define MWM_HINTS_DECORATIONS 2
689 #define MWM_DECOR_ALL 1
691 static void set_fullscreen_mwm(int fs)
693 struct mwm_hints hints;
694 int scr_width, scr_height;
697 get_window_pos(&prev_win_x, &prev_win_y);
698 get_window_size(&prev_win_width, &prev_win_height);
699 get_screen_size(&scr_width, &scr_height);
701 hints.decorations = 0;
702 hints.flags = MWM_HINTS_DECORATIONS;
703 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
704 PropModeReplace, (unsigned char*)&hints, 5);
706 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
708 XDeleteProperty(dpy, win, xa_motif_wm_hints);
709 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
713 static int have_netwm_fullscr(void)
717 unsigned long i, count, rem;
719 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
722 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
723 &type, &fmt, &count, &rem, (unsigned char**)&prop);
725 for(i=0; i<count; i++) {
726 if(prop[i] == xa_net_wm_state_fullscr) {
738 static void set_fullscreen_ewmh(int fs)
740 XClientMessageEvent msg = {0};
742 msg.type = ClientMessage;
744 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
746 msg.data.l[0] = fs ? 1 : 0;
747 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
749 msg.data.l[3] = 1; /* source regular application */
750 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
753 static void set_fullscreen(int fs)
755 if(fullscreen == fs) return;
757 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
758 set_fullscreen_ewmh(fs);
760 } else if(xa_motif_wm_hints) {
761 set_fullscreen_mwm(fs);
766 void glutPositionWindow(int x, int y)
769 XMoveWindow(dpy, win, x, y);
772 void glutReshapeWindow(int xsz, int ysz)
775 XResizeWindow(dpy, win, xsz, ysz);
778 void glutFullScreen(void)
783 void glutSetWindowTitle(const char *title)
786 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
789 XSetWMName(dpy, win, &tprop);
793 void glutSetIconTitle(const char *title)
796 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
799 XSetWMIconName(dpy, win, &tprop);
803 void glutSetCursor(int cidx)
808 case GLUT_CURSOR_LEFT_ARROW:
809 cur = XCreateFontCursor(dpy, XC_left_ptr);
811 case GLUT_CURSOR_INHERIT:
813 case GLUT_CURSOR_NONE:
820 XDefineCursor(dpy, win, cur);
824 void glutSetKeyRepeat(int repmode)
833 static XVisualInfo *choose_visual(unsigned int mode)
840 if(mode & GLUT_DOUBLE) {
841 *aptr++ = GLX_DOUBLEBUFFER;
844 if(mode & GLUT_INDEX) {
845 *aptr++ = GLX_BUFFER_SIZE;
849 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
850 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
851 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
853 if(mode & GLUT_ALPHA) {
854 *aptr++ = GLX_ALPHA_SIZE;
857 if(mode & GLUT_DEPTH) {
858 *aptr++ = GLX_DEPTH_SIZE;
861 if(mode & GLUT_STENCIL) {
862 *aptr++ = GLX_STENCIL_SIZE;
865 if(mode & GLUT_ACCUM) {
866 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
867 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
868 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
870 if(mode & GLUT_STEREO) {
871 *aptr++ = GLX_STEREO;
873 if(mode & GLUT_SRGB) {
874 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
876 if(mode & GLUT_MULTISAMPLE) {
877 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
879 *aptr++ = GLX_SAMPLES_ARB;
886 return glXChooseVisual(dpy, scr, attr);
888 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
897 static void create_window(const char *title)
899 XSetWindowAttributes xattr = {0};
901 unsigned int xattr_mask;
902 unsigned int mode = init_mode;
904 if(!(vi = choose_visual(mode))) {
906 if(!(vi = choose_visual(mode))) {
907 panic("Failed to find compatible visual\n");
911 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
913 panic("Failed to create OpenGL context\n");
916 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
917 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
918 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
919 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
920 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
921 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
922 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
923 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
924 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
925 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
927 xattr.background_pixel = BlackPixel(dpy, scr);
928 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
929 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
930 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
931 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
933 glXDestroyContext(dpy, ctx);
934 panic("Failed to create window\n");
938 XSelectInput(dpy, win, evmask);
942 glutSetWindowTitle(title);
943 glutSetIconTitle(title);
944 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
945 XMapWindow(dpy, win);
947 glXMakeCurrent(dpy, win, ctx);
950 static void get_window_pos(int *x, int *y)
953 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
956 static void get_window_size(int *w, int *h)
958 XWindowAttributes wattr;
959 XGetWindowAttributes(dpy, win, &wattr);
964 static void get_screen_size(int *scrw, int *scrh)
966 XWindowAttributes wattr;
967 XGetWindowAttributes(dpy, root, &wattr);
969 *scrh = wattr.height;
975 CMD_APP_WINDOW = 27695,
979 static Window get_daemon_window(Display *dpy);
980 static int catch_badwin(Display *dpy, XErrorEvent *err);
982 #define SPNAV_INITIALIZED (xa_motion_event)
984 static int spnav_window(Window win)
986 int (*prev_xerr_handler)(Display*, XErrorEvent*);
990 if(!SPNAV_INITIALIZED) {
994 if(!(daemon_win = get_daemon_window(dpy))) {
998 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1000 xev.type = ClientMessage;
1001 xev.xclient.send_event = False;
1002 xev.xclient.display = dpy;
1003 xev.xclient.window = win;
1004 xev.xclient.message_type = xa_command_event;
1005 xev.xclient.format = 16;
1006 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1007 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1008 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1010 XSendEvent(dpy, daemon_win, False, 0, &xev);
1013 XSetErrorHandler(prev_xerr_handler);
1017 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1019 int evtype = *(int*)arg;
1021 if(xev->type != ClientMessage) {
1025 if(xev->xclient.message_type == xa_motion_event) {
1026 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1028 if(xev->xclient.message_type == xa_button_press_event ||
1029 xev->xclient.message_type == xa_button_release_event) {
1030 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1035 static int spnav_remove_events(int type)
1039 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1045 static int spnav_event(const XEvent *xev, union spnav_event *event)
1050 xmsg_type = xev->xclient.message_type;
1052 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1053 xmsg_type != xa_button_release_event) {
1057 if(xmsg_type == xa_motion_event) {
1058 event->type = SPNAV_EVENT_MOTION;
1059 event->motion.data = &event->motion.x;
1061 for(i=0; i<6; i++) {
1062 event->motion.data[i] = xev->xclient.data.s[i + 2];
1064 event->motion.period = xev->xclient.data.s[8];
1066 event->type = SPNAV_EVENT_BUTTON;
1067 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1068 event->button.bnum = xev->xclient.data.s[2];
1073 static int mglut_strcmp(const char *s1, const char *s2)
1075 while(*s1 && *s1 == *s2) {
1082 static Window get_daemon_window(Display *dpy)
1085 XTextProperty wname;
1088 unsigned long nitems, bytes_after;
1089 unsigned char *prop;
1091 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1092 &type, &fmt, &nitems, &bytes_after, &prop);
1097 win = *(Window*)prop;
1101 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1109 static int catch_badwin(Display *dpy, XErrorEvent *err)
1116 #endif /* BUILD_X11 */
1119 /* --------------- windows implementation ----------------- */
1121 static int reshape_pending;
1123 static void update_modkeys(void);
1124 static int translate_vkey(int vkey);
1125 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1127 #ifdef MINIGLUT_WINMAIN
1128 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1131 char *argv[] = { "miniglut.exe", 0 };
1132 return main(argc, argv);
1136 void glutMainLoopEvent(void)
1141 panic("display callback not set");
1144 if(reshape_pending && cb_reshape) {
1145 reshape_pending = 0;
1146 get_window_size(&win_width, &win_height);
1147 cb_reshape(win_width, win_height);
1150 if(!upd_pending && !cb_idle) {
1151 GetMessage(&msg, 0, 0, 0);
1152 TranslateMessage(&msg);
1153 DispatchMessage(&msg);
1156 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1157 TranslateMessage(&msg);
1158 DispatchMessage(&msg);
1166 if(upd_pending && mapped) {
1172 static void cleanup(void)
1175 wglMakeCurrent(dc, 0);
1176 wglDeleteContext(ctx);
1177 UnregisterClass("MiniGLUT", hinst);
1181 void glutSwapBuffers(void)
1186 void glutPositionWindow(int x, int y)
1189 unsigned int flags = SWP_SHOWWINDOW;
1192 rect.left = prev_win_x;
1193 rect.top = prev_win_y;
1194 rect.right = rect.left + prev_win_width;
1195 rect.bottom = rect.top + prev_win_height;
1196 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1198 flags |= SWP_FRAMECHANGED;
1200 GetWindowRect(win, &rect);
1202 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1205 void glutReshapeWindow(int xsz, int ysz)
1208 unsigned int flags = SWP_SHOWWINDOW;
1211 rect.left = prev_win_x;
1212 rect.top = prev_win_y;
1213 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1215 flags |= SWP_FRAMECHANGED;
1217 GetWindowRect(win, &rect);
1219 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1222 void glutFullScreen(void)
1225 int scr_width, scr_height;
1227 if(fullscreen) return;
1229 GetWindowRect(win, &rect);
1230 prev_win_x = rect.left;
1231 prev_win_y = rect.top;
1232 prev_win_width = rect.right - rect.left;
1233 prev_win_height = rect.bottom - rect.top;
1235 get_screen_size(&scr_width, &scr_height);
1237 SetWindowLong(win, GWL_STYLE, 0);
1238 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1243 void glutSetWindowTitle(const char *title)
1245 SetWindowText(win, title);
1248 void glutSetIconTitle(const char *title)
1252 void glutSetCursor(int cidx)
1255 case GLUT_CURSOR_NONE:
1258 case GLUT_CURSOR_INHERIT:
1259 case GLUT_CURSOR_LEFT_ARROW:
1261 SetCursor(LoadCursor(0, IDC_ARROW));
1266 void glutSetKeyRepeat(int repmode)
1270 #define WGL_DRAW_TO_WINDOW 0x2001
1271 #define WGL_ACCELERATION 0x2003
1272 #define WGL_SUPPORT_OPENGL 0x2010
1273 #define WGL_DOUBLE_BUFFER 0x2011
1274 #define WGL_STEREO 0x2012
1275 #define WGL_PIXEL_TYPE 0x2013
1276 #define WGL_COLOR_BITS 0x2014
1277 #define WGL_RED_BITS 0x2015
1278 #define WGL_GREEN_BITS 0x2017
1279 #define WGL_BLUE_BITS 0x2019
1280 #define WGL_ALPHA_BITS 0x201b
1281 #define WGL_ACCUM_BITS 0x201d
1282 #define WGL_DEPTH_BITS 0x2022
1283 #define WGL_STENCIL_BITS 0x2023
1284 #define WGL_FULL_ACCELERATION 0x2027
1286 #define WGL_TYPE_RGBA 0x202b
1287 #define WGL_TYPE_COLORINDEX 0x202c
1289 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1290 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1291 #define WGL_SAMPLES_ARB 0x2042
1293 static PROC wglChoosePixelFormat;
1294 static PROC wglGetPixelFormatAttribiv;
1296 #define ATTR(a, v) \
1297 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1299 static unsigned int choose_pixfmt(unsigned int mode)
1301 unsigned int num_pixfmt, pixfmt = 0;
1302 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1303 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1304 float fattr[2] = {0, 0};
1306 int *aptr = attr + 6;
1309 if(mode & GLUT_DOUBLE) {
1310 ATTR(WGL_DOUBLE_BUFFER, 1);
1313 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1314 ATTR(WGL_COLOR_BITS, 8);
1315 if(mode & GLUT_ALPHA) {
1316 ATTR(WGL_ALPHA_BITS, 4);
1318 if(mode & GLUT_DEPTH) {
1319 ATTR(WGL_DEPTH_BITS, 16);
1321 if(mode & GLUT_STENCIL) {
1322 ATTR(WGL_STENCIL_BITS, 1);
1324 if(mode & GLUT_ACCUM) {
1325 ATTR(WGL_ACCUM_BITS, 1);
1327 if(mode & GLUT_STEREO) {
1328 ATTR(WGL_STEREO, 1);
1330 if(mode & GLUT_SRGB) {
1331 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1333 if(mode & GLUT_MULTISAMPLE) {
1334 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1335 *aptr++ = WGL_SAMPLES_ARB;
1341 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1350 static PIXELFORMATDESCRIPTOR pfd;
1351 static PIXELFORMATDESCRIPTOR tmppfd = {
1352 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1353 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1354 PFD_MAIN_PLANE, 0, 0, 0, 0
1356 #define TMPCLASS "TempMiniGLUT"
1358 #define GETATTR(attr, vptr) \
1361 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1364 static int create_window_wglext(const char *title, int width, int height)
1366 WNDCLASSEX wc = {0};
1372 /* create a temporary window and GL context, just to query and retrieve
1373 * the wglChoosePixelFormatEXT function
1375 wc.cbSize = sizeof wc;
1376 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1377 wc.hCursor = LoadCursor(0, IDC_ARROW);
1378 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1379 wc.hInstance = hinst;
1380 wc.lpfnWndProc = DefWindowProc;
1381 wc.lpszClassName = TMPCLASS;
1382 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1383 if(!RegisterClassEx(&wc)) {
1386 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1387 width, height, 0, 0, hinst, 0))) {
1390 tmpdc = GetDC(tmpwin);
1392 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1393 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1394 !(tmpctx = wglCreateContext(tmpdc))) {
1397 wglMakeCurrent(tmpdc, tmpctx);
1399 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1400 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1403 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1407 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1411 wglMakeCurrent(0, 0);
1412 wglDeleteContext(tmpctx);
1413 DestroyWindow(tmpwin);
1414 UnregisterClass(TMPCLASS, hinst);
1416 /* create the real window and context */
1417 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1418 init_y, width, height, 0, 0, hinst, 0))) {
1419 panic("Failed to create window\n");
1423 if(!(pixfmt = choose_pixfmt(init_mode))) {
1424 panic("Failed to find suitable pixel format\n");
1426 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1427 panic("Failed to set the selected pixel format\n");
1429 if(!(ctx = wglCreateContext(dc))) {
1430 panic("Failed to create the OpenGL context\n");
1432 wglMakeCurrent(dc, ctx);
1434 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1435 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1436 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1437 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1438 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1439 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1440 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1441 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1442 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1447 wglMakeCurrent(0, 0);
1448 wglDeleteContext(tmpctx);
1451 DestroyWindow(tmpwin);
1453 UnregisterClass(TMPCLASS, hinst);
1458 static void create_window(const char *title)
1466 rect.right = init_x + init_width;
1467 rect.bottom = init_y + init_height;
1468 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1469 width = rect.right - rect.left;
1470 height = rect.bottom - rect.top;
1472 memset(&pfd, 0, sizeof pfd);
1473 pfd.nSize = sizeof pfd;
1475 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1476 if(init_mode & GLUT_STEREO) {
1477 pfd.dwFlags |= PFD_STEREO;
1479 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1480 pfd.cColorBits = 24;
1481 if(init_mode & GLUT_ALPHA) {
1484 if(init_mode & GLUT_ACCUM) {
1485 pfd.cAccumBits = 24;
1487 if(init_mode & GLUT_DEPTH) {
1488 pfd.cDepthBits = 24;
1490 if(init_mode & GLUT_STENCIL) {
1491 pfd.cStencilBits = 8;
1493 pfd.iLayerType = PFD_MAIN_PLANE;
1496 if(create_window_wglext(title, width, height) == -1) {
1498 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1499 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1500 panic("Failed to create window\n");
1504 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1505 panic("Failed to find suitable pixel format\n");
1507 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1508 panic("Failed to set the selected pixel format\n");
1510 if(!(ctx = wglCreateContext(dc))) {
1511 panic("Failed to create the OpenGL context\n");
1513 wglMakeCurrent(dc, ctx);
1515 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1516 ctx_info.rsize = pfd.cRedBits;
1517 ctx_info.gsize = pfd.cGreenBits;
1518 ctx_info.bsize = pfd.cBlueBits;
1519 ctx_info.asize = pfd.cAlphaBits;
1520 ctx_info.zsize = pfd.cDepthBits;
1521 ctx_info.ssize = pfd.cStencilBits;
1522 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1523 ctx_info.samples = 0;
1528 SetForegroundWindow(win);
1531 reshape_pending = 1;
1534 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1536 static int mouse_x, mouse_y;
1541 if(win) DestroyWindow(win);
1552 ValidateRect(win, 0);
1556 x = lparam & 0xffff;
1558 if(x != win_width && y != win_height) {
1562 reshape_pending = 0;
1563 cb_reshape(win_width, win_height);
1570 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1576 key = translate_vkey(wparam);
1579 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1583 cb_skeydown(key, mouse_x, mouse_y);
1591 key = translate_vkey(wparam);
1594 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1598 cb_skeyup(key, mouse_x, mouse_y);
1603 case WM_LBUTTONDOWN:
1604 handle_mbutton(0, 1, wparam, lparam);
1606 case WM_MBUTTONDOWN:
1607 handle_mbutton(1, 1, wparam, lparam);
1609 case WM_RBUTTONDOWN:
1610 handle_mbutton(2, 1, wparam, lparam);
1613 handle_mbutton(0, 0, wparam, lparam);
1616 handle_mbutton(1, 0, wparam, lparam);
1619 handle_mbutton(2, 0, wparam, lparam);
1623 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1624 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1626 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1632 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1636 return DefWindowProc(win, msg, wparam, lparam);
1642 static void update_modkeys(void)
1644 if(GetKeyState(VK_SHIFT) & 0x8000) {
1645 modstate |= GLUT_ACTIVE_SHIFT;
1647 modstate &= ~GLUT_ACTIVE_SHIFT;
1649 if(GetKeyState(VK_CONTROL) & 0x8000) {
1650 modstate |= GLUT_ACTIVE_CTRL;
1652 modstate &= ~GLUT_ACTIVE_CTRL;
1654 if(GetKeyState(VK_MENU) & 0x8000) {
1655 modstate |= GLUT_ACTIVE_ALT;
1657 modstate &= ~GLUT_ACTIVE_ALT;
1661 static int translate_vkey(int vkey)
1664 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1665 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1666 case VK_END: return GLUT_KEY_END;
1667 case VK_HOME: return GLUT_KEY_HOME;
1668 case VK_LEFT: return GLUT_KEY_LEFT;
1669 case VK_UP: return GLUT_KEY_UP;
1670 case VK_RIGHT: return GLUT_KEY_RIGHT;
1671 case VK_DOWN: return GLUT_KEY_DOWN;
1676 if(vkey >= 'A' && vkey <= 'Z') {
1678 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1679 vkey -= VK_F1 + GLUT_KEY_F1;
1685 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1692 x = lparam & 0xffff;
1694 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1698 static void get_window_pos(int *x, int *y)
1701 GetWindowRect(win, &rect);
1706 static void get_window_size(int *w, int *h)
1709 GetClientRect(win, &rect);
1710 *w = rect.right - rect.left;
1711 *h = rect.bottom - rect.top;
1714 static void get_screen_size(int *scrw, int *scrh)
1716 *scrw = GetSystemMetrics(SM_CXSCREEN);
1717 *scrh = GetSystemMetrics(SM_CYSCREEN);
1719 #endif /* BUILD_WIN32 */
1721 #if defined(__unix__) || defined(__APPLE__)
1722 #include <sys/time.h>
1724 #ifdef MINIGLUT_USE_LIBC
1725 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1727 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1730 static long get_msec(void)
1732 static struct timeval tv0;
1735 sys_gettimeofday(&tv, 0);
1736 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1740 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1744 static long get_msec(void)
1749 #ifdef MINIGLUT_NO_WINMM
1750 tm = GetTickCount();
1762 static void panic(const char *msg)
1764 const char *end = msg;
1766 sys_write(2, msg, end - msg);
1771 #ifdef MINIGLUT_USE_LIBC
1779 static void sys_exit(int status)
1784 static int sys_write(int fd, const void *buf, int count)
1786 return write(fd, buf, count);
1789 #else /* !MINIGLUT_USE_LIBC */
1793 static void sys_exit(int status)
1797 :: "a"(60), "D"(status));
1799 static int sys_write(int fd, const void *buf, int count)
1805 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1808 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1814 : "a"(96), "D"(tv), "S"(tz));
1817 #endif /* __x86_64__ */
1819 static void sys_exit(int status)
1823 :: "a"(1), "b"(status));
1825 static int sys_write(int fd, const void *buf, int count)
1831 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1834 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1840 : "a"(78), "b"(tv), "c"(tz));
1843 #endif /* __i386__ */
1844 #endif /* __linux__ */
1847 static void sys_exit(int status)
1849 ExitProcess(status);
1851 static int sys_write(int fd, const void *buf, int count)
1853 unsigned long wrsz = 0;
1855 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1856 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1862 #endif /* !MINIGLUT_USE_LIBC */
1865 /* ----------------- primitives ------------------ */
1866 #ifdef MINIGLUT_USE_LIBC
1870 void mglut_sincos(float angle, float *sptr, float *cptr)
1876 float mglut_atan(float x)
1881 #else /* !MINIGLUT_USE_LIBC */
1884 void mglut_sincos(float angle, float *sptr, float *cptr)
1891 : "=m"(*sptr), "=m"(*cptr)
1896 float mglut_atan(float x)
1912 void mglut_sincos(float angle, float *sptr, float *cptr)
1925 float mglut_atan(float x)
1939 #pragma aux mglut_sincos = \
1941 "fstp dword ptr [edx]" \
1942 "fstp dword ptr [eax]" \
1943 parm[8087][eax][edx] \
1946 #pragma aux mglut_atan = \
1952 #endif /* __WATCOMC__ */
1954 #endif /* !MINIGLUT_USE_LIBC */
1956 #define PI 3.1415926536f
1958 void glutSolidSphere(float rad, int slices, int stacks)
1961 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1962 float du = 1.0f / (float)slices;
1963 float dv = 1.0f / (float)stacks;
1966 for(i=0; i<stacks; i++) {
1968 for(j=0; j<slices; j++) {
1970 for(k=0; k<4; k++) {
1971 gray = k ^ (k >> 1);
1972 s = gray & 1 ? u + du : u;
1973 t = gray & 2 ? v + dv : v;
1974 theta = s * PI * 2.0f;
1976 mglut_sincos(theta, &sintheta, &costheta);
1977 mglut_sincos(phi, &sinphi, &cosphi);
1978 x = sintheta * sinphi;
1979 y = costheta * sinphi;
1984 glNormal3f(x, y, z);
1985 glVertex3f(x * rad, y * rad, z * rad);
1992 void glutWireSphere(float rad, int slices, int stacks)
1994 glPushAttrib(GL_POLYGON_BIT);
1995 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1996 glutSolidSphere(rad, slices, stacks);
2000 void glutSolidCube(float sz)
2002 int i, j, idx, gray, flip, rotx;
2003 float vpos[3], norm[3];
2004 float rad = sz * 0.5f;
2007 for(i=0; i<6; i++) {
2010 idx = (~i & 2) - rotx;
2011 norm[0] = norm[1] = norm[2] = 0.0f;
2012 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2014 vpos[idx] = norm[idx] * rad;
2015 for(j=0; j<4; j++) {
2016 gray = j ^ (j >> 1);
2017 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2018 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2019 glTexCoord2f(gray & 1, gray >> 1);
2026 void glutWireCube(float sz)
2028 glPushAttrib(GL_POLYGON_BIT);
2029 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2034 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2037 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2038 float du = 1.0f / (float)slices;
2039 float dv = 1.0f / (float)stacks;
2042 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2043 mglut_sincos(phi, &sinphi, &cosphi);
2046 for(i=0; i<stacks; i++) {
2048 for(j=0; j<slices; j++) {
2050 for(k=0; k<4; k++) {
2051 gray = k ^ (k >> 1);
2052 s = gray & 2 ? u + du : u;
2053 t = gray & 1 ? v + dv : v;
2054 rad = rbot + (rtop - rbot) * t;
2055 theta = s * PI * 2.0f;
2056 mglut_sincos(theta, &sintheta, &costheta);
2058 x = sintheta * cosphi;
2059 y = costheta * cosphi;
2064 glNormal3f(x, y, z);
2065 glVertex3f(sintheta * rad, costheta * rad, t * height);
2072 void glutSolidCone(float base, float height, int slices, int stacks)
2074 draw_cylinder(base, 0, height, slices, stacks);
2077 void glutWireCone(float base, float height, int slices, int stacks)
2079 glPushAttrib(GL_POLYGON_BIT);
2080 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2081 glutSolidCone(base, height, slices, stacks);
2085 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2087 draw_cylinder(rad, rad, height, slices, stacks);
2090 void glutWireCylinder(float rad, float height, int slices, int stacks)
2092 glPushAttrib(GL_POLYGON_BIT);
2093 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2094 glutSolidCylinder(rad, height, slices, stacks);
2098 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2101 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2102 float du = 1.0f / (float)rings;
2103 float dv = 1.0f / (float)sides;
2106 for(i=0; i<rings; i++) {
2108 for(j=0; j<sides; j++) {
2110 for(k=0; k<4; k++) {
2111 gray = k ^ (k >> 1);
2112 s = gray & 1 ? u + du : u;
2113 t = gray & 2 ? v + dv : v;
2114 theta = s * PI * 2.0f;
2115 phi = t * PI * 2.0f;
2116 mglut_sincos(theta, &sintheta, &costheta);
2117 mglut_sincos(phi, &sinphi, &cosphi);
2118 x = sintheta * sinphi;
2119 y = costheta * sinphi;
2124 glNormal3f(x, y, z);
2126 x = x * inner_rad + sintheta * outer_rad;
2127 y = y * inner_rad + costheta * outer_rad;
2129 glVertex3f(x, y, z);
2136 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2138 glPushAttrib(GL_POLYGON_BIT);
2139 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2140 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2144 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2145 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2147 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2149 #define PATCH_SUBDIV 7
2151 static float teapot_part_flip[] = {
2152 1, 1, 1, 1, /* rim flip */
2153 1, 1, 1, 1, /* body1 flip */
2154 1, 1, 1, 1, /* body2 flip */
2155 1, 1, 1, 1, /* lid patch 1 flip */
2156 1, 1, 1, 1, /* lid patch 2 flip */
2157 1, -1, /* handle 1 flip */
2158 1, -1, /* handle 2 flip */
2159 1, -1, /* spout 1 flip */
2160 1, -1, /* spout 2 flip */
2161 1, 1, 1, 1 /* bottom flip */
2164 static float teapot_part_rot[] = {
2165 0, 90, 180, 270, /* rim rotations */
2166 0, 90, 180, 270, /* body patch 1 rotations */
2167 0, 90, 180, 270, /* body patch 2 rotations */
2168 0, 90, 180, 270, /* lid patch 1 rotations */
2169 0, 90, 180, 270, /* lid patch 2 rotations */
2170 0, 0, /* handle 1 rotations */
2171 0, 0, /* handle 2 rotations */
2172 0, 0, /* spout 1 rotations */
2173 0, 0, /* spout 2 rotations */
2174 0, 90, 180, 270 /* bottom rotations */
2178 static int teapot_index[] = {
2180 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2181 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2182 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2183 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2185 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2186 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2187 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2188 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2190 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2191 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2192 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2193 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2195 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2196 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2197 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2198 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2200 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2201 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2202 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2203 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2205 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2206 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2208 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2209 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2211 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2212 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2214 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2215 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2217 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2218 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2219 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2220 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2224 static float teapot_verts[][3] = {
2225 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2226 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2227 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2228 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2229 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2230 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2231 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2232 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2233 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2234 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2235 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2236 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2237 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2238 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2239 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2240 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2241 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2242 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2243 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2244 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2245 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2246 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2247 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2248 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2249 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2250 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2251 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2252 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2253 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2254 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2255 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2256 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2257 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2258 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2259 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2260 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2261 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2262 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2263 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2264 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2265 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2266 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2267 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2268 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2269 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2270 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2271 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2272 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2273 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2274 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2275 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2276 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2277 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2278 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2279 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2280 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2281 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2282 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2283 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2284 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2285 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2286 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2287 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2288 { 0.8400, -1.5000, 0.07500 }
2291 static void draw_patch(int *index, int flip, float scale);
2292 static float bernstein(int i, float x);
2294 void glutSolidTeapot(float size)
2300 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2301 float flip = teapot_part_flip[i];
2302 float rot = teapot_part_rot[i];
2304 glMatrixMode(GL_MODELVIEW);
2306 glTranslatef(0, -3.15 * size * 0.5, 0);
2307 glRotatef(rot, 0, 1, 0);
2308 glScalef(1, 1, flip);
2309 glRotatef(-90, 1, 0, 0);
2311 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2317 void glutWireTeapot(float size)
2319 glPushAttrib(GL_POLYGON_BIT);
2320 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2321 glutSolidTeapot(size);
2326 static void bezier_patch(float *res, float *cp, float u, float v)
2330 res[0] = res[1] = res[2] = 0.0f;
2332 for(j=0; j<4; j++) {
2333 for(i=0; i<4; i++) {
2334 float bu = bernstein(i, u);
2335 float bv = bernstein(j, v);
2337 res[0] += cp[0] * bu * bv;
2338 res[1] += cp[1] * bu * bv;
2339 res[2] += cp[2] * bu * bv;
2346 static float rsqrt(float x)
2348 float xhalf = x * 0.5f;
2350 i = 0x5f3759df - (i >> 1);
2352 x = x * (1.5f - xhalf * x * x);
2357 #define CROSS(res, a, b) \
2359 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2360 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2361 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2364 #define NORMALIZE(v) \
2366 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2374 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2376 float tang[3], bitan[3], tmp[3];
2378 bezier_patch(tang, cp, u + DT, v);
2379 bezier_patch(tmp, cp, u - DT, v);
2384 bezier_patch(bitan, cp, u, v + DT);
2385 bezier_patch(tmp, cp, u, v - DT);
2390 CROSS(res, tang, bitan);
2396 static float bernstein(int i, float x)
2398 float invx = 1.0f - x;
2402 return invx * invx * invx;
2404 return 3.0f * x * invx * invx;
2406 return 3.0f * x * x * invx;
2415 static void draw_patch(int *index, int flip, float scale)
2417 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2418 static const float voffs[4] = {0, 1, 1, 0};
2424 float du = 1.0 / PATCH_SUBDIV;
2425 float dv = 1.0 / PATCH_SUBDIV;
2427 /* collect control points */
2428 for(i=0; i<16; i++) {
2429 cp[i * 3] = teapot_verts[index[i]][0];
2430 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2431 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2438 for(i=0; i<PATCH_SUBDIV; i++) {
2440 for(j=0; j<PATCH_SUBDIV; j++) {
2442 for(k=0; k<4; k++) {
2443 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2445 /* top/bottom normal hack */
2449 } else if(pt[2] < 0.00001) {
2453 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2458 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);