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/>.
22 #include <X11/keysym.h>
23 #include <X11/cursorfont.h>
27 #ifndef GLX_SAMPLE_BUFFERS_ARB
28 #define GLX_SAMPLE_BUFFERS_ARB 100000
29 #define GLX_SAMPLES_ARB 100001
31 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
32 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
36 static Window win, root;
38 static GLXContext ctx;
39 static Atom xa_wm_proto, xa_wm_del_win;
40 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
41 static Atom xa_motif_wm_hints;
42 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
43 static unsigned int evmask;
44 static Cursor blank_cursor;
46 static int have_netwm_fullscr(void);
53 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
55 static HINSTANCE hinst;
61 #error unsupported platform
67 int rsize, gsize, bsize, asize;
75 static void cleanup(void);
76 static void create_window(const char *title);
77 static void get_window_pos(int *x, int *y);
78 static void get_window_size(int *w, int *h);
79 static void get_screen_size(int *scrw, int *scrh);
81 static long get_msec(void);
82 static void panic(const char *msg);
83 static void sys_exit(int status);
84 static int sys_write(int fd, const void *buf, int count);
87 static int init_x = -1, init_y, init_width = 256, init_height = 256;
88 static unsigned int init_mode;
90 static struct ctx_info ctx_info;
91 static int cur_cursor = GLUT_CURSOR_INHERIT;
92 static int ignore_key_repeat;
94 static glut_cb cb_display;
95 static glut_cb cb_idle;
96 static glut_cb_reshape cb_reshape;
97 static glut_cb_state cb_vis, cb_entry;
98 static glut_cb_keyb cb_keydown, cb_keyup;
99 static glut_cb_special cb_skeydown, cb_skeyup;
100 static glut_cb_mouse cb_mouse;
101 static glut_cb_motion cb_motion, cb_passive;
102 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
103 static glut_cb_sbbutton cb_sball_button;
105 static int fullscreen;
106 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
108 static int win_width, win_height;
111 static int upd_pending;
114 void glutInit(int *argc, char **argv)
120 if(!(dpy = XOpenDisplay(0))) {
121 panic("Failed to connect to the X server\n");
123 scr = DefaultScreen(dpy);
124 root = RootWindow(dpy, scr);
125 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
126 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
127 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
128 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
129 if(have_netwm_fullscr()) {
130 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
133 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
134 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
135 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
136 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
138 evmask = ExposureMask | StructureNotifyMask;
140 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
141 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
142 XFreePixmap(dpy, blankpix);
149 hinst = GetModuleHandle(0);
151 wc.cbSize = sizeof wc;
152 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
153 wc.hCursor = LoadCursor(0, IDC_ARROW);
154 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
155 wc.hInstance = hinst;
156 wc.lpfnWndProc = handle_message;
157 wc.lpszClassName = "MiniGLUT";
158 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
159 if(!RegisterClassEx(&wc)) {
160 panic("Failed to register \"MiniGLUT\" window class\n");
164 get_screen_size(&init_x, &init_y);
171 void glutInitWindowPosition(int x, int y)
177 void glutInitWindowSize(int xsz, int ysz)
183 void glutInitDisplayMode(unsigned int mode)
188 void glutCreateWindow(const char *title)
190 create_window(title);
198 void glutMainLoop(void)
205 void glutPostRedisplay(void)
210 void glutIgnoreKeyRepeat(int ignore)
212 ignore_key_repeat = ignore;
215 #define UPD_EVMASK(x) \
222 if(win) XSelectInput(dpy, win, evmask); \
226 void glutIdleFunc(glut_cb func)
231 void glutDisplayFunc(glut_cb func)
236 void glutReshapeFunc(glut_cb_reshape func)
241 void glutVisibilityFunc(glut_cb_state func)
245 UPD_EVMASK(VisibilityChangeMask);
249 void glutEntryFunc(glut_cb_state func)
253 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
257 void glutKeyboardFunc(glut_cb_keyb func)
261 UPD_EVMASK(KeyPressMask);
265 void glutKeyboardUpFunc(glut_cb_keyb func)
269 UPD_EVMASK(KeyReleaseMask);
273 void glutSpecialFunc(glut_cb_special func)
277 UPD_EVMASK(KeyPressMask);
281 void glutSpecialUpFunc(glut_cb_special func)
285 UPD_EVMASK(KeyReleaseMask);
289 void glutMouseFunc(glut_cb_mouse func)
293 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
297 void glutMotionFunc(glut_cb_motion func)
301 UPD_EVMASK(ButtonMotionMask);
305 void glutPassiveMotionFunc(glut_cb_motion func)
309 UPD_EVMASK(PointerMotionMask);
313 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
315 cb_sball_motion = func;
318 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
320 cb_sball_rotate = func;
323 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
325 cb_sball_button = func;
328 int glutGet(unsigned int s)
333 get_window_pos(&x, &y);
336 get_window_pos(&x, &y);
338 case GLUT_WINDOW_WIDTH:
339 get_window_size(&x, &y);
341 case GLUT_WINDOW_HEIGHT:
342 get_window_size(&x, &y);
344 case GLUT_WINDOW_BUFFER_SIZE:
345 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
346 case GLUT_WINDOW_STENCIL_SIZE:
347 return ctx_info.ssize;
348 case GLUT_WINDOW_DEPTH_SIZE:
349 return ctx_info.zsize;
350 case GLUT_WINDOW_RED_SIZE:
351 return ctx_info.rsize;
352 case GLUT_WINDOW_GREEN_SIZE:
353 return ctx_info.gsize;
354 case GLUT_WINDOW_BLUE_SIZE:
355 return ctx_info.bsize;
356 case GLUT_WINDOW_ALPHA_SIZE:
357 return ctx_info.asize;
358 case GLUT_WINDOW_DOUBLEBUFFER:
359 return ctx_info.dblbuf;
360 case GLUT_WINDOW_RGBA:
362 case GLUT_WINDOW_NUM_SAMPLES:
363 return ctx_info.samples;
364 case GLUT_WINDOW_STEREO:
365 return ctx_info.stereo;
366 case GLUT_WINDOW_SRGB:
367 return ctx_info.srgb;
368 case GLUT_WINDOW_CURSOR:
370 case GLUT_SCREEN_WIDTH:
371 get_screen_size(&x, &y);
373 case GLUT_SCREEN_HEIGHT:
374 get_screen_size(&x, &y);
376 case GLUT_INIT_DISPLAY_MODE:
378 case GLUT_INIT_WINDOW_X:
380 case GLUT_INIT_WINDOW_Y:
382 case GLUT_INIT_WINDOW_WIDTH:
384 case GLUT_INIT_WINDOW_HEIGHT:
386 case GLUT_ELAPSED_TIME:
394 int glutGetModifiers(void)
399 static int is_space(int c)
401 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
404 static const char *skip_space(const char *s)
406 while(*s && is_space(*s)) s++;
410 int glutExtensionSupported(char *ext)
412 const char *str, *eptr;
414 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
419 str = skip_space(str);
420 eptr = skip_space(ext);
421 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
425 if((!*str || is_space(*str)) && !*eptr) {
428 while(*str && !is_space(*str)) str++;
435 /* --------------- UNIX/X11 implementation ----------------- */
438 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
440 SPNAV_EVENT_BUTTON /* includes both press and release */
443 struct spnav_event_motion {
451 struct spnav_event_button {
459 struct spnav_event_motion motion;
460 struct spnav_event_button button;
464 static void handle_event(XEvent *ev);
466 static int spnav_window(Window win);
467 static int spnav_event(const XEvent *xev, union spnav_event *event);
468 static int spnav_remove_events(int type);
471 void glutMainLoopEvent(void)
476 panic("display callback not set");
479 if(!upd_pending && !cb_idle) {
480 XNextEvent(dpy, &ev);
484 while(XPending(dpy)) {
485 XNextEvent(dpy, &ev);
494 if(upd_pending && mapped) {
505 static void cleanup(void)
509 glXMakeCurrent(dpy, 0, 0);
510 XDestroyWindow(dpy, win);
514 static KeySym translate_keysym(KeySym sym)
535 static void handle_event(XEvent *ev)
538 union spnav_event sev;
547 case ConfigureNotify:
548 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
549 win_width = ev->xconfigure.width;
550 win_height = ev->xconfigure.height;
551 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
556 if(ev->xclient.message_type == xa_wm_proto) {
557 if(ev->xclient.data.l[0] == xa_wm_del_win) {
561 if(spnav_event(ev, &sev)) {
563 case SPNAV_EVENT_MOTION:
564 if(cb_sball_motion) {
565 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
567 if(cb_sball_rotate) {
568 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
570 spnav_remove_events(SPNAV_EVENT_MOTION);
573 case SPNAV_EVENT_BUTTON:
574 if(cb_sball_button) {
575 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
592 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
594 XPeekEvent(dpy, &next);
596 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
597 next.xkey.time == ev->xkey.time) {
598 /* this is a key-repeat event, ignore the release and consume
599 * the following press
601 XNextEvent(dpy, &next);
606 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
607 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
610 sym = translate_keysym(sym);
612 if(ev->type == KeyPress) {
613 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
615 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
618 if(ev->type == KeyPress) {
619 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
621 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
628 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
630 int bn = ev->xbutton.button - Button1;
631 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
632 ev->xbutton.x, ev->xbutton.y);
637 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
638 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
640 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
644 case VisibilityNotify:
646 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
650 if(cb_entry) cb_entry(GLUT_ENTERED);
653 if(cb_entry) cb_entry(GLUT_LEFT);
658 void glutSwapBuffers(void)
660 glXSwapBuffers(dpy, win);
664 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
665 * needs to resize the window to make it fullscreen. The way it does this is by
666 * querying the size of the root window (see get_screen_size), which in the
667 * case of multi-monitor setups will be the combined size of all monitors.
668 * This is problematic; the way to solve it is to use the XRandR extension, or
669 * the Xinerama extension, to figure out the dimensions of the correct video
670 * output, which would add potentially two extension support libraries to our
672 * Moreover, any X installation modern enough to support XR&R will almost
673 * certainly be running a window manager supporting the EHWM
674 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
675 * on manual resizing, and is used in preference if available, making this
676 * whole endeavor pointless.
677 * So I'll just leave it with set_fullscreen_mwm covering the entire
678 * multi-monitor area for now.
683 unsigned long functions;
684 unsigned long decorations;
686 unsigned long status;
689 #define MWM_HINTS_DECORATIONS 2
690 #define MWM_DECOR_ALL 1
692 static void set_fullscreen_mwm(int fs)
694 struct mwm_hints hints;
695 int scr_width, scr_height;
698 get_window_pos(&prev_win_x, &prev_win_y);
699 get_window_size(&prev_win_width, &prev_win_height);
700 get_screen_size(&scr_width, &scr_height);
702 hints.decorations = 0;
703 hints.flags = MWM_HINTS_DECORATIONS;
704 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
705 PropModeReplace, (unsigned char*)&hints, 5);
707 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
709 XDeleteProperty(dpy, win, xa_motif_wm_hints);
710 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
714 static int have_netwm_fullscr(void)
718 unsigned long i, count, rem;
720 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
723 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
724 &type, &fmt, &count, &rem, (unsigned char**)&prop);
726 for(i=0; i<count; i++) {
727 if(prop[i] == xa_net_wm_state_fullscr) {
739 static void set_fullscreen_ewmh(int fs)
741 XClientMessageEvent msg = {0};
743 msg.type = ClientMessage;
745 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
747 msg.data.l[0] = fs ? 1 : 0;
748 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
750 msg.data.l[3] = 1; /* source regular application */
751 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
754 static void set_fullscreen(int fs)
756 if(fullscreen == fs) return;
758 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
759 set_fullscreen_ewmh(fs);
761 } else if(xa_motif_wm_hints) {
762 set_fullscreen_mwm(fs);
767 void glutPositionWindow(int x, int y)
770 XMoveWindow(dpy, win, x, y);
773 void glutReshapeWindow(int xsz, int ysz)
776 XResizeWindow(dpy, win, xsz, ysz);
779 void glutFullScreen(void)
784 void glutSetWindowTitle(const char *title)
787 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
790 XSetWMName(dpy, win, &tprop);
794 void glutSetIconTitle(const char *title)
797 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
800 XSetWMIconName(dpy, win, &tprop);
804 void glutSetCursor(int cidx)
809 case GLUT_CURSOR_LEFT_ARROW:
810 cur = XCreateFontCursor(dpy, XC_left_ptr);
812 case GLUT_CURSOR_INHERIT:
814 case GLUT_CURSOR_NONE:
821 XDefineCursor(dpy, win, cur);
825 void glutSetKeyRepeat(int repmode)
834 static XVisualInfo *choose_visual(unsigned int mode)
841 if(mode & GLUT_DOUBLE) {
842 *aptr++ = GLX_DOUBLEBUFFER;
845 if(mode & GLUT_INDEX) {
846 *aptr++ = GLX_BUFFER_SIZE;
850 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
851 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
852 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
854 if(mode & GLUT_ALPHA) {
855 *aptr++ = GLX_ALPHA_SIZE;
858 if(mode & GLUT_DEPTH) {
859 *aptr++ = GLX_DEPTH_SIZE;
862 if(mode & GLUT_STENCIL) {
863 *aptr++ = GLX_STENCIL_SIZE;
866 if(mode & GLUT_ACCUM) {
867 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
868 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
869 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
871 if(mode & GLUT_STEREO) {
872 *aptr++ = GLX_STEREO;
874 if(mode & GLUT_SRGB) {
875 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
877 if(mode & GLUT_MULTISAMPLE) {
878 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
880 *aptr++ = GLX_SAMPLES_ARB;
887 return glXChooseVisual(dpy, scr, attr);
889 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
898 static void create_window(const char *title)
900 XSetWindowAttributes xattr = {0};
902 unsigned int xattr_mask;
903 unsigned int mode = init_mode;
905 if(!(vi = choose_visual(mode))) {
907 if(!(vi = choose_visual(mode))) {
908 panic("Failed to find compatible visual\n");
912 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
914 panic("Failed to create OpenGL context\n");
917 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
918 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
919 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
920 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
921 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
922 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
923 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
924 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
925 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
926 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
928 xattr.background_pixel = BlackPixel(dpy, scr);
929 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
930 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
931 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
932 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
934 glXDestroyContext(dpy, ctx);
935 panic("Failed to create window\n");
939 XSelectInput(dpy, win, evmask);
943 glutSetWindowTitle(title);
944 glutSetIconTitle(title);
945 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
946 XMapWindow(dpy, win);
948 glXMakeCurrent(dpy, win, ctx);
951 static void get_window_pos(int *x, int *y)
954 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
957 static void get_window_size(int *w, int *h)
959 XWindowAttributes wattr;
960 XGetWindowAttributes(dpy, win, &wattr);
965 static void get_screen_size(int *scrw, int *scrh)
967 XWindowAttributes wattr;
968 XGetWindowAttributes(dpy, root, &wattr);
970 *scrh = wattr.height;
976 CMD_APP_WINDOW = 27695,
980 static Window get_daemon_window(Display *dpy);
981 static int catch_badwin(Display *dpy, XErrorEvent *err);
983 #define SPNAV_INITIALIZED (xa_motion_event)
985 static int spnav_window(Window win)
987 int (*prev_xerr_handler)(Display*, XErrorEvent*);
991 if(!SPNAV_INITIALIZED) {
995 if(!(daemon_win = get_daemon_window(dpy))) {
999 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1001 xev.type = ClientMessage;
1002 xev.xclient.send_event = False;
1003 xev.xclient.display = dpy;
1004 xev.xclient.window = win;
1005 xev.xclient.message_type = xa_command_event;
1006 xev.xclient.format = 16;
1007 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1008 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1009 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1011 XSendEvent(dpy, daemon_win, False, 0, &xev);
1014 XSetErrorHandler(prev_xerr_handler);
1018 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1020 int evtype = *(int*)arg;
1022 if(xev->type != ClientMessage) {
1026 if(xev->xclient.message_type == xa_motion_event) {
1027 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1029 if(xev->xclient.message_type == xa_button_press_event ||
1030 xev->xclient.message_type == xa_button_release_event) {
1031 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1036 static int spnav_remove_events(int type)
1040 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1046 static int spnav_event(const XEvent *xev, union spnav_event *event)
1051 xmsg_type = xev->xclient.message_type;
1053 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1054 xmsg_type != xa_button_release_event) {
1058 if(xmsg_type == xa_motion_event) {
1059 event->type = SPNAV_EVENT_MOTION;
1060 event->motion.data = &event->motion.x;
1062 for(i=0; i<6; i++) {
1063 event->motion.data[i] = xev->xclient.data.s[i + 2];
1065 event->motion.period = xev->xclient.data.s[8];
1067 event->type = SPNAV_EVENT_BUTTON;
1068 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1069 event->button.bnum = xev->xclient.data.s[2];
1074 static int mglut_strcmp(const char *s1, const char *s2)
1076 while(*s1 && *s1 == *s2) {
1083 static Window get_daemon_window(Display *dpy)
1086 XTextProperty wname;
1089 unsigned long nitems, bytes_after;
1090 unsigned char *prop;
1092 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1093 &type, &fmt, &nitems, &bytes_after, &prop);
1098 win = *(Window*)prop;
1102 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1110 static int catch_badwin(Display *dpy, XErrorEvent *err)
1117 #endif /* BUILD_X11 */
1120 /* --------------- windows implementation ----------------- */
1122 static int reshape_pending;
1124 static void update_modkeys(void);
1125 static int translate_vkey(int vkey);
1126 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1128 #ifdef MINIGLUT_WINMAIN
1129 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1132 char *argv[] = { "miniglut.exe", 0 };
1133 return main(argc, argv);
1137 void glutMainLoopEvent(void)
1142 panic("display callback not set");
1145 if(reshape_pending && cb_reshape) {
1146 reshape_pending = 0;
1147 get_window_size(&win_width, &win_height);
1148 cb_reshape(win_width, win_height);
1151 if(!upd_pending && !cb_idle) {
1152 GetMessage(&msg, 0, 0, 0);
1153 TranslateMessage(&msg);
1154 DispatchMessage(&msg);
1157 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1158 TranslateMessage(&msg);
1159 DispatchMessage(&msg);
1167 if(upd_pending && mapped) {
1173 static void cleanup(void)
1176 wglMakeCurrent(dc, 0);
1177 wglDeleteContext(ctx);
1178 UnregisterClass("MiniGLUT", hinst);
1182 void glutSwapBuffers(void)
1187 void glutPositionWindow(int x, int y)
1190 unsigned int flags = SWP_SHOWWINDOW;
1193 rect.left = prev_win_x;
1194 rect.top = prev_win_y;
1195 rect.right = rect.left + prev_win_width;
1196 rect.bottom = rect.top + prev_win_height;
1197 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1199 flags |= SWP_FRAMECHANGED;
1201 GetWindowRect(win, &rect);
1203 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1206 void glutReshapeWindow(int xsz, int ysz)
1209 unsigned int flags = SWP_SHOWWINDOW;
1212 rect.left = prev_win_x;
1213 rect.top = prev_win_y;
1214 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1216 flags |= SWP_FRAMECHANGED;
1218 GetWindowRect(win, &rect);
1220 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1223 void glutFullScreen(void)
1226 int scr_width, scr_height;
1228 if(fullscreen) return;
1230 GetWindowRect(win, &rect);
1231 prev_win_x = rect.left;
1232 prev_win_y = rect.top;
1233 prev_win_width = rect.right - rect.left;
1234 prev_win_height = rect.bottom - rect.top;
1236 get_screen_size(&scr_width, &scr_height);
1238 SetWindowLong(win, GWL_STYLE, 0);
1239 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1244 void glutSetWindowTitle(const char *title)
1246 SetWindowText(win, title);
1249 void glutSetIconTitle(const char *title)
1253 void glutSetCursor(int cidx)
1256 case GLUT_CURSOR_NONE:
1259 case GLUT_CURSOR_INHERIT:
1260 case GLUT_CURSOR_LEFT_ARROW:
1262 SetCursor(LoadCursor(0, IDC_ARROW));
1267 void glutSetKeyRepeat(int repmode)
1271 #define WGL_DRAW_TO_WINDOW 0x2001
1272 #define WGL_ACCELERATION 0x2003
1273 #define WGL_SUPPORT_OPENGL 0x2010
1274 #define WGL_DOUBLE_BUFFER 0x2011
1275 #define WGL_STEREO 0x2012
1276 #define WGL_PIXEL_TYPE 0x2013
1277 #define WGL_COLOR_BITS 0x2014
1278 #define WGL_RED_BITS 0x2015
1279 #define WGL_GREEN_BITS 0x2017
1280 #define WGL_BLUE_BITS 0x2019
1281 #define WGL_ALPHA_BITS 0x201b
1282 #define WGL_ACCUM_BITS 0x201d
1283 #define WGL_DEPTH_BITS 0x2022
1284 #define WGL_STENCIL_BITS 0x2023
1285 #define WGL_FULL_ACCELERATION 0x2027
1287 #define WGL_TYPE_RGBA 0x202b
1288 #define WGL_TYPE_COLORINDEX 0x202c
1290 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1291 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1292 #define WGL_SAMPLES_ARB 0x2042
1294 static PROC wglChoosePixelFormat;
1295 static PROC wglGetPixelFormatAttribiv;
1297 #define ATTR(a, v) \
1298 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1300 static unsigned int choose_pixfmt(unsigned int mode)
1302 unsigned int num_pixfmt, pixfmt = 0;
1303 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1304 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1305 float fattr[2] = {0, 0};
1307 int *aptr = attr + 6;
1310 if(mode & GLUT_DOUBLE) {
1311 ATTR(WGL_DOUBLE_BUFFER, 1);
1314 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1315 ATTR(WGL_COLOR_BITS, 8);
1316 if(mode & GLUT_ALPHA) {
1317 ATTR(WGL_ALPHA_BITS, 4);
1319 if(mode & GLUT_DEPTH) {
1320 ATTR(WGL_DEPTH_BITS, 16);
1322 if(mode & GLUT_STENCIL) {
1323 ATTR(WGL_STENCIL_BITS, 1);
1325 if(mode & GLUT_ACCUM) {
1326 ATTR(WGL_ACCUM_BITS, 1);
1328 if(mode & GLUT_STEREO) {
1329 ATTR(WGL_STEREO, 1);
1331 if(mode & GLUT_SRGB) {
1332 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1334 if(mode & GLUT_MULTISAMPLE) {
1335 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1336 *aptr++ = WGL_SAMPLES_ARB;
1342 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1351 static PIXELFORMATDESCRIPTOR pfd;
1352 static PIXELFORMATDESCRIPTOR tmppfd = {
1353 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1354 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1355 PFD_MAIN_PLANE, 0, 0, 0, 0
1357 #define TMPCLASS "TempMiniGLUT"
1359 #define GETATTR(attr, vptr) \
1362 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1365 static int create_window_wglext(const char *title, int width, int height)
1367 WNDCLASSEX wc = {0};
1373 /* create a temporary window and GL context, just to query and retrieve
1374 * the wglChoosePixelFormatEXT function
1376 wc.cbSize = sizeof wc;
1377 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1378 wc.hCursor = LoadCursor(0, IDC_ARROW);
1379 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1380 wc.hInstance = hinst;
1381 wc.lpfnWndProc = DefWindowProc;
1382 wc.lpszClassName = TMPCLASS;
1383 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1384 if(!RegisterClassEx(&wc)) {
1387 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1388 width, height, 0, 0, hinst, 0))) {
1391 tmpdc = GetDC(tmpwin);
1393 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1394 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1395 !(tmpctx = wglCreateContext(tmpdc))) {
1398 wglMakeCurrent(tmpdc, tmpctx);
1400 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1401 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1404 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1408 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1412 wglMakeCurrent(0, 0);
1413 wglDeleteContext(tmpctx);
1414 DestroyWindow(tmpwin);
1415 UnregisterClass(TMPCLASS, hinst);
1417 /* create the real window and context */
1418 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1419 init_y, width, height, 0, 0, hinst, 0))) {
1420 panic("Failed to create window\n");
1424 if(!(pixfmt = choose_pixfmt(init_mode))) {
1425 panic("Failed to find suitable pixel format\n");
1427 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1428 panic("Failed to set the selected pixel format\n");
1430 if(!(ctx = wglCreateContext(dc))) {
1431 panic("Failed to create the OpenGL context\n");
1433 wglMakeCurrent(dc, ctx);
1435 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1436 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1437 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1438 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1439 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1440 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1441 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1442 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1443 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1448 wglMakeCurrent(0, 0);
1449 wglDeleteContext(tmpctx);
1452 DestroyWindow(tmpwin);
1454 UnregisterClass(TMPCLASS, hinst);
1459 static void create_window(const char *title)
1467 rect.right = init_x + init_width;
1468 rect.bottom = init_y + init_height;
1469 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1470 width = rect.right - rect.left;
1471 height = rect.bottom - rect.top;
1473 memset(&pfd, 0, sizeof pfd);
1474 pfd.nSize = sizeof pfd;
1476 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1477 if(init_mode & GLUT_STEREO) {
1478 pfd.dwFlags |= PFD_STEREO;
1480 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1481 pfd.cColorBits = 24;
1482 if(init_mode & GLUT_ALPHA) {
1485 if(init_mode & GLUT_ACCUM) {
1486 pfd.cAccumBits = 24;
1488 if(init_mode & GLUT_DEPTH) {
1489 pfd.cDepthBits = 24;
1491 if(init_mode & GLUT_STENCIL) {
1492 pfd.cStencilBits = 8;
1494 pfd.iLayerType = PFD_MAIN_PLANE;
1497 if(create_window_wglext(title, width, height) == -1) {
1499 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1500 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1501 panic("Failed to create window\n");
1505 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1506 panic("Failed to find suitable pixel format\n");
1508 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1509 panic("Failed to set the selected pixel format\n");
1511 if(!(ctx = wglCreateContext(dc))) {
1512 panic("Failed to create the OpenGL context\n");
1514 wglMakeCurrent(dc, ctx);
1516 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1517 ctx_info.rsize = pfd.cRedBits;
1518 ctx_info.gsize = pfd.cGreenBits;
1519 ctx_info.bsize = pfd.cBlueBits;
1520 ctx_info.asize = pfd.cAlphaBits;
1521 ctx_info.zsize = pfd.cDepthBits;
1522 ctx_info.ssize = pfd.cStencilBits;
1523 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1524 ctx_info.samples = 0;
1529 SetForegroundWindow(win);
1532 reshape_pending = 1;
1535 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1537 static int mouse_x, mouse_y;
1542 if(win) DestroyWindow(win);
1553 ValidateRect(win, 0);
1557 x = lparam & 0xffff;
1559 if(x != win_width && y != win_height) {
1563 reshape_pending = 0;
1564 cb_reshape(win_width, win_height);
1571 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1577 key = translate_vkey(wparam);
1580 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1584 cb_skeydown(key, mouse_x, mouse_y);
1592 key = translate_vkey(wparam);
1595 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1599 cb_skeyup(key, mouse_x, mouse_y);
1604 case WM_LBUTTONDOWN:
1605 handle_mbutton(0, 1, wparam, lparam);
1607 case WM_MBUTTONDOWN:
1608 handle_mbutton(1, 1, wparam, lparam);
1610 case WM_RBUTTONDOWN:
1611 handle_mbutton(2, 1, wparam, lparam);
1614 handle_mbutton(0, 0, wparam, lparam);
1617 handle_mbutton(1, 0, wparam, lparam);
1620 handle_mbutton(2, 0, wparam, lparam);
1624 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1625 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1627 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1633 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1637 return DefWindowProc(win, msg, wparam, lparam);
1643 static void update_modkeys(void)
1645 if(GetKeyState(VK_SHIFT) & 0x8000) {
1646 modstate |= GLUT_ACTIVE_SHIFT;
1648 modstate &= ~GLUT_ACTIVE_SHIFT;
1650 if(GetKeyState(VK_CONTROL) & 0x8000) {
1651 modstate |= GLUT_ACTIVE_CTRL;
1653 modstate &= ~GLUT_ACTIVE_CTRL;
1655 if(GetKeyState(VK_MENU) & 0x8000) {
1656 modstate |= GLUT_ACTIVE_ALT;
1658 modstate &= ~GLUT_ACTIVE_ALT;
1662 static int translate_vkey(int vkey)
1665 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1666 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1667 case VK_END: return GLUT_KEY_END;
1668 case VK_HOME: return GLUT_KEY_HOME;
1669 case VK_LEFT: return GLUT_KEY_LEFT;
1670 case VK_UP: return GLUT_KEY_UP;
1671 case VK_RIGHT: return GLUT_KEY_RIGHT;
1672 case VK_DOWN: return GLUT_KEY_DOWN;
1677 if(vkey >= 'A' && vkey <= 'Z') {
1679 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1680 vkey -= VK_F1 + GLUT_KEY_F1;
1686 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1693 x = lparam & 0xffff;
1695 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1699 static void get_window_pos(int *x, int *y)
1702 GetWindowRect(win, &rect);
1707 static void get_window_size(int *w, int *h)
1710 GetClientRect(win, &rect);
1711 *w = rect.right - rect.left;
1712 *h = rect.bottom - rect.top;
1715 static void get_screen_size(int *scrw, int *scrh)
1717 *scrw = GetSystemMetrics(SM_CXSCREEN);
1718 *scrh = GetSystemMetrics(SM_CYSCREEN);
1720 #endif /* BUILD_WIN32 */
1722 #if defined(__unix__) || defined(__APPLE__)
1723 #include <sys/time.h>
1725 #ifdef MINIGLUT_USE_LIBC
1726 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1728 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1731 static long get_msec(void)
1733 static struct timeval tv0;
1736 sys_gettimeofday(&tv, 0);
1737 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1741 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1745 static long get_msec(void)
1750 #ifdef MINIGLUT_NO_WINMM
1751 tm = GetTickCount();
1763 static void panic(const char *msg)
1765 const char *end = msg;
1767 sys_write(2, msg, end - msg);
1772 #ifdef MINIGLUT_USE_LIBC
1780 static void sys_exit(int status)
1785 static int sys_write(int fd, const void *buf, int count)
1787 return write(fd, buf, count);
1790 #else /* !MINIGLUT_USE_LIBC */
1794 static void sys_exit(int status)
1798 :: "a"(60), "D"(status));
1800 static int sys_write(int fd, const void *buf, int count)
1806 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1809 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1815 : "a"(96), "D"(tv), "S"(tz));
1818 #endif /* __x86_64__ */
1820 static void sys_exit(int status)
1824 :: "a"(1), "b"(status));
1826 static int sys_write(int fd, const void *buf, int count)
1832 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1835 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1841 : "a"(78), "b"(tv), "c"(tz));
1844 #endif /* __i386__ */
1845 #endif /* __linux__ */
1848 static void sys_exit(int status)
1850 ExitProcess(status);
1852 static int sys_write(int fd, const void *buf, int count)
1854 unsigned long wrsz = 0;
1856 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1857 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1863 #endif /* !MINIGLUT_USE_LIBC */
1866 /* ----------------- primitives ------------------ */
1867 #ifdef MINIGLUT_USE_LIBC
1871 void mglut_sincos(float angle, float *sptr, float *cptr)
1877 float mglut_atan(float x)
1882 #else /* !MINIGLUT_USE_LIBC */
1885 void mglut_sincos(float angle, float *sptr, float *cptr)
1892 : "=m"(*sptr), "=m"(*cptr)
1897 float mglut_atan(float x)
1913 void mglut_sincos(float angle, float *sptr, float *cptr)
1926 float mglut_atan(float x)
1940 #pragma aux mglut_sincos = \
1942 "fstp dword ptr [edx]" \
1943 "fstp dword ptr [eax]" \
1944 parm[8087][eax][edx] \
1947 #pragma aux mglut_atan = \
1953 #endif /* __WATCOMC__ */
1955 #endif /* !MINIGLUT_USE_LIBC */
1957 #define PI 3.1415926536f
1959 void glutSolidSphere(float rad, int slices, int stacks)
1962 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1963 float du = 1.0f / (float)slices;
1964 float dv = 1.0f / (float)stacks;
1967 for(i=0; i<stacks; i++) {
1969 for(j=0; j<slices; j++) {
1971 for(k=0; k<4; k++) {
1972 gray = k ^ (k >> 1);
1973 s = gray & 1 ? u + du : u;
1974 t = gray & 2 ? v + dv : v;
1975 theta = s * PI * 2.0f;
1977 mglut_sincos(theta, &sintheta, &costheta);
1978 mglut_sincos(phi, &sinphi, &cosphi);
1979 x = sintheta * sinphi;
1980 y = costheta * sinphi;
1985 glNormal3f(x, y, z);
1986 glVertex3f(x * rad, y * rad, z * rad);
1993 void glutWireSphere(float rad, int slices, int stacks)
1995 glPushAttrib(GL_POLYGON_BIT);
1996 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1997 glutSolidSphere(rad, slices, stacks);
2001 void glutSolidCube(float sz)
2003 int i, j, idx, gray, flip, rotx;
2004 float vpos[3], norm[3];
2005 float rad = sz * 0.5f;
2008 for(i=0; i<6; i++) {
2011 idx = (~i & 2) - rotx;
2012 norm[0] = norm[1] = norm[2] = 0.0f;
2013 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2015 vpos[idx] = norm[idx] * rad;
2016 for(j=0; j<4; j++) {
2017 gray = j ^ (j >> 1);
2018 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2019 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2020 glTexCoord2f(gray & 1, gray >> 1);
2027 void glutWireCube(float sz)
2029 glPushAttrib(GL_POLYGON_BIT);
2030 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2035 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2038 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2039 float du = 1.0f / (float)slices;
2040 float dv = 1.0f / (float)stacks;
2043 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2044 mglut_sincos(phi, &sinphi, &cosphi);
2047 for(i=0; i<stacks; i++) {
2049 for(j=0; j<slices; j++) {
2051 for(k=0; k<4; k++) {
2052 gray = k ^ (k >> 1);
2053 s = gray & 2 ? u + du : u;
2054 t = gray & 1 ? v + dv : v;
2055 rad = rbot + (rtop - rbot) * t;
2056 theta = s * PI * 2.0f;
2057 mglut_sincos(theta, &sintheta, &costheta);
2059 x = sintheta * cosphi;
2060 y = costheta * cosphi;
2065 glNormal3f(x, y, z);
2066 glVertex3f(sintheta * rad, costheta * rad, t * height);
2073 void glutSolidCone(float base, float height, int slices, int stacks)
2075 draw_cylinder(base, 0, height, slices, stacks);
2078 void glutWireCone(float base, float height, int slices, int stacks)
2080 glPushAttrib(GL_POLYGON_BIT);
2081 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2082 glutSolidCone(base, height, slices, stacks);
2086 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2088 draw_cylinder(rad, rad, height, slices, stacks);
2091 void glutWireCylinder(float rad, float height, int slices, int stacks)
2093 glPushAttrib(GL_POLYGON_BIT);
2094 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2095 glutSolidCylinder(rad, height, slices, stacks);
2099 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2102 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2103 float du = 1.0f / (float)rings;
2104 float dv = 1.0f / (float)sides;
2107 for(i=0; i<rings; i++) {
2109 for(j=0; j<sides; j++) {
2111 for(k=0; k<4; k++) {
2112 gray = k ^ (k >> 1);
2113 s = gray & 1 ? u + du : u;
2114 t = gray & 2 ? v + dv : v;
2115 theta = s * PI * 2.0f;
2116 phi = t * PI * 2.0f;
2117 mglut_sincos(theta, &sintheta, &costheta);
2118 mglut_sincos(phi, &sinphi, &cosphi);
2119 x = sintheta * sinphi;
2120 y = costheta * sinphi;
2125 glNormal3f(x, y, z);
2127 x = x * inner_rad + sintheta * outer_rad;
2128 y = y * inner_rad + costheta * outer_rad;
2130 glVertex3f(x, y, z);
2137 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2139 glPushAttrib(GL_POLYGON_BIT);
2140 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2141 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2145 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2146 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2148 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2150 #define PATCH_SUBDIV 7
2152 static float teapot_part_flip[] = {
2153 1, 1, 1, 1, /* rim flip */
2154 1, 1, 1, 1, /* body1 flip */
2155 1, 1, 1, 1, /* body2 flip */
2156 1, 1, 1, 1, /* lid patch 1 flip */
2157 1, 1, 1, 1, /* lid patch 2 flip */
2158 1, -1, /* handle 1 flip */
2159 1, -1, /* handle 2 flip */
2160 1, -1, /* spout 1 flip */
2161 1, -1, /* spout 2 flip */
2162 1, 1, 1, 1 /* bottom flip */
2165 static float teapot_part_rot[] = {
2166 0, 90, 180, 270, /* rim rotations */
2167 0, 90, 180, 270, /* body patch 1 rotations */
2168 0, 90, 180, 270, /* body patch 2 rotations */
2169 0, 90, 180, 270, /* lid patch 1 rotations */
2170 0, 90, 180, 270, /* lid patch 2 rotations */
2171 0, 0, /* handle 1 rotations */
2172 0, 0, /* handle 2 rotations */
2173 0, 0, /* spout 1 rotations */
2174 0, 0, /* spout 2 rotations */
2175 0, 90, 180, 270 /* bottom rotations */
2179 static int teapot_index[] = {
2181 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2182 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2183 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2184 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2186 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2187 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2188 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2189 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2191 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2192 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2193 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2194 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2196 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2197 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2198 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2199 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2201 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2202 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2203 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2204 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2206 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2207 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2209 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2210 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2212 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2213 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2215 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2216 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2218 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2219 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2220 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2221 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2225 static float teapot_verts[][3] = {
2226 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2227 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2228 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2229 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2230 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2231 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2232 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2233 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2234 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2235 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2236 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2237 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2238 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2239 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2240 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2241 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2242 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2243 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2244 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2245 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2246 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2247 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2248 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2249 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2250 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2251 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2252 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2253 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2254 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2255 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2256 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2257 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2258 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2259 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2260 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2261 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2262 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2263 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2264 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2265 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2266 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2267 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2268 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2269 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2270 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2271 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2272 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2273 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2274 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2275 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2276 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2277 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2278 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2279 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2280 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2281 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2282 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2283 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2284 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2285 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2286 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2287 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2288 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2289 { 0.8400, -1.5000, 0.07500 }
2292 static void draw_patch(int *index, int flip, float scale);
2293 static float bernstein(int i, float x);
2295 void glutSolidTeapot(float size)
2301 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2302 float flip = teapot_part_flip[i];
2303 float rot = teapot_part_rot[i];
2305 glMatrixMode(GL_MODELVIEW);
2307 glTranslatef(0, -3.15 * size * 0.5, 0);
2308 glRotatef(rot, 0, 1, 0);
2309 glScalef(1, 1, flip);
2310 glRotatef(-90, 1, 0, 0);
2312 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2318 void glutWireTeapot(float size)
2320 glPushAttrib(GL_POLYGON_BIT);
2321 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2322 glutSolidTeapot(size);
2327 static void bezier_patch(float *res, float *cp, float u, float v)
2331 res[0] = res[1] = res[2] = 0.0f;
2333 for(j=0; j<4; j++) {
2334 for(i=0; i<4; i++) {
2335 float bu = bernstein(i, u);
2336 float bv = bernstein(j, v);
2338 res[0] += cp[0] * bu * bv;
2339 res[1] += cp[1] * bu * bv;
2340 res[2] += cp[2] * bu * bv;
2347 static float rsqrt(float x)
2349 float xhalf = x * 0.5f;
2351 i = 0x5f3759df - (i >> 1);
2353 x = x * (1.5f - xhalf * x * x);
2358 #define CROSS(res, a, b) \
2360 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2361 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2362 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2365 #define NORMALIZE(v) \
2367 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2375 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2377 float tang[3], bitan[3], tmp[3];
2379 bezier_patch(tang, cp, u + DT, v);
2380 bezier_patch(tmp, cp, u - DT, v);
2385 bezier_patch(bitan, cp, u, v + DT);
2386 bezier_patch(tmp, cp, u, v - DT);
2391 CROSS(res, tang, bitan);
2397 static float bernstein(int i, float x)
2399 float invx = 1.0f - x;
2403 return invx * invx * invx;
2405 return 3.0f * x * invx * invx;
2407 return 3.0f * x * x * invx;
2416 static void draw_patch(int *index, int flip, float scale)
2418 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2419 static const float voffs[4] = {0, 1, 1, 0};
2425 float du = 1.0 / PATCH_SUBDIV;
2426 float dv = 1.0 / PATCH_SUBDIV;
2428 /* collect control points */
2429 for(i=0; i<16; i++) {
2430 cp[i * 3] = teapot_verts[index[i]][0];
2431 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2432 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2439 for(i=0; i<PATCH_SUBDIV; i++) {
2441 for(j=0; j<PATCH_SUBDIV; j++) {
2443 for(k=0; k<4; k++) {
2444 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2446 /* top/bottom normal hack */
2450 } else if(pt[2] < 0.00001) {
2454 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2459 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);