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;
92 static glut_cb cb_display;
93 static glut_cb cb_idle;
94 static glut_cb_reshape cb_reshape;
95 static glut_cb_state cb_vis, cb_entry;
96 static glut_cb_keyb cb_keydown, cb_keyup;
97 static glut_cb_special cb_skeydown, cb_skeyup;
98 static glut_cb_mouse cb_mouse;
99 static glut_cb_motion cb_motion, cb_passive;
100 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
101 static glut_cb_sbbutton cb_sball_button;
103 static int fullscreen;
104 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
106 static int win_width, win_height;
109 static int upd_pending;
112 void glutInit(int *argc, char **argv)
118 if(!(dpy = XOpenDisplay(0))) {
119 panic("Failed to connect to the X server\n");
121 scr = DefaultScreen(dpy);
122 root = RootWindow(dpy, scr);
123 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
124 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
125 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
126 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
127 if(have_netwm_fullscr()) {
128 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
131 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
132 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
133 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
134 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
136 evmask = ExposureMask | StructureNotifyMask;
138 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
139 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
140 XFreePixmap(dpy, blankpix);
147 hinst = GetModuleHandle(0);
149 wc.cbSize = sizeof wc;
150 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
151 wc.hCursor = LoadCursor(0, IDC_ARROW);
152 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
153 wc.hInstance = hinst;
154 wc.lpfnWndProc = handle_message;
155 wc.lpszClassName = "MiniGLUT";
156 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
157 if(!RegisterClassEx(&wc)) {
158 panic("Failed to register \"MiniGLUT\" window class\n");
162 get_screen_size(&init_x, &init_y);
169 void glutInitWindowPosition(int x, int y)
175 void glutInitWindowSize(int xsz, int ysz)
181 void glutInitDisplayMode(unsigned int mode)
186 void glutCreateWindow(const char *title)
188 create_window(title);
196 void glutMainLoop(void)
203 void glutPostRedisplay(void)
208 #define UPD_EVMASK(x) \
215 if(win) XSelectInput(dpy, win, evmask); \
219 void glutIdleFunc(glut_cb func)
224 void glutDisplayFunc(glut_cb func)
229 void glutReshapeFunc(glut_cb_reshape func)
234 void glutVisibilityFunc(glut_cb_state func)
238 UPD_EVMASK(VisibilityChangeMask);
242 void glutEntryFunc(glut_cb_state func)
246 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
250 void glutKeyboardFunc(glut_cb_keyb func)
254 UPD_EVMASK(KeyPressMask);
258 void glutKeyboardUpFunc(glut_cb_keyb func)
262 UPD_EVMASK(KeyReleaseMask);
266 void glutSpecialFunc(glut_cb_special func)
270 UPD_EVMASK(KeyPressMask);
274 void glutSpecialUpFunc(glut_cb_special func)
278 UPD_EVMASK(KeyReleaseMask);
282 void glutMouseFunc(glut_cb_mouse func)
286 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
290 void glutMotionFunc(glut_cb_motion func)
294 UPD_EVMASK(ButtonMotionMask);
298 void glutPassiveMotionFunc(glut_cb_motion func)
302 UPD_EVMASK(PointerMotionMask);
306 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
308 cb_sball_motion = func;
311 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
313 cb_sball_rotate = func;
316 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
318 cb_sball_button = func;
321 int glutGet(unsigned int s)
326 get_window_pos(&x, &y);
329 get_window_pos(&x, &y);
331 case GLUT_WINDOW_WIDTH:
332 get_window_size(&x, &y);
334 case GLUT_WINDOW_HEIGHT:
335 get_window_size(&x, &y);
337 case GLUT_WINDOW_BUFFER_SIZE:
338 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
339 case GLUT_WINDOW_STENCIL_SIZE:
340 return ctx_info.ssize;
341 case GLUT_WINDOW_DEPTH_SIZE:
342 return ctx_info.zsize;
343 case GLUT_WINDOW_RED_SIZE:
344 return ctx_info.rsize;
345 case GLUT_WINDOW_GREEN_SIZE:
346 return ctx_info.gsize;
347 case GLUT_WINDOW_BLUE_SIZE:
348 return ctx_info.bsize;
349 case GLUT_WINDOW_ALPHA_SIZE:
350 return ctx_info.asize;
351 case GLUT_WINDOW_DOUBLEBUFFER:
352 return ctx_info.dblbuf;
353 case GLUT_WINDOW_RGBA:
355 case GLUT_WINDOW_NUM_SAMPLES:
356 return ctx_info.samples;
357 case GLUT_WINDOW_STEREO:
358 return ctx_info.stereo;
359 case GLUT_WINDOW_SRGB:
360 return ctx_info.srgb;
361 case GLUT_WINDOW_CURSOR:
363 case GLUT_SCREEN_WIDTH:
364 get_screen_size(&x, &y);
366 case GLUT_SCREEN_HEIGHT:
367 get_screen_size(&x, &y);
369 case GLUT_INIT_DISPLAY_MODE:
371 case GLUT_INIT_WINDOW_X:
373 case GLUT_INIT_WINDOW_Y:
375 case GLUT_INIT_WINDOW_WIDTH:
377 case GLUT_INIT_WINDOW_HEIGHT:
379 case GLUT_ELAPSED_TIME:
387 int glutGetModifiers(void)
392 static int is_space(int c)
394 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
397 static const char *skip_space(const char *s)
399 while(*s && is_space(*s)) s++;
403 int glutExtensionSupported(char *ext)
405 const char *str, *eptr;
407 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
412 str = skip_space(str);
413 eptr = skip_space(ext);
414 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
418 if((!*str || is_space(*str)) && !*eptr) {
421 while(*str && !is_space(*str)) str++;
428 /* --------------- UNIX/X11 implementation ----------------- */
431 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
433 SPNAV_EVENT_BUTTON /* includes both press and release */
436 struct spnav_event_motion {
444 struct spnav_event_button {
452 struct spnav_event_motion motion;
453 struct spnav_event_button button;
457 static void handle_event(XEvent *ev);
459 static int spnav_window(Window win);
460 static int spnav_event(const XEvent *xev, union spnav_event *event);
461 static int spnav_remove_events(int type);
464 void glutMainLoopEvent(void)
469 panic("display callback not set");
472 if(!upd_pending && !cb_idle) {
473 XNextEvent(dpy, &ev);
477 while(XPending(dpy)) {
478 XNextEvent(dpy, &ev);
487 if(upd_pending && mapped) {
498 static void cleanup(void)
502 glXMakeCurrent(dpy, 0, 0);
503 XDestroyWindow(dpy, win);
507 static KeySym translate_keysym(KeySym sym)
528 static void handle_event(XEvent *ev)
531 union spnav_event sev;
540 case ConfigureNotify:
541 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
542 win_width = ev->xconfigure.width;
543 win_height = ev->xconfigure.height;
544 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
549 if(ev->xclient.message_type == xa_wm_proto) {
550 if(ev->xclient.data.l[0] == xa_wm_del_win) {
554 if(spnav_event(ev, &sev)) {
556 case SPNAV_EVENT_MOTION:
557 if(cb_sball_motion) {
558 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
560 if(cb_sball_rotate) {
561 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
563 spnav_remove_events(SPNAV_EVENT_MOTION);
566 case SPNAV_EVENT_BUTTON:
567 if(cb_sball_button) {
568 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
584 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
585 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
588 sym = translate_keysym(sym);
590 if(ev->type == KeyPress) {
591 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
593 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
596 if(ev->type == KeyPress) {
597 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
599 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
606 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
608 int bn = ev->xbutton.button - Button1;
609 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
610 ev->xbutton.x, ev->xbutton.y);
615 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
616 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
618 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
622 case VisibilityNotify:
624 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
628 if(cb_entry) cb_entry(GLUT_ENTERED);
631 if(cb_entry) cb_entry(GLUT_LEFT);
636 void glutSwapBuffers(void)
638 glXSwapBuffers(dpy, win);
642 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
643 * needs to resize the window to make it fullscreen. The way it does this is by
644 * querying the size of the root window (see get_screen_size), which in the
645 * case of multi-monitor setups will be the combined size of all monitors.
646 * This is problematic; the way to solve it is to use the XRandR extension, or
647 * the Xinerama extension, to figure out the dimensions of the correct video
648 * output, which would add potentially two extension support libraries to our
650 * Moreover, any X installation modern enough to support XR&R will almost
651 * certainly be running a window manager supporting the EHWM
652 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
653 * on manual resizing, and is used in preference if available, making this
654 * whole endeavor pointless.
655 * So I'll just leave it with set_fullscreen_mwm covering the entire
656 * multi-monitor area for now.
661 unsigned long functions;
662 unsigned long decorations;
664 unsigned long status;
667 #define MWM_HINTS_DECORATIONS 2
668 #define MWM_DECOR_ALL 1
670 static void set_fullscreen_mwm(int fs)
672 struct mwm_hints hints;
673 int scr_width, scr_height;
676 get_window_pos(&prev_win_x, &prev_win_y);
677 get_window_size(&prev_win_width, &prev_win_height);
678 get_screen_size(&scr_width, &scr_height);
680 hints.decorations = 0;
681 hints.flags = MWM_HINTS_DECORATIONS;
682 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
683 PropModeReplace, (unsigned char*)&hints, 5);
685 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
687 XDeleteProperty(dpy, win, xa_motif_wm_hints);
688 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
692 static int have_netwm_fullscr(void)
696 unsigned long i, count, rem;
698 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
701 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
702 &type, &fmt, &count, &rem, (unsigned char**)prop);
704 for(i=0; i<count; i++) {
705 if(prop[i] == xa_net_wm_state_fullscr) {
715 static void set_fullscreen_ewmh(int fs)
717 XClientMessageEvent msg = {0};
719 msg.type = ClientMessage;
721 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
723 msg.data.l[0] = fs ? 1 : 0;
724 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
726 msg.data.l[3] = 1; /* source regular application */
727 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
730 static void set_fullscreen(int fs)
732 if(fullscreen == fs) return;
734 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
735 set_fullscreen_ewmh(fs);
737 } else if(xa_motif_wm_hints) {
738 set_fullscreen_mwm(fs);
743 void glutPositionWindow(int x, int y)
746 XMoveWindow(dpy, win, x, y);
749 void glutReshapeWindow(int xsz, int ysz)
752 XResizeWindow(dpy, win, xsz, ysz);
755 void glutFullScreen(void)
760 void glutSetWindowTitle(const char *title)
763 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
766 XSetWMName(dpy, win, &tprop);
770 void glutSetIconTitle(const char *title)
773 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
776 XSetWMIconName(dpy, win, &tprop);
780 void glutSetCursor(int cidx)
785 case GLUT_CURSOR_LEFT_ARROW:
786 cur = XCreateFontCursor(dpy, XC_left_ptr);
788 case GLUT_CURSOR_INHERIT:
790 case GLUT_CURSOR_NONE:
797 XDefineCursor(dpy, win, cur);
801 static XVisualInfo *choose_visual(unsigned int mode)
808 if(mode & GLUT_DOUBLE) {
809 *aptr++ = GLX_DOUBLEBUFFER;
812 if(mode & GLUT_INDEX) {
813 *aptr++ = GLX_BUFFER_SIZE;
817 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
818 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
819 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
821 if(mode & GLUT_ALPHA) {
822 *aptr++ = GLX_ALPHA_SIZE;
825 if(mode & GLUT_DEPTH) {
826 *aptr++ = GLX_DEPTH_SIZE;
829 if(mode & GLUT_STENCIL) {
830 *aptr++ = GLX_STENCIL_SIZE;
833 if(mode & GLUT_ACCUM) {
834 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
835 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
836 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
838 if(mode & GLUT_STEREO) {
839 *aptr++ = GLX_STEREO;
841 if(mode & GLUT_SRGB) {
842 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
844 if(mode & GLUT_MULTISAMPLE) {
845 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
847 *aptr++ = GLX_SAMPLES_ARB;
854 return glXChooseVisual(dpy, scr, attr);
856 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
865 static void create_window(const char *title)
867 XSetWindowAttributes xattr = {0};
869 unsigned int xattr_mask;
870 unsigned int mode = init_mode;
872 if(!(vi = choose_visual(mode))) {
874 if(!(vi = choose_visual(mode))) {
875 panic("Failed to find compatible visual\n");
879 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
881 panic("Failed to create OpenGL context\n");
884 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
885 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
886 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
887 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
888 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
889 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
890 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
891 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
892 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
893 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
895 xattr.background_pixel = BlackPixel(dpy, scr);
896 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
897 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
898 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
899 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
901 glXDestroyContext(dpy, ctx);
902 panic("Failed to create window\n");
906 XSelectInput(dpy, win, evmask);
910 glutSetWindowTitle(title);
911 glutSetIconTitle(title);
912 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
913 XMapWindow(dpy, win);
915 glXMakeCurrent(dpy, win, ctx);
918 static void get_window_pos(int *x, int *y)
921 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
924 static void get_window_size(int *w, int *h)
926 XWindowAttributes wattr;
927 XGetWindowAttributes(dpy, win, &wattr);
932 static void get_screen_size(int *scrw, int *scrh)
934 XWindowAttributes wattr;
935 XGetWindowAttributes(dpy, root, &wattr);
937 *scrh = wattr.height;
943 CMD_APP_WINDOW = 27695,
947 static Window get_daemon_window(Display *dpy);
948 static int catch_badwin(Display *dpy, XErrorEvent *err);
950 #define SPNAV_INITIALIZED (xa_motion_event)
952 static int spnav_window(Window win)
954 int (*prev_xerr_handler)(Display*, XErrorEvent*);
958 if(!SPNAV_INITIALIZED) {
962 if(!(daemon_win = get_daemon_window(dpy))) {
966 prev_xerr_handler = XSetErrorHandler(catch_badwin);
968 xev.type = ClientMessage;
969 xev.xclient.send_event = False;
970 xev.xclient.display = dpy;
971 xev.xclient.window = win;
972 xev.xclient.message_type = xa_command_event;
973 xev.xclient.format = 16;
974 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
975 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
976 xev.xclient.data.s[2] = CMD_APP_WINDOW;
978 XSendEvent(dpy, daemon_win, False, 0, &xev);
981 XSetErrorHandler(prev_xerr_handler);
985 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
987 int evtype = *(int*)arg;
989 if(xev->type != ClientMessage) {
993 if(xev->xclient.message_type == xa_motion_event) {
994 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
996 if(xev->xclient.message_type == xa_button_press_event ||
997 xev->xclient.message_type == xa_button_release_event) {
998 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1003 static int spnav_remove_events(int type)
1007 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1013 static int spnav_event(const XEvent *xev, union spnav_event *event)
1018 xmsg_type = xev->xclient.message_type;
1020 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1021 xmsg_type != xa_button_release_event) {
1025 if(xmsg_type == xa_motion_event) {
1026 event->type = SPNAV_EVENT_MOTION;
1027 event->motion.data = &event->motion.x;
1029 for(i=0; i<6; i++) {
1030 event->motion.data[i] = xev->xclient.data.s[i + 2];
1032 event->motion.period = xev->xclient.data.s[8];
1034 event->type = SPNAV_EVENT_BUTTON;
1035 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1036 event->button.bnum = xev->xclient.data.s[2];
1041 static int mglut_strcmp(const char *s1, const char *s2)
1043 while(*s1 && *s1 == *s2) {
1050 static Window get_daemon_window(Display *dpy)
1053 XTextProperty wname;
1056 unsigned long nitems, bytes_after;
1057 unsigned char *prop;
1059 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1060 &type, &fmt, &nitems, &bytes_after, &prop);
1065 win = *(Window*)prop;
1068 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1075 static int catch_badwin(Display *dpy, XErrorEvent *err)
1082 #endif /* BUILD_X11 */
1085 /* --------------- windows implementation ----------------- */
1087 static int reshape_pending;
1089 static void update_modkeys(void);
1090 static int translate_vkey(int vkey);
1091 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1093 #ifdef MINIGLUT_WINMAIN
1094 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1097 char *argv[] = { "miniglut.exe", 0 };
1098 return main(argc, argv);
1102 void glutMainLoopEvent(void)
1107 panic("display callback not set");
1110 if(reshape_pending && cb_reshape) {
1111 reshape_pending = 0;
1112 get_window_size(&win_width, &win_height);
1113 cb_reshape(win_width, win_height);
1116 if(!upd_pending && !cb_idle) {
1117 GetMessage(&msg, 0, 0, 0);
1118 TranslateMessage(&msg);
1119 DispatchMessage(&msg);
1122 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1123 TranslateMessage(&msg);
1124 DispatchMessage(&msg);
1132 if(upd_pending && mapped) {
1138 static void cleanup(void)
1141 wglMakeCurrent(dc, 0);
1142 wglDeleteContext(ctx);
1143 UnregisterClass("MiniGLUT", hinst);
1147 void glutSwapBuffers(void)
1152 void glutPositionWindow(int x, int y)
1155 unsigned int flags = SWP_SHOWWINDOW;
1158 rect.left = prev_win_x;
1159 rect.top = prev_win_y;
1160 rect.right = rect.left + prev_win_width;
1161 rect.bottom = rect.top + prev_win_height;
1162 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1164 flags |= SWP_FRAMECHANGED;
1166 GetWindowRect(win, &rect);
1168 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1171 void glutReshapeWindow(int xsz, int ysz)
1174 unsigned int flags = SWP_SHOWWINDOW;
1177 rect.left = prev_win_x;
1178 rect.top = prev_win_y;
1179 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1181 flags |= SWP_FRAMECHANGED;
1183 GetWindowRect(win, &rect);
1185 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1188 void glutFullScreen(void)
1191 int scr_width, scr_height;
1193 if(fullscreen) return;
1195 GetWindowRect(win, &rect);
1196 prev_win_x = rect.left;
1197 prev_win_y = rect.top;
1198 prev_win_width = rect.right - rect.left;
1199 prev_win_height = rect.bottom - rect.top;
1201 get_screen_size(&scr_width, &scr_height);
1203 SetWindowLong(win, GWL_STYLE, 0);
1204 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1209 void glutSetWindowTitle(const char *title)
1211 SetWindowText(win, title);
1214 void glutSetIconTitle(const char *title)
1218 void glutSetCursor(int cidx)
1221 case GLUT_CURSOR_NONE:
1224 case GLUT_CURSOR_INHERIT:
1225 case GLUT_CURSOR_LEFT_ARROW:
1227 SetCursor(LoadCursor(0, IDC_ARROW));
1232 #define WGL_DRAW_TO_WINDOW 0x2001
1233 #define WGL_SUPPORT_OPENGL 0x2010
1234 #define WGL_DOUBLE_BUFFER 0x2011
1235 #define WGL_STEREO 0x2012
1236 #define WGL_PIXEL_TYPE 0x2013
1237 #define WGL_COLOR_BITS 0x2014
1238 #define WGL_RED_BITS 0x2015
1239 #define WGL_GREEN_BITS 0x2017
1240 #define WGL_BLUE_BITS 0x2019
1241 #define WGL_ALPHA_BITS 0x201b
1242 #define WGL_ACCUM_BITS 0x201d
1243 #define WGL_DEPTH_BITS 0x2022
1244 #define WGL_STENCIL_BITS 0x2023
1246 #define WGL_TYPE_RGBA 0x202b
1247 #define WGL_TYPE_COLORINDEX 0x202c
1249 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1250 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1251 #define WGL_SAMPLES_ARB 0x2042
1253 static PROC wglChoosePixelFormat;
1254 static PROC wglGetPixelFormatAttribiv;
1256 #define ATTR(a, v) \
1257 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1259 static unsigned int choose_pixfmt(unsigned int mode)
1261 unsigned int num_pixfmt, pixfmt = 0;
1262 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1 };
1267 if(mode & GLUT_DOUBLE) {
1268 ATTR(WGL_DOUBLE_BUFFER, 1);
1271 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1272 ATTR(WGL_COLOR_BITS, 8);
1273 if(mode & GLUT_ALPHA) {
1274 ATTR(WGL_ALPHA_BITS, 4);
1276 if(mode & GLUT_DEPTH) {
1277 ATTR(WGL_DEPTH_BITS, 16);
1279 if(mode & GLUT_STENCIL) {
1280 ATTR(WGL_STENCIL_BITS, 1);
1282 if(mode & GLUT_ACCUM) {
1283 ATTR(WGL_ACCUM_BITS, 1);
1285 if(mode & GLUT_STEREO) {
1286 ATTR(WGL_STEREO, 1);
1288 if(mode & GLUT_SRGB) {
1289 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1291 if(mode & GLUT_MULTISAMPLE) {
1292 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1293 *aptr++ = WGL_SAMPLES_ARB;
1299 while((!wglChoosePixelFormat(dc, attr, 0, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1308 static PIXELFORMATDESCRIPTOR tmppfd = {
1309 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1310 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1311 PFD_MAIN_PLANE, 0, 0, 0, 0
1313 #define TMPCLASS "TempMiniGLUT"
1315 #define GETATTR(attr, vptr) \
1318 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1321 static int create_window_wglext(const char *title, int width, int height)
1323 WNDCLASSEX wc = {0};
1329 /* create a temporary window and GL context, just to query and retrieve
1330 * the wglChoosePixelFormatEXT function
1332 wc.cbSize = sizeof wc;
1333 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1334 wc.hCursor = LoadCursor(0, IDC_ARROW);
1335 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1336 wc.hInstance = hinst;
1337 wc.lpfnWndProc = DefWindowProc;
1338 wc.lpszClassName = TMPCLASS;
1339 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1340 if(!RegisterClassEx(&wc)) {
1343 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1344 width, height, 0, 0, hinst, 0))) {
1347 tmpdc = GetDC(tmpwin);
1349 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1350 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1351 !(tmpctx = wglCreateContext(tmpdc))) {
1354 wglMakeCurrent(tmpdc, tmpctx);
1356 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1357 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1360 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1364 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1368 wglMakeCurrent(0, 0);
1369 wglDeleteContext(tmpctx);
1370 DestroyWindow(tmpwin);
1371 UnregisterClass(TMPCLASS, hinst);
1373 /* create the real window and context */
1374 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1375 init_y, width, height, 0, 0, hinst, 0))) {
1376 panic("Failed to create window\n");
1380 if(!(pixfmt = choose_pixfmt(init_mode))) {
1381 panic("Failed to find suitable pixel format\n");
1383 if(!SetPixelFormat(dc, pixfmt, &tmppfd)) {
1384 panic("Failed to set the selected pixel format\n");
1386 if(!(ctx = wglCreateContext(dc))) {
1387 panic("Failed to create the OpenGL context\n");
1389 wglMakeCurrent(dc, ctx);
1391 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1392 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1393 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1394 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1395 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1396 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1397 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1398 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1399 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1404 wglMakeCurrent(0, 0);
1405 wglDeleteContext(tmpctx);
1408 DestroyWindow(tmpwin);
1410 UnregisterClass(TMPCLASS, hinst);
1415 static void create_window(const char *title)
1418 PIXELFORMATDESCRIPTOR pfd = {0};
1424 rect.right = init_x + init_width;
1425 rect.bottom = init_y + init_height;
1426 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1427 width = rect.right - rect.left;
1428 height = rect.bottom - rect.top;
1430 if(create_window_wglext(title, width, height) == -1) {
1432 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1433 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1434 panic("Failed to create window\n");
1438 pfd.nSize = sizeof pfd;
1440 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1441 if(init_mode & GLUT_STEREO) {
1442 pfd.dwFlags |= PFD_STEREO;
1444 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1445 pfd.cColorBits = 24;
1446 if(init_mode & GLUT_ALPHA) {
1449 if(init_mode & GLUT_ACCUM) {
1450 pfd.cAccumBits = 24;
1452 if(init_mode & GLUT_DEPTH) {
1453 pfd.cDepthBits = 24;
1455 if(init_mode & GLUT_STENCIL) {
1456 pfd.cStencilBits = 8;
1458 pfd.iLayerType = PFD_MAIN_PLANE;
1460 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1461 panic("Failed to find suitable pixel format\n");
1463 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1464 panic("Failed to set the selected pixel format\n");
1466 if(!(ctx = wglCreateContext(dc))) {
1467 panic("Failed to create the OpenGL context\n");
1469 wglMakeCurrent(dc, ctx);
1471 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1472 ctx_info.rsize = pfd.cRedBits;
1473 ctx_info.gsize = pfd.cGreenBits;
1474 ctx_info.bsize = pfd.cBlueBits;
1475 ctx_info.asize = pfd.cAlphaBits;
1476 ctx_info.zsize = pfd.cDepthBits;
1477 ctx_info.ssize = pfd.cStencilBits;
1478 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1479 ctx_info.samples = 0;
1484 SetForegroundWindow(win);
1487 reshape_pending = 1;
1490 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1492 static int mouse_x, mouse_y;
1497 if(win) DestroyWindow(win);
1508 ValidateRect(win, 0);
1512 x = lparam & 0xffff;
1514 if(x != win_width && y != win_height) {
1518 reshape_pending = 0;
1519 cb_reshape(win_width, win_height);
1526 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1532 key = translate_vkey(wparam);
1535 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1539 cb_skeydown(key, mouse_x, mouse_y);
1547 key = translate_vkey(wparam);
1550 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1554 cb_skeyup(key, mouse_x, mouse_y);
1559 case WM_LBUTTONDOWN:
1560 handle_mbutton(0, 1, wparam, lparam);
1562 case WM_MBUTTONDOWN:
1563 handle_mbutton(1, 1, wparam, lparam);
1565 case WM_RBUTTONDOWN:
1566 handle_mbutton(2, 1, wparam, lparam);
1569 handle_mbutton(0, 0, wparam, lparam);
1572 handle_mbutton(1, 0, wparam, lparam);
1575 handle_mbutton(2, 0, wparam, lparam);
1579 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1580 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1582 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1588 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1592 return DefWindowProc(win, msg, wparam, lparam);
1598 static void update_modkeys(void)
1600 if(GetKeyState(VK_SHIFT) & 0x8000) {
1601 modstate |= GLUT_ACTIVE_SHIFT;
1603 modstate &= ~GLUT_ACTIVE_SHIFT;
1605 if(GetKeyState(VK_CONTROL) & 0x8000) {
1606 modstate |= GLUT_ACTIVE_CTRL;
1608 modstate &= ~GLUT_ACTIVE_CTRL;
1610 if(GetKeyState(VK_MENU) & 0x8000) {
1611 modstate |= GLUT_ACTIVE_ALT;
1613 modstate &= ~GLUT_ACTIVE_ALT;
1617 static int translate_vkey(int vkey)
1620 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1621 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1622 case VK_END: return GLUT_KEY_END;
1623 case VK_HOME: return GLUT_KEY_HOME;
1624 case VK_LEFT: return GLUT_KEY_LEFT;
1625 case VK_UP: return GLUT_KEY_UP;
1626 case VK_RIGHT: return GLUT_KEY_RIGHT;
1627 case VK_DOWN: return GLUT_KEY_DOWN;
1632 if(vkey >= 'A' && vkey <= 'Z') {
1634 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1635 vkey -= VK_F1 + GLUT_KEY_F1;
1641 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1648 x = lparam & 0xffff;
1650 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1654 static void get_window_pos(int *x, int *y)
1657 GetWindowRect(win, &rect);
1662 static void get_window_size(int *w, int *h)
1665 GetClientRect(win, &rect);
1666 *w = rect.right - rect.left;
1667 *h = rect.bottom - rect.top;
1670 static void get_screen_size(int *scrw, int *scrh)
1672 *scrw = GetSystemMetrics(SM_CXSCREEN);
1673 *scrh = GetSystemMetrics(SM_CYSCREEN);
1675 #endif /* BUILD_WIN32 */
1677 #if defined(__unix__) || defined(__APPLE__)
1678 #include <sys/time.h>
1680 #ifdef MINIGLUT_USE_LIBC
1681 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1683 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1686 static long get_msec(void)
1688 static struct timeval tv0;
1691 sys_gettimeofday(&tv, 0);
1692 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1696 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1700 static long get_msec(void)
1705 #ifdef MINIGLUT_NO_WINMM
1706 tm = GetTickCount();
1718 static void panic(const char *msg)
1720 const char *end = msg;
1722 sys_write(2, msg, end - msg);
1727 #ifdef MINIGLUT_USE_LIBC
1733 static void sys_exit(int status)
1738 static int sys_write(int fd, const void *buf, int count)
1740 return write(fd, buf, count);
1743 #else /* !MINIGLUT_USE_LIBC */
1747 static void sys_exit(int status)
1751 :: "a"(60), "D"(status));
1753 static int sys_write(int fd, const void *buf, int count)
1759 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1762 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1768 : "a"(96), "D"(tv), "S"(tz));
1771 #endif /* __x86_64__ */
1773 static void sys_exit(int status)
1777 :: "a"(1), "b"(status));
1779 static int sys_write(int fd, const void *buf, int count)
1785 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1788 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1794 : "a"(78), "b"(tv), "c"(tz));
1797 #endif /* __i386__ */
1798 #endif /* __linux__ */
1801 static void sys_exit(int status)
1803 ExitProcess(status);
1805 static int sys_write(int fd, const void *buf, int count)
1807 unsigned long wrsz = 0;
1809 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1810 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1816 #endif /* !MINIGLUT_USE_LIBC */
1819 /* ----------------- primitives ------------------ */
1820 #ifdef MINIGLUT_USE_LIBC
1824 void mglut_sincos(float angle, float *sptr, float *cptr)
1830 float mglut_atan(float x)
1835 #else /* !MINIGLUT_USE_LIBC */
1838 void mglut_sincos(float angle, float *sptr, float *cptr)
1845 : "=m"(*sptr), "=m"(*cptr)
1850 float mglut_atan(float x)
1866 void mglut_sincos(float angle, float *sptr, float *cptr)
1879 float mglut_atan(float x)
1893 #pragma aux mglut_sincos = \
1895 "fstp dword ptr [edx]" \
1896 "fstp dword ptr [eax]" \
1897 parm[8087][eax][edx] \
1900 #pragma aux mglut_atan = \
1906 #endif /* __WATCOMC__ */
1908 #endif /* !MINIGLUT_USE_LIBC */
1910 #define PI 3.1415926536f
1912 void glutSolidSphere(float rad, int slices, int stacks)
1915 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1916 float du = 1.0f / (float)slices;
1917 float dv = 1.0f / (float)stacks;
1920 for(i=0; i<stacks; i++) {
1922 for(j=0; j<slices; j++) {
1924 for(k=0; k<4; k++) {
1925 gray = k ^ (k >> 1);
1926 s = gray & 1 ? u + du : u;
1927 t = gray & 2 ? v + dv : v;
1928 theta = s * PI * 2.0f;
1930 mglut_sincos(theta, &sintheta, &costheta);
1931 mglut_sincos(phi, &sinphi, &cosphi);
1932 x = sintheta * sinphi;
1933 y = costheta * sinphi;
1938 glNormal3f(x, y, z);
1939 glVertex3f(x * rad, y * rad, z * rad);
1946 void glutWireSphere(float rad, int slices, int stacks)
1948 glPushAttrib(GL_POLYGON_BIT);
1949 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1950 glutSolidSphere(rad, slices, stacks);
1954 void glutSolidCube(float sz)
1956 int i, j, idx, gray, flip, rotx;
1957 float vpos[3], norm[3];
1958 float rad = sz * 0.5f;
1961 for(i=0; i<6; i++) {
1964 idx = (~i & 2) - rotx;
1965 norm[0] = norm[1] = norm[2] = 0.0f;
1966 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
1968 vpos[idx] = norm[idx] * rad;
1969 for(j=0; j<4; j++) {
1970 gray = j ^ (j >> 1);
1971 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
1972 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
1973 glTexCoord2f(gray & 1, gray >> 1);
1980 void glutWireCube(float sz)
1982 glPushAttrib(GL_POLYGON_BIT);
1983 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1988 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
1991 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
1992 float du = 1.0f / (float)slices;
1993 float dv = 1.0f / (float)stacks;
1996 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
1997 mglut_sincos(phi, &sinphi, &cosphi);
2000 for(i=0; i<stacks; i++) {
2002 for(j=0; j<slices; j++) {
2004 for(k=0; k<4; k++) {
2005 gray = k ^ (k >> 1);
2006 s = gray & 2 ? u + du : u;
2007 t = gray & 1 ? v + dv : v;
2008 rad = rbot + (rtop - rbot) * t;
2009 theta = s * PI * 2.0f;
2010 mglut_sincos(theta, &sintheta, &costheta);
2012 x = sintheta * cosphi;
2013 y = costheta * cosphi;
2018 glNormal3f(x, y, z);
2019 glVertex3f(sintheta * rad, costheta * rad, t * height);
2026 void glutSolidCone(float base, float height, int slices, int stacks)
2028 draw_cylinder(base, 0, height, slices, stacks);
2031 void glutWireCone(float base, float height, int slices, int stacks)
2033 glPushAttrib(GL_POLYGON_BIT);
2034 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2035 glutSolidCone(base, height, slices, stacks);
2039 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2041 draw_cylinder(rad, rad, height, slices, stacks);
2044 void glutWireCylinder(float rad, float height, int slices, int stacks)
2046 glPushAttrib(GL_POLYGON_BIT);
2047 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2048 glutSolidCylinder(rad, height, slices, stacks);
2052 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2055 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2056 float du = 1.0f / (float)rings;
2057 float dv = 1.0f / (float)sides;
2060 for(i=0; i<rings; i++) {
2062 for(j=0; j<sides; j++) {
2064 for(k=0; k<4; k++) {
2065 gray = k ^ (k >> 1);
2066 s = gray & 1 ? u + du : u;
2067 t = gray & 2 ? v + dv : v;
2068 theta = s * PI * 2.0f;
2069 phi = t * PI * 2.0f;
2070 mglut_sincos(theta, &sintheta, &costheta);
2071 mglut_sincos(phi, &sinphi, &cosphi);
2072 x = sintheta * sinphi;
2073 y = costheta * sinphi;
2078 glNormal3f(x, y, z);
2080 x = x * inner_rad + sintheta * outer_rad;
2081 y = y * inner_rad + costheta * outer_rad;
2083 glVertex3f(x, y, z);
2090 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2092 glPushAttrib(GL_POLYGON_BIT);
2093 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2094 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2098 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2099 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2101 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2103 #define PATCH_SUBDIV 7
2105 static float teapot_part_flip[] = {
2106 1, 1, 1, 1, /* rim flip */
2107 1, 1, 1, 1, /* body1 flip */
2108 1, 1, 1, 1, /* body2 flip */
2109 1, 1, 1, 1, /* lid patch 1 flip */
2110 1, 1, 1, 1, /* lid patch 2 flip */
2111 1, -1, /* handle 1 flip */
2112 1, -1, /* handle 2 flip */
2113 1, -1, /* spout 1 flip */
2114 1, -1, /* spout 2 flip */
2115 1, 1, 1, 1 /* bottom flip */
2118 static float teapot_part_rot[] = {
2119 0, 90, 180, 270, /* rim rotations */
2120 0, 90, 180, 270, /* body patch 1 rotations */
2121 0, 90, 180, 270, /* body patch 2 rotations */
2122 0, 90, 180, 270, /* lid patch 1 rotations */
2123 0, 90, 180, 270, /* lid patch 2 rotations */
2124 0, 0, /* handle 1 rotations */
2125 0, 0, /* handle 2 rotations */
2126 0, 0, /* spout 1 rotations */
2127 0, 0, /* spout 2 rotations */
2128 0, 90, 180, 270 /* bottom rotations */
2132 static int teapot_index[] = {
2134 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2135 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2136 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2137 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2139 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2140 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2141 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2142 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2144 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2145 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2146 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2147 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2149 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2150 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2151 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2152 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2154 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2155 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2156 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2157 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2159 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2160 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2162 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2163 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2165 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2166 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2168 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2169 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2171 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2172 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2173 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2174 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2178 static float teapot_verts[][3] = {
2179 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2180 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2181 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2182 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2183 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2184 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2185 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2186 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2187 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2188 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2189 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2190 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2191 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2192 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2193 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2194 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2195 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2196 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2197 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2198 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2199 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2200 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2201 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2202 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2203 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2204 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2205 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2206 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2207 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2208 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2209 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2210 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2211 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2212 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2213 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2214 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2215 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2216 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2217 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2218 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2219 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2220 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2221 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2222 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2223 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2224 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2225 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2226 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2227 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2228 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2229 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2230 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2231 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2232 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2233 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2234 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2235 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2236 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2237 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2238 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2239 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2240 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2241 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2242 { 0.8400, -1.5000, 0.07500 }
2245 static void draw_patch(int *index, int flip, float scale);
2246 static float bernstein(int i, float x);
2248 void glutSolidTeapot(float size)
2254 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2255 float flip = teapot_part_flip[i];
2256 float rot = teapot_part_rot[i];
2258 glMatrixMode(GL_MODELVIEW);
2260 glTranslatef(0, -3.15 * size * 0.5, 0);
2261 glRotatef(rot, 0, 1, 0);
2262 glScalef(1, 1, flip);
2263 glRotatef(-90, 1, 0, 0);
2265 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2271 void glutWireTeapot(float size)
2273 glPushAttrib(GL_POLYGON_BIT);
2274 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2275 glutSolidTeapot(size);
2280 static void bezier_patch(float *res, float *cp, float u, float v)
2284 res[0] = res[1] = res[2] = 0.0f;
2286 for(j=0; j<4; j++) {
2287 for(i=0; i<4; i++) {
2288 float bu = bernstein(i, u);
2289 float bv = bernstein(j, v);
2291 res[0] += cp[0] * bu * bv;
2292 res[1] += cp[1] * bu * bv;
2293 res[2] += cp[2] * bu * bv;
2300 static float rsqrt(float x)
2302 float xhalf = x * 0.5f;
2304 i = 0x5f3759df - (i >> 1);
2306 x = x * (1.5f - xhalf * x * x);
2311 #define CROSS(res, a, b) \
2313 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2314 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2315 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2318 #define NORMALIZE(v) \
2320 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2328 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2330 float tang[3], bitan[3], tmp[3];
2332 bezier_patch(tang, cp, u + DT, v);
2333 bezier_patch(tmp, cp, u - DT, v);
2338 bezier_patch(bitan, cp, u, v + DT);
2339 bezier_patch(tmp, cp, u, v - DT);
2344 CROSS(res, tang, bitan);
2350 static float bernstein(int i, float x)
2352 float invx = 1.0f - x;
2356 return invx * invx * invx;
2358 return 3.0f * x * invx * invx;
2360 return 3.0f * x * x * invx;
2369 static void draw_patch(int *index, int flip, float scale)
2371 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2372 static const float voffs[4] = {0, 1, 1, 0};
2378 float du = 1.0 / PATCH_SUBDIV;
2379 float dv = 1.0 / PATCH_SUBDIV;
2381 /* collect control points */
2382 for(i=0; i<16; i++) {
2383 cp[i * 3] = teapot_verts[index[i]][0];
2384 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2385 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2392 for(i=0; i<PATCH_SUBDIV; i++) {
2394 for(j=0; j<PATCH_SUBDIV; j++) {
2396 for(k=0; k<4; k++) {
2397 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2399 /* top/bottom normal hack */
2403 } else if(pt[2] < 0.00001) {
2407 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2412 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);