2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
26 #ifndef GLX_SAMPLE_BUFFERS_ARB
27 #define GLX_SAMPLE_BUFFERS_ARB 100000
28 #define GLX_SAMPLES_ARB 100001
30 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
31 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
35 static Window win, root;
37 static GLXContext ctx;
38 static Atom xa_wm_proto, xa_wm_del_win;
39 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
40 static Atom xa_motif_wm_hints;
41 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
42 static unsigned int evmask;
43 static Cursor blank_cursor;
45 static int have_netwm_fullscr(void);
52 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
54 static HINSTANCE hinst;
60 #error unsupported platform
66 int rsize, gsize, bsize, asize;
74 static void cleanup(void);
75 static void create_window(const char *title);
76 static void get_window_pos(int *x, int *y);
77 static void get_window_size(int *w, int *h);
78 static void get_screen_size(int *scrw, int *scrh);
80 static long get_msec(void);
81 static void panic(const char *msg);
82 static void sys_exit(int status);
83 static int sys_write(int fd, const void *buf, int count);
86 static int init_x = -1, init_y, init_width = 256, init_height = 256;
87 static unsigned int init_mode;
89 static struct ctx_info ctx_info;
90 static int cur_cursor = GLUT_CURSOR_INHERIT;
92 static glut_cb cb_display;
93 static glut_cb cb_idle;
94 static glut_cb_reshape cb_reshape;
95 static glut_cb_state cb_vis, cb_entry;
96 static glut_cb_keyb cb_keydown, cb_keyup;
97 static glut_cb_special cb_skeydown, cb_skeyup;
98 static glut_cb_mouse cb_mouse;
99 static glut_cb_motion cb_motion, cb_passive;
100 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
101 static glut_cb_sbbutton cb_sball_button;
103 static int fullscreen;
104 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
106 static int win_width, win_height;
109 static int upd_pending;
112 void glutInit(int *argc, char **argv)
118 if(!(dpy = XOpenDisplay(0))) {
119 panic("Failed to connect to the X server\n");
121 scr = DefaultScreen(dpy);
122 root = RootWindow(dpy, scr);
123 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
124 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
125 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
126 if(have_netwm_fullscr()) {
127 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
128 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
131 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
132 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
133 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
134 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
136 evmask = ExposureMask | StructureNotifyMask;
138 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
139 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
140 XFreePixmap(dpy, blankpix);
147 hinst = GetModuleHandle(0);
149 wc.cbSize = sizeof wc;
150 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
151 wc.hCursor = LoadCursor(0, IDC_ARROW);
152 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
153 wc.hInstance = hinst;
154 wc.lpfnWndProc = handle_message;
155 wc.lpszClassName = "MiniGLUT";
156 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
157 if(!RegisterClassEx(&wc)) {
158 panic("Failed to register \"MiniGLUT\" window class\n");
162 get_screen_size(&init_x, &init_y);
169 void glutInitWindowPosition(int x, int y)
175 void glutInitWindowSize(int xsz, int ysz)
181 void glutInitDisplayMode(unsigned int mode)
186 void glutCreateWindow(const char *title)
188 create_window(title);
196 void glutMainLoop(void)
203 void glutPostRedisplay(void)
208 #define UPD_EVMASK(x) \
215 if(win) XSelectInput(dpy, win, evmask); \
219 void glutIdleFunc(glut_cb func)
224 void glutDisplayFunc(glut_cb func)
229 void glutReshapeFunc(glut_cb_reshape func)
234 void glutVisibilityFunc(glut_cb_state func)
238 UPD_EVMASK(VisibilityChangeMask);
242 void glutEntryFunc(glut_cb_state func)
246 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
250 void glutKeyboardFunc(glut_cb_keyb func)
254 UPD_EVMASK(KeyPressMask);
258 void glutKeyboardUpFunc(glut_cb_keyb func)
262 UPD_EVMASK(KeyReleaseMask);
266 void glutSpecialFunc(glut_cb_special func)
270 UPD_EVMASK(KeyPressMask);
274 void glutSpecialUpFunc(glut_cb_special func)
278 UPD_EVMASK(KeyReleaseMask);
282 void glutMouseFunc(glut_cb_mouse func)
286 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
290 void glutMotionFunc(glut_cb_motion func)
294 UPD_EVMASK(ButtonMotionMask);
298 void glutPassiveMotionFunc(glut_cb_motion func)
302 UPD_EVMASK(PointerMotionMask);
306 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
308 cb_sball_motion = func;
311 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
313 cb_sball_rotate = func;
316 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
318 cb_sball_button = func;
321 int glutGet(unsigned int s)
326 get_window_pos(&x, &y);
329 get_window_pos(&x, &y);
331 case GLUT_WINDOW_WIDTH:
332 get_window_size(&x, &y);
334 case GLUT_WINDOW_HEIGHT:
335 get_window_size(&x, &y);
337 case GLUT_WINDOW_BUFFER_SIZE:
338 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
339 case GLUT_WINDOW_STENCIL_SIZE:
340 return ctx_info.ssize;
341 case GLUT_WINDOW_DEPTH_SIZE:
342 return ctx_info.zsize;
343 case GLUT_WINDOW_RED_SIZE:
344 return ctx_info.rsize;
345 case GLUT_WINDOW_GREEN_SIZE:
346 return ctx_info.gsize;
347 case GLUT_WINDOW_BLUE_SIZE:
348 return ctx_info.bsize;
349 case GLUT_WINDOW_ALPHA_SIZE:
350 return ctx_info.asize;
351 case GLUT_WINDOW_DOUBLEBUFFER:
352 return ctx_info.dblbuf;
353 case GLUT_WINDOW_RGBA:
355 case GLUT_WINDOW_NUM_SAMPLES:
356 return ctx_info.samples;
357 case GLUT_WINDOW_STEREO:
358 return ctx_info.stereo;
359 case GLUT_WINDOW_SRGB:
360 return ctx_info.srgb;
361 case GLUT_WINDOW_CURSOR:
363 case GLUT_SCREEN_WIDTH:
364 get_screen_size(&x, &y);
366 case GLUT_SCREEN_HEIGHT:
367 get_screen_size(&x, &y);
369 case GLUT_INIT_DISPLAY_MODE:
371 case GLUT_INIT_WINDOW_X:
373 case GLUT_INIT_WINDOW_Y:
375 case GLUT_INIT_WINDOW_WIDTH:
377 case GLUT_INIT_WINDOW_HEIGHT:
379 case GLUT_ELAPSED_TIME:
387 int glutGetModifiers(void)
392 static int is_space(int c)
394 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
397 static const char *skip_space(const char *s)
399 while(*s && is_space(*s)) s++;
403 int glutExtensionSupported(char *ext)
405 const char *str, *eptr;
407 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
412 str = skip_space(str);
413 eptr = skip_space(ext);
414 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
418 if((!*str || is_space(*str)) && !*eptr) {
421 while(*str && !is_space(*str)) str++;
428 /* --------------- UNIX/X11 implementation ----------------- */
431 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
433 SPNAV_EVENT_BUTTON /* includes both press and release */
436 struct spnav_event_motion {
444 struct spnav_event_button {
452 struct spnav_event_motion motion;
453 struct spnav_event_button button;
457 static void handle_event(XEvent *ev);
459 static int spnav_window(Window win);
460 static int spnav_event(const XEvent *xev, union spnav_event *event);
461 static int spnav_remove_events(int type);
464 void glutMainLoopEvent(void)
469 panic("display callback not set");
472 if(!upd_pending && !cb_idle) {
473 XNextEvent(dpy, &ev);
477 while(XPending(dpy)) {
478 XNextEvent(dpy, &ev);
487 if(upd_pending && mapped) {
498 static void cleanup(void)
502 glXMakeCurrent(dpy, 0, 0);
503 XDestroyWindow(dpy, win);
507 static KeySym translate_keysym(KeySym sym)
528 static void handle_event(XEvent *ev)
531 union spnav_event sev;
540 case ConfigureNotify:
541 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
542 win_width = ev->xconfigure.width;
543 win_height = ev->xconfigure.height;
544 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
549 if(ev->xclient.message_type == xa_wm_proto) {
550 if(ev->xclient.data.l[0] == xa_wm_del_win) {
554 if(spnav_event(ev, &sev)) {
556 case SPNAV_EVENT_MOTION:
557 if(cb_sball_motion) {
558 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
560 if(cb_sball_rotate) {
561 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
563 spnav_remove_events(SPNAV_EVENT_MOTION);
566 case SPNAV_EVENT_BUTTON:
567 if(cb_sball_button) {
568 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
584 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
585 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
588 sym = translate_keysym(sym);
590 if(ev->type == KeyPress) {
591 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
593 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
596 if(ev->type == KeyPress) {
597 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
599 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
606 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
608 int bn = ev->xbutton.button - Button1;
609 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
610 ev->xbutton.x, ev->xbutton.y);
615 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
616 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
618 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
622 case VisibilityNotify:
624 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
628 if(cb_entry) cb_entry(GLUT_ENTERED);
631 if(cb_entry) cb_entry(GLUT_LEFT);
636 void glutSwapBuffers(void)
638 glXSwapBuffers(dpy, win);
642 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
643 * needs to resize the window to make it fullscreen. The way it does this is by
644 * querying the size of the root window (see get_screen_size), which in the
645 * case of multi-monitor setups will be the combined size of all monitors.
646 * This is problematic; the way to solve it is to use the XRandR extension, or
647 * the Xinerama extension, to figure out the dimensions of the correct video
648 * output, which would add potentially two extension support libraries to our
650 * Moreover, any X installation modern enough to support XR&R will almost
651 * certainly be running a window manager supporting the EHWM
652 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
653 * on manual resizing, and is used in preference if available, making this
654 * whole endeavor pointless.
655 * So I'll just leave it with set_fullscreen_mwm covering the entire
656 * multi-monitor area for now.
661 unsigned long functions;
662 unsigned long decorations;
664 unsigned long status;
667 #define MWM_HINTS_DECORATIONS 2
668 #define MWM_DECOR_ALL 1
670 static void set_fullscreen_mwm(int fs)
672 struct mwm_hints hints;
673 int scr_width, scr_height;
676 get_window_pos(&prev_win_x, &prev_win_y);
677 get_window_size(&prev_win_width, &prev_win_height);
678 get_screen_size(&scr_width, &scr_height);
680 hints.decorations = 0;
681 hints.flags = MWM_HINTS_DECORATIONS;
682 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
683 PropModeReplace, (unsigned char*)&hints, 5);
685 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
687 XDeleteProperty(dpy, win, xa_motif_wm_hints);
688 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
692 static int have_netwm_fullscr(void)
696 unsigned long i, count, rem;
698 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
701 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
702 &type, &fmt, &count, &rem, (unsigned char**)prop);
704 for(i=0; i<count; i++) {
705 if(prop[i] == xa_net_wm_state_fullscr) {
715 static void set_fullscreen_ewmh(int fs)
717 XClientMessageEvent msg = {0};
719 msg.type = ClientMessage;
721 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
723 msg.data.l[0] = fs ? 1 : 0;
724 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
726 msg.data.l[3] = 1; /* source regular application */
727 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
730 static void set_fullscreen(int fs)
732 if(fullscreen == fs) return;
734 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
735 set_fullscreen_ewmh(fs);
737 } else if(xa_motif_wm_hints) {
738 set_fullscreen_mwm(fs);
743 void glutPositionWindow(int x, int y)
746 XMoveWindow(dpy, win, x, y);
749 void glutReshapeWindow(int xsz, int ysz)
752 XResizeWindow(dpy, win, xsz, ysz);
755 void glutFullScreen(void)
760 void glutSetWindowTitle(const char *title)
763 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
766 XSetWMName(dpy, win, &tprop);
770 void glutSetIconTitle(const char *title)
773 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
776 XSetWMIconName(dpy, win, &tprop);
780 void glutSetCursor(int cidx)
785 case GLUT_CURSOR_LEFT_ARROW:
786 cur = XCreateFontCursor(dpy, XC_left_ptr);
788 case GLUT_CURSOR_INHERIT:
790 case GLUT_CURSOR_NONE:
797 XDefineCursor(dpy, win, cur);
801 static XVisualInfo *choose_visual(unsigned int mode)
808 if(mode & GLUT_DOUBLE) {
809 *aptr++ = GLX_DOUBLEBUFFER;
812 if(mode & GLUT_INDEX) {
813 *aptr++ = GLX_BUFFER_SIZE;
817 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
818 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
819 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
821 if(mode & GLUT_ALPHA) {
822 *aptr++ = GLX_ALPHA_SIZE;
825 if(mode & GLUT_DEPTH) {
826 *aptr++ = GLX_DEPTH_SIZE;
829 if(mode & GLUT_STENCIL) {
830 *aptr++ = GLX_STENCIL_SIZE;
833 if(mode & GLUT_ACCUM) {
834 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
835 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
836 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
838 if(mode & GLUT_STEREO) {
839 *aptr++ = GLX_STEREO;
841 if(mode & GLUT_SRGB) {
842 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
844 if(mode & GLUT_MULTISAMPLE) {
845 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
847 *aptr++ = GLX_SAMPLES_ARB;
854 return glXChooseVisual(dpy, scr, attr);
856 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
865 static void create_window(const char *title)
867 XSetWindowAttributes xattr = {0};
869 unsigned int xattr_mask;
870 unsigned int mode = init_mode;
872 if(!(vi = choose_visual(mode))) {
874 if(!(vi = choose_visual(mode))) {
875 panic("Failed to find compatible visual\n");
879 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
881 panic("Failed to create OpenGL context\n");
884 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
885 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
886 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
887 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
888 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
889 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
890 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
891 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
892 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
893 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
895 xattr.background_pixel = BlackPixel(dpy, scr);
896 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
897 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
898 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
899 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
901 glXDestroyContext(dpy, ctx);
902 panic("Failed to create window\n");
906 XSelectInput(dpy, win, evmask);
910 glutSetWindowTitle(title);
911 glutSetIconTitle(title);
912 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
913 XMapWindow(dpy, win);
915 glXMakeCurrent(dpy, win, ctx);
918 static void get_window_pos(int *x, int *y)
921 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
924 static void get_window_size(int *w, int *h)
926 XWindowAttributes wattr;
927 XGetWindowAttributes(dpy, win, &wattr);
932 static void get_screen_size(int *scrw, int *scrh)
934 XWindowAttributes wattr;
935 XGetWindowAttributes(dpy, root, &wattr);
937 *scrh = wattr.height;
943 CMD_APP_WINDOW = 27695,
947 static Window get_daemon_window(Display *dpy);
948 static int catch_badwin(Display *dpy, XErrorEvent *err);
950 #define SPNAV_INITIALIZED (xa_motion_event)
952 static int spnav_window(Window win)
954 int (*prev_xerr_handler)(Display*, XErrorEvent*);
958 if(!SPNAV_INITIALIZED) {
962 if(!(daemon_win = get_daemon_window(dpy))) {
966 prev_xerr_handler = XSetErrorHandler(catch_badwin);
968 xev.type = ClientMessage;
969 xev.xclient.send_event = False;
970 xev.xclient.display = dpy;
971 xev.xclient.window = win;
972 xev.xclient.message_type = xa_command_event;
973 xev.xclient.format = 16;
974 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
975 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
976 xev.xclient.data.s[2] = CMD_APP_WINDOW;
978 XSendEvent(dpy, daemon_win, False, 0, &xev);
981 XSetErrorHandler(prev_xerr_handler);
985 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
987 int evtype = *(int*)arg;
989 if(xev->type != ClientMessage) {
993 if(xev->xclient.message_type == xa_motion_event) {
994 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
996 if(xev->xclient.message_type == xa_button_press_event ||
997 xev->xclient.message_type == xa_button_release_event) {
998 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1003 static int spnav_remove_events(int type)
1007 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1013 static int spnav_event(const XEvent *xev, union spnav_event *event)
1018 xmsg_type = xev->xclient.message_type;
1020 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1021 xmsg_type != xa_button_release_event) {
1025 if(xmsg_type == xa_motion_event) {
1026 event->type = SPNAV_EVENT_MOTION;
1027 event->motion.data = &event->motion.x;
1029 for(i=0; i<6; i++) {
1030 event->motion.data[i] = xev->xclient.data.s[i + 2];
1032 event->motion.period = xev->xclient.data.s[8];
1034 event->type = SPNAV_EVENT_BUTTON;
1035 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1036 event->button.bnum = xev->xclient.data.s[2];
1041 static int mglut_strcmp(const char *s1, const char *s2)
1043 while(*s1 && *s1 == *s2) {
1050 static Window get_daemon_window(Display *dpy)
1053 XTextProperty wname;
1056 unsigned long nitems, bytes_after;
1057 unsigned char *prop;
1059 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1060 &type, &fmt, &nitems, &bytes_after, &prop);
1065 win = *(Window*)prop;
1068 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1075 static int catch_badwin(Display *dpy, XErrorEvent *err)
1082 #endif /* BUILD_X11 */
1085 /* --------------- windows implementation ----------------- */
1087 static int reshape_pending;
1089 static void update_modkeys(void);
1090 static int translate_vkey(int vkey);
1091 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1093 #ifdef MINIGLUT_WINMAIN
1094 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1097 char *argv[] = { "miniglut.exe", 0 };
1098 return main(argc, argv);
1102 void glutMainLoopEvent(void)
1107 panic("display callback not set");
1110 if(reshape_pending && cb_reshape) {
1111 reshape_pending = 0;
1112 get_window_size(&win_width, &win_height);
1113 cb_reshape(win_width, win_height);
1116 if(!upd_pending && !cb_idle) {
1117 GetMessage(&msg, 0, 0, 0);
1118 TranslateMessage(&msg);
1119 DispatchMessage(&msg);
1122 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1123 TranslateMessage(&msg);
1124 DispatchMessage(&msg);
1132 if(upd_pending && mapped) {
1138 static void cleanup(void)
1141 wglMakeCurrent(dc, 0);
1142 wglDeleteContext(ctx);
1143 UnregisterClass("MiniGLUT", hinst);
1147 void glutSwapBuffers(void)
1152 void glutPositionWindow(int x, int y)
1155 unsigned int flags = SWP_SHOWWINDOW;
1158 rect.left = prev_win_x;
1159 rect.top = prev_win_y;
1160 rect.right = rect.left + prev_win_width;
1161 rect.bottom = rect.top + prev_win_height;
1162 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1164 flags |= SWP_FRAMECHANGED;
1166 GetWindowRect(win, &rect);
1168 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1171 void glutReshapeWindow(int xsz, int ysz)
1174 unsigned int flags = SWP_SHOWWINDOW;
1177 rect.left = prev_win_x;
1178 rect.top = prev_win_y;
1179 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1181 flags |= SWP_FRAMECHANGED;
1183 GetWindowRect(win, &rect);
1185 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1188 void glutFullScreen(void)
1191 int scr_width, scr_height;
1193 if(fullscreen) return;
1195 GetWindowRect(win, &rect);
1196 prev_win_x = rect.left;
1197 prev_win_y = rect.top;
1198 prev_win_width = rect.right - rect.left;
1199 prev_win_height = rect.bottom - rect.top;
1201 get_screen_size(&scr_width, &scr_height);
1203 SetWindowLong(win, GWL_STYLE, 0);
1204 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1209 void glutSetWindowTitle(const char *title)
1211 SetWindowText(win, title);
1214 void glutSetIconTitle(const char *title)
1218 void glutSetCursor(int cidx)
1221 case GLUT_CURSOR_NONE:
1224 case GLUT_CURSOR_INHERIT:
1225 case GLUT_CURSOR_LEFT_ARROW:
1227 SetCursor(LoadCursor(0, IDC_ARROW));
1232 #define WGL_DRAW_TO_WINDOW 0x2001
1233 #define WGL_SUPPORT_OPENGL 0x2010
1234 #define WGL_DOUBLE_BUFFER 0x2011
1235 #define WGL_STEREO 0x2012
1236 #define WGL_PIXEL_TYPE 0x2013
1237 #define WGL_COLOR_BITS 0x2014
1238 #define WGL_RED_BITS 0x2015
1239 #define WGL_GREEN_BITS 0x2017
1240 #define WGL_BLUE_BITS 0x2019
1241 #define WGL_ALPHA_BITS 0x201b
1242 #define WGL_ACCUM_BITS 0x201d
1243 #define WGL_DEPTH_BITS 0x2022
1244 #define WGL_STENCIL_BITS 0x2023
1246 #define WGL_TYPE_RGBA 0x202b
1247 #define WGL_TYPE_COLORINDEX 0x202c
1249 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1250 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1251 #define WGL_SAMPLES_ARB 0x2042
1253 static PROC wglChoosePixelFormat;
1254 static PROC wglGetPixelFormatAttribiv;
1256 #define ATTR(a, v) \
1257 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1259 static unsigned int choose_pixfmt(unsigned int mode)
1261 unsigned int num_pixfmt, pixfmt = 0;
1262 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1 };
1267 if(mode & GLUT_DOUBLE) {
1268 ATTR(WGL_DOUBLE_BUFFER, 1);
1271 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1272 ATTR(WGL_COLOR_BITS, 8);
1273 if(mode & GLUT_ALPHA) {
1274 ATTR(WGL_ALPHA_BITS, 4);
1276 if(mode & GLUT_DEPTH) {
1277 ATTR(WGL_DEPTH_BITS, 16);
1279 if(mode & GLUT_STENCIL) {
1280 ATTR(WGL_STENCIL_BITS, 1);
1282 if(mode & GLUT_ACCUM) {
1283 ATTR(WGL_ACCUM_BITS, 1);
1285 if(mode & GLUT_STEREO) {
1286 ATTR(WGL_STEREO, 1);
1288 if(mode & GLUT_SRGB) {
1289 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1291 if(mode & GLUT_MULTISAMPLE) {
1292 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1293 *aptr++ = WGL_SAMPLES_ARB;
1299 while((!wglChoosePixelFormat(dc, attr, 0, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1308 static PIXELFORMATDESCRIPTOR tmppfd = {
1309 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1310 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1311 PFD_MAIN_PLANE, 0, 0, 0, 0
1313 #define TMPCLASS "TempMiniGLUT"
1315 #define GETATTR(attr, vptr) \
1318 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1321 static int create_window_wglext(const char *title, int width, int height)
1323 WNDCLASSEX wc = {0};
1329 /* create a temporary window and GL context, just to query and retrieve
1330 * the wglChoosePixelFormatEXT function
1332 wc.cbSize = sizeof wc;
1333 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1334 wc.hCursor = LoadCursor(0, IDC_ARROW);
1335 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1336 wc.hInstance = hinst;
1337 wc.lpfnWndProc = DefWindowProc;
1338 wc.lpszClassName = TMPCLASS;
1339 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1340 if(!RegisterClassEx(&wc)) {
1343 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1344 width, height, 0, 0, hinst, 0))) {
1347 tmpdc = GetDC(tmpwin);
1349 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1350 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1351 !(tmpctx = wglCreateContext(tmpdc))) {
1354 wglMakeCurrent(tmpdc, tmpctx);
1356 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1357 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1360 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1364 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1368 wglMakeCurrent(0, 0);
1369 wglDeleteContext(tmpctx);
1370 DestroyWindow(tmpwin);
1371 UnregisterClass(TMPCLASS, hinst);
1373 /* create the real window and context */
1374 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1375 init_y, width, height, 0, 0, hinst, 0))) {
1376 panic("Failed to create window\n");
1380 if(!(pixfmt = choose_pixfmt(init_mode))) {
1381 panic("Failed to find suitable pixel format\n");
1383 if(!SetPixelFormat(dc, pixfmt, &tmppfd)) {
1384 panic("Failed to set the selected pixel format\n");
1386 if(!(ctx = wglCreateContext(dc))) {
1387 panic("Failed to create the OpenGL context\n");
1389 wglMakeCurrent(dc, ctx);
1391 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1392 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1393 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1394 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1395 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1396 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1397 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1398 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1399 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1404 wglMakeCurrent(0, 0);
1405 wglDeleteContext(tmpctx);
1408 DestroyWindow(tmpwin);
1410 UnregisterClass(TMPCLASS, hinst);
1415 static void create_window(const char *title)
1418 PIXELFORMATDESCRIPTOR pfd = {0};
1424 rect.right = init_x + init_width;
1425 rect.bottom = init_y + init_height;
1426 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1427 width = rect.right - rect.left;
1428 height = rect.bottom - rect.top;
1430 if(create_window_wglext(title, width, height) == -1) {
1432 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1433 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1434 panic("Failed to create window\n");
1438 pfd.nSize = sizeof pfd;
1440 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1441 if(init_mode & GLUT_STEREO) {
1442 pfd.dwFlags |= PFD_STEREO;
1444 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1445 pfd.cColorBits = 24;
1446 if(init_mode & GLUT_ALPHA) {
1449 if(init_mode & GLUT_ACCUM) {
1450 pfd.cAccumBits = 24;
1452 if(init_mode & GLUT_DEPTH) {
1453 pfd.cDepthBits = 24;
1455 if(init_mode & GLUT_STENCIL) {
1456 pfd.cStencilBits = 8;
1458 pfd.iLayerType = PFD_MAIN_PLANE;
1460 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1461 panic("Failed to find suitable pixel format\n");
1463 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1464 panic("Failed to set the selected pixel format\n");
1466 if(!(ctx = wglCreateContext(dc))) {
1467 panic("Failed to create the OpenGL context\n");
1469 wglMakeCurrent(dc, ctx);
1471 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1472 ctx_info.rsize = pfd.cRedBits;
1473 ctx_info.gsize = pfd.cGreenBits;
1474 ctx_info.bsize = pfd.cBlueBits;
1475 ctx_info.asize = pfd.cAlphaBits;
1476 ctx_info.zsize = pfd.cDepthBits;
1477 ctx_info.ssize = pfd.cStencilBits;
1478 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1479 ctx_info.samples = 0;
1484 SetForegroundWindow(win);
1487 reshape_pending = 1;
1490 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1492 static int mouse_x, mouse_y;
1497 if(win) DestroyWindow(win);
1508 ValidateRect(win, 0);
1512 x = lparam & 0xffff;
1514 if(x != win_width && y != win_height) {
1518 reshape_pending = 0;
1519 cb_reshape(win_width, win_height);
1526 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1532 key = translate_vkey(wparam);
1535 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1539 cb_skeydown(key, mouse_x, mouse_y);
1547 key = translate_vkey(wparam);
1550 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1554 cb_skeyup(key, mouse_x, mouse_y);
1559 case WM_LBUTTONDOWN:
1560 handle_mbutton(0, 1, wparam, lparam);
1562 case WM_MBUTTONDOWN:
1563 handle_mbutton(1, 1, wparam, lparam);
1565 case WM_RBUTTONDOWN:
1566 handle_mbutton(2, 1, wparam, lparam);
1569 handle_mbutton(0, 0, wparam, lparam);
1572 handle_mbutton(1, 0, wparam, lparam);
1575 handle_mbutton(2, 0, wparam, lparam);
1579 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1580 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1582 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1588 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1592 return DefWindowProc(win, msg, wparam, lparam);
1598 static void update_modkeys(void)
1600 if(GetKeyState(VK_SHIFT) & 0x8000) {
1601 modstate |= GLUT_ACTIVE_SHIFT;
1603 modstate &= ~GLUT_ACTIVE_SHIFT;
1605 if(GetKeyState(VK_CONTROL) & 0x8000) {
1606 modstate |= GLUT_ACTIVE_CTRL;
1608 modstate &= ~GLUT_ACTIVE_CTRL;
1610 if(GetKeyState(VK_MENU) & 0x8000) {
1611 modstate |= GLUT_ACTIVE_ALT;
1613 modstate &= ~GLUT_ACTIVE_ALT;
1617 static int translate_vkey(int vkey)
1620 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1621 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1622 case VK_END: return GLUT_KEY_END;
1623 case VK_HOME: return GLUT_KEY_HOME;
1624 case VK_LEFT: return GLUT_KEY_LEFT;
1625 case VK_UP: return GLUT_KEY_UP;
1626 case VK_RIGHT: return GLUT_KEY_RIGHT;
1627 case VK_DOWN: return GLUT_KEY_DOWN;
1632 if(vkey >= 'A' && vkey <= 'Z') {
1634 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1635 vkey -= VK_F1 + GLUT_KEY_F1;
1641 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1648 x = lparam & 0xffff;
1650 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1654 static void get_window_pos(int *x, int *y)
1657 GetWindowRect(win, &rect);
1662 static void get_window_size(int *w, int *h)
1665 GetClientRect(win, &rect);
1666 *w = rect.right - rect.left;
1667 *h = rect.bottom - rect.top;
1670 static void get_screen_size(int *scrw, int *scrh)
1672 *scrw = GetSystemMetrics(SM_CXSCREEN);
1673 *scrh = GetSystemMetrics(SM_CYSCREEN);
1675 #endif /* BUILD_WIN32 */
1677 #if defined(__unix__) || defined(__APPLE__)
1678 #include <sys/time.h>
1680 #ifdef MINIGLUT_USE_LIBC
1681 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1683 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1686 static long get_msec(void)
1688 static struct timeval tv0;
1691 sys_gettimeofday(&tv, 0);
1692 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1696 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1700 static long get_msec(void)
1705 #ifdef MINIGLUT_NO_WINMM
1706 tm = GetTickCount();
1718 static void panic(const char *msg)
1720 const char *end = msg;
1722 sys_write(2, msg, end - msg);
1727 #ifdef MINIGLUT_USE_LIBC
1733 static void sys_exit(int status)
1738 static int sys_write(int fd, const void *buf, int count)
1740 return write(fd, buf, count);
1743 #else /* !MINIGLUT_USE_LIBC */
1747 static void sys_exit(int status)
1751 :: "a"(60), "D"(status));
1753 static int sys_write(int fd, const void *buf, int count)
1759 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1762 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1768 : "a"(96), "D"(tv), "S"(tz));
1771 #endif /* __x86_64__ */
1773 static void sys_exit(int status)
1777 :: "a"(1), "b"(status));
1779 static int sys_write(int fd, const void *buf, int count)
1785 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1788 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1794 : "a"(78), "b"(tv), "c"(tz));
1797 #endif /* __i386__ */
1798 #endif /* __linux__ */
1801 static void sys_exit(int status)
1803 ExitProcess(status);
1805 static int sys_write(int fd, const void *buf, int count)
1807 unsigned long wrsz = 0;
1809 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1810 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1816 #endif /* !MINIGLUT_USE_LIBC */
1819 /* ----------------- primitives ------------------ */
1820 #ifdef MINIGLUT_USE_LIBC
1824 void mglut_sincos(float angle, float *sptr, float *cptr)
1830 float mglut_atan(float x)
1835 #else /* !MINIGLUT_USE_LIBC */
1838 void mglut_sincos(float angle, float *sptr, float *cptr)
1845 : "=m"(*sptr), "=m"(*cptr)
1850 float mglut_atan(float x)
1866 void mglut_sincos(float angle, float *sptr, float *cptr)
1879 float mglut_atan(float x)
1893 #pragma aux mglut_sincos = \
1895 "fstp dword ptr [edx]" \
1896 "fstp dword ptr [eax]" \
1897 parm[8087][eax][edx] \
1900 #pragma aux mglut_atan = \
1906 #endif /* __WATCOMC__ */
1908 #endif /* !MINIGLUT_USE_LIBC */
1910 #define PI 3.1415926536f
1912 void glutSolidSphere(float rad, int slices, int stacks)
1915 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1916 float du = 1.0f / (float)slices;
1917 float dv = 1.0f / (float)stacks;
1920 for(i=0; i<stacks; i++) {
1922 for(j=0; j<slices; j++) {
1924 for(k=0; k<4; k++) {
1925 gray = k ^ (k >> 1);
1926 s = gray & 1 ? u + du : u;
1927 t = gray & 2 ? v + dv : v;
1928 theta = s * PI * 2.0f;
1930 mglut_sincos(theta, &sintheta, &costheta);
1931 mglut_sincos(phi, &sinphi, &cosphi);
1932 x = sintheta * sinphi;
1933 y = costheta * sinphi;
1938 glNormal3f(x, y, z);
1939 glVertex3f(x * rad, y * rad, z * rad);
1946 void glutWireSphere(float rad, int slices, int stacks)
1948 glPushAttrib(GL_POLYGON_BIT);
1949 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1950 glutSolidSphere(rad, slices, stacks);
1954 void glutSolidCube(float sz)
1956 int i, j, idx, gray, flip, rotx;
1957 float vpos[3], norm[3];
1958 float rad = sz * 0.5f;
1961 for(i=0; i<6; i++) {
1964 idx = (~i & 2) - rotx;
1965 norm[0] = norm[1] = norm[2] = 0.0f;
1966 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
1968 vpos[idx] = norm[idx] * rad;
1969 for(j=0; j<4; j++) {
1970 gray = j ^ (j >> 1);
1971 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
1972 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
1973 glTexCoord2f(gray & 1, gray >> 1);
1980 void glutWireCube(float sz)
1982 glPushAttrib(GL_POLYGON_BIT);
1983 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1988 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
1991 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
1992 float du = 1.0f / (float)slices;
1993 float dv = 1.0f / (float)stacks;
1996 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
1997 mglut_sincos(phi, &sinphi, &cosphi);
2000 for(i=0; i<stacks; i++) {
2002 for(j=0; j<slices; j++) {
2004 for(k=0; k<4; k++) {
2005 gray = k ^ (k >> 1);
2006 s = gray & 2 ? u + du : u;
2007 t = gray & 1 ? v + dv : v;
2008 rad = rbot + (rtop - rbot) * t;
2009 theta = s * PI * 2.0f;
2010 mglut_sincos(theta, &sintheta, &costheta);
2012 x = sintheta * cosphi;
2013 y = costheta * cosphi;
2018 glNormal3f(x, y, z);
2019 glVertex3f(sintheta * rad, costheta * rad, t * height);
2026 void glutSolidCone(float base, float height, int slices, int stacks)
2028 draw_cylinder(base, 0, height, slices, stacks);
2031 void glutWireCone(float base, float height, int slices, int stacks)
2033 glPushAttrib(GL_POLYGON_BIT);
2034 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2035 glutSolidCone(base, height, slices, stacks);
2039 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2041 draw_cylinder(rad, rad, height, slices, stacks);
2044 void glutWireCylinder(float rad, float height, int slices, int stacks)
2046 glPushAttrib(GL_POLYGON_BIT);
2047 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2048 glutSolidCylinder(rad, height, slices, stacks);
2052 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2055 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2056 float du = 1.0f / (float)rings;
2057 float dv = 1.0f / (float)sides;
2060 for(i=0; i<rings; i++) {
2062 for(j=0; j<sides; j++) {
2064 for(k=0; k<4; k++) {
2065 gray = k ^ (k >> 1);
2066 s = gray & 1 ? u + du : u;
2067 t = gray & 2 ? v + dv : v;
2068 theta = s * PI * 2.0f;
2069 phi = t * PI * 2.0f;
2070 mglut_sincos(theta, &sintheta, &costheta);
2071 mglut_sincos(phi, &sinphi, &cosphi);
2072 x = sintheta * sinphi;
2073 y = costheta * sinphi;
2078 glNormal3f(x, y, z);
2080 x = x * inner_rad + sintheta * outer_rad;
2081 y = y * inner_rad + costheta * outer_rad;
2083 glVertex3f(x, y, z);
2090 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2092 glPushAttrib(GL_POLYGON_BIT);
2093 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2094 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2098 void glutSolidTeapot(float size)
2102 void glutWireTeapot(float size)