2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020 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 HRESULT 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_SUPPORT_OPENGL 0x2010
1272 #define WGL_DOUBLE_BUFFER 0x2011
1273 #define WGL_STEREO 0x2012
1274 #define WGL_PIXEL_TYPE 0x2013
1275 #define WGL_COLOR_BITS 0x2014
1276 #define WGL_RED_BITS 0x2015
1277 #define WGL_GREEN_BITS 0x2017
1278 #define WGL_BLUE_BITS 0x2019
1279 #define WGL_ALPHA_BITS 0x201b
1280 #define WGL_ACCUM_BITS 0x201d
1281 #define WGL_DEPTH_BITS 0x2022
1282 #define WGL_STENCIL_BITS 0x2023
1284 #define WGL_TYPE_RGBA 0x202b
1285 #define WGL_TYPE_COLORINDEX 0x202c
1287 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1288 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1289 #define WGL_SAMPLES_ARB 0x2042
1291 static PROC wglChoosePixelFormat;
1292 static PROC wglGetPixelFormatAttribiv;
1294 #define ATTR(a, v) \
1295 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1297 static unsigned int choose_pixfmt(unsigned int mode)
1299 unsigned int num_pixfmt, pixfmt = 0;
1300 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1 };
1305 if(mode & GLUT_DOUBLE) {
1306 ATTR(WGL_DOUBLE_BUFFER, 1);
1309 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1310 ATTR(WGL_COLOR_BITS, 8);
1311 if(mode & GLUT_ALPHA) {
1312 ATTR(WGL_ALPHA_BITS, 4);
1314 if(mode & GLUT_DEPTH) {
1315 ATTR(WGL_DEPTH_BITS, 16);
1317 if(mode & GLUT_STENCIL) {
1318 ATTR(WGL_STENCIL_BITS, 1);
1320 if(mode & GLUT_ACCUM) {
1321 ATTR(WGL_ACCUM_BITS, 1);
1323 if(mode & GLUT_STEREO) {
1324 ATTR(WGL_STEREO, 1);
1326 if(mode & GLUT_SRGB) {
1327 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1329 if(mode & GLUT_MULTISAMPLE) {
1330 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1331 *aptr++ = WGL_SAMPLES_ARB;
1337 while((!wglChoosePixelFormat(dc, attr, 0, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1346 static PIXELFORMATDESCRIPTOR tmppfd = {
1347 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1348 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1349 PFD_MAIN_PLANE, 0, 0, 0, 0
1351 #define TMPCLASS "TempMiniGLUT"
1353 #define GETATTR(attr, vptr) \
1356 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1359 static int create_window_wglext(const char *title, int width, int height)
1361 WNDCLASSEX wc = {0};
1367 /* create a temporary window and GL context, just to query and retrieve
1368 * the wglChoosePixelFormatEXT function
1370 wc.cbSize = sizeof wc;
1371 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1372 wc.hCursor = LoadCursor(0, IDC_ARROW);
1373 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1374 wc.hInstance = hinst;
1375 wc.lpfnWndProc = DefWindowProc;
1376 wc.lpszClassName = TMPCLASS;
1377 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1378 if(!RegisterClassEx(&wc)) {
1381 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1382 width, height, 0, 0, hinst, 0))) {
1385 tmpdc = GetDC(tmpwin);
1387 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1388 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1389 !(tmpctx = wglCreateContext(tmpdc))) {
1392 wglMakeCurrent(tmpdc, tmpctx);
1394 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1395 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1398 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1402 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1406 wglMakeCurrent(0, 0);
1407 wglDeleteContext(tmpctx);
1408 DestroyWindow(tmpwin);
1409 UnregisterClass(TMPCLASS, hinst);
1411 /* create the real window and context */
1412 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1413 init_y, width, height, 0, 0, hinst, 0))) {
1414 panic("Failed to create window\n");
1418 if(!(pixfmt = choose_pixfmt(init_mode))) {
1419 panic("Failed to find suitable pixel format\n");
1421 if(!SetPixelFormat(dc, pixfmt, &tmppfd)) {
1422 panic("Failed to set the selected pixel format\n");
1424 if(!(ctx = wglCreateContext(dc))) {
1425 panic("Failed to create the OpenGL context\n");
1427 wglMakeCurrent(dc, ctx);
1429 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1430 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1431 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1432 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1433 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1434 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1435 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1436 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1437 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1442 wglMakeCurrent(0, 0);
1443 wglDeleteContext(tmpctx);
1446 DestroyWindow(tmpwin);
1448 UnregisterClass(TMPCLASS, hinst);
1453 static void create_window(const char *title)
1456 PIXELFORMATDESCRIPTOR pfd = {0};
1462 rect.right = init_x + init_width;
1463 rect.bottom = init_y + init_height;
1464 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1465 width = rect.right - rect.left;
1466 height = rect.bottom - rect.top;
1468 if(create_window_wglext(title, width, height) == -1) {
1470 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1471 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1472 panic("Failed to create window\n");
1476 pfd.nSize = sizeof pfd;
1478 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1479 if(init_mode & GLUT_STEREO) {
1480 pfd.dwFlags |= PFD_STEREO;
1482 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1483 pfd.cColorBits = 24;
1484 if(init_mode & GLUT_ALPHA) {
1487 if(init_mode & GLUT_ACCUM) {
1488 pfd.cAccumBits = 24;
1490 if(init_mode & GLUT_DEPTH) {
1491 pfd.cDepthBits = 24;
1493 if(init_mode & GLUT_STENCIL) {
1494 pfd.cStencilBits = 8;
1496 pfd.iLayerType = PFD_MAIN_PLANE;
1498 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1499 panic("Failed to find suitable pixel format\n");
1501 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1502 panic("Failed to set the selected pixel format\n");
1504 if(!(ctx = wglCreateContext(dc))) {
1505 panic("Failed to create the OpenGL context\n");
1507 wglMakeCurrent(dc, ctx);
1509 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1510 ctx_info.rsize = pfd.cRedBits;
1511 ctx_info.gsize = pfd.cGreenBits;
1512 ctx_info.bsize = pfd.cBlueBits;
1513 ctx_info.asize = pfd.cAlphaBits;
1514 ctx_info.zsize = pfd.cDepthBits;
1515 ctx_info.ssize = pfd.cStencilBits;
1516 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1517 ctx_info.samples = 0;
1522 SetForegroundWindow(win);
1525 reshape_pending = 1;
1528 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1530 static int mouse_x, mouse_y;
1535 if(win) DestroyWindow(win);
1546 ValidateRect(win, 0);
1550 x = lparam & 0xffff;
1552 if(x != win_width && y != win_height) {
1556 reshape_pending = 0;
1557 cb_reshape(win_width, win_height);
1564 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1570 key = translate_vkey(wparam);
1573 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1577 cb_skeydown(key, mouse_x, mouse_y);
1585 key = translate_vkey(wparam);
1588 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1592 cb_skeyup(key, mouse_x, mouse_y);
1597 case WM_LBUTTONDOWN:
1598 handle_mbutton(0, 1, wparam, lparam);
1600 case WM_MBUTTONDOWN:
1601 handle_mbutton(1, 1, wparam, lparam);
1603 case WM_RBUTTONDOWN:
1604 handle_mbutton(2, 1, wparam, lparam);
1607 handle_mbutton(0, 0, wparam, lparam);
1610 handle_mbutton(1, 0, wparam, lparam);
1613 handle_mbutton(2, 0, wparam, lparam);
1617 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1618 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1620 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1626 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1630 return DefWindowProc(win, msg, wparam, lparam);
1636 static void update_modkeys(void)
1638 if(GetKeyState(VK_SHIFT) & 0x8000) {
1639 modstate |= GLUT_ACTIVE_SHIFT;
1641 modstate &= ~GLUT_ACTIVE_SHIFT;
1643 if(GetKeyState(VK_CONTROL) & 0x8000) {
1644 modstate |= GLUT_ACTIVE_CTRL;
1646 modstate &= ~GLUT_ACTIVE_CTRL;
1648 if(GetKeyState(VK_MENU) & 0x8000) {
1649 modstate |= GLUT_ACTIVE_ALT;
1651 modstate &= ~GLUT_ACTIVE_ALT;
1655 static int translate_vkey(int vkey)
1658 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1659 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1660 case VK_END: return GLUT_KEY_END;
1661 case VK_HOME: return GLUT_KEY_HOME;
1662 case VK_LEFT: return GLUT_KEY_LEFT;
1663 case VK_UP: return GLUT_KEY_UP;
1664 case VK_RIGHT: return GLUT_KEY_RIGHT;
1665 case VK_DOWN: return GLUT_KEY_DOWN;
1670 if(vkey >= 'A' && vkey <= 'Z') {
1672 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1673 vkey -= VK_F1 + GLUT_KEY_F1;
1679 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1686 x = lparam & 0xffff;
1688 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1692 static void get_window_pos(int *x, int *y)
1695 GetWindowRect(win, &rect);
1700 static void get_window_size(int *w, int *h)
1703 GetClientRect(win, &rect);
1704 *w = rect.right - rect.left;
1705 *h = rect.bottom - rect.top;
1708 static void get_screen_size(int *scrw, int *scrh)
1710 *scrw = GetSystemMetrics(SM_CXSCREEN);
1711 *scrh = GetSystemMetrics(SM_CYSCREEN);
1713 #endif /* BUILD_WIN32 */
1715 #if defined(__unix__) || defined(__APPLE__)
1716 #include <sys/time.h>
1718 #ifdef MINIGLUT_USE_LIBC
1719 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1721 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1724 static long get_msec(void)
1726 static struct timeval tv0;
1729 sys_gettimeofday(&tv, 0);
1730 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1734 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1738 static long get_msec(void)
1743 #ifdef MINIGLUT_NO_WINMM
1744 tm = GetTickCount();
1756 static void panic(const char *msg)
1758 const char *end = msg;
1760 sys_write(2, msg, end - msg);
1765 #ifdef MINIGLUT_USE_LIBC
1771 static void sys_exit(int status)
1776 static int sys_write(int fd, const void *buf, int count)
1778 return write(fd, buf, count);
1781 #else /* !MINIGLUT_USE_LIBC */
1785 static void sys_exit(int status)
1789 :: "a"(60), "D"(status));
1791 static int sys_write(int fd, const void *buf, int count)
1797 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1800 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1806 : "a"(96), "D"(tv), "S"(tz));
1809 #endif /* __x86_64__ */
1811 static void sys_exit(int status)
1815 :: "a"(1), "b"(status));
1817 static int sys_write(int fd, const void *buf, int count)
1823 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1826 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1832 : "a"(78), "b"(tv), "c"(tz));
1835 #endif /* __i386__ */
1836 #endif /* __linux__ */
1839 static void sys_exit(int status)
1841 ExitProcess(status);
1843 static int sys_write(int fd, const void *buf, int count)
1845 unsigned long wrsz = 0;
1847 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1848 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1854 #endif /* !MINIGLUT_USE_LIBC */
1857 /* ----------------- primitives ------------------ */
1858 #ifdef MINIGLUT_USE_LIBC
1862 void mglut_sincos(float angle, float *sptr, float *cptr)
1868 float mglut_atan(float x)
1873 #else /* !MINIGLUT_USE_LIBC */
1876 void mglut_sincos(float angle, float *sptr, float *cptr)
1883 : "=m"(*sptr), "=m"(*cptr)
1888 float mglut_atan(float x)
1904 void mglut_sincos(float angle, float *sptr, float *cptr)
1917 float mglut_atan(float x)
1931 #pragma aux mglut_sincos = \
1933 "fstp dword ptr [edx]" \
1934 "fstp dword ptr [eax]" \
1935 parm[8087][eax][edx] \
1938 #pragma aux mglut_atan = \
1944 #endif /* __WATCOMC__ */
1946 #endif /* !MINIGLUT_USE_LIBC */
1948 #define PI 3.1415926536f
1950 void glutSolidSphere(float rad, int slices, int stacks)
1953 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1954 float du = 1.0f / (float)slices;
1955 float dv = 1.0f / (float)stacks;
1958 for(i=0; i<stacks; i++) {
1960 for(j=0; j<slices; j++) {
1962 for(k=0; k<4; k++) {
1963 gray = k ^ (k >> 1);
1964 s = gray & 1 ? u + du : u;
1965 t = gray & 2 ? v + dv : v;
1966 theta = s * PI * 2.0f;
1968 mglut_sincos(theta, &sintheta, &costheta);
1969 mglut_sincos(phi, &sinphi, &cosphi);
1970 x = sintheta * sinphi;
1971 y = costheta * sinphi;
1976 glNormal3f(x, y, z);
1977 glVertex3f(x * rad, y * rad, z * rad);
1984 void glutWireSphere(float rad, int slices, int stacks)
1986 glPushAttrib(GL_POLYGON_BIT);
1987 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1988 glutSolidSphere(rad, slices, stacks);
1992 void glutSolidCube(float sz)
1994 int i, j, idx, gray, flip, rotx;
1995 float vpos[3], norm[3];
1996 float rad = sz * 0.5f;
1999 for(i=0; i<6; i++) {
2002 idx = (~i & 2) - rotx;
2003 norm[0] = norm[1] = norm[2] = 0.0f;
2004 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2006 vpos[idx] = norm[idx] * rad;
2007 for(j=0; j<4; j++) {
2008 gray = j ^ (j >> 1);
2009 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2010 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2011 glTexCoord2f(gray & 1, gray >> 1);
2018 void glutWireCube(float sz)
2020 glPushAttrib(GL_POLYGON_BIT);
2021 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2026 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2029 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2030 float du = 1.0f / (float)slices;
2031 float dv = 1.0f / (float)stacks;
2034 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2035 mglut_sincos(phi, &sinphi, &cosphi);
2038 for(i=0; i<stacks; i++) {
2040 for(j=0; j<slices; j++) {
2042 for(k=0; k<4; k++) {
2043 gray = k ^ (k >> 1);
2044 s = gray & 2 ? u + du : u;
2045 t = gray & 1 ? v + dv : v;
2046 rad = rbot + (rtop - rbot) * t;
2047 theta = s * PI * 2.0f;
2048 mglut_sincos(theta, &sintheta, &costheta);
2050 x = sintheta * cosphi;
2051 y = costheta * cosphi;
2056 glNormal3f(x, y, z);
2057 glVertex3f(sintheta * rad, costheta * rad, t * height);
2064 void glutSolidCone(float base, float height, int slices, int stacks)
2066 draw_cylinder(base, 0, height, slices, stacks);
2069 void glutWireCone(float base, float height, int slices, int stacks)
2071 glPushAttrib(GL_POLYGON_BIT);
2072 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2073 glutSolidCone(base, height, slices, stacks);
2077 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2079 draw_cylinder(rad, rad, height, slices, stacks);
2082 void glutWireCylinder(float rad, float height, int slices, int stacks)
2084 glPushAttrib(GL_POLYGON_BIT);
2085 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2086 glutSolidCylinder(rad, height, slices, stacks);
2090 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2093 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2094 float du = 1.0f / (float)rings;
2095 float dv = 1.0f / (float)sides;
2098 for(i=0; i<rings; i++) {
2100 for(j=0; j<sides; j++) {
2102 for(k=0; k<4; k++) {
2103 gray = k ^ (k >> 1);
2104 s = gray & 1 ? u + du : u;
2105 t = gray & 2 ? v + dv : v;
2106 theta = s * PI * 2.0f;
2107 phi = t * PI * 2.0f;
2108 mglut_sincos(theta, &sintheta, &costheta);
2109 mglut_sincos(phi, &sinphi, &cosphi);
2110 x = sintheta * sinphi;
2111 y = costheta * sinphi;
2116 glNormal3f(x, y, z);
2118 x = x * inner_rad + sintheta * outer_rad;
2119 y = y * inner_rad + costheta * outer_rad;
2121 glVertex3f(x, y, z);
2128 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2130 glPushAttrib(GL_POLYGON_BIT);
2131 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2132 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2136 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2137 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2139 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2141 #define PATCH_SUBDIV 7
2143 static float teapot_part_flip[] = {
2144 1, 1, 1, 1, /* rim flip */
2145 1, 1, 1, 1, /* body1 flip */
2146 1, 1, 1, 1, /* body2 flip */
2147 1, 1, 1, 1, /* lid patch 1 flip */
2148 1, 1, 1, 1, /* lid patch 2 flip */
2149 1, -1, /* handle 1 flip */
2150 1, -1, /* handle 2 flip */
2151 1, -1, /* spout 1 flip */
2152 1, -1, /* spout 2 flip */
2153 1, 1, 1, 1 /* bottom flip */
2156 static float teapot_part_rot[] = {
2157 0, 90, 180, 270, /* rim rotations */
2158 0, 90, 180, 270, /* body patch 1 rotations */
2159 0, 90, 180, 270, /* body patch 2 rotations */
2160 0, 90, 180, 270, /* lid patch 1 rotations */
2161 0, 90, 180, 270, /* lid patch 2 rotations */
2162 0, 0, /* handle 1 rotations */
2163 0, 0, /* handle 2 rotations */
2164 0, 0, /* spout 1 rotations */
2165 0, 0, /* spout 2 rotations */
2166 0, 90, 180, 270 /* bottom rotations */
2170 static int teapot_index[] = {
2172 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2173 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2174 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2175 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2177 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2178 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2179 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2180 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2182 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2183 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2184 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2185 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2187 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2188 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2189 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2190 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2192 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2193 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2194 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2195 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2197 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2198 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2200 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2201 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2203 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2204 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2206 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2207 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2209 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2210 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2211 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2212 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2216 static float teapot_verts[][3] = {
2217 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2218 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2219 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2220 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2221 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2222 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2223 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2224 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2225 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2226 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2227 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2228 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2229 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2230 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2231 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2232 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2233 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2234 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2235 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2236 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2237 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2238 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2239 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2240 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2241 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2242 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2243 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2244 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2245 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2246 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2247 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2248 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2249 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2250 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2251 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2252 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2253 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2254 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2255 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2256 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2257 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2258 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2259 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2260 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2261 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2262 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2263 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2264 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2265 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2266 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2267 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2268 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2269 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2270 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2271 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2272 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2273 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2274 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2275 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2276 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2277 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2278 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2279 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2280 { 0.8400, -1.5000, 0.07500 }
2283 static void draw_patch(int *index, int flip, float scale);
2284 static float bernstein(int i, float x);
2286 void glutSolidTeapot(float size)
2292 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2293 float flip = teapot_part_flip[i];
2294 float rot = teapot_part_rot[i];
2296 glMatrixMode(GL_MODELVIEW);
2298 glTranslatef(0, -3.15 * size * 0.5, 0);
2299 glRotatef(rot, 0, 1, 0);
2300 glScalef(1, 1, flip);
2301 glRotatef(-90, 1, 0, 0);
2303 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2309 void glutWireTeapot(float size)
2311 glPushAttrib(GL_POLYGON_BIT);
2312 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2313 glutSolidTeapot(size);
2318 static void bezier_patch(float *res, float *cp, float u, float v)
2322 res[0] = res[1] = res[2] = 0.0f;
2324 for(j=0; j<4; j++) {
2325 for(i=0; i<4; i++) {
2326 float bu = bernstein(i, u);
2327 float bv = bernstein(j, v);
2329 res[0] += cp[0] * bu * bv;
2330 res[1] += cp[1] * bu * bv;
2331 res[2] += cp[2] * bu * bv;
2338 static float rsqrt(float x)
2340 float xhalf = x * 0.5f;
2342 i = 0x5f3759df - (i >> 1);
2344 x = x * (1.5f - xhalf * x * x);
2349 #define CROSS(res, a, b) \
2351 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2352 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2353 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2356 #define NORMALIZE(v) \
2358 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2366 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2368 float tang[3], bitan[3], tmp[3];
2370 bezier_patch(tang, cp, u + DT, v);
2371 bezier_patch(tmp, cp, u - DT, v);
2376 bezier_patch(bitan, cp, u, v + DT);
2377 bezier_patch(tmp, cp, u, v - DT);
2382 CROSS(res, tang, bitan);
2388 static float bernstein(int i, float x)
2390 float invx = 1.0f - x;
2394 return invx * invx * invx;
2396 return 3.0f * x * invx * invx;
2398 return 3.0f * x * x * invx;
2407 static void draw_patch(int *index, int flip, float scale)
2409 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2410 static const float voffs[4] = {0, 1, 1, 0};
2416 float du = 1.0 / PATCH_SUBDIV;
2417 float dv = 1.0 / PATCH_SUBDIV;
2419 /* collect control points */
2420 for(i=0; i<16; i++) {
2421 cp[i * 3] = teapot_verts[index[i]][0];
2422 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2423 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2430 for(i=0; i<PATCH_SUBDIV; i++) {
2432 for(j=0; j<PATCH_SUBDIV; j++) {
2434 for(k=0; k<4; k++) {
2435 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2437 /* top/bottom normal hack */
2441 } else if(pt[2] < 0.00001) {
2445 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2450 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);