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;
64 #error unsupported platform
70 #pragma warning (disable: 4244 4305)
75 int rsize, gsize, bsize, asize;
83 static void cleanup(void);
84 static void create_window(const char *title);
85 static void get_window_pos(int *x, int *y);
86 static void get_window_size(int *w, int *h);
87 static void get_screen_size(int *scrw, int *scrh);
89 static long get_msec(void);
90 static void panic(const char *msg);
91 static void sys_exit(int status);
92 static int sys_write(int fd, const void *buf, int count);
95 static int init_x = -1, init_y, init_width = 256, init_height = 256;
96 static unsigned int init_mode;
98 static struct ctx_info ctx_info;
99 static int cur_cursor = GLUT_CURSOR_INHERIT;
100 static int ignore_key_repeat;
102 static glut_cb cb_display;
103 static glut_cb cb_idle;
104 static glut_cb_reshape cb_reshape;
105 static glut_cb_state cb_vis, cb_entry;
106 static glut_cb_keyb cb_keydown, cb_keyup;
107 static glut_cb_special cb_skeydown, cb_skeyup;
108 static glut_cb_mouse cb_mouse;
109 static glut_cb_motion cb_motion, cb_passive;
110 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
111 static glut_cb_sbbutton cb_sball_button;
113 static int fullscreen;
114 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
116 static int win_width, win_height;
119 static int upd_pending;
122 void glutInit(int *argc, char **argv)
128 if(!(dpy = XOpenDisplay(0))) {
129 panic("Failed to connect to the X server\n");
131 scr = DefaultScreen(dpy);
132 root = RootWindow(dpy, scr);
133 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
134 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
135 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
136 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
137 if(have_netwm_fullscr()) {
138 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
141 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
142 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
143 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
144 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
146 evmask = ExposureMask | StructureNotifyMask;
148 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
149 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
150 XFreePixmap(dpy, blankpix);
157 hinst = GetModuleHandle(0);
159 wc.cbSize = sizeof wc;
160 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
161 wc.hCursor = LoadCursor(0, IDC_ARROW);
162 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
163 wc.hInstance = hinst;
164 wc.lpfnWndProc = handle_message;
165 wc.lpszClassName = "MiniGLUT";
166 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
167 if(!RegisterClassEx(&wc)) {
168 panic("Failed to register \"MiniGLUT\" window class\n");
172 get_screen_size(&init_x, &init_y);
179 void glutInitWindowPosition(int x, int y)
185 void glutInitWindowSize(int xsz, int ysz)
191 void glutInitDisplayMode(unsigned int mode)
196 void glutCreateWindow(const char *title)
198 create_window(title);
206 void glutMainLoop(void)
213 void glutPostRedisplay(void)
218 void glutIgnoreKeyRepeat(int ignore)
220 ignore_key_repeat = ignore;
223 #define UPD_EVMASK(x) \
230 if(win) XSelectInput(dpy, win, evmask); \
234 void glutIdleFunc(glut_cb func)
239 void glutDisplayFunc(glut_cb func)
244 void glutReshapeFunc(glut_cb_reshape func)
249 void glutVisibilityFunc(glut_cb_state func)
253 UPD_EVMASK(VisibilityChangeMask);
257 void glutEntryFunc(glut_cb_state func)
261 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
265 void glutKeyboardFunc(glut_cb_keyb func)
269 UPD_EVMASK(KeyPressMask);
273 void glutKeyboardUpFunc(glut_cb_keyb func)
277 UPD_EVMASK(KeyReleaseMask);
281 void glutSpecialFunc(glut_cb_special func)
285 UPD_EVMASK(KeyPressMask);
289 void glutSpecialUpFunc(glut_cb_special func)
293 UPD_EVMASK(KeyReleaseMask);
297 void glutMouseFunc(glut_cb_mouse func)
301 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
305 void glutMotionFunc(glut_cb_motion func)
309 UPD_EVMASK(ButtonMotionMask);
313 void glutPassiveMotionFunc(glut_cb_motion func)
317 UPD_EVMASK(PointerMotionMask);
321 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
323 cb_sball_motion = func;
326 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
328 cb_sball_rotate = func;
331 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
333 cb_sball_button = func;
336 int glutGet(unsigned int s)
341 get_window_pos(&x, &y);
344 get_window_pos(&x, &y);
346 case GLUT_WINDOW_WIDTH:
347 get_window_size(&x, &y);
349 case GLUT_WINDOW_HEIGHT:
350 get_window_size(&x, &y);
352 case GLUT_WINDOW_BUFFER_SIZE:
353 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
354 case GLUT_WINDOW_STENCIL_SIZE:
355 return ctx_info.ssize;
356 case GLUT_WINDOW_DEPTH_SIZE:
357 return ctx_info.zsize;
358 case GLUT_WINDOW_RED_SIZE:
359 return ctx_info.rsize;
360 case GLUT_WINDOW_GREEN_SIZE:
361 return ctx_info.gsize;
362 case GLUT_WINDOW_BLUE_SIZE:
363 return ctx_info.bsize;
364 case GLUT_WINDOW_ALPHA_SIZE:
365 return ctx_info.asize;
366 case GLUT_WINDOW_DOUBLEBUFFER:
367 return ctx_info.dblbuf;
368 case GLUT_WINDOW_RGBA:
370 case GLUT_WINDOW_NUM_SAMPLES:
371 return ctx_info.samples;
372 case GLUT_WINDOW_STEREO:
373 return ctx_info.stereo;
374 case GLUT_WINDOW_SRGB:
375 return ctx_info.srgb;
376 case GLUT_WINDOW_CURSOR:
378 case GLUT_WINDOW_COLORMAP_SIZE:
380 case GLUT_SCREEN_WIDTH:
381 get_screen_size(&x, &y);
383 case GLUT_SCREEN_HEIGHT:
384 get_screen_size(&x, &y);
386 case GLUT_INIT_DISPLAY_MODE:
388 case GLUT_INIT_WINDOW_X:
390 case GLUT_INIT_WINDOW_Y:
392 case GLUT_INIT_WINDOW_WIDTH:
394 case GLUT_INIT_WINDOW_HEIGHT:
396 case GLUT_ELAPSED_TIME:
404 int glutGetModifiers(void)
409 static int is_space(int c)
411 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
414 static const char *skip_space(const char *s)
416 while(*s && is_space(*s)) s++;
420 int glutExtensionSupported(char *ext)
422 const char *str, *eptr;
424 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
429 str = skip_space(str);
430 eptr = skip_space(ext);
431 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
435 if((!*str || is_space(*str)) && !*eptr) {
438 while(*str && !is_space(*str)) str++;
445 /* --------------- UNIX/X11 implementation ----------------- */
448 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
450 SPNAV_EVENT_BUTTON /* includes both press and release */
453 struct spnav_event_motion {
461 struct spnav_event_button {
469 struct spnav_event_motion motion;
470 struct spnav_event_button button;
474 static void handle_event(XEvent *ev);
476 static int spnav_window(Window win);
477 static int spnav_event(const XEvent *xev, union spnav_event *event);
478 static int spnav_remove_events(int type);
481 void glutMainLoopEvent(void)
486 panic("display callback not set");
489 if(!upd_pending && !cb_idle) {
490 XNextEvent(dpy, &ev);
494 while(XPending(dpy)) {
495 XNextEvent(dpy, &ev);
504 if(upd_pending && mapped) {
515 static void cleanup(void)
519 glXMakeCurrent(dpy, 0, 0);
520 XDestroyWindow(dpy, win);
524 static KeySym translate_keysym(KeySym sym)
545 static void handle_event(XEvent *ev)
548 union spnav_event sev;
557 case ConfigureNotify:
558 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
559 win_width = ev->xconfigure.width;
560 win_height = ev->xconfigure.height;
561 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
566 if(ev->xclient.message_type == xa_wm_proto) {
567 if(ev->xclient.data.l[0] == xa_wm_del_win) {
571 if(spnav_event(ev, &sev)) {
573 case SPNAV_EVENT_MOTION:
574 if(cb_sball_motion) {
575 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
577 if(cb_sball_rotate) {
578 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
580 spnav_remove_events(SPNAV_EVENT_MOTION);
583 case SPNAV_EVENT_BUTTON:
584 if(cb_sball_button) {
585 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
602 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
604 XPeekEvent(dpy, &next);
606 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
607 next.xkey.time == ev->xkey.time) {
608 /* this is a key-repeat event, ignore the release and consume
609 * the following press
611 XNextEvent(dpy, &next);
616 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
617 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
620 sym = translate_keysym(sym);
622 if(ev->type == KeyPress) {
623 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
625 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
628 if(ev->type == KeyPress) {
629 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
631 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
638 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
640 int bn = ev->xbutton.button - Button1;
641 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
642 ev->xbutton.x, ev->xbutton.y);
647 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
648 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
650 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
654 case VisibilityNotify:
656 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
660 if(cb_entry) cb_entry(GLUT_ENTERED);
663 if(cb_entry) cb_entry(GLUT_LEFT);
668 void glutSwapBuffers(void)
670 glXSwapBuffers(dpy, win);
674 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
675 * needs to resize the window to make it fullscreen. The way it does this is by
676 * querying the size of the root window (see get_screen_size), which in the
677 * case of multi-monitor setups will be the combined size of all monitors.
678 * This is problematic; the way to solve it is to use the XRandR extension, or
679 * the Xinerama extension, to figure out the dimensions of the correct video
680 * output, which would add potentially two extension support libraries to our
682 * Moreover, any X installation modern enough to support XR&R will almost
683 * certainly be running a window manager supporting the EHWM
684 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
685 * on manual resizing, and is used in preference if available, making this
686 * whole endeavor pointless.
687 * So I'll just leave it with set_fullscreen_mwm covering the entire
688 * multi-monitor area for now.
693 unsigned long functions;
694 unsigned long decorations;
696 unsigned long status;
699 #define MWM_HINTS_DECORATIONS 2
700 #define MWM_DECOR_ALL 1
702 static void set_fullscreen_mwm(int fs)
704 struct mwm_hints hints;
705 int scr_width, scr_height;
708 get_window_pos(&prev_win_x, &prev_win_y);
709 get_window_size(&prev_win_width, &prev_win_height);
710 get_screen_size(&scr_width, &scr_height);
712 hints.decorations = 0;
713 hints.flags = MWM_HINTS_DECORATIONS;
714 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
715 PropModeReplace, (unsigned char*)&hints, 5);
717 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
719 XDeleteProperty(dpy, win, xa_motif_wm_hints);
720 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
724 static int have_netwm_fullscr(void)
728 unsigned long i, count, rem;
730 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
733 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
734 &type, &fmt, &count, &rem, (unsigned char**)&prop);
736 for(i=0; i<count; i++) {
737 if(prop[i] == xa_net_wm_state_fullscr) {
749 static void set_fullscreen_ewmh(int fs)
751 XClientMessageEvent msg = {0};
753 msg.type = ClientMessage;
755 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
757 msg.data.l[0] = fs ? 1 : 0;
758 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
760 msg.data.l[3] = 1; /* source regular application */
761 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
764 static void set_fullscreen(int fs)
766 if(fullscreen == fs) return;
768 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
769 set_fullscreen_ewmh(fs);
771 } else if(xa_motif_wm_hints) {
772 set_fullscreen_mwm(fs);
777 void glutPositionWindow(int x, int y)
780 XMoveWindow(dpy, win, x, y);
783 void glutReshapeWindow(int xsz, int ysz)
786 XResizeWindow(dpy, win, xsz, ysz);
789 void glutFullScreen(void)
794 void glutSetWindowTitle(const char *title)
797 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
800 XSetWMName(dpy, win, &tprop);
804 void glutSetIconTitle(const char *title)
807 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
810 XSetWMIconName(dpy, win, &tprop);
814 void glutSetCursor(int cidx)
819 case GLUT_CURSOR_LEFT_ARROW:
820 cur = XCreateFontCursor(dpy, XC_left_ptr);
822 case GLUT_CURSOR_INHERIT:
824 case GLUT_CURSOR_NONE:
831 XDefineCursor(dpy, win, cur);
835 void glutSetColor(int idx, float r, float g, float b)
839 if(idx >= 0 && idx < cmap_size) {
841 color.red = (unsigned short)(r * 65535.0f);
842 color.green = (unsigned short)(g * 65535.0f);
843 color.blue = (unsigned short)(b * 65535.0f);
844 color.flags = DoRed | DoGreen | DoBlue;
845 XStoreColor(dpy, cmap, &color);
849 float glutGetColor(int idx, int comp)
853 if(idx < 0 || idx >= cmap_size) {
858 XQueryColor(dpy, cmap, &color);
861 return color.red / 65535.0f;
863 return color.green / 65535.0f;
865 return color.blue / 65535.0f;
872 void glutSetKeyRepeat(int repmode)
881 static XVisualInfo *choose_visual(unsigned int mode)
888 if(mode & GLUT_DOUBLE) {
889 *aptr++ = GLX_DOUBLEBUFFER;
892 if(mode & GLUT_INDEX) {
893 *aptr++ = GLX_BUFFER_SIZE;
897 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
898 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
899 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
901 if(mode & GLUT_ALPHA) {
902 *aptr++ = GLX_ALPHA_SIZE;
905 if(mode & GLUT_DEPTH) {
906 *aptr++ = GLX_DEPTH_SIZE;
909 if(mode & GLUT_STENCIL) {
910 *aptr++ = GLX_STENCIL_SIZE;
913 if(mode & GLUT_ACCUM) {
914 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
915 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
916 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
918 if(mode & GLUT_STEREO) {
919 *aptr++ = GLX_STEREO;
921 if(mode & GLUT_SRGB) {
922 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
924 if(mode & GLUT_MULTISAMPLE) {
925 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
927 *aptr++ = GLX_SAMPLES_ARB;
934 return glXChooseVisual(dpy, scr, attr);
936 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
945 static void create_window(const char *title)
947 XSetWindowAttributes xattr = {0};
949 unsigned int xattr_mask;
950 unsigned int mode = init_mode;
952 if(!(vi = choose_visual(mode))) {
954 if(!(vi = choose_visual(mode))) {
955 panic("Failed to find compatible visual\n");
959 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
961 panic("Failed to create OpenGL context\n");
964 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
965 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
966 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
967 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
968 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
969 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
970 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
971 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
972 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
973 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
975 if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
977 glXDestroyContext(dpy, ctx);
978 panic("Failed to create colormap\n");
980 cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
982 xattr.background_pixel = BlackPixel(dpy, scr);
983 xattr.colormap = cmap;
984 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
985 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
986 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
988 glXDestroyContext(dpy, ctx);
989 XFreeColormap(dpy, cmap);
990 panic("Failed to create window\n");
994 XSelectInput(dpy, win, evmask);
998 glutSetWindowTitle(title);
999 glutSetIconTitle(title);
1000 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
1001 XMapWindow(dpy, win);
1003 glXMakeCurrent(dpy, win, ctx);
1006 static void get_window_pos(int *x, int *y)
1009 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1012 static void get_window_size(int *w, int *h)
1014 XWindowAttributes wattr;
1015 XGetWindowAttributes(dpy, win, &wattr);
1020 static void get_screen_size(int *scrw, int *scrh)
1022 XWindowAttributes wattr;
1023 XGetWindowAttributes(dpy, root, &wattr);
1024 *scrw = wattr.width;
1025 *scrh = wattr.height;
1031 CMD_APP_WINDOW = 27695,
1035 static Window get_daemon_window(Display *dpy);
1036 static int catch_badwin(Display *dpy, XErrorEvent *err);
1038 #define SPNAV_INITIALIZED (xa_motion_event)
1040 static int spnav_window(Window win)
1042 int (*prev_xerr_handler)(Display*, XErrorEvent*);
1046 if(!SPNAV_INITIALIZED) {
1050 if(!(daemon_win = get_daemon_window(dpy))) {
1054 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1056 xev.type = ClientMessage;
1057 xev.xclient.send_event = False;
1058 xev.xclient.display = dpy;
1059 xev.xclient.window = win;
1060 xev.xclient.message_type = xa_command_event;
1061 xev.xclient.format = 16;
1062 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1063 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1064 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1066 XSendEvent(dpy, daemon_win, False, 0, &xev);
1069 XSetErrorHandler(prev_xerr_handler);
1073 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1075 int evtype = *(int*)arg;
1077 if(xev->type != ClientMessage) {
1081 if(xev->xclient.message_type == xa_motion_event) {
1082 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1084 if(xev->xclient.message_type == xa_button_press_event ||
1085 xev->xclient.message_type == xa_button_release_event) {
1086 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1091 static int spnav_remove_events(int type)
1095 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1101 static int spnav_event(const XEvent *xev, union spnav_event *event)
1106 xmsg_type = xev->xclient.message_type;
1108 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1109 xmsg_type != xa_button_release_event) {
1113 if(xmsg_type == xa_motion_event) {
1114 event->type = SPNAV_EVENT_MOTION;
1115 event->motion.data = &event->motion.x;
1117 for(i=0; i<6; i++) {
1118 event->motion.data[i] = xev->xclient.data.s[i + 2];
1120 event->motion.period = xev->xclient.data.s[8];
1122 event->type = SPNAV_EVENT_BUTTON;
1123 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1124 event->button.bnum = xev->xclient.data.s[2];
1129 static int mglut_strcmp(const char *s1, const char *s2)
1131 while(*s1 && *s1 == *s2) {
1138 static Window get_daemon_window(Display *dpy)
1141 XTextProperty wname;
1144 unsigned long nitems, bytes_after;
1145 unsigned char *prop;
1147 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1148 &type, &fmt, &nitems, &bytes_after, &prop);
1153 win = *(Window*)prop;
1157 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1165 static int catch_badwin(Display *dpy, XErrorEvent *err)
1172 #endif /* BUILD_X11 */
1175 /* --------------- windows implementation ----------------- */
1177 static int reshape_pending;
1179 static void update_modkeys(void);
1180 static int translate_vkey(int vkey);
1181 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1183 #ifdef MINIGLUT_WINMAIN
1184 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1187 char *argv[] = { "miniglut.exe", 0 };
1188 return main(argc, argv);
1192 void glutMainLoopEvent(void)
1197 panic("display callback not set");
1200 if(reshape_pending && cb_reshape) {
1201 reshape_pending = 0;
1202 get_window_size(&win_width, &win_height);
1203 cb_reshape(win_width, win_height);
1206 if(!upd_pending && !cb_idle) {
1207 GetMessage(&msg, 0, 0, 0);
1208 TranslateMessage(&msg);
1209 DispatchMessage(&msg);
1212 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1213 TranslateMessage(&msg);
1214 DispatchMessage(&msg);
1222 if(upd_pending && mapped) {
1228 static void cleanup(void)
1231 wglMakeCurrent(dc, 0);
1232 wglDeleteContext(ctx);
1233 UnregisterClass("MiniGLUT", hinst);
1237 void glutSwapBuffers(void)
1242 void glutPositionWindow(int x, int y)
1245 unsigned int flags = SWP_SHOWWINDOW;
1248 rect.left = prev_win_x;
1249 rect.top = prev_win_y;
1250 rect.right = rect.left + prev_win_width;
1251 rect.bottom = rect.top + prev_win_height;
1252 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1254 flags |= SWP_FRAMECHANGED;
1256 GetWindowRect(win, &rect);
1258 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1261 void glutReshapeWindow(int xsz, int ysz)
1264 unsigned int flags = SWP_SHOWWINDOW;
1267 rect.left = prev_win_x;
1268 rect.top = prev_win_y;
1269 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1271 flags |= SWP_FRAMECHANGED;
1273 GetWindowRect(win, &rect);
1275 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1278 void glutFullScreen(void)
1281 int scr_width, scr_height;
1283 if(fullscreen) return;
1285 GetWindowRect(win, &rect);
1286 prev_win_x = rect.left;
1287 prev_win_y = rect.top;
1288 prev_win_width = rect.right - rect.left;
1289 prev_win_height = rect.bottom - rect.top;
1291 get_screen_size(&scr_width, &scr_height);
1293 SetWindowLong(win, GWL_STYLE, 0);
1294 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1299 void glutSetWindowTitle(const char *title)
1301 SetWindowText(win, title);
1304 void glutSetIconTitle(const char *title)
1308 void glutSetCursor(int cidx)
1311 case GLUT_CURSOR_NONE:
1314 case GLUT_CURSOR_INHERIT:
1315 case GLUT_CURSOR_LEFT_ARROW:
1317 SetCursor(LoadCursor(0, IDC_ARROW));
1322 void glutSetColor(int idx, float r, float g, float b)
1326 if(idx < 0 || idx >= 256 || !cmap) {
1330 col.peRed = (int)(r * 255.0f);
1331 col.peGreen = (int)(g * 255.0f);
1332 col.peBlue = (int)(b * 255.0f);
1333 col.peFlags = PC_NOCOLLAPSE;
1335 SetPaletteEntries(cmap, idx, 1, &col);
1338 UnrealizeObject(cmap);
1339 SelectPalette(dc, cmap, 0);
1344 float glutGetColor(int idx, int comp)
1348 if(idx < 0 || idx >= 256 || !cmap) {
1352 if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1358 return col.peRed / 255.0f;
1360 return col.peGreen / 255.0f;
1362 return col.peBlue / 255.0f;
1369 void glutSetKeyRepeat(int repmode)
1373 #define WGL_DRAW_TO_WINDOW 0x2001
1374 #define WGL_ACCELERATION 0x2003
1375 #define WGL_SUPPORT_OPENGL 0x2010
1376 #define WGL_DOUBLE_BUFFER 0x2011
1377 #define WGL_STEREO 0x2012
1378 #define WGL_PIXEL_TYPE 0x2013
1379 #define WGL_COLOR_BITS 0x2014
1380 #define WGL_RED_BITS 0x2015
1381 #define WGL_GREEN_BITS 0x2017
1382 #define WGL_BLUE_BITS 0x2019
1383 #define WGL_ALPHA_BITS 0x201b
1384 #define WGL_ACCUM_BITS 0x201d
1385 #define WGL_DEPTH_BITS 0x2022
1386 #define WGL_STENCIL_BITS 0x2023
1387 #define WGL_FULL_ACCELERATION 0x2027
1389 #define WGL_TYPE_RGBA 0x202b
1390 #define WGL_TYPE_COLORINDEX 0x202c
1392 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1393 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1394 #define WGL_SAMPLES_ARB 0x2042
1396 static PROC wglChoosePixelFormat;
1397 static PROC wglGetPixelFormatAttribiv;
1399 #define ATTR(a, v) \
1400 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1402 static unsigned int choose_pixfmt(unsigned int mode)
1404 unsigned int num_pixfmt, pixfmt = 0;
1405 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1406 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1407 float fattr[2] = {0, 0};
1409 int *aptr = attr + 6;
1412 if(mode & GLUT_DOUBLE) {
1413 ATTR(WGL_DOUBLE_BUFFER, 1);
1416 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1417 ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1418 if(mode & GLUT_ALPHA) {
1419 ATTR(WGL_ALPHA_BITS, 4);
1421 if(mode & GLUT_DEPTH) {
1422 ATTR(WGL_DEPTH_BITS, 16);
1424 if(mode & GLUT_STENCIL) {
1425 ATTR(WGL_STENCIL_BITS, 1);
1427 if(mode & GLUT_ACCUM) {
1428 ATTR(WGL_ACCUM_BITS, 1);
1430 if(mode & GLUT_STEREO) {
1431 ATTR(WGL_STEREO, 1);
1433 if(mode & GLUT_SRGB) {
1434 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1436 if(mode & GLUT_MULTISAMPLE) {
1437 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1438 *aptr++ = WGL_SAMPLES_ARB;
1444 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1453 static PIXELFORMATDESCRIPTOR pfd;
1454 static PIXELFORMATDESCRIPTOR tmppfd = {
1455 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1456 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1457 PFD_MAIN_PLANE, 0, 0, 0, 0
1459 #define TMPCLASS "TempMiniGLUT"
1461 #define GETATTR(attr, vptr) \
1464 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1467 static int create_window_wglext(const char *title, int width, int height)
1469 WNDCLASSEX wc = {0};
1475 /* create a temporary window and GL context, just to query and retrieve
1476 * the wglChoosePixelFormatEXT function
1478 wc.cbSize = sizeof wc;
1479 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1480 wc.hCursor = LoadCursor(0, IDC_ARROW);
1481 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1482 wc.hInstance = hinst;
1483 wc.lpfnWndProc = DefWindowProc;
1484 wc.lpszClassName = TMPCLASS;
1485 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1486 if(!RegisterClassEx(&wc)) {
1489 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1490 width, height, 0, 0, hinst, 0))) {
1493 tmpdc = GetDC(tmpwin);
1495 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1496 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1497 !(tmpctx = wglCreateContext(tmpdc))) {
1500 wglMakeCurrent(tmpdc, tmpctx);
1502 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1503 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1506 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1510 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1514 wglMakeCurrent(0, 0);
1515 wglDeleteContext(tmpctx);
1516 DestroyWindow(tmpwin);
1517 UnregisterClass(TMPCLASS, hinst);
1519 /* create the real window and context */
1520 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1521 init_y, width, height, 0, 0, hinst, 0))) {
1522 panic("Failed to create window\n");
1526 if(!(pixfmt = choose_pixfmt(init_mode))) {
1527 panic("Failed to find suitable pixel format\n");
1529 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1530 panic("Failed to set the selected pixel format\n");
1532 if(!(ctx = wglCreateContext(dc))) {
1533 panic("Failed to create the OpenGL context\n");
1535 wglMakeCurrent(dc, ctx);
1537 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1538 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1539 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1540 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1541 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1542 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1543 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1544 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1545 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1551 wglMakeCurrent(0, 0);
1552 wglDeleteContext(tmpctx);
1555 DestroyWindow(tmpwin);
1557 UnregisterClass(TMPCLASS, hinst);
1562 static void create_window(const char *title)
1565 int i, pixfmt, width, height;
1566 char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1572 rect.right = init_x + init_width;
1573 rect.bottom = init_y + init_height;
1574 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1575 width = rect.right - rect.left;
1576 height = rect.bottom - rect.top;
1578 memset(&pfd, 0, sizeof pfd);
1579 pfd.nSize = sizeof pfd;
1581 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1582 if(init_mode & GLUT_STEREO) {
1583 pfd.dwFlags |= PFD_STEREO;
1585 if(init_mode & GLUT_INDEX) {
1586 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1589 pfd.iPixelType = PFD_TYPE_RGBA;
1590 pfd.cColorBits = 24;
1592 if(init_mode & GLUT_ALPHA) {
1595 if(init_mode & GLUT_ACCUM) {
1596 pfd.cAccumBits = 24;
1598 if(init_mode & GLUT_DEPTH) {
1599 pfd.cDepthBits = 24;
1601 if(init_mode & GLUT_STENCIL) {
1602 pfd.cStencilBits = 8;
1604 pfd.iLayerType = PFD_MAIN_PLANE;
1606 if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) {
1607 if(create_window_wglext(title, width, height) != -1) {
1612 /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method
1613 * failed, just use the old-style ChoosePixelFormat method instead
1615 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1616 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1617 panic("Failed to create window\n");
1621 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1622 panic("Failed to find suitable pixel format\n");
1624 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1625 panic("Failed to set the selected pixel format\n");
1627 if(!(ctx = wglCreateContext(dc))) {
1628 panic("Failed to create the OpenGL context\n");
1630 wglMakeCurrent(dc, ctx);
1632 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1633 ctx_info.rsize = pfd.cRedBits;
1634 ctx_info.gsize = pfd.cGreenBits;
1635 ctx_info.bsize = pfd.cBlueBits;
1636 ctx_info.asize = pfd.cAlphaBits;
1637 ctx_info.zsize = pfd.cDepthBits;
1638 ctx_info.ssize = pfd.cStencilBits;
1639 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1640 ctx_info.samples = 0;
1645 SetForegroundWindow(win);
1648 if(init_mode & GLUT_INDEX) {
1649 logpal = (LOGPALETTE*)palbuf;
1651 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1653 logpal->palVersion = 0x300;
1654 logpal->palNumEntries = 256;
1656 if(!(cmap = CreatePalette(logpal))) {
1657 panic("Failed to create palette in indexed mode");
1659 SelectPalette(dc, cmap, 0);
1664 if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) {
1665 /* for RGB mode in 8bpp displays we also need to set up a palette
1666 * with RGB 332 colors
1668 logpal = (LOGPALETTE*)palbuf;
1670 logpal->palVersion = 0x300;
1671 logpal->palNumEntries = 256;
1673 for(i=0; i<256; i++) {
1675 int g = (i >> 3) & 7;
1676 int b = (i >> 5) & 3;
1678 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1679 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1680 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1681 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1684 if((cmap = CreatePalette(logpal))) {
1685 SelectPalette(dc, cmap, 0);
1693 reshape_pending = 1;
1696 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1698 static int mouse_x, mouse_y;
1703 if(win) DestroyWindow(win);
1714 ValidateRect(win, 0);
1718 x = lparam & 0xffff;
1720 if(x != win_width && y != win_height) {
1724 reshape_pending = 0;
1725 cb_reshape(win_width, win_height);
1732 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1738 key = translate_vkey(wparam);
1741 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1745 cb_skeydown(key, mouse_x, mouse_y);
1753 key = translate_vkey(wparam);
1756 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1760 cb_skeyup(key, mouse_x, mouse_y);
1765 case WM_LBUTTONDOWN:
1766 handle_mbutton(0, 1, wparam, lparam);
1768 case WM_MBUTTONDOWN:
1769 handle_mbutton(1, 1, wparam, lparam);
1771 case WM_RBUTTONDOWN:
1772 handle_mbutton(2, 1, wparam, lparam);
1775 handle_mbutton(0, 0, wparam, lparam);
1778 handle_mbutton(1, 0, wparam, lparam);
1781 handle_mbutton(2, 0, wparam, lparam);
1785 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1786 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1788 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1794 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1798 return DefWindowProc(win, msg, wparam, lparam);
1804 static void update_modkeys(void)
1806 if(GetKeyState(VK_SHIFT) & 0x8000) {
1807 modstate |= GLUT_ACTIVE_SHIFT;
1809 modstate &= ~GLUT_ACTIVE_SHIFT;
1811 if(GetKeyState(VK_CONTROL) & 0x8000) {
1812 modstate |= GLUT_ACTIVE_CTRL;
1814 modstate &= ~GLUT_ACTIVE_CTRL;
1816 if(GetKeyState(VK_MENU) & 0x8000) {
1817 modstate |= GLUT_ACTIVE_ALT;
1819 modstate &= ~GLUT_ACTIVE_ALT;
1824 #define VK_OEM_1 0xba
1825 #define VK_OEM_2 0xbf
1826 #define VK_OEM_3 0xc0
1827 #define VK_OEM_4 0xdb
1828 #define VK_OEM_5 0xdc
1829 #define VK_OEM_6 0xdd
1830 #define VK_OEM_7 0xde
1833 static int translate_vkey(int vkey)
1836 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1837 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1838 case VK_END: return GLUT_KEY_END;
1839 case VK_HOME: return GLUT_KEY_HOME;
1840 case VK_LEFT: return GLUT_KEY_LEFT;
1841 case VK_UP: return GLUT_KEY_UP;
1842 case VK_RIGHT: return GLUT_KEY_RIGHT;
1843 case VK_DOWN: return GLUT_KEY_DOWN;
1844 case VK_OEM_1: return ';';
1845 case VK_OEM_2: return '/';
1846 case VK_OEM_3: return '`';
1847 case VK_OEM_4: return '[';
1848 case VK_OEM_5: return '\\';
1849 case VK_OEM_6: return ']';
1850 case VK_OEM_7: return '\'';
1855 if(vkey >= 'A' && vkey <= 'Z') {
1857 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1858 vkey -= VK_F1 + GLUT_KEY_F1;
1864 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1871 x = lparam & 0xffff;
1873 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1877 static void get_window_pos(int *x, int *y)
1880 GetWindowRect(win, &rect);
1885 static void get_window_size(int *w, int *h)
1888 GetClientRect(win, &rect);
1889 *w = rect.right - rect.left;
1890 *h = rect.bottom - rect.top;
1893 static void get_screen_size(int *scrw, int *scrh)
1895 *scrw = GetSystemMetrics(SM_CXSCREEN);
1896 *scrh = GetSystemMetrics(SM_CYSCREEN);
1898 #endif /* BUILD_WIN32 */
1900 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1901 #include <sys/time.h>
1903 #ifdef MINIGLUT_USE_LIBC
1904 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1906 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1909 static long get_msec(void)
1911 static struct timeval tv0;
1914 sys_gettimeofday(&tv, 0);
1915 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1919 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1923 static long get_msec(void)
1928 #ifdef MINIGLUT_NO_WINMM
1929 tm = GetTickCount();
1941 static void panic(const char *msg)
1943 const char *end = msg;
1945 sys_write(2, msg, end - msg);
1950 #ifdef MINIGLUT_USE_LIBC
1958 static void sys_exit(int status)
1963 static int sys_write(int fd, const void *buf, int count)
1965 return write(fd, buf, count);
1968 #else /* !MINIGLUT_USE_LIBC */
1972 static void sys_exit(int status)
1976 :: "a"(60), "D"(status));
1978 static int sys_write(int fd, const void *buf, int count)
1984 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1987 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1993 : "a"(96), "D"(tv), "S"(tz));
1996 #endif /* __x86_64__ */
1998 static void sys_exit(int status)
2002 :: "a"(1), "b"(status));
2004 static int sys_write(int fd, const void *buf, int count)
2010 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2013 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2019 : "a"(78), "b"(tv), "c"(tz));
2022 #endif /* __i386__ */
2023 #endif /* __linux__ */
2026 static void sys_exit(int status)
2028 ExitProcess(status);
2030 static int sys_write(int fd, const void *buf, int count)
2032 unsigned long wrsz = 0;
2034 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2035 if(!WriteFile(out, buf, count, &wrsz, 0)) {
2041 #endif /* !MINIGLUT_USE_LIBC */
2044 /* ----------------- primitives ------------------ */
2045 #ifdef MINIGLUT_USE_LIBC
2049 void mglut_sincos(float angle, float *sptr, float *cptr)
2055 float mglut_atan(float x)
2060 #else /* !MINIGLUT_USE_LIBC */
2063 void mglut_sincos(float angle, float *sptr, float *cptr)
2070 : "=m"(*sptr), "=m"(*cptr)
2075 float mglut_atan(float x)
2091 void mglut_sincos(float angle, float *sptr, float *cptr)
2104 float mglut_atan(float x)
2118 #pragma aux mglut_sincos = \
2120 "fstp dword ptr [edx]" \
2121 "fstp dword ptr [eax]" \
2122 parm[8087][eax][edx] \
2125 #pragma aux mglut_atan = \
2131 #endif /* __WATCOMC__ */
2133 #endif /* !MINIGLUT_USE_LIBC */
2135 #define PI 3.1415926536f
2137 void glutSolidSphere(float rad, int slices, int stacks)
2140 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2141 float du = 1.0f / (float)slices;
2142 float dv = 1.0f / (float)stacks;
2145 for(i=0; i<stacks; i++) {
2147 for(j=0; j<slices; j++) {
2149 for(k=0; k<4; k++) {
2150 gray = k ^ (k >> 1);
2151 s = gray & 1 ? u + du : u;
2152 t = gray & 2 ? v + dv : v;
2153 theta = s * PI * 2.0f;
2155 mglut_sincos(theta, &sintheta, &costheta);
2156 mglut_sincos(phi, &sinphi, &cosphi);
2157 x = sintheta * sinphi;
2158 y = costheta * sinphi;
2163 glNormal3f(x, y, z);
2164 glVertex3f(x * rad, y * rad, z * rad);
2171 void glutWireSphere(float rad, int slices, int stacks)
2173 glPushAttrib(GL_POLYGON_BIT);
2174 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2175 glutSolidSphere(rad, slices, stacks);
2179 void glutSolidCube(float sz)
2181 int i, j, idx, gray, flip, rotx;
2182 float vpos[3], norm[3];
2183 float rad = sz * 0.5f;
2186 for(i=0; i<6; i++) {
2189 idx = (~i & 2) - rotx;
2190 norm[0] = norm[1] = norm[2] = 0.0f;
2191 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2193 vpos[idx] = norm[idx] * rad;
2194 for(j=0; j<4; j++) {
2195 gray = j ^ (j >> 1);
2196 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2197 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2198 glTexCoord2f(gray & 1, gray >> 1);
2205 void glutWireCube(float sz)
2207 glPushAttrib(GL_POLYGON_BIT);
2208 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2213 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2216 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2217 float du = 1.0f / (float)slices;
2218 float dv = 1.0f / (float)stacks;
2221 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2222 mglut_sincos(phi, &sinphi, &cosphi);
2225 for(i=0; i<stacks; i++) {
2227 for(j=0; j<slices; j++) {
2229 for(k=0; k<4; k++) {
2230 gray = k ^ (k >> 1);
2231 s = gray & 2 ? u + du : u;
2232 t = gray & 1 ? v + dv : v;
2233 rad = rbot + (rtop - rbot) * t;
2234 theta = s * PI * 2.0f;
2235 mglut_sincos(theta, &sintheta, &costheta);
2237 x = sintheta * cosphi;
2238 y = costheta * cosphi;
2243 glNormal3f(x, y, z);
2244 glVertex3f(sintheta * rad, costheta * rad, t * height);
2251 void glutSolidCone(float base, float height, int slices, int stacks)
2253 draw_cylinder(base, 0, height, slices, stacks);
2256 void glutWireCone(float base, float height, int slices, int stacks)
2258 glPushAttrib(GL_POLYGON_BIT);
2259 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2260 glutSolidCone(base, height, slices, stacks);
2264 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2266 draw_cylinder(rad, rad, height, slices, stacks);
2269 void glutWireCylinder(float rad, float height, int slices, int stacks)
2271 glPushAttrib(GL_POLYGON_BIT);
2272 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2273 glutSolidCylinder(rad, height, slices, stacks);
2277 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2280 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2281 float du = 1.0f / (float)rings;
2282 float dv = 1.0f / (float)sides;
2285 for(i=0; i<rings; i++) {
2287 for(j=0; j<sides; j++) {
2289 for(k=0; k<4; k++) {
2290 gray = k ^ (k >> 1);
2291 s = gray & 1 ? u + du : u;
2292 t = gray & 2 ? v + dv : v;
2293 theta = s * PI * 2.0f;
2294 phi = t * PI * 2.0f;
2295 mglut_sincos(theta, &sintheta, &costheta);
2296 mglut_sincos(phi, &sinphi, &cosphi);
2297 x = sintheta * sinphi;
2298 y = costheta * sinphi;
2303 glNormal3f(x, y, z);
2305 x = x * inner_rad + sintheta * outer_rad;
2306 y = y * inner_rad + costheta * outer_rad;
2308 glVertex3f(x, y, z);
2315 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2317 glPushAttrib(GL_POLYGON_BIT);
2318 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2319 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2323 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2324 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2326 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2328 #define PATCH_SUBDIV 7
2330 static float teapot_part_flip[] = {
2331 1, 1, 1, 1, /* rim flip */
2332 1, 1, 1, 1, /* body1 flip */
2333 1, 1, 1, 1, /* body2 flip */
2334 1, 1, 1, 1, /* lid patch 1 flip */
2335 1, 1, 1, 1, /* lid patch 2 flip */
2336 1, -1, /* handle 1 flip */
2337 1, -1, /* handle 2 flip */
2338 1, -1, /* spout 1 flip */
2339 1, -1, /* spout 2 flip */
2340 1, 1, 1, 1 /* bottom flip */
2343 static float teapot_part_rot[] = {
2344 0, 90, 180, 270, /* rim rotations */
2345 0, 90, 180, 270, /* body patch 1 rotations */
2346 0, 90, 180, 270, /* body patch 2 rotations */
2347 0, 90, 180, 270, /* lid patch 1 rotations */
2348 0, 90, 180, 270, /* lid patch 2 rotations */
2349 0, 0, /* handle 1 rotations */
2350 0, 0, /* handle 2 rotations */
2351 0, 0, /* spout 1 rotations */
2352 0, 0, /* spout 2 rotations */
2353 0, 90, 180, 270 /* bottom rotations */
2357 static int teapot_index[] = {
2359 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2360 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2361 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2362 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2364 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2365 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2366 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2367 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2369 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2370 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2371 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2372 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2374 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2375 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2376 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2377 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2379 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2380 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2381 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2382 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2384 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2385 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2387 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2388 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2390 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2391 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2393 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2394 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2396 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2397 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2398 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2399 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2403 static float teapot_verts[][3] = {
2404 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2405 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2406 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2407 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2408 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2409 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2410 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2411 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2412 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2413 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2414 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2415 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2416 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2417 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2418 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2419 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2420 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2421 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2422 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2423 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2424 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2425 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2426 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2427 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2428 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2429 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2430 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2431 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2432 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2433 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2434 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2435 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2436 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2437 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2438 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2439 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2440 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2441 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2442 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2443 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2444 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2445 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2446 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2447 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2448 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2449 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2450 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2451 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2452 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2453 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2454 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2455 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2456 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2457 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2458 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2459 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2460 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2461 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2462 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2463 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2464 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2465 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2466 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2467 { 0.8400, -1.5000, 0.07500 }
2470 static void draw_patch(int *index, int flip, float scale);
2471 static float bernstein(int i, float x);
2473 void glutSolidTeapot(float size)
2479 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2480 float flip = teapot_part_flip[i];
2481 float rot = teapot_part_rot[i];
2483 glMatrixMode(GL_MODELVIEW);
2485 glTranslatef(0, -3.15 * size * 0.5, 0);
2486 glRotatef(rot, 0, 1, 0);
2487 glScalef(1, 1, flip);
2488 glRotatef(-90, 1, 0, 0);
2490 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2496 void glutWireTeapot(float size)
2498 glPushAttrib(GL_POLYGON_BIT);
2499 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2500 glutSolidTeapot(size);
2505 static void bezier_patch(float *res, float *cp, float u, float v)
2509 res[0] = res[1] = res[2] = 0.0f;
2511 for(j=0; j<4; j++) {
2512 for(i=0; i<4; i++) {
2513 float bu = bernstein(i, u);
2514 float bv = bernstein(j, v);
2516 res[0] += cp[0] * bu * bv;
2517 res[1] += cp[1] * bu * bv;
2518 res[2] += cp[2] * bu * bv;
2525 static float rsqrt(float x)
2527 float xhalf = x * 0.5f;
2529 i = 0x5f3759df - (i >> 1);
2531 x = x * (1.5f - xhalf * x * x);
2536 #define CROSS(res, a, b) \
2538 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2539 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2540 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2543 #define NORMALIZE(v) \
2545 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2553 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2555 float tang[3], bitan[3], tmp[3];
2557 bezier_patch(tang, cp, u + DT, v);
2558 bezier_patch(tmp, cp, u - DT, v);
2563 bezier_patch(bitan, cp, u, v + DT);
2564 bezier_patch(tmp, cp, u, v - DT);
2569 CROSS(res, tang, bitan);
2575 static float bernstein(int i, float x)
2577 float invx = 1.0f - x;
2581 return invx * invx * invx;
2583 return 3.0f * x * invx * invx;
2585 return 3.0f * x * x * invx;
2594 static void draw_patch(int *index, int flip, float scale)
2596 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2597 static const float voffs[4] = {0, 1, 1, 0};
2603 float du = 1.0 / PATCH_SUBDIV;
2604 float dv = 1.0 / PATCH_SUBDIV;
2606 /* collect control points */
2607 for(i=0; i<16; i++) {
2608 cp[i * 3] = teapot_verts[index[i]][0];
2609 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2610 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2617 for(i=0; i<PATCH_SUBDIV; i++) {
2619 for(j=0; j<PATCH_SUBDIV; j++) {
2621 for(k=0; k<4; k++) {
2622 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2624 /* top/bottom normal hack */
2628 } else if(pt[2] < 0.00001) {
2632 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2637 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);