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
1773 static void sys_exit(int status)
1778 static int sys_write(int fd, const void *buf, int count)
1780 return write(fd, buf, count);
1783 #else /* !MINIGLUT_USE_LIBC */
1787 static void sys_exit(int status)
1791 :: "a"(60), "D"(status));
1793 static int sys_write(int fd, const void *buf, int count)
1799 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1802 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1808 : "a"(96), "D"(tv), "S"(tz));
1811 #endif /* __x86_64__ */
1813 static void sys_exit(int status)
1817 :: "a"(1), "b"(status));
1819 static int sys_write(int fd, const void *buf, int count)
1825 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1828 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1834 : "a"(78), "b"(tv), "c"(tz));
1837 #endif /* __i386__ */
1838 #endif /* __linux__ */
1841 static void sys_exit(int status)
1843 ExitProcess(status);
1845 static int sys_write(int fd, const void *buf, int count)
1847 unsigned long wrsz = 0;
1849 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1850 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1856 #endif /* !MINIGLUT_USE_LIBC */
1859 /* ----------------- primitives ------------------ */
1860 #ifdef MINIGLUT_USE_LIBC
1864 void mglut_sincos(float angle, float *sptr, float *cptr)
1870 float mglut_atan(float x)
1875 #else /* !MINIGLUT_USE_LIBC */
1878 void mglut_sincos(float angle, float *sptr, float *cptr)
1885 : "=m"(*sptr), "=m"(*cptr)
1890 float mglut_atan(float x)
1906 void mglut_sincos(float angle, float *sptr, float *cptr)
1919 float mglut_atan(float x)
1933 #pragma aux mglut_sincos = \
1935 "fstp dword ptr [edx]" \
1936 "fstp dword ptr [eax]" \
1937 parm[8087][eax][edx] \
1940 #pragma aux mglut_atan = \
1946 #endif /* __WATCOMC__ */
1948 #endif /* !MINIGLUT_USE_LIBC */
1950 #define PI 3.1415926536f
1952 void glutSolidSphere(float rad, int slices, int stacks)
1955 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1956 float du = 1.0f / (float)slices;
1957 float dv = 1.0f / (float)stacks;
1960 for(i=0; i<stacks; i++) {
1962 for(j=0; j<slices; j++) {
1964 for(k=0; k<4; k++) {
1965 gray = k ^ (k >> 1);
1966 s = gray & 1 ? u + du : u;
1967 t = gray & 2 ? v + dv : v;
1968 theta = s * PI * 2.0f;
1970 mglut_sincos(theta, &sintheta, &costheta);
1971 mglut_sincos(phi, &sinphi, &cosphi);
1972 x = sintheta * sinphi;
1973 y = costheta * sinphi;
1978 glNormal3f(x, y, z);
1979 glVertex3f(x * rad, y * rad, z * rad);
1986 void glutWireSphere(float rad, int slices, int stacks)
1988 glPushAttrib(GL_POLYGON_BIT);
1989 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1990 glutSolidSphere(rad, slices, stacks);
1994 void glutSolidCube(float sz)
1996 int i, j, idx, gray, flip, rotx;
1997 float vpos[3], norm[3];
1998 float rad = sz * 0.5f;
2001 for(i=0; i<6; i++) {
2004 idx = (~i & 2) - rotx;
2005 norm[0] = norm[1] = norm[2] = 0.0f;
2006 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2008 vpos[idx] = norm[idx] * rad;
2009 for(j=0; j<4; j++) {
2010 gray = j ^ (j >> 1);
2011 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2012 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2013 glTexCoord2f(gray & 1, gray >> 1);
2020 void glutWireCube(float sz)
2022 glPushAttrib(GL_POLYGON_BIT);
2023 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2028 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2031 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2032 float du = 1.0f / (float)slices;
2033 float dv = 1.0f / (float)stacks;
2036 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2037 mglut_sincos(phi, &sinphi, &cosphi);
2040 for(i=0; i<stacks; i++) {
2042 for(j=0; j<slices; j++) {
2044 for(k=0; k<4; k++) {
2045 gray = k ^ (k >> 1);
2046 s = gray & 2 ? u + du : u;
2047 t = gray & 1 ? v + dv : v;
2048 rad = rbot + (rtop - rbot) * t;
2049 theta = s * PI * 2.0f;
2050 mglut_sincos(theta, &sintheta, &costheta);
2052 x = sintheta * cosphi;
2053 y = costheta * cosphi;
2058 glNormal3f(x, y, z);
2059 glVertex3f(sintheta * rad, costheta * rad, t * height);
2066 void glutSolidCone(float base, float height, int slices, int stacks)
2068 draw_cylinder(base, 0, height, slices, stacks);
2071 void glutWireCone(float base, float height, int slices, int stacks)
2073 glPushAttrib(GL_POLYGON_BIT);
2074 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2075 glutSolidCone(base, height, slices, stacks);
2079 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2081 draw_cylinder(rad, rad, height, slices, stacks);
2084 void glutWireCylinder(float rad, float height, int slices, int stacks)
2086 glPushAttrib(GL_POLYGON_BIT);
2087 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2088 glutSolidCylinder(rad, height, slices, stacks);
2092 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2095 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2096 float du = 1.0f / (float)rings;
2097 float dv = 1.0f / (float)sides;
2100 for(i=0; i<rings; i++) {
2102 for(j=0; j<sides; j++) {
2104 for(k=0; k<4; k++) {
2105 gray = k ^ (k >> 1);
2106 s = gray & 1 ? u + du : u;
2107 t = gray & 2 ? v + dv : v;
2108 theta = s * PI * 2.0f;
2109 phi = t * PI * 2.0f;
2110 mglut_sincos(theta, &sintheta, &costheta);
2111 mglut_sincos(phi, &sinphi, &cosphi);
2112 x = sintheta * sinphi;
2113 y = costheta * sinphi;
2118 glNormal3f(x, y, z);
2120 x = x * inner_rad + sintheta * outer_rad;
2121 y = y * inner_rad + costheta * outer_rad;
2123 glVertex3f(x, y, z);
2130 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2132 glPushAttrib(GL_POLYGON_BIT);
2133 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2134 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2138 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2139 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2141 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2143 #define PATCH_SUBDIV 7
2145 static float teapot_part_flip[] = {
2146 1, 1, 1, 1, /* rim flip */
2147 1, 1, 1, 1, /* body1 flip */
2148 1, 1, 1, 1, /* body2 flip */
2149 1, 1, 1, 1, /* lid patch 1 flip */
2150 1, 1, 1, 1, /* lid patch 2 flip */
2151 1, -1, /* handle 1 flip */
2152 1, -1, /* handle 2 flip */
2153 1, -1, /* spout 1 flip */
2154 1, -1, /* spout 2 flip */
2155 1, 1, 1, 1 /* bottom flip */
2158 static float teapot_part_rot[] = {
2159 0, 90, 180, 270, /* rim rotations */
2160 0, 90, 180, 270, /* body patch 1 rotations */
2161 0, 90, 180, 270, /* body patch 2 rotations */
2162 0, 90, 180, 270, /* lid patch 1 rotations */
2163 0, 90, 180, 270, /* lid patch 2 rotations */
2164 0, 0, /* handle 1 rotations */
2165 0, 0, /* handle 2 rotations */
2166 0, 0, /* spout 1 rotations */
2167 0, 0, /* spout 2 rotations */
2168 0, 90, 180, 270 /* bottom rotations */
2172 static int teapot_index[] = {
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,
2176 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2177 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
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,
2181 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2182 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
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,
2186 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2187 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
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,
2191 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2192 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
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,
2196 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2197 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2199 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2200 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2202 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2203 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2205 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2206 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2208 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2209 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
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,
2213 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2214 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2218 static float teapot_verts[][3] = {
2219 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2220 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2221 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2222 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2223 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2224 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2225 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2226 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2227 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2228 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2229 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2230 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2231 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2232 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2233 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2234 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2235 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2236 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2237 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2238 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2239 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2240 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2241 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2242 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2243 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2244 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2245 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2246 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2247 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2248 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2249 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2250 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2251 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2252 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2253 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2254 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2255 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2256 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2257 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2258 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2259 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2260 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2261 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2262 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2263 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2264 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2265 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2266 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2267 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2268 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2269 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2270 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2271 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2272 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2273 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2274 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2275 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2276 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2277 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2278 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2279 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2280 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2281 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2282 { 0.8400, -1.5000, 0.07500 }
2285 static void draw_patch(int *index, int flip, float scale);
2286 static float bernstein(int i, float x);
2288 void glutSolidTeapot(float size)
2294 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2295 float flip = teapot_part_flip[i];
2296 float rot = teapot_part_rot[i];
2298 glMatrixMode(GL_MODELVIEW);
2300 glTranslatef(0, -3.15 * size * 0.5, 0);
2301 glRotatef(rot, 0, 1, 0);
2302 glScalef(1, 1, flip);
2303 glRotatef(-90, 1, 0, 0);
2305 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2311 void glutWireTeapot(float size)
2313 glPushAttrib(GL_POLYGON_BIT);
2314 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2315 glutSolidTeapot(size);
2320 static void bezier_patch(float *res, float *cp, float u, float v)
2324 res[0] = res[1] = res[2] = 0.0f;
2326 for(j=0; j<4; j++) {
2327 for(i=0; i<4; i++) {
2328 float bu = bernstein(i, u);
2329 float bv = bernstein(j, v);
2331 res[0] += cp[0] * bu * bv;
2332 res[1] += cp[1] * bu * bv;
2333 res[2] += cp[2] * bu * bv;
2340 static float rsqrt(float x)
2342 float xhalf = x * 0.5f;
2344 i = 0x5f3759df - (i >> 1);
2346 x = x * (1.5f - xhalf * x * x);
2351 #define CROSS(res, a, b) \
2353 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2354 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2355 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2358 #define NORMALIZE(v) \
2360 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2368 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2370 float tang[3], bitan[3], tmp[3];
2372 bezier_patch(tang, cp, u + DT, v);
2373 bezier_patch(tmp, cp, u - DT, v);
2378 bezier_patch(bitan, cp, u, v + DT);
2379 bezier_patch(tmp, cp, u, v - DT);
2384 CROSS(res, tang, bitan);
2390 static float bernstein(int i, float x)
2392 float invx = 1.0f - x;
2396 return invx * invx * invx;
2398 return 3.0f * x * invx * invx;
2400 return 3.0f * x * x * invx;
2409 static void draw_patch(int *index, int flip, float scale)
2411 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2412 static const float voffs[4] = {0, 1, 1, 0};
2418 float du = 1.0 / PATCH_SUBDIV;
2419 float dv = 1.0 / PATCH_SUBDIV;
2421 /* collect control points */
2422 for(i=0; i<16; i++) {
2423 cp[i * 3] = teapot_verts[index[i]][0];
2424 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2425 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2432 for(i=0; i<PATCH_SUBDIV; i++) {
2434 for(j=0; j<PATCH_SUBDIV; j++) {
2436 for(k=0; k<4; k++) {
2437 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2439 /* top/bottom normal hack */
2443 } else if(pt[2] < 0.00001) {
2447 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2452 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);