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__)
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++ = 1;
850 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
851 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
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;
1672 case VK_OEM_1: return ';';
1673 case VK_OEM_2: return '/';
1674 case VK_OEM_3: return '`';
1675 case VK_OEM_4: return '[';
1676 case VK_OEM_5: return '\\';
1677 case VK_OEM_6: return ']';
1678 case VK_OEM_7: return '\'';
1683 if(vkey >= 'A' && vkey <= 'Z') {
1685 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1686 vkey -= VK_F1 + GLUT_KEY_F1;
1692 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1699 x = lparam & 0xffff;
1701 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1705 static void get_window_pos(int *x, int *y)
1708 GetWindowRect(win, &rect);
1713 static void get_window_size(int *w, int *h)
1716 GetClientRect(win, &rect);
1717 *w = rect.right - rect.left;
1718 *h = rect.bottom - rect.top;
1721 static void get_screen_size(int *scrw, int *scrh)
1723 *scrw = GetSystemMetrics(SM_CXSCREEN);
1724 *scrh = GetSystemMetrics(SM_CYSCREEN);
1726 #endif /* BUILD_WIN32 */
1728 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1729 #include <sys/time.h>
1731 #ifdef MINIGLUT_USE_LIBC
1732 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1734 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1737 static long get_msec(void)
1739 static struct timeval tv0;
1742 sys_gettimeofday(&tv, 0);
1743 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1747 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1751 static long get_msec(void)
1756 #ifdef MINIGLUT_NO_WINMM
1757 tm = GetTickCount();
1769 static void panic(const char *msg)
1771 const char *end = msg;
1773 sys_write(2, msg, end - msg);
1778 #ifdef MINIGLUT_USE_LIBC
1786 static void sys_exit(int status)
1791 static int sys_write(int fd, const void *buf, int count)
1793 return write(fd, buf, count);
1796 #else /* !MINIGLUT_USE_LIBC */
1800 static void sys_exit(int status)
1804 :: "a"(60), "D"(status));
1806 static int sys_write(int fd, const void *buf, int count)
1812 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1815 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1821 : "a"(96), "D"(tv), "S"(tz));
1824 #endif /* __x86_64__ */
1826 static void sys_exit(int status)
1830 :: "a"(1), "b"(status));
1832 static int sys_write(int fd, const void *buf, int count)
1838 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1841 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1847 : "a"(78), "b"(tv), "c"(tz));
1850 #endif /* __i386__ */
1851 #endif /* __linux__ */
1854 static void sys_exit(int status)
1856 ExitProcess(status);
1858 static int sys_write(int fd, const void *buf, int count)
1860 unsigned long wrsz = 0;
1862 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1863 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1869 #endif /* !MINIGLUT_USE_LIBC */
1872 /* ----------------- primitives ------------------ */
1873 #ifdef MINIGLUT_USE_LIBC
1877 void mglut_sincos(float angle, float *sptr, float *cptr)
1883 float mglut_atan(float x)
1888 #else /* !MINIGLUT_USE_LIBC */
1891 void mglut_sincos(float angle, float *sptr, float *cptr)
1898 : "=m"(*sptr), "=m"(*cptr)
1903 float mglut_atan(float x)
1919 void mglut_sincos(float angle, float *sptr, float *cptr)
1932 float mglut_atan(float x)
1946 #pragma aux mglut_sincos = \
1948 "fstp dword ptr [edx]" \
1949 "fstp dword ptr [eax]" \
1950 parm[8087][eax][edx] \
1953 #pragma aux mglut_atan = \
1959 #endif /* __WATCOMC__ */
1961 #endif /* !MINIGLUT_USE_LIBC */
1963 #define PI 3.1415926536f
1965 void glutSolidSphere(float rad, int slices, int stacks)
1968 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1969 float du = 1.0f / (float)slices;
1970 float dv = 1.0f / (float)stacks;
1973 for(i=0; i<stacks; i++) {
1975 for(j=0; j<slices; j++) {
1977 for(k=0; k<4; k++) {
1978 gray = k ^ (k >> 1);
1979 s = gray & 1 ? u + du : u;
1980 t = gray & 2 ? v + dv : v;
1981 theta = s * PI * 2.0f;
1983 mglut_sincos(theta, &sintheta, &costheta);
1984 mglut_sincos(phi, &sinphi, &cosphi);
1985 x = sintheta * sinphi;
1986 y = costheta * sinphi;
1991 glNormal3f(x, y, z);
1992 glVertex3f(x * rad, y * rad, z * rad);
1999 void glutWireSphere(float rad, int slices, int stacks)
2001 glPushAttrib(GL_POLYGON_BIT);
2002 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2003 glutSolidSphere(rad, slices, stacks);
2007 void glutSolidCube(float sz)
2009 int i, j, idx, gray, flip, rotx;
2010 float vpos[3], norm[3];
2011 float rad = sz * 0.5f;
2014 for(i=0; i<6; i++) {
2017 idx = (~i & 2) - rotx;
2018 norm[0] = norm[1] = norm[2] = 0.0f;
2019 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2021 vpos[idx] = norm[idx] * rad;
2022 for(j=0; j<4; j++) {
2023 gray = j ^ (j >> 1);
2024 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2025 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2026 glTexCoord2f(gray & 1, gray >> 1);
2033 void glutWireCube(float sz)
2035 glPushAttrib(GL_POLYGON_BIT);
2036 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2041 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2044 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2045 float du = 1.0f / (float)slices;
2046 float dv = 1.0f / (float)stacks;
2049 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2050 mglut_sincos(phi, &sinphi, &cosphi);
2053 for(i=0; i<stacks; i++) {
2055 for(j=0; j<slices; j++) {
2057 for(k=0; k<4; k++) {
2058 gray = k ^ (k >> 1);
2059 s = gray & 2 ? u + du : u;
2060 t = gray & 1 ? v + dv : v;
2061 rad = rbot + (rtop - rbot) * t;
2062 theta = s * PI * 2.0f;
2063 mglut_sincos(theta, &sintheta, &costheta);
2065 x = sintheta * cosphi;
2066 y = costheta * cosphi;
2071 glNormal3f(x, y, z);
2072 glVertex3f(sintheta * rad, costheta * rad, t * height);
2079 void glutSolidCone(float base, float height, int slices, int stacks)
2081 draw_cylinder(base, 0, height, slices, stacks);
2084 void glutWireCone(float base, float height, int slices, int stacks)
2086 glPushAttrib(GL_POLYGON_BIT);
2087 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2088 glutSolidCone(base, height, slices, stacks);
2092 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2094 draw_cylinder(rad, rad, height, slices, stacks);
2097 void glutWireCylinder(float rad, float height, int slices, int stacks)
2099 glPushAttrib(GL_POLYGON_BIT);
2100 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2101 glutSolidCylinder(rad, height, slices, stacks);
2105 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2108 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2109 float du = 1.0f / (float)rings;
2110 float dv = 1.0f / (float)sides;
2113 for(i=0; i<rings; i++) {
2115 for(j=0; j<sides; j++) {
2117 for(k=0; k<4; k++) {
2118 gray = k ^ (k >> 1);
2119 s = gray & 1 ? u + du : u;
2120 t = gray & 2 ? v + dv : v;
2121 theta = s * PI * 2.0f;
2122 phi = t * PI * 2.0f;
2123 mglut_sincos(theta, &sintheta, &costheta);
2124 mglut_sincos(phi, &sinphi, &cosphi);
2125 x = sintheta * sinphi;
2126 y = costheta * sinphi;
2131 glNormal3f(x, y, z);
2133 x = x * inner_rad + sintheta * outer_rad;
2134 y = y * inner_rad + costheta * outer_rad;
2136 glVertex3f(x, y, z);
2143 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2145 glPushAttrib(GL_POLYGON_BIT);
2146 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2147 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2151 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2152 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2154 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2156 #define PATCH_SUBDIV 7
2158 static float teapot_part_flip[] = {
2159 1, 1, 1, 1, /* rim flip */
2160 1, 1, 1, 1, /* body1 flip */
2161 1, 1, 1, 1, /* body2 flip */
2162 1, 1, 1, 1, /* lid patch 1 flip */
2163 1, 1, 1, 1, /* lid patch 2 flip */
2164 1, -1, /* handle 1 flip */
2165 1, -1, /* handle 2 flip */
2166 1, -1, /* spout 1 flip */
2167 1, -1, /* spout 2 flip */
2168 1, 1, 1, 1 /* bottom flip */
2171 static float teapot_part_rot[] = {
2172 0, 90, 180, 270, /* rim rotations */
2173 0, 90, 180, 270, /* body patch 1 rotations */
2174 0, 90, 180, 270, /* body patch 2 rotations */
2175 0, 90, 180, 270, /* lid patch 1 rotations */
2176 0, 90, 180, 270, /* lid patch 2 rotations */
2177 0, 0, /* handle 1 rotations */
2178 0, 0, /* handle 2 rotations */
2179 0, 0, /* spout 1 rotations */
2180 0, 0, /* spout 2 rotations */
2181 0, 90, 180, 270 /* bottom rotations */
2185 static int teapot_index[] = {
2187 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2188 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2189 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2190 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2192 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2193 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2194 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2195 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2197 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2198 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2199 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2200 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2202 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2203 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2204 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2205 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2207 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2208 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2209 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2210 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2212 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2213 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2215 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2216 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2218 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2219 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2221 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2222 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2224 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2225 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2226 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2227 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2231 static float teapot_verts[][3] = {
2232 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2233 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2234 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2235 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2236 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2237 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2238 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2239 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2240 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2241 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2242 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2243 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2244 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2245 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2246 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2247 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2248 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2249 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2250 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2251 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2252 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2253 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2254 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2255 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2256 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2257 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2258 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2259 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2260 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2261 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2262 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2263 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2264 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2265 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2266 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2267 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2268 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2269 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2270 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2271 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2272 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2273 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2274 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2275 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2276 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2277 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2278 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2279 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2280 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2281 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2282 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2283 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2284 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2285 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2286 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2287 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2288 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2289 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2290 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2291 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2292 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2293 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2294 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2295 { 0.8400, -1.5000, 0.07500 }
2298 static void draw_patch(int *index, int flip, float scale);
2299 static float bernstein(int i, float x);
2301 void glutSolidTeapot(float size)
2307 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2308 float flip = teapot_part_flip[i];
2309 float rot = teapot_part_rot[i];
2311 glMatrixMode(GL_MODELVIEW);
2313 glTranslatef(0, -3.15 * size * 0.5, 0);
2314 glRotatef(rot, 0, 1, 0);
2315 glScalef(1, 1, flip);
2316 glRotatef(-90, 1, 0, 0);
2318 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2324 void glutWireTeapot(float size)
2326 glPushAttrib(GL_POLYGON_BIT);
2327 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2328 glutSolidTeapot(size);
2333 static void bezier_patch(float *res, float *cp, float u, float v)
2337 res[0] = res[1] = res[2] = 0.0f;
2339 for(j=0; j<4; j++) {
2340 for(i=0; i<4; i++) {
2341 float bu = bernstein(i, u);
2342 float bv = bernstein(j, v);
2344 res[0] += cp[0] * bu * bv;
2345 res[1] += cp[1] * bu * bv;
2346 res[2] += cp[2] * bu * bv;
2353 static float rsqrt(float x)
2355 float xhalf = x * 0.5f;
2357 i = 0x5f3759df - (i >> 1);
2359 x = x * (1.5f - xhalf * x * x);
2364 #define CROSS(res, a, b) \
2366 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2367 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2368 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2371 #define NORMALIZE(v) \
2373 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2381 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2383 float tang[3], bitan[3], tmp[3];
2385 bezier_patch(tang, cp, u + DT, v);
2386 bezier_patch(tmp, cp, u - DT, v);
2391 bezier_patch(bitan, cp, u, v + DT);
2392 bezier_patch(tmp, cp, u, v - DT);
2397 CROSS(res, tang, bitan);
2403 static float bernstein(int i, float x)
2405 float invx = 1.0f - x;
2409 return invx * invx * invx;
2411 return 3.0f * x * invx * invx;
2413 return 3.0f * x * x * invx;
2422 static void draw_patch(int *index, int flip, float scale)
2424 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2425 static const float voffs[4] = {0, 1, 1, 0};
2431 float du = 1.0 / PATCH_SUBDIV;
2432 float dv = 1.0 / PATCH_SUBDIV;
2434 /* collect control points */
2435 for(i=0; i<16; i++) {
2436 cp[i * 3] = teapot_verts[index[i]][0];
2437 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2438 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2445 for(i=0; i<PATCH_SUBDIV; i++) {
2447 for(j=0; j<PATCH_SUBDIV; j++) {
2449 for(k=0; k<4; k++) {
2450 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2452 /* top/bottom normal hack */
2456 } else if(pt[2] < 0.00001) {
2460 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2465 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);