2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020-2022 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/>.
18 #if defined(unix) || defined(__unix__)
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;
39 static GLXContext ctx;
40 static Atom xa_wm_proto, xa_wm_del_win;
41 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
42 static Atom xa_motif_wm_hints;
43 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
44 static unsigned int evmask;
45 static Cursor blank_cursor;
47 static int have_netwm_fullscr(void);
54 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
56 static HINSTANCE hinst;
62 #error unsupported platform
68 int rsize, gsize, bsize, asize;
76 static void cleanup(void);
77 static void create_window(const char *title);
78 static void get_window_pos(int *x, int *y);
79 static void get_window_size(int *w, int *h);
80 static void get_screen_size(int *scrw, int *scrh);
82 static long get_msec(void);
83 static void panic(const char *msg);
84 static void sys_exit(int status);
85 static int sys_write(int fd, const void *buf, int count);
88 static int init_x = -1, init_y, init_width = 256, init_height = 256;
89 static unsigned int init_mode;
91 static struct ctx_info ctx_info;
92 static int cur_cursor = GLUT_CURSOR_INHERIT;
93 static int ignore_key_repeat;
95 static glut_cb cb_display;
96 static glut_cb cb_idle;
97 static glut_cb_reshape cb_reshape;
98 static glut_cb_state cb_vis, cb_entry;
99 static glut_cb_keyb cb_keydown, cb_keyup;
100 static glut_cb_special cb_skeydown, cb_skeyup;
101 static glut_cb_mouse cb_mouse;
102 static glut_cb_motion cb_motion, cb_passive;
103 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
104 static glut_cb_sbbutton cb_sball_button;
106 static int fullscreen;
107 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
109 static int win_width, win_height;
112 static int upd_pending;
115 void glutInit(int *argc, char **argv)
121 if(!(dpy = XOpenDisplay(0))) {
122 panic("Failed to connect to the X server\n");
124 scr = DefaultScreen(dpy);
125 root = RootWindow(dpy, scr);
126 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
127 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
128 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
129 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
130 if(have_netwm_fullscr()) {
131 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
134 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
135 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
136 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
137 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
139 evmask = ExposureMask | StructureNotifyMask;
141 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
142 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
143 XFreePixmap(dpy, blankpix);
150 hinst = GetModuleHandle(0);
152 wc.cbSize = sizeof wc;
153 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
154 wc.hCursor = LoadCursor(0, IDC_ARROW);
155 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
156 wc.hInstance = hinst;
157 wc.lpfnWndProc = handle_message;
158 wc.lpszClassName = "MiniGLUT";
159 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
160 if(!RegisterClassEx(&wc)) {
161 panic("Failed to register \"MiniGLUT\" window class\n");
165 get_screen_size(&init_x, &init_y);
172 void glutInitWindowPosition(int x, int y)
178 void glutInitWindowSize(int xsz, int ysz)
184 void glutInitDisplayMode(unsigned int mode)
189 void glutCreateWindow(const char *title)
191 create_window(title);
199 void glutMainLoop(void)
206 void glutPostRedisplay(void)
211 void glutIgnoreKeyRepeat(int ignore)
213 ignore_key_repeat = ignore;
216 #define UPD_EVMASK(x) \
223 if(win) XSelectInput(dpy, win, evmask); \
227 void glutIdleFunc(glut_cb func)
232 void glutDisplayFunc(glut_cb func)
237 void glutReshapeFunc(glut_cb_reshape func)
242 void glutVisibilityFunc(glut_cb_state func)
246 UPD_EVMASK(VisibilityChangeMask);
250 void glutEntryFunc(glut_cb_state func)
254 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
258 void glutKeyboardFunc(glut_cb_keyb func)
262 UPD_EVMASK(KeyPressMask);
266 void glutKeyboardUpFunc(glut_cb_keyb func)
270 UPD_EVMASK(KeyReleaseMask);
274 void glutSpecialFunc(glut_cb_special func)
278 UPD_EVMASK(KeyPressMask);
282 void glutSpecialUpFunc(glut_cb_special func)
286 UPD_EVMASK(KeyReleaseMask);
290 void glutMouseFunc(glut_cb_mouse func)
294 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
298 void glutMotionFunc(glut_cb_motion func)
302 UPD_EVMASK(ButtonMotionMask);
306 void glutPassiveMotionFunc(glut_cb_motion func)
310 UPD_EVMASK(PointerMotionMask);
314 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
316 cb_sball_motion = func;
319 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
321 cb_sball_rotate = func;
324 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
326 cb_sball_button = func;
329 int glutGet(unsigned int s)
334 get_window_pos(&x, &y);
337 get_window_pos(&x, &y);
339 case GLUT_WINDOW_WIDTH:
340 get_window_size(&x, &y);
342 case GLUT_WINDOW_HEIGHT:
343 get_window_size(&x, &y);
345 case GLUT_WINDOW_BUFFER_SIZE:
346 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
347 case GLUT_WINDOW_STENCIL_SIZE:
348 return ctx_info.ssize;
349 case GLUT_WINDOW_DEPTH_SIZE:
350 return ctx_info.zsize;
351 case GLUT_WINDOW_RED_SIZE:
352 return ctx_info.rsize;
353 case GLUT_WINDOW_GREEN_SIZE:
354 return ctx_info.gsize;
355 case GLUT_WINDOW_BLUE_SIZE:
356 return ctx_info.bsize;
357 case GLUT_WINDOW_ALPHA_SIZE:
358 return ctx_info.asize;
359 case GLUT_WINDOW_DOUBLEBUFFER:
360 return ctx_info.dblbuf;
361 case GLUT_WINDOW_RGBA:
363 case GLUT_WINDOW_NUM_SAMPLES:
364 return ctx_info.samples;
365 case GLUT_WINDOW_STEREO:
366 return ctx_info.stereo;
367 case GLUT_WINDOW_SRGB:
368 return ctx_info.srgb;
369 case GLUT_WINDOW_CURSOR:
371 case GLUT_WINDOW_COLORMAP_SIZE:
373 case GLUT_SCREEN_WIDTH:
374 get_screen_size(&x, &y);
376 case GLUT_SCREEN_HEIGHT:
377 get_screen_size(&x, &y);
379 case GLUT_INIT_DISPLAY_MODE:
381 case GLUT_INIT_WINDOW_X:
383 case GLUT_INIT_WINDOW_Y:
385 case GLUT_INIT_WINDOW_WIDTH:
387 case GLUT_INIT_WINDOW_HEIGHT:
389 case GLUT_ELAPSED_TIME:
397 int glutGetModifiers(void)
402 static int is_space(int c)
404 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
407 static const char *skip_space(const char *s)
409 while(*s && is_space(*s)) s++;
413 int glutExtensionSupported(char *ext)
415 const char *str, *eptr;
417 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
422 str = skip_space(str);
423 eptr = skip_space(ext);
424 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
428 if((!*str || is_space(*str)) && !*eptr) {
431 while(*str && !is_space(*str)) str++;
438 /* --------------- UNIX/X11 implementation ----------------- */
441 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
443 SPNAV_EVENT_BUTTON /* includes both press and release */
446 struct spnav_event_motion {
454 struct spnav_event_button {
462 struct spnav_event_motion motion;
463 struct spnav_event_button button;
467 static void handle_event(XEvent *ev);
469 static int spnav_window(Window win);
470 static int spnav_event(const XEvent *xev, union spnav_event *event);
471 static int spnav_remove_events(int type);
474 void glutMainLoopEvent(void)
479 panic("display callback not set");
482 if(!upd_pending && !cb_idle) {
483 XNextEvent(dpy, &ev);
487 while(XPending(dpy)) {
488 XNextEvent(dpy, &ev);
497 if(upd_pending && mapped) {
508 static void cleanup(void)
512 glXMakeCurrent(dpy, 0, 0);
513 XDestroyWindow(dpy, win);
517 static KeySym translate_keysym(KeySym sym)
538 static void handle_event(XEvent *ev)
541 union spnav_event sev;
550 case ConfigureNotify:
551 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
552 win_width = ev->xconfigure.width;
553 win_height = ev->xconfigure.height;
554 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
559 if(ev->xclient.message_type == xa_wm_proto) {
560 if(ev->xclient.data.l[0] == xa_wm_del_win) {
564 if(spnav_event(ev, &sev)) {
566 case SPNAV_EVENT_MOTION:
567 if(cb_sball_motion) {
568 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
570 if(cb_sball_rotate) {
571 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
573 spnav_remove_events(SPNAV_EVENT_MOTION);
576 case SPNAV_EVENT_BUTTON:
577 if(cb_sball_button) {
578 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
595 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
597 XPeekEvent(dpy, &next);
599 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
600 next.xkey.time == ev->xkey.time) {
601 /* this is a key-repeat event, ignore the release and consume
602 * the following press
604 XNextEvent(dpy, &next);
609 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
610 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
613 sym = translate_keysym(sym);
615 if(ev->type == KeyPress) {
616 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
618 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
621 if(ev->type == KeyPress) {
622 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
624 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
631 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
633 int bn = ev->xbutton.button - Button1;
634 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
635 ev->xbutton.x, ev->xbutton.y);
640 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
641 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
643 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
647 case VisibilityNotify:
649 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
653 if(cb_entry) cb_entry(GLUT_ENTERED);
656 if(cb_entry) cb_entry(GLUT_LEFT);
661 void glutSwapBuffers(void)
663 glXSwapBuffers(dpy, win);
667 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
668 * needs to resize the window to make it fullscreen. The way it does this is by
669 * querying the size of the root window (see get_screen_size), which in the
670 * case of multi-monitor setups will be the combined size of all monitors.
671 * This is problematic; the way to solve it is to use the XRandR extension, or
672 * the Xinerama extension, to figure out the dimensions of the correct video
673 * output, which would add potentially two extension support libraries to our
675 * Moreover, any X installation modern enough to support XR&R will almost
676 * certainly be running a window manager supporting the EHWM
677 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
678 * on manual resizing, and is used in preference if available, making this
679 * whole endeavor pointless.
680 * So I'll just leave it with set_fullscreen_mwm covering the entire
681 * multi-monitor area for now.
686 unsigned long functions;
687 unsigned long decorations;
689 unsigned long status;
692 #define MWM_HINTS_DECORATIONS 2
693 #define MWM_DECOR_ALL 1
695 static void set_fullscreen_mwm(int fs)
697 struct mwm_hints hints;
698 int scr_width, scr_height;
701 get_window_pos(&prev_win_x, &prev_win_y);
702 get_window_size(&prev_win_width, &prev_win_height);
703 get_screen_size(&scr_width, &scr_height);
705 hints.decorations = 0;
706 hints.flags = MWM_HINTS_DECORATIONS;
707 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
708 PropModeReplace, (unsigned char*)&hints, 5);
710 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
712 XDeleteProperty(dpy, win, xa_motif_wm_hints);
713 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
717 static int have_netwm_fullscr(void)
721 unsigned long i, count, rem;
723 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
726 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
727 &type, &fmt, &count, &rem, (unsigned char**)&prop);
729 for(i=0; i<count; i++) {
730 if(prop[i] == xa_net_wm_state_fullscr) {
742 static void set_fullscreen_ewmh(int fs)
744 XClientMessageEvent msg = {0};
746 msg.type = ClientMessage;
748 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
750 msg.data.l[0] = fs ? 1 : 0;
751 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
753 msg.data.l[3] = 1; /* source regular application */
754 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
757 static void set_fullscreen(int fs)
759 if(fullscreen == fs) return;
761 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
762 set_fullscreen_ewmh(fs);
764 } else if(xa_motif_wm_hints) {
765 set_fullscreen_mwm(fs);
770 void glutPositionWindow(int x, int y)
773 XMoveWindow(dpy, win, x, y);
776 void glutReshapeWindow(int xsz, int ysz)
779 XResizeWindow(dpy, win, xsz, ysz);
782 void glutFullScreen(void)
787 void glutSetWindowTitle(const char *title)
790 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
793 XSetWMName(dpy, win, &tprop);
797 void glutSetIconTitle(const char *title)
800 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
803 XSetWMIconName(dpy, win, &tprop);
807 void glutSetCursor(int cidx)
812 case GLUT_CURSOR_LEFT_ARROW:
813 cur = XCreateFontCursor(dpy, XC_left_ptr);
815 case GLUT_CURSOR_INHERIT:
817 case GLUT_CURSOR_NONE:
824 XDefineCursor(dpy, win, cur);
828 void glutSetColor(int idx, float r, float g, float b)
832 if(idx >= 0 && idx < cmap_size) {
834 color.red = (unsigned short)(r * 65535.0f);
835 color.green = (unsigned short)(g * 65535.0f);
836 color.blue = (unsigned short)(b * 65535.0f);
837 color.flags = DoRed | DoGreen | DoBlue;
838 XStoreColor(dpy, cmap, &color);
842 float glutGetColor(int idx, int comp)
846 if(idx < 0 || idx >= cmap_size) {
851 XQueryColor(dpy, cmap, &color);
854 return color.red / 65535.0f;
856 return color.green / 65535.0f;
858 return color.blue / 65535.0f;
865 void glutSetKeyRepeat(int repmode)
874 static XVisualInfo *choose_visual(unsigned int mode)
881 if(mode & GLUT_DOUBLE) {
882 *aptr++ = GLX_DOUBLEBUFFER;
885 if(mode & GLUT_INDEX) {
886 *aptr++ = GLX_BUFFER_SIZE;
890 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
891 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
892 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
894 if(mode & GLUT_ALPHA) {
895 *aptr++ = GLX_ALPHA_SIZE;
898 if(mode & GLUT_DEPTH) {
899 *aptr++ = GLX_DEPTH_SIZE;
902 if(mode & GLUT_STENCIL) {
903 *aptr++ = GLX_STENCIL_SIZE;
906 if(mode & GLUT_ACCUM) {
907 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
908 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
909 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
911 if(mode & GLUT_STEREO) {
912 *aptr++ = GLX_STEREO;
914 if(mode & GLUT_SRGB) {
915 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
917 if(mode & GLUT_MULTISAMPLE) {
918 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
920 *aptr++ = GLX_SAMPLES_ARB;
927 return glXChooseVisual(dpy, scr, attr);
929 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
938 static void create_window(const char *title)
940 XSetWindowAttributes xattr = {0};
942 unsigned int xattr_mask;
943 unsigned int mode = init_mode;
945 if(!(vi = choose_visual(mode))) {
947 if(!(vi = choose_visual(mode))) {
948 panic("Failed to find compatible visual\n");
952 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
954 panic("Failed to create OpenGL context\n");
957 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
958 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
959 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
960 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
961 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
962 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
963 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
964 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
965 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
966 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
968 if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
970 glXDestroyContext(dpy, ctx);
971 panic("Failed to create colormap\n");
973 cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
975 xattr.background_pixel = BlackPixel(dpy, scr);
976 xattr.colormap = cmap;
977 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
978 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
979 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
981 glXDestroyContext(dpy, ctx);
982 XFreeColormap(dpy, cmap);
983 panic("Failed to create window\n");
987 XSelectInput(dpy, win, evmask);
991 glutSetWindowTitle(title);
992 glutSetIconTitle(title);
993 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
994 XMapWindow(dpy, win);
996 glXMakeCurrent(dpy, win, ctx);
999 static void get_window_pos(int *x, int *y)
1002 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1005 static void get_window_size(int *w, int *h)
1007 XWindowAttributes wattr;
1008 XGetWindowAttributes(dpy, win, &wattr);
1013 static void get_screen_size(int *scrw, int *scrh)
1015 XWindowAttributes wattr;
1016 XGetWindowAttributes(dpy, root, &wattr);
1017 *scrw = wattr.width;
1018 *scrh = wattr.height;
1024 CMD_APP_WINDOW = 27695,
1028 static Window get_daemon_window(Display *dpy);
1029 static int catch_badwin(Display *dpy, XErrorEvent *err);
1031 #define SPNAV_INITIALIZED (xa_motion_event)
1033 static int spnav_window(Window win)
1035 int (*prev_xerr_handler)(Display*, XErrorEvent*);
1039 if(!SPNAV_INITIALIZED) {
1043 if(!(daemon_win = get_daemon_window(dpy))) {
1047 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1049 xev.type = ClientMessage;
1050 xev.xclient.send_event = False;
1051 xev.xclient.display = dpy;
1052 xev.xclient.window = win;
1053 xev.xclient.message_type = xa_command_event;
1054 xev.xclient.format = 16;
1055 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1056 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1057 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1059 XSendEvent(dpy, daemon_win, False, 0, &xev);
1062 XSetErrorHandler(prev_xerr_handler);
1066 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1068 int evtype = *(int*)arg;
1070 if(xev->type != ClientMessage) {
1074 if(xev->xclient.message_type == xa_motion_event) {
1075 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1077 if(xev->xclient.message_type == xa_button_press_event ||
1078 xev->xclient.message_type == xa_button_release_event) {
1079 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1084 static int spnav_remove_events(int type)
1088 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1094 static int spnav_event(const XEvent *xev, union spnav_event *event)
1099 xmsg_type = xev->xclient.message_type;
1101 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1102 xmsg_type != xa_button_release_event) {
1106 if(xmsg_type == xa_motion_event) {
1107 event->type = SPNAV_EVENT_MOTION;
1108 event->motion.data = &event->motion.x;
1110 for(i=0; i<6; i++) {
1111 event->motion.data[i] = xev->xclient.data.s[i + 2];
1113 event->motion.period = xev->xclient.data.s[8];
1115 event->type = SPNAV_EVENT_BUTTON;
1116 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1117 event->button.bnum = xev->xclient.data.s[2];
1122 static int mglut_strcmp(const char *s1, const char *s2)
1124 while(*s1 && *s1 == *s2) {
1131 static Window get_daemon_window(Display *dpy)
1134 XTextProperty wname;
1137 unsigned long nitems, bytes_after;
1138 unsigned char *prop;
1140 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1141 &type, &fmt, &nitems, &bytes_after, &prop);
1146 win = *(Window*)prop;
1150 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1158 static int catch_badwin(Display *dpy, XErrorEvent *err)
1165 #endif /* BUILD_X11 */
1168 /* --------------- windows implementation ----------------- */
1170 static int reshape_pending;
1172 static void update_modkeys(void);
1173 static int translate_vkey(int vkey);
1174 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1176 #ifdef MINIGLUT_WINMAIN
1177 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1180 char *argv[] = { "miniglut.exe", 0 };
1181 return main(argc, argv);
1185 void glutMainLoopEvent(void)
1190 panic("display callback not set");
1193 if(reshape_pending && cb_reshape) {
1194 reshape_pending = 0;
1195 get_window_size(&win_width, &win_height);
1196 cb_reshape(win_width, win_height);
1199 if(!upd_pending && !cb_idle) {
1200 GetMessage(&msg, 0, 0, 0);
1201 TranslateMessage(&msg);
1202 DispatchMessage(&msg);
1205 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1206 TranslateMessage(&msg);
1207 DispatchMessage(&msg);
1215 if(upd_pending && mapped) {
1221 static void cleanup(void)
1224 wglMakeCurrent(dc, 0);
1225 wglDeleteContext(ctx);
1226 UnregisterClass("MiniGLUT", hinst);
1230 void glutSwapBuffers(void)
1235 void glutPositionWindow(int x, int y)
1238 unsigned int flags = SWP_SHOWWINDOW;
1241 rect.left = prev_win_x;
1242 rect.top = prev_win_y;
1243 rect.right = rect.left + prev_win_width;
1244 rect.bottom = rect.top + prev_win_height;
1245 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1247 flags |= SWP_FRAMECHANGED;
1249 GetWindowRect(win, &rect);
1251 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1254 void glutReshapeWindow(int xsz, int ysz)
1257 unsigned int flags = SWP_SHOWWINDOW;
1260 rect.left = prev_win_x;
1261 rect.top = prev_win_y;
1262 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1264 flags |= SWP_FRAMECHANGED;
1266 GetWindowRect(win, &rect);
1268 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1271 void glutFullScreen(void)
1274 int scr_width, scr_height;
1276 if(fullscreen) return;
1278 GetWindowRect(win, &rect);
1279 prev_win_x = rect.left;
1280 prev_win_y = rect.top;
1281 prev_win_width = rect.right - rect.left;
1282 prev_win_height = rect.bottom - rect.top;
1284 get_screen_size(&scr_width, &scr_height);
1286 SetWindowLong(win, GWL_STYLE, 0);
1287 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1292 void glutSetWindowTitle(const char *title)
1294 SetWindowText(win, title);
1297 void glutSetIconTitle(const char *title)
1301 void glutSetCursor(int cidx)
1304 case GLUT_CURSOR_NONE:
1307 case GLUT_CURSOR_INHERIT:
1308 case GLUT_CURSOR_LEFT_ARROW:
1310 SetCursor(LoadCursor(0, IDC_ARROW));
1315 void glutSetColor(int idx, float r, float g, float b)
1320 float glutGetColor(int idx, int comp)
1326 void glutSetKeyRepeat(int repmode)
1330 #define WGL_DRAW_TO_WINDOW 0x2001
1331 #define WGL_ACCELERATION 0x2003
1332 #define WGL_SUPPORT_OPENGL 0x2010
1333 #define WGL_DOUBLE_BUFFER 0x2011
1334 #define WGL_STEREO 0x2012
1335 #define WGL_PIXEL_TYPE 0x2013
1336 #define WGL_COLOR_BITS 0x2014
1337 #define WGL_RED_BITS 0x2015
1338 #define WGL_GREEN_BITS 0x2017
1339 #define WGL_BLUE_BITS 0x2019
1340 #define WGL_ALPHA_BITS 0x201b
1341 #define WGL_ACCUM_BITS 0x201d
1342 #define WGL_DEPTH_BITS 0x2022
1343 #define WGL_STENCIL_BITS 0x2023
1344 #define WGL_FULL_ACCELERATION 0x2027
1346 #define WGL_TYPE_RGBA 0x202b
1347 #define WGL_TYPE_COLORINDEX 0x202c
1349 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1350 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1351 #define WGL_SAMPLES_ARB 0x2042
1353 static PROC wglChoosePixelFormat;
1354 static PROC wglGetPixelFormatAttribiv;
1356 #define ATTR(a, v) \
1357 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1359 static unsigned int choose_pixfmt(unsigned int mode)
1361 unsigned int num_pixfmt, pixfmt = 0;
1362 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1363 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1364 float fattr[2] = {0, 0};
1366 int *aptr = attr + 6;
1369 if(mode & GLUT_DOUBLE) {
1370 ATTR(WGL_DOUBLE_BUFFER, 1);
1373 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1374 ATTR(WGL_COLOR_BITS, 8);
1375 if(mode & GLUT_ALPHA) {
1376 ATTR(WGL_ALPHA_BITS, 4);
1378 if(mode & GLUT_DEPTH) {
1379 ATTR(WGL_DEPTH_BITS, 16);
1381 if(mode & GLUT_STENCIL) {
1382 ATTR(WGL_STENCIL_BITS, 1);
1384 if(mode & GLUT_ACCUM) {
1385 ATTR(WGL_ACCUM_BITS, 1);
1387 if(mode & GLUT_STEREO) {
1388 ATTR(WGL_STEREO, 1);
1390 if(mode & GLUT_SRGB) {
1391 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1393 if(mode & GLUT_MULTISAMPLE) {
1394 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1395 *aptr++ = WGL_SAMPLES_ARB;
1401 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1410 static PIXELFORMATDESCRIPTOR pfd;
1411 static PIXELFORMATDESCRIPTOR tmppfd = {
1412 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1413 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1414 PFD_MAIN_PLANE, 0, 0, 0, 0
1416 #define TMPCLASS "TempMiniGLUT"
1418 #define GETATTR(attr, vptr) \
1421 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1424 static int create_window_wglext(const char *title, int width, int height)
1426 WNDCLASSEX wc = {0};
1432 /* create a temporary window and GL context, just to query and retrieve
1433 * the wglChoosePixelFormatEXT function
1435 wc.cbSize = sizeof wc;
1436 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1437 wc.hCursor = LoadCursor(0, IDC_ARROW);
1438 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1439 wc.hInstance = hinst;
1440 wc.lpfnWndProc = DefWindowProc;
1441 wc.lpszClassName = TMPCLASS;
1442 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1443 if(!RegisterClassEx(&wc)) {
1446 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1447 width, height, 0, 0, hinst, 0))) {
1450 tmpdc = GetDC(tmpwin);
1452 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1453 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1454 !(tmpctx = wglCreateContext(tmpdc))) {
1457 wglMakeCurrent(tmpdc, tmpctx);
1459 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1460 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1463 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1467 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1471 wglMakeCurrent(0, 0);
1472 wglDeleteContext(tmpctx);
1473 DestroyWindow(tmpwin);
1474 UnregisterClass(TMPCLASS, hinst);
1476 /* create the real window and context */
1477 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1478 init_y, width, height, 0, 0, hinst, 0))) {
1479 panic("Failed to create window\n");
1483 if(!(pixfmt = choose_pixfmt(init_mode))) {
1484 panic("Failed to find suitable pixel format\n");
1486 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1487 panic("Failed to set the selected pixel format\n");
1489 if(!(ctx = wglCreateContext(dc))) {
1490 panic("Failed to create the OpenGL context\n");
1492 wglMakeCurrent(dc, ctx);
1494 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1495 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1496 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1497 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1498 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1499 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1500 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1501 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1502 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1507 wglMakeCurrent(0, 0);
1508 wglDeleteContext(tmpctx);
1511 DestroyWindow(tmpwin);
1513 UnregisterClass(TMPCLASS, hinst);
1518 static void create_window(const char *title)
1526 rect.right = init_x + init_width;
1527 rect.bottom = init_y + init_height;
1528 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1529 width = rect.right - rect.left;
1530 height = rect.bottom - rect.top;
1532 memset(&pfd, 0, sizeof pfd);
1533 pfd.nSize = sizeof pfd;
1535 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1536 if(init_mode & GLUT_STEREO) {
1537 pfd.dwFlags |= PFD_STEREO;
1539 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1540 pfd.cColorBits = 24;
1541 if(init_mode & GLUT_ALPHA) {
1544 if(init_mode & GLUT_ACCUM) {
1545 pfd.cAccumBits = 24;
1547 if(init_mode & GLUT_DEPTH) {
1548 pfd.cDepthBits = 24;
1550 if(init_mode & GLUT_STENCIL) {
1551 pfd.cStencilBits = 8;
1553 pfd.iLayerType = PFD_MAIN_PLANE;
1556 if(create_window_wglext(title, width, height) == -1) {
1558 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1559 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1560 panic("Failed to create window\n");
1564 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1565 panic("Failed to find suitable pixel format\n");
1567 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1568 panic("Failed to set the selected pixel format\n");
1570 if(!(ctx = wglCreateContext(dc))) {
1571 panic("Failed to create the OpenGL context\n");
1573 wglMakeCurrent(dc, ctx);
1575 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1576 ctx_info.rsize = pfd.cRedBits;
1577 ctx_info.gsize = pfd.cGreenBits;
1578 ctx_info.bsize = pfd.cBlueBits;
1579 ctx_info.asize = pfd.cAlphaBits;
1580 ctx_info.zsize = pfd.cDepthBits;
1581 ctx_info.ssize = pfd.cStencilBits;
1582 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1583 ctx_info.samples = 0;
1588 SetForegroundWindow(win);
1591 reshape_pending = 1;
1594 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1596 static int mouse_x, mouse_y;
1601 if(win) DestroyWindow(win);
1612 ValidateRect(win, 0);
1616 x = lparam & 0xffff;
1618 if(x != win_width && y != win_height) {
1622 reshape_pending = 0;
1623 cb_reshape(win_width, win_height);
1630 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1636 key = translate_vkey(wparam);
1639 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1643 cb_skeydown(key, mouse_x, mouse_y);
1651 key = translate_vkey(wparam);
1654 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1658 cb_skeyup(key, mouse_x, mouse_y);
1663 case WM_LBUTTONDOWN:
1664 handle_mbutton(0, 1, wparam, lparam);
1666 case WM_MBUTTONDOWN:
1667 handle_mbutton(1, 1, wparam, lparam);
1669 case WM_RBUTTONDOWN:
1670 handle_mbutton(2, 1, wparam, lparam);
1673 handle_mbutton(0, 0, wparam, lparam);
1676 handle_mbutton(1, 0, wparam, lparam);
1679 handle_mbutton(2, 0, wparam, lparam);
1683 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1684 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1686 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1692 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1696 return DefWindowProc(win, msg, wparam, lparam);
1702 static void update_modkeys(void)
1704 if(GetKeyState(VK_SHIFT) & 0x8000) {
1705 modstate |= GLUT_ACTIVE_SHIFT;
1707 modstate &= ~GLUT_ACTIVE_SHIFT;
1709 if(GetKeyState(VK_CONTROL) & 0x8000) {
1710 modstate |= GLUT_ACTIVE_CTRL;
1712 modstate &= ~GLUT_ACTIVE_CTRL;
1714 if(GetKeyState(VK_MENU) & 0x8000) {
1715 modstate |= GLUT_ACTIVE_ALT;
1717 modstate &= ~GLUT_ACTIVE_ALT;
1721 static int translate_vkey(int vkey)
1724 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1725 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1726 case VK_END: return GLUT_KEY_END;
1727 case VK_HOME: return GLUT_KEY_HOME;
1728 case VK_LEFT: return GLUT_KEY_LEFT;
1729 case VK_UP: return GLUT_KEY_UP;
1730 case VK_RIGHT: return GLUT_KEY_RIGHT;
1731 case VK_DOWN: return GLUT_KEY_DOWN;
1732 case VK_OEM_1: return ';';
1733 case VK_OEM_2: return '/';
1734 case VK_OEM_3: return '`';
1735 case VK_OEM_4: return '[';
1736 case VK_OEM_5: return '\\';
1737 case VK_OEM_6: return ']';
1738 case VK_OEM_7: return '\'';
1743 if(vkey >= 'A' && vkey <= 'Z') {
1745 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1746 vkey -= VK_F1 + GLUT_KEY_F1;
1752 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1759 x = lparam & 0xffff;
1761 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1765 static void get_window_pos(int *x, int *y)
1768 GetWindowRect(win, &rect);
1773 static void get_window_size(int *w, int *h)
1776 GetClientRect(win, &rect);
1777 *w = rect.right - rect.left;
1778 *h = rect.bottom - rect.top;
1781 static void get_screen_size(int *scrw, int *scrh)
1783 *scrw = GetSystemMetrics(SM_CXSCREEN);
1784 *scrh = GetSystemMetrics(SM_CYSCREEN);
1786 #endif /* BUILD_WIN32 */
1788 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1789 #include <sys/time.h>
1791 #ifdef MINIGLUT_USE_LIBC
1792 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1794 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1797 static long get_msec(void)
1799 static struct timeval tv0;
1802 sys_gettimeofday(&tv, 0);
1803 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1807 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1811 static long get_msec(void)
1816 #ifdef MINIGLUT_NO_WINMM
1817 tm = GetTickCount();
1829 static void panic(const char *msg)
1831 const char *end = msg;
1833 sys_write(2, msg, end - msg);
1838 #ifdef MINIGLUT_USE_LIBC
1846 static void sys_exit(int status)
1851 static int sys_write(int fd, const void *buf, int count)
1853 return write(fd, buf, count);
1856 #else /* !MINIGLUT_USE_LIBC */
1860 static void sys_exit(int status)
1864 :: "a"(60), "D"(status));
1866 static int sys_write(int fd, const void *buf, int count)
1872 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1875 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1881 : "a"(96), "D"(tv), "S"(tz));
1884 #endif /* __x86_64__ */
1886 static void sys_exit(int status)
1890 :: "a"(1), "b"(status));
1892 static int sys_write(int fd, const void *buf, int count)
1898 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1901 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1907 : "a"(78), "b"(tv), "c"(tz));
1910 #endif /* __i386__ */
1911 #endif /* __linux__ */
1914 static void sys_exit(int status)
1916 ExitProcess(status);
1918 static int sys_write(int fd, const void *buf, int count)
1920 unsigned long wrsz = 0;
1922 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1923 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1929 #endif /* !MINIGLUT_USE_LIBC */
1932 /* ----------------- primitives ------------------ */
1933 #ifdef MINIGLUT_USE_LIBC
1937 void mglut_sincos(float angle, float *sptr, float *cptr)
1943 float mglut_atan(float x)
1948 #else /* !MINIGLUT_USE_LIBC */
1951 void mglut_sincos(float angle, float *sptr, float *cptr)
1958 : "=m"(*sptr), "=m"(*cptr)
1963 float mglut_atan(float x)
1979 void mglut_sincos(float angle, float *sptr, float *cptr)
1992 float mglut_atan(float x)
2006 #pragma aux mglut_sincos = \
2008 "fstp dword ptr [edx]" \
2009 "fstp dword ptr [eax]" \
2010 parm[8087][eax][edx] \
2013 #pragma aux mglut_atan = \
2019 #endif /* __WATCOMC__ */
2021 #endif /* !MINIGLUT_USE_LIBC */
2023 #define PI 3.1415926536f
2025 void glutSolidSphere(float rad, int slices, int stacks)
2028 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2029 float du = 1.0f / (float)slices;
2030 float dv = 1.0f / (float)stacks;
2033 for(i=0; i<stacks; i++) {
2035 for(j=0; j<slices; j++) {
2037 for(k=0; k<4; k++) {
2038 gray = k ^ (k >> 1);
2039 s = gray & 1 ? u + du : u;
2040 t = gray & 2 ? v + dv : v;
2041 theta = s * PI * 2.0f;
2043 mglut_sincos(theta, &sintheta, &costheta);
2044 mglut_sincos(phi, &sinphi, &cosphi);
2045 x = sintheta * sinphi;
2046 y = costheta * sinphi;
2051 glNormal3f(x, y, z);
2052 glVertex3f(x * rad, y * rad, z * rad);
2059 void glutWireSphere(float rad, int slices, int stacks)
2061 glPushAttrib(GL_POLYGON_BIT);
2062 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2063 glutSolidSphere(rad, slices, stacks);
2067 void glutSolidCube(float sz)
2069 int i, j, idx, gray, flip, rotx;
2070 float vpos[3], norm[3];
2071 float rad = sz * 0.5f;
2074 for(i=0; i<6; i++) {
2077 idx = (~i & 2) - rotx;
2078 norm[0] = norm[1] = norm[2] = 0.0f;
2079 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2081 vpos[idx] = norm[idx] * rad;
2082 for(j=0; j<4; j++) {
2083 gray = j ^ (j >> 1);
2084 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2085 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2086 glTexCoord2f(gray & 1, gray >> 1);
2093 void glutWireCube(float sz)
2095 glPushAttrib(GL_POLYGON_BIT);
2096 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2101 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2104 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2105 float du = 1.0f / (float)slices;
2106 float dv = 1.0f / (float)stacks;
2109 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2110 mglut_sincos(phi, &sinphi, &cosphi);
2113 for(i=0; i<stacks; i++) {
2115 for(j=0; j<slices; j++) {
2117 for(k=0; k<4; k++) {
2118 gray = k ^ (k >> 1);
2119 s = gray & 2 ? u + du : u;
2120 t = gray & 1 ? v + dv : v;
2121 rad = rbot + (rtop - rbot) * t;
2122 theta = s * PI * 2.0f;
2123 mglut_sincos(theta, &sintheta, &costheta);
2125 x = sintheta * cosphi;
2126 y = costheta * cosphi;
2131 glNormal3f(x, y, z);
2132 glVertex3f(sintheta * rad, costheta * rad, t * height);
2139 void glutSolidCone(float base, float height, int slices, int stacks)
2141 draw_cylinder(base, 0, height, slices, stacks);
2144 void glutWireCone(float base, float height, int slices, int stacks)
2146 glPushAttrib(GL_POLYGON_BIT);
2147 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2148 glutSolidCone(base, height, slices, stacks);
2152 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2154 draw_cylinder(rad, rad, height, slices, stacks);
2157 void glutWireCylinder(float rad, float height, int slices, int stacks)
2159 glPushAttrib(GL_POLYGON_BIT);
2160 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2161 glutSolidCylinder(rad, height, slices, stacks);
2165 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2168 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2169 float du = 1.0f / (float)rings;
2170 float dv = 1.0f / (float)sides;
2173 for(i=0; i<rings; i++) {
2175 for(j=0; j<sides; j++) {
2177 for(k=0; k<4; k++) {
2178 gray = k ^ (k >> 1);
2179 s = gray & 1 ? u + du : u;
2180 t = gray & 2 ? v + dv : v;
2181 theta = s * PI * 2.0f;
2182 phi = t * PI * 2.0f;
2183 mglut_sincos(theta, &sintheta, &costheta);
2184 mglut_sincos(phi, &sinphi, &cosphi);
2185 x = sintheta * sinphi;
2186 y = costheta * sinphi;
2191 glNormal3f(x, y, z);
2193 x = x * inner_rad + sintheta * outer_rad;
2194 y = y * inner_rad + costheta * outer_rad;
2196 glVertex3f(x, y, z);
2203 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2205 glPushAttrib(GL_POLYGON_BIT);
2206 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2207 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2211 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2212 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2214 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2216 #define PATCH_SUBDIV 7
2218 static float teapot_part_flip[] = {
2219 1, 1, 1, 1, /* rim flip */
2220 1, 1, 1, 1, /* body1 flip */
2221 1, 1, 1, 1, /* body2 flip */
2222 1, 1, 1, 1, /* lid patch 1 flip */
2223 1, 1, 1, 1, /* lid patch 2 flip */
2224 1, -1, /* handle 1 flip */
2225 1, -1, /* handle 2 flip */
2226 1, -1, /* spout 1 flip */
2227 1, -1, /* spout 2 flip */
2228 1, 1, 1, 1 /* bottom flip */
2231 static float teapot_part_rot[] = {
2232 0, 90, 180, 270, /* rim rotations */
2233 0, 90, 180, 270, /* body patch 1 rotations */
2234 0, 90, 180, 270, /* body patch 2 rotations */
2235 0, 90, 180, 270, /* lid patch 1 rotations */
2236 0, 90, 180, 270, /* lid patch 2 rotations */
2237 0, 0, /* handle 1 rotations */
2238 0, 0, /* handle 2 rotations */
2239 0, 0, /* spout 1 rotations */
2240 0, 0, /* spout 2 rotations */
2241 0, 90, 180, 270 /* bottom rotations */
2245 static int teapot_index[] = {
2247 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2248 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2249 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2250 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2252 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2253 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2254 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2255 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2257 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2258 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2259 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2260 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2262 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2263 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2264 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2265 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2267 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2268 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2269 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2270 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2272 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2273 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2275 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2276 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2278 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2279 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2281 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2282 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2284 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2285 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2286 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2287 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2291 static float teapot_verts[][3] = {
2292 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2293 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2294 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2295 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2296 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2297 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2298 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2299 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2300 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2301 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2302 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2303 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2304 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2305 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2306 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2307 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2308 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2309 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2310 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2311 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2312 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2313 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2314 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2315 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2316 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2317 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2318 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2319 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2320 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2321 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2322 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2323 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2324 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2325 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2326 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2327 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2328 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2329 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2330 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2331 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2332 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2333 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2334 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2335 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2336 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2337 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2338 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2339 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2340 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2341 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2342 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2343 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2344 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2345 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2346 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2347 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2348 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2349 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2350 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2351 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2352 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2353 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2354 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2355 { 0.8400, -1.5000, 0.07500 }
2358 static void draw_patch(int *index, int flip, float scale);
2359 static float bernstein(int i, float x);
2361 void glutSolidTeapot(float size)
2367 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2368 float flip = teapot_part_flip[i];
2369 float rot = teapot_part_rot[i];
2371 glMatrixMode(GL_MODELVIEW);
2373 glTranslatef(0, -3.15 * size * 0.5, 0);
2374 glRotatef(rot, 0, 1, 0);
2375 glScalef(1, 1, flip);
2376 glRotatef(-90, 1, 0, 0);
2378 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2384 void glutWireTeapot(float size)
2386 glPushAttrib(GL_POLYGON_BIT);
2387 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2388 glutSolidTeapot(size);
2393 static void bezier_patch(float *res, float *cp, float u, float v)
2397 res[0] = res[1] = res[2] = 0.0f;
2399 for(j=0; j<4; j++) {
2400 for(i=0; i<4; i++) {
2401 float bu = bernstein(i, u);
2402 float bv = bernstein(j, v);
2404 res[0] += cp[0] * bu * bv;
2405 res[1] += cp[1] * bu * bv;
2406 res[2] += cp[2] * bu * bv;
2413 static float rsqrt(float x)
2415 float xhalf = x * 0.5f;
2417 i = 0x5f3759df - (i >> 1);
2419 x = x * (1.5f - xhalf * x * x);
2424 #define CROSS(res, a, b) \
2426 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2427 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2428 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2431 #define NORMALIZE(v) \
2433 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2441 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2443 float tang[3], bitan[3], tmp[3];
2445 bezier_patch(tang, cp, u + DT, v);
2446 bezier_patch(tmp, cp, u - DT, v);
2451 bezier_patch(bitan, cp, u, v + DT);
2452 bezier_patch(tmp, cp, u, v - DT);
2457 CROSS(res, tang, bitan);
2463 static float bernstein(int i, float x)
2465 float invx = 1.0f - x;
2469 return invx * invx * invx;
2471 return 3.0f * x * invx * invx;
2473 return 3.0f * x * x * invx;
2482 static void draw_patch(int *index, int flip, float scale)
2484 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2485 static const float voffs[4] = {0, 1, 1, 0};
2491 float du = 1.0 / PATCH_SUBDIV;
2492 float dv = 1.0 / PATCH_SUBDIV;
2494 /* collect control points */
2495 for(i=0; i<16; i++) {
2496 cp[i * 3] = teapot_verts[index[i]][0];
2497 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2498 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2505 for(i=0; i<PATCH_SUBDIV; i++) {
2507 for(j=0; j<PATCH_SUBDIV; j++) {
2509 for(k=0; k<4; k++) {
2510 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2512 /* top/bottom normal hack */
2516 } else if(pt[2] < 0.00001) {
2520 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2525 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);