2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
26 #ifndef GLX_SAMPLE_BUFFERS_ARB
27 #define GLX_SAMPLE_BUFFERS_ARB 100000
28 #define GLX_SAMPLES_ARB 100001
30 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
31 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
35 static Window win, root;
37 static GLXContext ctx;
38 static Atom xa_wm_proto, xa_wm_del_win;
39 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
40 static Atom xa_motif_wm_hints;
41 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
42 static unsigned int evmask;
43 static Cursor blank_cursor;
45 static int have_netwm_fullscr(void);
52 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
54 static HINSTANCE hinst;
60 #error unsupported platform
66 int rsize, gsize, bsize, asize;
74 static void cleanup(void);
75 static void create_window(const char *title);
76 static void get_window_pos(int *x, int *y);
77 static void get_window_size(int *w, int *h);
78 static void get_screen_size(int *scrw, int *scrh);
80 static long get_msec(void);
81 static void panic(const char *msg);
82 static void sys_exit(int status);
83 static int sys_write(int fd, const void *buf, int count);
86 static int init_x = -1, init_y, init_width = 256, init_height = 256;
87 static unsigned int init_mode;
89 static struct ctx_info ctx_info;
90 static int cur_cursor = GLUT_CURSOR_INHERIT;
91 static int ignore_key_repeat;
93 static glut_cb cb_display;
94 static glut_cb cb_idle;
95 static glut_cb_reshape cb_reshape;
96 static glut_cb_state cb_vis, cb_entry;
97 static glut_cb_keyb cb_keydown, cb_keyup;
98 static glut_cb_special cb_skeydown, cb_skeyup;
99 static glut_cb_mouse cb_mouse;
100 static glut_cb_motion cb_motion, cb_passive;
101 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
102 static glut_cb_sbbutton cb_sball_button;
104 static int fullscreen;
105 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
107 static int win_width, win_height;
110 static int upd_pending;
113 void glutInit(int *argc, char **argv)
119 if(!(dpy = XOpenDisplay(0))) {
120 panic("Failed to connect to the X server\n");
122 scr = DefaultScreen(dpy);
123 root = RootWindow(dpy, scr);
124 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
125 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
126 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
127 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
128 if(have_netwm_fullscr()) {
129 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
132 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
133 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
134 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
135 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
137 evmask = ExposureMask | StructureNotifyMask;
139 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
140 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
141 XFreePixmap(dpy, blankpix);
148 hinst = GetModuleHandle(0);
150 wc.cbSize = sizeof wc;
151 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
152 wc.hCursor = LoadCursor(0, IDC_ARROW);
153 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
154 wc.hInstance = hinst;
155 wc.lpfnWndProc = handle_message;
156 wc.lpszClassName = "MiniGLUT";
157 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
158 if(!RegisterClassEx(&wc)) {
159 panic("Failed to register \"MiniGLUT\" window class\n");
163 get_screen_size(&init_x, &init_y);
170 void glutInitWindowPosition(int x, int y)
176 void glutInitWindowSize(int xsz, int ysz)
182 void glutInitDisplayMode(unsigned int mode)
187 void glutCreateWindow(const char *title)
189 create_window(title);
197 void glutMainLoop(void)
204 void glutPostRedisplay(void)
209 void glutIgnoreKeyRepeat(int ignore)
211 ignore_key_repeat = ignore;
214 #define UPD_EVMASK(x) \
221 if(win) XSelectInput(dpy, win, evmask); \
225 void glutIdleFunc(glut_cb func)
230 void glutDisplayFunc(glut_cb func)
235 void glutReshapeFunc(glut_cb_reshape func)
240 void glutVisibilityFunc(glut_cb_state func)
244 UPD_EVMASK(VisibilityChangeMask);
248 void glutEntryFunc(glut_cb_state func)
252 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
256 void glutKeyboardFunc(glut_cb_keyb func)
260 UPD_EVMASK(KeyPressMask);
264 void glutKeyboardUpFunc(glut_cb_keyb func)
268 UPD_EVMASK(KeyReleaseMask);
272 void glutSpecialFunc(glut_cb_special func)
276 UPD_EVMASK(KeyPressMask);
280 void glutSpecialUpFunc(glut_cb_special func)
284 UPD_EVMASK(KeyReleaseMask);
288 void glutMouseFunc(glut_cb_mouse func)
292 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
296 void glutMotionFunc(glut_cb_motion func)
300 UPD_EVMASK(ButtonMotionMask);
304 void glutPassiveMotionFunc(glut_cb_motion func)
308 UPD_EVMASK(PointerMotionMask);
312 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
314 cb_sball_motion = func;
317 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
319 cb_sball_rotate = func;
322 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
324 cb_sball_button = func;
327 int glutGet(unsigned int s)
332 get_window_pos(&x, &y);
335 get_window_pos(&x, &y);
337 case GLUT_WINDOW_WIDTH:
338 get_window_size(&x, &y);
340 case GLUT_WINDOW_HEIGHT:
341 get_window_size(&x, &y);
343 case GLUT_WINDOW_BUFFER_SIZE:
344 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
345 case GLUT_WINDOW_STENCIL_SIZE:
346 return ctx_info.ssize;
347 case GLUT_WINDOW_DEPTH_SIZE:
348 return ctx_info.zsize;
349 case GLUT_WINDOW_RED_SIZE:
350 return ctx_info.rsize;
351 case GLUT_WINDOW_GREEN_SIZE:
352 return ctx_info.gsize;
353 case GLUT_WINDOW_BLUE_SIZE:
354 return ctx_info.bsize;
355 case GLUT_WINDOW_ALPHA_SIZE:
356 return ctx_info.asize;
357 case GLUT_WINDOW_DOUBLEBUFFER:
358 return ctx_info.dblbuf;
359 case GLUT_WINDOW_RGBA:
361 case GLUT_WINDOW_NUM_SAMPLES:
362 return ctx_info.samples;
363 case GLUT_WINDOW_STEREO:
364 return ctx_info.stereo;
365 case GLUT_WINDOW_SRGB:
366 return ctx_info.srgb;
367 case GLUT_WINDOW_CURSOR:
369 case GLUT_SCREEN_WIDTH:
370 get_screen_size(&x, &y);
372 case GLUT_SCREEN_HEIGHT:
373 get_screen_size(&x, &y);
375 case GLUT_INIT_DISPLAY_MODE:
377 case GLUT_INIT_WINDOW_X:
379 case GLUT_INIT_WINDOW_Y:
381 case GLUT_INIT_WINDOW_WIDTH:
383 case GLUT_INIT_WINDOW_HEIGHT:
385 case GLUT_ELAPSED_TIME:
393 int glutGetModifiers(void)
398 static int is_space(int c)
400 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
403 static const char *skip_space(const char *s)
405 while(*s && is_space(*s)) s++;
409 int glutExtensionSupported(char *ext)
411 const char *str, *eptr;
413 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
418 str = skip_space(str);
419 eptr = skip_space(ext);
420 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
424 if((!*str || is_space(*str)) && !*eptr) {
427 while(*str && !is_space(*str)) str++;
434 /* --------------- UNIX/X11 implementation ----------------- */
437 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
439 SPNAV_EVENT_BUTTON /* includes both press and release */
442 struct spnav_event_motion {
450 struct spnav_event_button {
458 struct spnav_event_motion motion;
459 struct spnav_event_button button;
463 static void handle_event(XEvent *ev);
465 static int spnav_window(Window win);
466 static int spnav_event(const XEvent *xev, union spnav_event *event);
467 static int spnav_remove_events(int type);
470 void glutMainLoopEvent(void)
475 panic("display callback not set");
478 if(!upd_pending && !cb_idle) {
479 XNextEvent(dpy, &ev);
483 while(XPending(dpy)) {
484 XNextEvent(dpy, &ev);
493 if(upd_pending && mapped) {
504 static void cleanup(void)
508 glXMakeCurrent(dpy, 0, 0);
509 XDestroyWindow(dpy, win);
513 static KeySym translate_keysym(KeySym sym)
534 static void handle_event(XEvent *ev)
537 union spnav_event sev;
546 case ConfigureNotify:
547 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
548 win_width = ev->xconfigure.width;
549 win_height = ev->xconfigure.height;
550 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
555 if(ev->xclient.message_type == xa_wm_proto) {
556 if(ev->xclient.data.l[0] == xa_wm_del_win) {
560 if(spnav_event(ev, &sev)) {
562 case SPNAV_EVENT_MOTION:
563 if(cb_sball_motion) {
564 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
566 if(cb_sball_rotate) {
567 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
569 spnav_remove_events(SPNAV_EVENT_MOTION);
572 case SPNAV_EVENT_BUTTON:
573 if(cb_sball_button) {
574 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
591 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
593 XPeekEvent(dpy, &next);
595 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
596 next.xkey.time == ev->xkey.time) {
597 /* this is a key-repeat event, ignore the release and consume
598 * the following press
600 XNextEvent(dpy, &next);
605 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
606 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
609 sym = translate_keysym(sym);
611 if(ev->type == KeyPress) {
612 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
614 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
617 if(ev->type == KeyPress) {
618 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
620 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
627 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
629 int bn = ev->xbutton.button - Button1;
630 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
631 ev->xbutton.x, ev->xbutton.y);
636 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
637 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
639 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
643 case VisibilityNotify:
645 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
649 if(cb_entry) cb_entry(GLUT_ENTERED);
652 if(cb_entry) cb_entry(GLUT_LEFT);
657 void glutSwapBuffers(void)
659 glXSwapBuffers(dpy, win);
663 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
664 * needs to resize the window to make it fullscreen. The way it does this is by
665 * querying the size of the root window (see get_screen_size), which in the
666 * case of multi-monitor setups will be the combined size of all monitors.
667 * This is problematic; the way to solve it is to use the XRandR extension, or
668 * the Xinerama extension, to figure out the dimensions of the correct video
669 * output, which would add potentially two extension support libraries to our
671 * Moreover, any X installation modern enough to support XR&R will almost
672 * certainly be running a window manager supporting the EHWM
673 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
674 * on manual resizing, and is used in preference if available, making this
675 * whole endeavor pointless.
676 * So I'll just leave it with set_fullscreen_mwm covering the entire
677 * multi-monitor area for now.
682 unsigned long functions;
683 unsigned long decorations;
685 unsigned long status;
688 #define MWM_HINTS_DECORATIONS 2
689 #define MWM_DECOR_ALL 1
691 static void set_fullscreen_mwm(int fs)
693 struct mwm_hints hints;
694 int scr_width, scr_height;
697 get_window_pos(&prev_win_x, &prev_win_y);
698 get_window_size(&prev_win_width, &prev_win_height);
699 get_screen_size(&scr_width, &scr_height);
701 hints.decorations = 0;
702 hints.flags = MWM_HINTS_DECORATIONS;
703 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
704 PropModeReplace, (unsigned char*)&hints, 5);
706 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
708 XDeleteProperty(dpy, win, xa_motif_wm_hints);
709 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
713 static int have_netwm_fullscr(void)
717 unsigned long i, count, rem;
719 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
722 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
723 &type, &fmt, &count, &rem, (unsigned char**)prop);
725 for(i=0; i<count; i++) {
726 if(prop[i] == xa_net_wm_state_fullscr) {
736 static void set_fullscreen_ewmh(int fs)
738 XClientMessageEvent msg = {0};
740 msg.type = ClientMessage;
742 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
744 msg.data.l[0] = fs ? 1 : 0;
745 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
747 msg.data.l[3] = 1; /* source regular application */
748 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
751 static void set_fullscreen(int fs)
753 if(fullscreen == fs) return;
755 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
756 set_fullscreen_ewmh(fs);
758 } else if(xa_motif_wm_hints) {
759 set_fullscreen_mwm(fs);
764 void glutPositionWindow(int x, int y)
767 XMoveWindow(dpy, win, x, y);
770 void glutReshapeWindow(int xsz, int ysz)
773 XResizeWindow(dpy, win, xsz, ysz);
776 void glutFullScreen(void)
781 void glutSetWindowTitle(const char *title)
784 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
787 XSetWMName(dpy, win, &tprop);
791 void glutSetIconTitle(const char *title)
794 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
797 XSetWMIconName(dpy, win, &tprop);
801 void glutSetCursor(int cidx)
806 case GLUT_CURSOR_LEFT_ARROW:
807 cur = XCreateFontCursor(dpy, XC_left_ptr);
809 case GLUT_CURSOR_INHERIT:
811 case GLUT_CURSOR_NONE:
818 XDefineCursor(dpy, win, cur);
822 void glutSetKeyRepeat(int repmode)
831 static XVisualInfo *choose_visual(unsigned int mode)
838 if(mode & GLUT_DOUBLE) {
839 *aptr++ = GLX_DOUBLEBUFFER;
842 if(mode & GLUT_INDEX) {
843 *aptr++ = GLX_BUFFER_SIZE;
847 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
848 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
849 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
851 if(mode & GLUT_ALPHA) {
852 *aptr++ = GLX_ALPHA_SIZE;
855 if(mode & GLUT_DEPTH) {
856 *aptr++ = GLX_DEPTH_SIZE;
859 if(mode & GLUT_STENCIL) {
860 *aptr++ = GLX_STENCIL_SIZE;
863 if(mode & GLUT_ACCUM) {
864 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
865 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
866 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
868 if(mode & GLUT_STEREO) {
869 *aptr++ = GLX_STEREO;
871 if(mode & GLUT_SRGB) {
872 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
874 if(mode & GLUT_MULTISAMPLE) {
875 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
877 *aptr++ = GLX_SAMPLES_ARB;
884 return glXChooseVisual(dpy, scr, attr);
886 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
895 static void create_window(const char *title)
897 XSetWindowAttributes xattr = {0};
899 unsigned int xattr_mask;
900 unsigned int mode = init_mode;
902 if(!(vi = choose_visual(mode))) {
904 if(!(vi = choose_visual(mode))) {
905 panic("Failed to find compatible visual\n");
909 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
911 panic("Failed to create OpenGL context\n");
914 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
915 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
916 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
917 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
918 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
919 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
920 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
921 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
922 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
923 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
925 xattr.background_pixel = BlackPixel(dpy, scr);
926 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
927 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
928 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
929 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
931 glXDestroyContext(dpy, ctx);
932 panic("Failed to create window\n");
936 XSelectInput(dpy, win, evmask);
940 glutSetWindowTitle(title);
941 glutSetIconTitle(title);
942 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
943 XMapWindow(dpy, win);
945 glXMakeCurrent(dpy, win, ctx);
948 static void get_window_pos(int *x, int *y)
951 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
954 static void get_window_size(int *w, int *h)
956 XWindowAttributes wattr;
957 XGetWindowAttributes(dpy, win, &wattr);
962 static void get_screen_size(int *scrw, int *scrh)
964 XWindowAttributes wattr;
965 XGetWindowAttributes(dpy, root, &wattr);
967 *scrh = wattr.height;
973 CMD_APP_WINDOW = 27695,
977 static Window get_daemon_window(Display *dpy);
978 static int catch_badwin(Display *dpy, XErrorEvent *err);
980 #define SPNAV_INITIALIZED (xa_motion_event)
982 static int spnav_window(Window win)
984 int (*prev_xerr_handler)(Display*, XErrorEvent*);
988 if(!SPNAV_INITIALIZED) {
992 if(!(daemon_win = get_daemon_window(dpy))) {
996 prev_xerr_handler = XSetErrorHandler(catch_badwin);
998 xev.type = ClientMessage;
999 xev.xclient.send_event = False;
1000 xev.xclient.display = dpy;
1001 xev.xclient.window = win;
1002 xev.xclient.message_type = xa_command_event;
1003 xev.xclient.format = 16;
1004 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1005 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1006 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1008 XSendEvent(dpy, daemon_win, False, 0, &xev);
1011 XSetErrorHandler(prev_xerr_handler);
1015 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1017 int evtype = *(int*)arg;
1019 if(xev->type != ClientMessage) {
1023 if(xev->xclient.message_type == xa_motion_event) {
1024 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1026 if(xev->xclient.message_type == xa_button_press_event ||
1027 xev->xclient.message_type == xa_button_release_event) {
1028 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1033 static int spnav_remove_events(int type)
1037 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1043 static int spnav_event(const XEvent *xev, union spnav_event *event)
1048 xmsg_type = xev->xclient.message_type;
1050 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1051 xmsg_type != xa_button_release_event) {
1055 if(xmsg_type == xa_motion_event) {
1056 event->type = SPNAV_EVENT_MOTION;
1057 event->motion.data = &event->motion.x;
1059 for(i=0; i<6; i++) {
1060 event->motion.data[i] = xev->xclient.data.s[i + 2];
1062 event->motion.period = xev->xclient.data.s[8];
1064 event->type = SPNAV_EVENT_BUTTON;
1065 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1066 event->button.bnum = xev->xclient.data.s[2];
1071 static int mglut_strcmp(const char *s1, const char *s2)
1073 while(*s1 && *s1 == *s2) {
1080 static Window get_daemon_window(Display *dpy)
1083 XTextProperty wname;
1086 unsigned long nitems, bytes_after;
1087 unsigned char *prop;
1089 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1090 &type, &fmt, &nitems, &bytes_after, &prop);
1095 win = *(Window*)prop;
1098 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1105 static int catch_badwin(Display *dpy, XErrorEvent *err)
1112 #endif /* BUILD_X11 */
1115 /* --------------- windows implementation ----------------- */
1117 static int reshape_pending;
1119 static void update_modkeys(void);
1120 static int translate_vkey(int vkey);
1121 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1123 #ifdef MINIGLUT_WINMAIN
1124 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1127 char *argv[] = { "miniglut.exe", 0 };
1128 return main(argc, argv);
1132 void glutMainLoopEvent(void)
1137 panic("display callback not set");
1140 if(reshape_pending && cb_reshape) {
1141 reshape_pending = 0;
1142 get_window_size(&win_width, &win_height);
1143 cb_reshape(win_width, win_height);
1146 if(!upd_pending && !cb_idle) {
1147 GetMessage(&msg, 0, 0, 0);
1148 TranslateMessage(&msg);
1149 DispatchMessage(&msg);
1152 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1153 TranslateMessage(&msg);
1154 DispatchMessage(&msg);
1162 if(upd_pending && mapped) {
1168 static void cleanup(void)
1171 wglMakeCurrent(dc, 0);
1172 wglDeleteContext(ctx);
1173 UnregisterClass("MiniGLUT", hinst);
1177 void glutSwapBuffers(void)
1182 void glutPositionWindow(int x, int y)
1185 unsigned int flags = SWP_SHOWWINDOW;
1188 rect.left = prev_win_x;
1189 rect.top = prev_win_y;
1190 rect.right = rect.left + prev_win_width;
1191 rect.bottom = rect.top + prev_win_height;
1192 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1194 flags |= SWP_FRAMECHANGED;
1196 GetWindowRect(win, &rect);
1198 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1201 void glutReshapeWindow(int xsz, int ysz)
1204 unsigned int flags = SWP_SHOWWINDOW;
1207 rect.left = prev_win_x;
1208 rect.top = prev_win_y;
1209 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1211 flags |= SWP_FRAMECHANGED;
1213 GetWindowRect(win, &rect);
1215 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1218 void glutFullScreen(void)
1221 int scr_width, scr_height;
1223 if(fullscreen) return;
1225 GetWindowRect(win, &rect);
1226 prev_win_x = rect.left;
1227 prev_win_y = rect.top;
1228 prev_win_width = rect.right - rect.left;
1229 prev_win_height = rect.bottom - rect.top;
1231 get_screen_size(&scr_width, &scr_height);
1233 SetWindowLong(win, GWL_STYLE, 0);
1234 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1239 void glutSetWindowTitle(const char *title)
1241 SetWindowText(win, title);
1244 void glutSetIconTitle(const char *title)
1248 void glutSetCursor(int cidx)
1251 case GLUT_CURSOR_NONE:
1254 case GLUT_CURSOR_INHERIT:
1255 case GLUT_CURSOR_LEFT_ARROW:
1257 SetCursor(LoadCursor(0, IDC_ARROW));
1262 void glutSetKeyRepeat(int repmode)
1266 #define WGL_DRAW_TO_WINDOW 0x2001
1267 #define WGL_SUPPORT_OPENGL 0x2010
1268 #define WGL_DOUBLE_BUFFER 0x2011
1269 #define WGL_STEREO 0x2012
1270 #define WGL_PIXEL_TYPE 0x2013
1271 #define WGL_COLOR_BITS 0x2014
1272 #define WGL_RED_BITS 0x2015
1273 #define WGL_GREEN_BITS 0x2017
1274 #define WGL_BLUE_BITS 0x2019
1275 #define WGL_ALPHA_BITS 0x201b
1276 #define WGL_ACCUM_BITS 0x201d
1277 #define WGL_DEPTH_BITS 0x2022
1278 #define WGL_STENCIL_BITS 0x2023
1280 #define WGL_TYPE_RGBA 0x202b
1281 #define WGL_TYPE_COLORINDEX 0x202c
1283 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1284 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1285 #define WGL_SAMPLES_ARB 0x2042
1287 static PROC wglChoosePixelFormat;
1288 static PROC wglGetPixelFormatAttribiv;
1290 #define ATTR(a, v) \
1291 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1293 static unsigned int choose_pixfmt(unsigned int mode)
1295 unsigned int num_pixfmt, pixfmt = 0;
1296 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1 };
1301 if(mode & GLUT_DOUBLE) {
1302 ATTR(WGL_DOUBLE_BUFFER, 1);
1305 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1306 ATTR(WGL_COLOR_BITS, 8);
1307 if(mode & GLUT_ALPHA) {
1308 ATTR(WGL_ALPHA_BITS, 4);
1310 if(mode & GLUT_DEPTH) {
1311 ATTR(WGL_DEPTH_BITS, 16);
1313 if(mode & GLUT_STENCIL) {
1314 ATTR(WGL_STENCIL_BITS, 1);
1316 if(mode & GLUT_ACCUM) {
1317 ATTR(WGL_ACCUM_BITS, 1);
1319 if(mode & GLUT_STEREO) {
1320 ATTR(WGL_STEREO, 1);
1322 if(mode & GLUT_SRGB) {
1323 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1325 if(mode & GLUT_MULTISAMPLE) {
1326 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1327 *aptr++ = WGL_SAMPLES_ARB;
1333 while((!wglChoosePixelFormat(dc, attr, 0, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1342 static PIXELFORMATDESCRIPTOR tmppfd = {
1343 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1344 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1345 PFD_MAIN_PLANE, 0, 0, 0, 0
1347 #define TMPCLASS "TempMiniGLUT"
1349 #define GETATTR(attr, vptr) \
1352 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1355 static int create_window_wglext(const char *title, int width, int height)
1357 WNDCLASSEX wc = {0};
1363 /* create a temporary window and GL context, just to query and retrieve
1364 * the wglChoosePixelFormatEXT function
1366 wc.cbSize = sizeof wc;
1367 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1368 wc.hCursor = LoadCursor(0, IDC_ARROW);
1369 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1370 wc.hInstance = hinst;
1371 wc.lpfnWndProc = DefWindowProc;
1372 wc.lpszClassName = TMPCLASS;
1373 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1374 if(!RegisterClassEx(&wc)) {
1377 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1378 width, height, 0, 0, hinst, 0))) {
1381 tmpdc = GetDC(tmpwin);
1383 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1384 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1385 !(tmpctx = wglCreateContext(tmpdc))) {
1388 wglMakeCurrent(tmpdc, tmpctx);
1390 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1391 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1394 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1398 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1402 wglMakeCurrent(0, 0);
1403 wglDeleteContext(tmpctx);
1404 DestroyWindow(tmpwin);
1405 UnregisterClass(TMPCLASS, hinst);
1407 /* create the real window and context */
1408 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1409 init_y, width, height, 0, 0, hinst, 0))) {
1410 panic("Failed to create window\n");
1414 if(!(pixfmt = choose_pixfmt(init_mode))) {
1415 panic("Failed to find suitable pixel format\n");
1417 if(!SetPixelFormat(dc, pixfmt, &tmppfd)) {
1418 panic("Failed to set the selected pixel format\n");
1420 if(!(ctx = wglCreateContext(dc))) {
1421 panic("Failed to create the OpenGL context\n");
1423 wglMakeCurrent(dc, ctx);
1425 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1426 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1427 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1428 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1429 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1430 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1431 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1432 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1433 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1438 wglMakeCurrent(0, 0);
1439 wglDeleteContext(tmpctx);
1442 DestroyWindow(tmpwin);
1444 UnregisterClass(TMPCLASS, hinst);
1449 static void create_window(const char *title)
1452 PIXELFORMATDESCRIPTOR pfd = {0};
1458 rect.right = init_x + init_width;
1459 rect.bottom = init_y + init_height;
1460 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1461 width = rect.right - rect.left;
1462 height = rect.bottom - rect.top;
1464 if(create_window_wglext(title, width, height) == -1) {
1466 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1467 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1468 panic("Failed to create window\n");
1472 pfd.nSize = sizeof pfd;
1474 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1475 if(init_mode & GLUT_STEREO) {
1476 pfd.dwFlags |= PFD_STEREO;
1478 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1479 pfd.cColorBits = 24;
1480 if(init_mode & GLUT_ALPHA) {
1483 if(init_mode & GLUT_ACCUM) {
1484 pfd.cAccumBits = 24;
1486 if(init_mode & GLUT_DEPTH) {
1487 pfd.cDepthBits = 24;
1489 if(init_mode & GLUT_STENCIL) {
1490 pfd.cStencilBits = 8;
1492 pfd.iLayerType = PFD_MAIN_PLANE;
1494 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1495 panic("Failed to find suitable pixel format\n");
1497 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1498 panic("Failed to set the selected pixel format\n");
1500 if(!(ctx = wglCreateContext(dc))) {
1501 panic("Failed to create the OpenGL context\n");
1503 wglMakeCurrent(dc, ctx);
1505 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1506 ctx_info.rsize = pfd.cRedBits;
1507 ctx_info.gsize = pfd.cGreenBits;
1508 ctx_info.bsize = pfd.cBlueBits;
1509 ctx_info.asize = pfd.cAlphaBits;
1510 ctx_info.zsize = pfd.cDepthBits;
1511 ctx_info.ssize = pfd.cStencilBits;
1512 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1513 ctx_info.samples = 0;
1518 SetForegroundWindow(win);
1521 reshape_pending = 1;
1524 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1526 static int mouse_x, mouse_y;
1531 if(win) DestroyWindow(win);
1542 ValidateRect(win, 0);
1546 x = lparam & 0xffff;
1548 if(x != win_width && y != win_height) {
1552 reshape_pending = 0;
1553 cb_reshape(win_width, win_height);
1560 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1566 key = translate_vkey(wparam);
1569 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1573 cb_skeydown(key, mouse_x, mouse_y);
1581 key = translate_vkey(wparam);
1584 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1588 cb_skeyup(key, mouse_x, mouse_y);
1593 case WM_LBUTTONDOWN:
1594 handle_mbutton(0, 1, wparam, lparam);
1596 case WM_MBUTTONDOWN:
1597 handle_mbutton(1, 1, wparam, lparam);
1599 case WM_RBUTTONDOWN:
1600 handle_mbutton(2, 1, wparam, lparam);
1603 handle_mbutton(0, 0, wparam, lparam);
1606 handle_mbutton(1, 0, wparam, lparam);
1609 handle_mbutton(2, 0, wparam, lparam);
1613 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1614 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1616 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1622 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1626 return DefWindowProc(win, msg, wparam, lparam);
1632 static void update_modkeys(void)
1634 if(GetKeyState(VK_SHIFT) & 0x8000) {
1635 modstate |= GLUT_ACTIVE_SHIFT;
1637 modstate &= ~GLUT_ACTIVE_SHIFT;
1639 if(GetKeyState(VK_CONTROL) & 0x8000) {
1640 modstate |= GLUT_ACTIVE_CTRL;
1642 modstate &= ~GLUT_ACTIVE_CTRL;
1644 if(GetKeyState(VK_MENU) & 0x8000) {
1645 modstate |= GLUT_ACTIVE_ALT;
1647 modstate &= ~GLUT_ACTIVE_ALT;
1651 static int translate_vkey(int vkey)
1654 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1655 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1656 case VK_END: return GLUT_KEY_END;
1657 case VK_HOME: return GLUT_KEY_HOME;
1658 case VK_LEFT: return GLUT_KEY_LEFT;
1659 case VK_UP: return GLUT_KEY_UP;
1660 case VK_RIGHT: return GLUT_KEY_RIGHT;
1661 case VK_DOWN: return GLUT_KEY_DOWN;
1666 if(vkey >= 'A' && vkey <= 'Z') {
1668 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1669 vkey -= VK_F1 + GLUT_KEY_F1;
1675 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1682 x = lparam & 0xffff;
1684 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1688 static void get_window_pos(int *x, int *y)
1691 GetWindowRect(win, &rect);
1696 static void get_window_size(int *w, int *h)
1699 GetClientRect(win, &rect);
1700 *w = rect.right - rect.left;
1701 *h = rect.bottom - rect.top;
1704 static void get_screen_size(int *scrw, int *scrh)
1706 *scrw = GetSystemMetrics(SM_CXSCREEN);
1707 *scrh = GetSystemMetrics(SM_CYSCREEN);
1709 #endif /* BUILD_WIN32 */
1711 #if defined(__unix__) || defined(__APPLE__)
1712 #include <sys/time.h>
1714 #ifdef MINIGLUT_USE_LIBC
1715 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1717 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1720 static long get_msec(void)
1722 static struct timeval tv0;
1725 sys_gettimeofday(&tv, 0);
1726 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1730 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1734 static long get_msec(void)
1739 #ifdef MINIGLUT_NO_WINMM
1740 tm = GetTickCount();
1752 static void panic(const char *msg)
1754 const char *end = msg;
1756 sys_write(2, msg, end - msg);
1761 #ifdef MINIGLUT_USE_LIBC
1767 static void sys_exit(int status)
1772 static int sys_write(int fd, const void *buf, int count)
1774 return write(fd, buf, count);
1777 #else /* !MINIGLUT_USE_LIBC */
1781 static void sys_exit(int status)
1785 :: "a"(60), "D"(status));
1787 static int sys_write(int fd, const void *buf, int count)
1793 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1796 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1802 : "a"(96), "D"(tv), "S"(tz));
1805 #endif /* __x86_64__ */
1807 static void sys_exit(int status)
1811 :: "a"(1), "b"(status));
1813 static int sys_write(int fd, const void *buf, int count)
1819 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1822 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1828 : "a"(78), "b"(tv), "c"(tz));
1831 #endif /* __i386__ */
1832 #endif /* __linux__ */
1835 static void sys_exit(int status)
1837 ExitProcess(status);
1839 static int sys_write(int fd, const void *buf, int count)
1841 unsigned long wrsz = 0;
1843 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1844 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1850 #endif /* !MINIGLUT_USE_LIBC */
1853 /* ----------------- primitives ------------------ */
1854 #ifdef MINIGLUT_USE_LIBC
1858 void mglut_sincos(float angle, float *sptr, float *cptr)
1864 float mglut_atan(float x)
1869 #else /* !MINIGLUT_USE_LIBC */
1872 void mglut_sincos(float angle, float *sptr, float *cptr)
1879 : "=m"(*sptr), "=m"(*cptr)
1884 float mglut_atan(float x)
1900 void mglut_sincos(float angle, float *sptr, float *cptr)
1913 float mglut_atan(float x)
1927 #pragma aux mglut_sincos = \
1929 "fstp dword ptr [edx]" \
1930 "fstp dword ptr [eax]" \
1931 parm[8087][eax][edx] \
1934 #pragma aux mglut_atan = \
1940 #endif /* __WATCOMC__ */
1942 #endif /* !MINIGLUT_USE_LIBC */
1944 #define PI 3.1415926536f
1946 void glutSolidSphere(float rad, int slices, int stacks)
1949 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1950 float du = 1.0f / (float)slices;
1951 float dv = 1.0f / (float)stacks;
1954 for(i=0; i<stacks; i++) {
1956 for(j=0; j<slices; j++) {
1958 for(k=0; k<4; k++) {
1959 gray = k ^ (k >> 1);
1960 s = gray & 1 ? u + du : u;
1961 t = gray & 2 ? v + dv : v;
1962 theta = s * PI * 2.0f;
1964 mglut_sincos(theta, &sintheta, &costheta);
1965 mglut_sincos(phi, &sinphi, &cosphi);
1966 x = sintheta * sinphi;
1967 y = costheta * sinphi;
1972 glNormal3f(x, y, z);
1973 glVertex3f(x * rad, y * rad, z * rad);
1980 void glutWireSphere(float rad, int slices, int stacks)
1982 glPushAttrib(GL_POLYGON_BIT);
1983 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1984 glutSolidSphere(rad, slices, stacks);
1988 void glutSolidCube(float sz)
1990 int i, j, idx, gray, flip, rotx;
1991 float vpos[3], norm[3];
1992 float rad = sz * 0.5f;
1995 for(i=0; i<6; i++) {
1998 idx = (~i & 2) - rotx;
1999 norm[0] = norm[1] = norm[2] = 0.0f;
2000 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2002 vpos[idx] = norm[idx] * rad;
2003 for(j=0; j<4; j++) {
2004 gray = j ^ (j >> 1);
2005 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2006 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2007 glTexCoord2f(gray & 1, gray >> 1);
2014 void glutWireCube(float sz)
2016 glPushAttrib(GL_POLYGON_BIT);
2017 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2022 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2025 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2026 float du = 1.0f / (float)slices;
2027 float dv = 1.0f / (float)stacks;
2030 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2031 mglut_sincos(phi, &sinphi, &cosphi);
2034 for(i=0; i<stacks; i++) {
2036 for(j=0; j<slices; j++) {
2038 for(k=0; k<4; k++) {
2039 gray = k ^ (k >> 1);
2040 s = gray & 2 ? u + du : u;
2041 t = gray & 1 ? v + dv : v;
2042 rad = rbot + (rtop - rbot) * t;
2043 theta = s * PI * 2.0f;
2044 mglut_sincos(theta, &sintheta, &costheta);
2046 x = sintheta * cosphi;
2047 y = costheta * cosphi;
2052 glNormal3f(x, y, z);
2053 glVertex3f(sintheta * rad, costheta * rad, t * height);
2060 void glutSolidCone(float base, float height, int slices, int stacks)
2062 draw_cylinder(base, 0, height, slices, stacks);
2065 void glutWireCone(float base, float height, int slices, int stacks)
2067 glPushAttrib(GL_POLYGON_BIT);
2068 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2069 glutSolidCone(base, height, slices, stacks);
2073 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2075 draw_cylinder(rad, rad, height, slices, stacks);
2078 void glutWireCylinder(float rad, float height, int slices, int stacks)
2080 glPushAttrib(GL_POLYGON_BIT);
2081 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2082 glutSolidCylinder(rad, height, slices, stacks);
2086 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2089 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2090 float du = 1.0f / (float)rings;
2091 float dv = 1.0f / (float)sides;
2094 for(i=0; i<rings; i++) {
2096 for(j=0; j<sides; j++) {
2098 for(k=0; k<4; k++) {
2099 gray = k ^ (k >> 1);
2100 s = gray & 1 ? u + du : u;
2101 t = gray & 2 ? v + dv : v;
2102 theta = s * PI * 2.0f;
2103 phi = t * PI * 2.0f;
2104 mglut_sincos(theta, &sintheta, &costheta);
2105 mglut_sincos(phi, &sinphi, &cosphi);
2106 x = sintheta * sinphi;
2107 y = costheta * sinphi;
2112 glNormal3f(x, y, z);
2114 x = x * inner_rad + sintheta * outer_rad;
2115 y = y * inner_rad + costheta * outer_rad;
2117 glVertex3f(x, y, z);
2124 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2126 glPushAttrib(GL_POLYGON_BIT);
2127 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2128 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2132 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2133 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2135 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2137 #define PATCH_SUBDIV 7
2139 static float teapot_part_flip[] = {
2140 1, 1, 1, 1, /* rim flip */
2141 1, 1, 1, 1, /* body1 flip */
2142 1, 1, 1, 1, /* body2 flip */
2143 1, 1, 1, 1, /* lid patch 1 flip */
2144 1, 1, 1, 1, /* lid patch 2 flip */
2145 1, -1, /* handle 1 flip */
2146 1, -1, /* handle 2 flip */
2147 1, -1, /* spout 1 flip */
2148 1, -1, /* spout 2 flip */
2149 1, 1, 1, 1 /* bottom flip */
2152 static float teapot_part_rot[] = {
2153 0, 90, 180, 270, /* rim rotations */
2154 0, 90, 180, 270, /* body patch 1 rotations */
2155 0, 90, 180, 270, /* body patch 2 rotations */
2156 0, 90, 180, 270, /* lid patch 1 rotations */
2157 0, 90, 180, 270, /* lid patch 2 rotations */
2158 0, 0, /* handle 1 rotations */
2159 0, 0, /* handle 2 rotations */
2160 0, 0, /* spout 1 rotations */
2161 0, 0, /* spout 2 rotations */
2162 0, 90, 180, 270 /* bottom rotations */
2166 static int teapot_index[] = {
2168 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2169 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2170 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2171 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2173 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2174 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2175 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2176 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2178 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2179 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2180 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2181 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2183 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2184 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2185 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2186 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2188 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2189 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2190 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2191 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2193 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2194 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2196 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2197 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2199 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2200 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2202 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2203 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2205 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2206 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2207 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2208 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2212 static float teapot_verts[][3] = {
2213 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2214 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2215 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2216 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2217 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2218 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2219 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2220 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2221 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2222 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2223 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2224 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2225 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2226 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2227 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2228 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2229 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2230 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2231 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2232 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2233 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2234 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2235 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2236 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2237 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2238 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2239 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2240 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2241 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2242 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2243 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2244 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2245 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2246 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2247 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2248 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2249 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2250 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2251 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2252 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2253 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2254 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2255 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2256 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2257 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2258 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2259 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2260 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2261 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2262 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2263 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2264 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2265 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2266 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2267 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2268 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2269 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2270 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2271 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2272 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2273 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2274 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2275 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2276 { 0.8400, -1.5000, 0.07500 }
2279 static void draw_patch(int *index, int flip, float scale);
2280 static float bernstein(int i, float x);
2282 void glutSolidTeapot(float size)
2288 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2289 float flip = teapot_part_flip[i];
2290 float rot = teapot_part_rot[i];
2292 glMatrixMode(GL_MODELVIEW);
2294 glTranslatef(0, -3.15 * size * 0.5, 0);
2295 glRotatef(rot, 0, 1, 0);
2296 glScalef(1, 1, flip);
2297 glRotatef(-90, 1, 0, 0);
2299 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2305 void glutWireTeapot(float size)
2307 glPushAttrib(GL_POLYGON_BIT);
2308 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2309 glutSolidTeapot(size);
2314 static void bezier_patch(float *res, float *cp, float u, float v)
2318 res[0] = res[1] = res[2] = 0.0f;
2320 for(j=0; j<4; j++) {
2321 for(i=0; i<4; i++) {
2322 float bu = bernstein(i, u);
2323 float bv = bernstein(j, v);
2325 res[0] += cp[0] * bu * bv;
2326 res[1] += cp[1] * bu * bv;
2327 res[2] += cp[2] * bu * bv;
2334 static float rsqrt(float x)
2336 float xhalf = x * 0.5f;
2338 i = 0x5f3759df - (i >> 1);
2340 x = x * (1.5f - xhalf * x * x);
2345 #define CROSS(res, a, b) \
2347 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2348 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2349 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2352 #define NORMALIZE(v) \
2354 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2362 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2364 float tang[3], bitan[3], tmp[3];
2366 bezier_patch(tang, cp, u + DT, v);
2367 bezier_patch(tmp, cp, u - DT, v);
2372 bezier_patch(bitan, cp, u, v + DT);
2373 bezier_patch(tmp, cp, u, v - DT);
2378 CROSS(res, tang, bitan);
2384 static float bernstein(int i, float x)
2386 float invx = 1.0f - x;
2390 return invx * invx * invx;
2392 return 3.0f * x * invx * invx;
2394 return 3.0f * x * x * invx;
2403 static void draw_patch(int *index, int flip, float scale)
2405 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2406 static const float voffs[4] = {0, 1, 1, 0};
2412 float du = 1.0 / PATCH_SUBDIV;
2413 float dv = 1.0 / PATCH_SUBDIV;
2415 /* collect control points */
2416 for(i=0; i<16; i++) {
2417 cp[i * 3] = teapot_verts[index[i]][0];
2418 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2419 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2426 for(i=0; i<PATCH_SUBDIV; i++) {
2428 for(j=0; j<PATCH_SUBDIV; j++) {
2430 for(k=0; k<4; k++) {
2431 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2433 /* top/bottom normal hack */
2437 } else if(pt[2] < 0.00001) {
2441 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2446 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);