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 int rsize, gsize, bsize, asize;
78 static void cleanup(void);
79 static void create_window(const char *title);
80 static void get_window_pos(int *x, int *y);
81 static void get_window_size(int *w, int *h);
82 static void get_screen_size(int *scrw, int *scrh);
84 static long get_msec(void);
85 static void panic(const char *msg);
86 static void warn(const char *msg);
87 static void sys_exit(int status);
88 static int sys_write(int fd, const void *buf, int count);
91 static int init_x = -1, init_y, init_width = 256, init_height = 256;
92 static unsigned int init_mode;
94 static struct ctx_info ctx_info;
95 static int cur_cursor = GLUT_CURSOR_INHERIT;
96 static int ignore_key_repeat;
98 static glut_cb cb_display;
99 static glut_cb cb_idle;
100 static glut_cb_reshape cb_reshape;
101 static glut_cb_state cb_vis, cb_entry;
102 static glut_cb_keyb cb_keydown, cb_keyup;
103 static glut_cb_special cb_skeydown, cb_skeyup;
104 static glut_cb_mouse cb_mouse;
105 static glut_cb_motion cb_motion, cb_passive;
106 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
107 static glut_cb_sbbutton cb_sball_button;
109 static int fullscreen;
110 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
112 static int win_width, win_height;
115 static int upd_pending;
118 void glutInit(int *argc, char **argv)
124 if(!(dpy = XOpenDisplay(0))) {
125 panic("Failed to connect to the X server\n");
127 scr = DefaultScreen(dpy);
128 root = RootWindow(dpy, scr);
129 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
130 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
131 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
132 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
133 if(have_netwm_fullscr()) {
134 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
137 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
138 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
139 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
140 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
142 evmask = ExposureMask | StructureNotifyMask;
144 if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) {
145 blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0);
146 XFreePixmap(dpy, blankpix);
153 hinst = GetModuleHandle(0);
155 wc.cbSize = sizeof wc;
156 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
157 wc.hCursor = LoadCursor(0, IDC_ARROW);
158 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
159 wc.hInstance = hinst;
160 wc.lpfnWndProc = handle_message;
161 wc.lpszClassName = "MiniGLUT";
162 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
163 if(!RegisterClassEx(&wc)) {
164 panic("Failed to register \"MiniGLUT\" window class\n");
168 get_screen_size(&init_x, &init_y);
175 void glutInitWindowPosition(int x, int y)
181 void glutInitWindowSize(int xsz, int ysz)
187 void glutInitDisplayMode(unsigned int mode)
192 void glutCreateWindow(const char *title)
194 create_window(title);
202 void glutMainLoop(void)
209 void glutPostRedisplay(void)
214 void glutIgnoreKeyRepeat(int ignore)
216 ignore_key_repeat = ignore;
219 #define UPD_EVMASK(x) \
226 if(win) XSelectInput(dpy, win, evmask); \
230 void glutIdleFunc(glut_cb func)
235 void glutDisplayFunc(glut_cb func)
240 void glutReshapeFunc(glut_cb_reshape func)
245 void glutVisibilityFunc(glut_cb_state func)
249 UPD_EVMASK(VisibilityChangeMask);
253 void glutEntryFunc(glut_cb_state func)
257 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
261 void glutKeyboardFunc(glut_cb_keyb func)
265 UPD_EVMASK(KeyPressMask);
269 void glutKeyboardUpFunc(glut_cb_keyb func)
273 UPD_EVMASK(KeyReleaseMask);
277 void glutSpecialFunc(glut_cb_special func)
281 UPD_EVMASK(KeyPressMask);
285 void glutSpecialUpFunc(glut_cb_special func)
289 UPD_EVMASK(KeyReleaseMask);
293 void glutMouseFunc(glut_cb_mouse func)
297 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
301 void glutMotionFunc(glut_cb_motion func)
305 UPD_EVMASK(ButtonMotionMask);
309 void glutPassiveMotionFunc(glut_cb_motion func)
313 UPD_EVMASK(PointerMotionMask);
317 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
319 cb_sball_motion = func;
322 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
324 cb_sball_rotate = func;
327 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
329 cb_sball_button = func;
332 int glutGet(unsigned int s)
337 get_window_pos(&x, &y);
340 get_window_pos(&x, &y);
342 case GLUT_WINDOW_WIDTH:
343 get_window_size(&x, &y);
345 case GLUT_WINDOW_HEIGHT:
346 get_window_size(&x, &y);
348 case GLUT_WINDOW_BUFFER_SIZE:
349 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
350 case GLUT_WINDOW_STENCIL_SIZE:
351 return ctx_info.ssize;
352 case GLUT_WINDOW_DEPTH_SIZE:
353 return ctx_info.zsize;
354 case GLUT_WINDOW_RED_SIZE:
355 return ctx_info.rsize;
356 case GLUT_WINDOW_GREEN_SIZE:
357 return ctx_info.gsize;
358 case GLUT_WINDOW_BLUE_SIZE:
359 return ctx_info.bsize;
360 case GLUT_WINDOW_ALPHA_SIZE:
361 return ctx_info.asize;
362 case GLUT_WINDOW_DOUBLEBUFFER:
363 return ctx_info.dblbuf;
364 case GLUT_WINDOW_RGBA:
366 case GLUT_WINDOW_NUM_SAMPLES:
367 return ctx_info.samples;
368 case GLUT_WINDOW_STEREO:
369 return ctx_info.stereo;
370 case GLUT_WINDOW_SRGB:
371 return ctx_info.srgb;
372 case GLUT_WINDOW_CURSOR:
374 case GLUT_WINDOW_COLORMAP_SIZE:
376 case GLUT_SCREEN_WIDTH:
377 get_screen_size(&x, &y);
379 case GLUT_SCREEN_HEIGHT:
380 get_screen_size(&x, &y);
382 case GLUT_INIT_DISPLAY_MODE:
384 case GLUT_INIT_WINDOW_X:
386 case GLUT_INIT_WINDOW_Y:
388 case GLUT_INIT_WINDOW_WIDTH:
390 case GLUT_INIT_WINDOW_HEIGHT:
392 case GLUT_ELAPSED_TIME:
400 int glutGetModifiers(void)
405 static int is_space(int c)
407 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
410 static const char *skip_space(const char *s)
412 while(*s && is_space(*s)) s++;
416 int glutExtensionSupported(char *ext)
418 const char *str, *eptr;
420 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
425 str = skip_space(str);
426 eptr = skip_space(ext);
427 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
431 if((!*str || is_space(*str)) && !*eptr) {
434 while(*str && !is_space(*str)) str++;
441 /* --------------- UNIX/X11 implementation ----------------- */
444 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
446 SPNAV_EVENT_BUTTON /* includes both press and release */
449 struct spnav_event_motion {
457 struct spnav_event_button {
465 struct spnav_event_motion motion;
466 struct spnav_event_button button;
470 static void handle_event(XEvent *ev);
472 static int spnav_window(Window win);
473 static int spnav_event(const XEvent *xev, union spnav_event *event);
474 static int spnav_remove_events(int type);
477 void glutMainLoopEvent(void)
482 panic("display callback not set");
485 if(!upd_pending && !cb_idle) {
486 XNextEvent(dpy, &ev);
490 while(XPending(dpy)) {
491 XNextEvent(dpy, &ev);
500 if(upd_pending && mapped) {
511 static void cleanup(void)
515 glXMakeCurrent(dpy, 0, 0);
516 XDestroyWindow(dpy, win);
520 static KeySym translate_keysym(KeySym sym)
541 static void handle_event(XEvent *ev)
544 union spnav_event sev;
553 case ConfigureNotify:
554 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
555 win_width = ev->xconfigure.width;
556 win_height = ev->xconfigure.height;
557 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
562 if(ev->xclient.message_type == xa_wm_proto) {
563 if(ev->xclient.data.l[0] == xa_wm_del_win) {
567 if(spnav_event(ev, &sev)) {
569 case SPNAV_EVENT_MOTION:
570 if(cb_sball_motion) {
571 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
573 if(cb_sball_rotate) {
574 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
576 spnav_remove_events(SPNAV_EVENT_MOTION);
579 case SPNAV_EVENT_BUTTON:
580 if(cb_sball_button) {
581 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
598 if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) {
600 XPeekEvent(dpy, &next);
602 if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode &&
603 next.xkey.time == ev->xkey.time) {
604 /* this is a key-repeat event, ignore the release and consume
605 * the following press
607 XNextEvent(dpy, &next);
612 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
613 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
616 sym = translate_keysym(sym);
618 if(ev->type == KeyPress) {
619 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
621 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
624 if(ev->type == KeyPress) {
625 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
627 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
634 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
636 int bn = ev->xbutton.button - Button1;
637 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
638 ev->xbutton.x, ev->xbutton.y);
643 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
644 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
646 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
650 case VisibilityNotify:
652 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
656 if(cb_entry) cb_entry(GLUT_ENTERED);
659 if(cb_entry) cb_entry(GLUT_LEFT);
664 void glutSwapBuffers(void)
666 glXSwapBuffers(dpy, win);
670 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
671 * needs to resize the window to make it fullscreen. The way it does this is by
672 * querying the size of the root window (see get_screen_size), which in the
673 * case of multi-monitor setups will be the combined size of all monitors.
674 * This is problematic; the way to solve it is to use the XRandR extension, or
675 * the Xinerama extension, to figure out the dimensions of the correct video
676 * output, which would add potentially two extension support libraries to our
678 * Moreover, any X installation modern enough to support XR&R will almost
679 * certainly be running a window manager supporting the EHWM
680 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
681 * on manual resizing, and is used in preference if available, making this
682 * whole endeavor pointless.
683 * So I'll just leave it with set_fullscreen_mwm covering the entire
684 * multi-monitor area for now.
689 unsigned long functions;
690 unsigned long decorations;
692 unsigned long status;
695 #define MWM_HINTS_DECORATIONS 2
696 #define MWM_DECOR_ALL 1
698 static void set_fullscreen_mwm(int fs)
700 struct mwm_hints hints;
701 int scr_width, scr_height;
704 get_window_pos(&prev_win_x, &prev_win_y);
705 get_window_size(&prev_win_width, &prev_win_height);
706 get_screen_size(&scr_width, &scr_height);
708 hints.decorations = 0;
709 hints.flags = MWM_HINTS_DECORATIONS;
710 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
711 PropModeReplace, (unsigned char*)&hints, 5);
713 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
715 XDeleteProperty(dpy, win, xa_motif_wm_hints);
716 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
720 static int have_netwm_fullscr(void)
724 unsigned long i, count, rem;
726 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
729 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
730 &type, &fmt, &count, &rem, (unsigned char**)&prop);
732 for(i=0; i<count; i++) {
733 if(prop[i] == xa_net_wm_state_fullscr) {
745 static void set_fullscreen_ewmh(int fs)
747 XClientMessageEvent msg = {0};
749 msg.type = ClientMessage;
751 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
753 msg.data.l[0] = fs ? 1 : 0;
754 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
756 msg.data.l[3] = 1; /* source regular application */
757 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
760 static void set_fullscreen(int fs)
762 if(fullscreen == fs) return;
764 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
765 set_fullscreen_ewmh(fs);
767 } else if(xa_motif_wm_hints) {
768 set_fullscreen_mwm(fs);
773 void glutPositionWindow(int x, int y)
776 XMoveWindow(dpy, win, x, y);
779 void glutReshapeWindow(int xsz, int ysz)
782 XResizeWindow(dpy, win, xsz, ysz);
785 void glutFullScreen(void)
790 void glutSetWindowTitle(const char *title)
793 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
796 XSetWMName(dpy, win, &tprop);
800 void glutSetIconTitle(const char *title)
803 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
806 XSetWMIconName(dpy, win, &tprop);
810 void glutSetCursor(int cidx)
815 case GLUT_CURSOR_LEFT_ARROW:
816 cur = XCreateFontCursor(dpy, XC_left_ptr);
818 case GLUT_CURSOR_INHERIT:
820 case GLUT_CURSOR_NONE:
827 XDefineCursor(dpy, win, cur);
831 void glutSetColor(int idx, float r, float g, float b)
835 if(idx >= 0 && idx < cmap_size) {
837 color.red = (unsigned short)(r * 65535.0f);
838 color.green = (unsigned short)(g * 65535.0f);
839 color.blue = (unsigned short)(b * 65535.0f);
840 color.flags = DoRed | DoGreen | DoBlue;
841 XStoreColor(dpy, cmap, &color);
845 float glutGetColor(int idx, int comp)
849 if(idx < 0 || idx >= cmap_size) {
854 XQueryColor(dpy, cmap, &color);
857 return color.red / 65535.0f;
859 return color.green / 65535.0f;
861 return color.blue / 65535.0f;
868 void glutSetKeyRepeat(int repmode)
877 static XVisualInfo *choose_visual(unsigned int mode)
884 if(mode & GLUT_DOUBLE) {
885 *aptr++ = GLX_DOUBLEBUFFER;
888 if(mode & GLUT_INDEX) {
889 *aptr++ = GLX_BUFFER_SIZE;
893 *aptr++ = GLX_RED_SIZE; *aptr++ = 1;
894 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1;
895 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1;
897 if(mode & GLUT_ALPHA) {
898 *aptr++ = GLX_ALPHA_SIZE;
901 if(mode & GLUT_DEPTH) {
902 *aptr++ = GLX_DEPTH_SIZE;
905 if(mode & GLUT_STENCIL) {
906 *aptr++ = GLX_STENCIL_SIZE;
909 if(mode & GLUT_ACCUM) {
910 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
911 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
912 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
914 if(mode & GLUT_STEREO) {
915 *aptr++ = GLX_STEREO;
917 if(mode & GLUT_SRGB) {
918 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
920 if(mode & GLUT_MULTISAMPLE) {
921 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
923 *aptr++ = GLX_SAMPLES_ARB;
930 return glXChooseVisual(dpy, scr, attr);
932 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
941 static void create_window(const char *title)
943 XSetWindowAttributes xattr = {0};
945 unsigned int xattr_mask;
946 unsigned int mode = init_mode;
948 if(!(vi = choose_visual(mode))) {
950 if(!(vi = choose_visual(mode))) {
951 panic("Failed to find compatible visual\n");
955 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
957 panic("Failed to create OpenGL context\n");
960 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
961 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
962 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
963 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
964 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
965 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
966 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
967 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
968 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
969 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
971 if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) {
973 glXDestroyContext(dpy, ctx);
974 panic("Failed to create colormap\n");
976 cmap_size = GLUT_INDEX ? vi->colormap_size : 0;
978 xattr.background_pixel = BlackPixel(dpy, scr);
979 xattr.colormap = cmap;
980 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
981 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
982 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
984 glXDestroyContext(dpy, ctx);
985 XFreeColormap(dpy, cmap);
986 panic("Failed to create window\n");
990 XSelectInput(dpy, win, evmask);
994 glutSetWindowTitle(title);
995 glutSetIconTitle(title);
996 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
997 XMapWindow(dpy, win);
999 glXMakeCurrent(dpy, win, ctx);
1002 static void get_window_pos(int *x, int *y)
1005 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
1008 static void get_window_size(int *w, int *h)
1010 XWindowAttributes wattr;
1011 XGetWindowAttributes(dpy, win, &wattr);
1016 static void get_screen_size(int *scrw, int *scrh)
1018 XWindowAttributes wattr;
1019 XGetWindowAttributes(dpy, root, &wattr);
1020 *scrw = wattr.width;
1021 *scrh = wattr.height;
1027 CMD_APP_WINDOW = 27695,
1031 static Window get_daemon_window(Display *dpy);
1032 static int catch_badwin(Display *dpy, XErrorEvent *err);
1034 #define SPNAV_INITIALIZED (xa_motion_event)
1036 static int spnav_window(Window win)
1038 int (*prev_xerr_handler)(Display*, XErrorEvent*);
1042 if(!SPNAV_INITIALIZED) {
1046 if(!(daemon_win = get_daemon_window(dpy))) {
1050 prev_xerr_handler = XSetErrorHandler(catch_badwin);
1052 xev.type = ClientMessage;
1053 xev.xclient.send_event = False;
1054 xev.xclient.display = dpy;
1055 xev.xclient.window = win;
1056 xev.xclient.message_type = xa_command_event;
1057 xev.xclient.format = 16;
1058 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
1059 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
1060 xev.xclient.data.s[2] = CMD_APP_WINDOW;
1062 XSendEvent(dpy, daemon_win, False, 0, &xev);
1065 XSetErrorHandler(prev_xerr_handler);
1069 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
1071 int evtype = *(int*)arg;
1073 if(xev->type != ClientMessage) {
1077 if(xev->xclient.message_type == xa_motion_event) {
1078 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
1080 if(xev->xclient.message_type == xa_button_press_event ||
1081 xev->xclient.message_type == xa_button_release_event) {
1082 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
1087 static int spnav_remove_events(int type)
1091 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1097 static int spnav_event(const XEvent *xev, union spnav_event *event)
1102 xmsg_type = xev->xclient.message_type;
1104 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1105 xmsg_type != xa_button_release_event) {
1109 if(xmsg_type == xa_motion_event) {
1110 event->type = SPNAV_EVENT_MOTION;
1111 event->motion.data = &event->motion.x;
1113 for(i=0; i<6; i++) {
1114 event->motion.data[i] = xev->xclient.data.s[i + 2];
1116 event->motion.period = xev->xclient.data.s[8];
1118 event->type = SPNAV_EVENT_BUTTON;
1119 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1120 event->button.bnum = xev->xclient.data.s[2];
1125 static int mglut_strcmp(const char *s1, const char *s2)
1127 while(*s1 && *s1 == *s2) {
1134 static Window get_daemon_window(Display *dpy)
1137 XTextProperty wname;
1140 unsigned long nitems, bytes_after;
1141 unsigned char *prop;
1143 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1144 &type, &fmt, &nitems, &bytes_after, &prop);
1149 win = *(Window*)prop;
1153 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1161 static int catch_badwin(Display *dpy, XErrorEvent *err)
1168 #endif /* BUILD_X11 */
1171 /* --------------- windows implementation ----------------- */
1173 static int reshape_pending;
1175 static void update_modkeys(void);
1176 static int translate_vkey(int vkey);
1177 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1179 #ifdef MINIGLUT_WINMAIN
1180 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1183 char *argv[] = { "miniglut.exe", 0 };
1184 return main(argc, argv);
1188 void glutMainLoopEvent(void)
1193 panic("display callback not set");
1196 if(reshape_pending && cb_reshape) {
1197 reshape_pending = 0;
1198 get_window_size(&win_width, &win_height);
1199 cb_reshape(win_width, win_height);
1202 if(!upd_pending && !cb_idle) {
1203 GetMessage(&msg, 0, 0, 0);
1204 TranslateMessage(&msg);
1205 DispatchMessage(&msg);
1208 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1209 TranslateMessage(&msg);
1210 DispatchMessage(&msg);
1218 if(upd_pending && mapped) {
1224 static void cleanup(void)
1227 wglMakeCurrent(dc, 0);
1228 wglDeleteContext(ctx);
1229 UnregisterClass("MiniGLUT", hinst);
1233 void glutSwapBuffers(void)
1238 void glutPositionWindow(int x, int y)
1241 unsigned int flags = SWP_SHOWWINDOW;
1244 rect.left = prev_win_x;
1245 rect.top = prev_win_y;
1246 rect.right = rect.left + prev_win_width;
1247 rect.bottom = rect.top + prev_win_height;
1248 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1250 flags |= SWP_FRAMECHANGED;
1252 GetWindowRect(win, &rect);
1254 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1257 void glutReshapeWindow(int xsz, int ysz)
1260 unsigned int flags = SWP_SHOWWINDOW;
1263 rect.left = prev_win_x;
1264 rect.top = prev_win_y;
1265 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1267 flags |= SWP_FRAMECHANGED;
1269 GetWindowRect(win, &rect);
1271 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1274 void glutFullScreen(void)
1277 int scr_width, scr_height;
1279 if(fullscreen) return;
1281 GetWindowRect(win, &rect);
1282 prev_win_x = rect.left;
1283 prev_win_y = rect.top;
1284 prev_win_width = rect.right - rect.left;
1285 prev_win_height = rect.bottom - rect.top;
1287 get_screen_size(&scr_width, &scr_height);
1289 SetWindowLong(win, GWL_STYLE, 0);
1290 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1295 void glutSetWindowTitle(const char *title)
1297 SetWindowText(win, title);
1300 void glutSetIconTitle(const char *title)
1304 void glutSetCursor(int cidx)
1307 case GLUT_CURSOR_NONE:
1310 case GLUT_CURSOR_INHERIT:
1311 case GLUT_CURSOR_LEFT_ARROW:
1313 SetCursor(LoadCursor(0, IDC_ARROW));
1318 void glutSetColor(int idx, float r, float g, float b)
1322 if(idx < 0 || idx >= 256 || !cmap) {
1326 col.peRed = (int)(r * 255.0f);
1327 col.peGreen = (int)(g * 255.0f);
1328 col.peBlue = (int)(b * 255.0f);
1329 col.peFlags = PC_NOCOLLAPSE;
1331 SetPaletteEntries(cmap, idx, 1, &col);
1334 UnrealizeObject(cmap);
1335 SelectPalette(dc, cmap, 0);
1340 float glutGetColor(int idx, int comp)
1344 if(idx < 0 || idx >= 256 || !cmap) {
1348 if(!GetPaletteEntries(cmap, idx, 1, &col)) {
1354 return col.peRed / 255.0f;
1356 return col.peGreen / 255.0f;
1358 return col.peBlue / 255.0f;
1365 void glutSetKeyRepeat(int repmode)
1369 #define WGL_DRAW_TO_WINDOW 0x2001
1370 #define WGL_ACCELERATION 0x2003
1371 #define WGL_SUPPORT_OPENGL 0x2010
1372 #define WGL_DOUBLE_BUFFER 0x2011
1373 #define WGL_STEREO 0x2012
1374 #define WGL_PIXEL_TYPE 0x2013
1375 #define WGL_COLOR_BITS 0x2014
1376 #define WGL_RED_BITS 0x2015
1377 #define WGL_GREEN_BITS 0x2017
1378 #define WGL_BLUE_BITS 0x2019
1379 #define WGL_ALPHA_BITS 0x201b
1380 #define WGL_ACCUM_BITS 0x201d
1381 #define WGL_DEPTH_BITS 0x2022
1382 #define WGL_STENCIL_BITS 0x2023
1383 #define WGL_FULL_ACCELERATION 0x2027
1385 #define WGL_TYPE_RGBA 0x202b
1386 #define WGL_TYPE_COLORINDEX 0x202c
1388 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
1389 #define WGL_SAMPLE_BUFFERS_ARB 0x2041
1390 #define WGL_SAMPLES_ARB 0x2042
1392 static PROC wglChoosePixelFormat;
1393 static PROC wglGetPixelFormatAttribiv;
1395 #define ATTR(a, v) \
1396 do { *aptr++ = (a); *aptr++ = (v); } while(0)
1398 static unsigned int choose_pixfmt(unsigned int mode)
1400 unsigned int num_pixfmt, pixfmt = 0;
1401 int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1,
1402 WGL_ACCELERATION, WGL_FULL_ACCELERATION };
1403 float fattr[2] = {0, 0};
1405 int *aptr = attr + 6;
1408 if(mode & GLUT_DOUBLE) {
1409 ATTR(WGL_DOUBLE_BUFFER, 1);
1412 ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA);
1413 ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24);
1414 if(mode & GLUT_ALPHA) {
1415 ATTR(WGL_ALPHA_BITS, 4);
1417 if(mode & GLUT_DEPTH) {
1418 ATTR(WGL_DEPTH_BITS, 16);
1420 if(mode & GLUT_STENCIL) {
1421 ATTR(WGL_STENCIL_BITS, 1);
1423 if(mode & GLUT_ACCUM) {
1424 ATTR(WGL_ACCUM_BITS, 1);
1426 if(mode & GLUT_STEREO) {
1427 ATTR(WGL_STEREO, 1);
1429 if(mode & GLUT_SRGB) {
1430 ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
1432 if(mode & GLUT_MULTISAMPLE) {
1433 ATTR(WGL_SAMPLE_BUFFERS_ARB, 1);
1434 *aptr++ = WGL_SAMPLES_ARB;
1440 while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) {
1449 static PIXELFORMATDESCRIPTOR pfd;
1450 static PIXELFORMATDESCRIPTOR tmppfd = {
1451 sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1452 PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0,
1453 PFD_MAIN_PLANE, 0, 0, 0, 0
1455 #define TMPCLASS "TempMiniGLUT"
1457 #define GETATTR(attr, vptr) \
1460 wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \
1463 static int create_window_wglext(const char *title, int width, int height)
1465 WNDCLASSEX wc = {0};
1469 int i, pixfmt, curbpp;
1470 char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)];
1473 /* create a temporary window and GL context, just to query and retrieve
1474 * the wglChoosePixelFormatEXT function
1476 wc.cbSize = sizeof wc;
1477 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
1478 wc.hCursor = LoadCursor(0, IDC_ARROW);
1479 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
1480 wc.hInstance = hinst;
1481 wc.lpfnWndProc = DefWindowProc;
1482 wc.lpszClassName = TMPCLASS;
1483 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1484 if(!RegisterClassEx(&wc)) {
1487 if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0,
1488 width, height, 0, 0, hinst, 0))) {
1491 tmpdc = GetDC(tmpwin);
1493 if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) ||
1494 !SetPixelFormat(tmpdc, pixfmt, &tmppfd) ||
1495 !(tmpctx = wglCreateContext(tmpdc))) {
1498 wglMakeCurrent(tmpdc, tmpctx);
1500 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) {
1501 if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) {
1504 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) {
1508 if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) {
1512 wglMakeCurrent(0, 0);
1513 wglDeleteContext(tmpctx);
1514 DestroyWindow(tmpwin);
1515 UnregisterClass(TMPCLASS, hinst);
1517 /* create the real window and context */
1518 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x,
1519 init_y, width, height, 0, 0, hinst, 0))) {
1520 panic("Failed to create window\n");
1524 if(!(pixfmt = choose_pixfmt(init_mode))) {
1525 panic("Failed to find suitable pixel format\n");
1527 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1528 panic("Failed to set the selected pixel format\n");
1530 if(!(ctx = wglCreateContext(dc))) {
1531 panic("Failed to create the OpenGL context\n");
1533 wglMakeCurrent(dc, ctx);
1535 GETATTR(WGL_RED_BITS, &ctx_info.rsize);
1536 GETATTR(WGL_GREEN_BITS, &ctx_info.gsize);
1537 GETATTR(WGL_BLUE_BITS, &ctx_info.bsize);
1538 GETATTR(WGL_ALPHA_BITS, &ctx_info.asize);
1539 GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize);
1540 GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize);
1541 GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf);
1542 GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
1543 GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples);
1545 if(init_mode & GLUT_INDEX) {
1546 logpal = (LOGPALETTE*)palbuf;
1548 GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry);
1550 logpal->palVersion = 0x300;
1551 logpal->palNumEntries = 256;
1553 if(!(cmap = CreatePalette(logpal))) {
1554 panic("Failed to create palette in indexed mode");
1556 SelectPalette(dc, cmap, 0);
1561 if((curbpp = GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES)) <= 8) {
1562 /* for RGB mode in 8bpp displays we also need to set up a palette
1563 * with RGB 332 colors
1565 logpal = (LOGPALETTE*)palbuf;
1567 logpal->palVersion = 0x300;
1568 logpal->palNumEntries = 256;
1570 for(i=0; i<256; i++) {
1572 int g = (i >> 3) & 7;
1573 int b = (i >> 5) & 3;
1575 logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1);
1576 logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1);
1577 logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b;
1578 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1581 if(!(cmap = CreatePalette(logpal))) {
1582 warn("Failed to create RGB 332 palette on palettized mode. Colors will be wrong\n");
1584 SelectPalette(dc, cmap, 0);
1595 wglMakeCurrent(0, 0);
1596 wglDeleteContext(tmpctx);
1599 DestroyWindow(tmpwin);
1601 UnregisterClass(TMPCLASS, hinst);
1606 static void create_window(const char *title)
1614 rect.right = init_x + init_width;
1615 rect.bottom = init_y + init_height;
1616 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1617 width = rect.right - rect.left;
1618 height = rect.bottom - rect.top;
1620 memset(&pfd, 0, sizeof pfd);
1621 pfd.nSize = sizeof pfd;
1623 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1624 if(init_mode & GLUT_STEREO) {
1625 pfd.dwFlags |= PFD_STEREO;
1627 if(init_mode & GLUT_INDEX) {
1628 pfd.iPixelType = PFD_TYPE_RGBA;
1631 pfd.iPixelType = PFD_TYPE_COLORINDEX;
1632 pfd.cColorBits = 24;
1634 if(init_mode & GLUT_ALPHA) {
1637 if(init_mode & GLUT_ACCUM) {
1638 pfd.cAccumBits = 24;
1640 if(init_mode & GLUT_DEPTH) {
1641 pfd.cDepthBits = 24;
1643 if(init_mode & GLUT_STENCIL) {
1644 pfd.cStencilBits = 8;
1646 pfd.iLayerType = PFD_MAIN_PLANE;
1649 if(create_window_wglext(title, width, height) == -1) {
1651 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW,
1652 rect.left, rect.top, width, height, 0, 0, hinst, 0))) {
1653 panic("Failed to create window\n");
1657 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1658 panic("Failed to find suitable pixel format\n");
1660 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1661 panic("Failed to set the selected pixel format\n");
1663 if(!(ctx = wglCreateContext(dc))) {
1664 panic("Failed to create the OpenGL context\n");
1666 wglMakeCurrent(dc, ctx);
1668 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1669 ctx_info.rsize = pfd.cRedBits;
1670 ctx_info.gsize = pfd.cGreenBits;
1671 ctx_info.bsize = pfd.cBlueBits;
1672 ctx_info.asize = pfd.cAlphaBits;
1673 ctx_info.zsize = pfd.cDepthBits;
1674 ctx_info.ssize = pfd.cStencilBits;
1675 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1676 ctx_info.samples = 0;
1681 SetForegroundWindow(win);
1684 reshape_pending = 1;
1687 static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1689 static int mouse_x, mouse_y;
1694 if(win) DestroyWindow(win);
1705 ValidateRect(win, 0);
1709 x = lparam & 0xffff;
1711 if(x != win_width && y != win_height) {
1715 reshape_pending = 0;
1716 cb_reshape(win_width, win_height);
1723 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1729 key = translate_vkey(wparam);
1732 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1736 cb_skeydown(key, mouse_x, mouse_y);
1744 key = translate_vkey(wparam);
1747 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1751 cb_skeyup(key, mouse_x, mouse_y);
1756 case WM_LBUTTONDOWN:
1757 handle_mbutton(0, 1, wparam, lparam);
1759 case WM_MBUTTONDOWN:
1760 handle_mbutton(1, 1, wparam, lparam);
1762 case WM_RBUTTONDOWN:
1763 handle_mbutton(2, 1, wparam, lparam);
1766 handle_mbutton(0, 0, wparam, lparam);
1769 handle_mbutton(1, 0, wparam, lparam);
1772 handle_mbutton(2, 0, wparam, lparam);
1776 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1777 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1779 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1785 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1789 return DefWindowProc(win, msg, wparam, lparam);
1795 static void update_modkeys(void)
1797 if(GetKeyState(VK_SHIFT) & 0x8000) {
1798 modstate |= GLUT_ACTIVE_SHIFT;
1800 modstate &= ~GLUT_ACTIVE_SHIFT;
1802 if(GetKeyState(VK_CONTROL) & 0x8000) {
1803 modstate |= GLUT_ACTIVE_CTRL;
1805 modstate &= ~GLUT_ACTIVE_CTRL;
1807 if(GetKeyState(VK_MENU) & 0x8000) {
1808 modstate |= GLUT_ACTIVE_ALT;
1810 modstate &= ~GLUT_ACTIVE_ALT;
1814 static int translate_vkey(int vkey)
1817 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1818 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1819 case VK_END: return GLUT_KEY_END;
1820 case VK_HOME: return GLUT_KEY_HOME;
1821 case VK_LEFT: return GLUT_KEY_LEFT;
1822 case VK_UP: return GLUT_KEY_UP;
1823 case VK_RIGHT: return GLUT_KEY_RIGHT;
1824 case VK_DOWN: return GLUT_KEY_DOWN;
1825 case VK_OEM_1: return ';';
1826 case VK_OEM_2: return '/';
1827 case VK_OEM_3: return '`';
1828 case VK_OEM_4: return '[';
1829 case VK_OEM_5: return '\\';
1830 case VK_OEM_6: return ']';
1831 case VK_OEM_7: return '\'';
1836 if(vkey >= 'A' && vkey <= 'Z') {
1838 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1839 vkey -= VK_F1 + GLUT_KEY_F1;
1845 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1852 x = lparam & 0xffff;
1854 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1858 static void get_window_pos(int *x, int *y)
1861 GetWindowRect(win, &rect);
1866 static void get_window_size(int *w, int *h)
1869 GetClientRect(win, &rect);
1870 *w = rect.right - rect.left;
1871 *h = rect.bottom - rect.top;
1874 static void get_screen_size(int *scrw, int *scrh)
1876 *scrw = GetSystemMetrics(SM_CXSCREEN);
1877 *scrh = GetSystemMetrics(SM_CYSCREEN);
1879 #endif /* BUILD_WIN32 */
1881 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
1882 #include <sys/time.h>
1884 #ifdef MINIGLUT_USE_LIBC
1885 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1887 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1890 static long get_msec(void)
1892 static struct timeval tv0;
1895 sys_gettimeofday(&tv, 0);
1896 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1900 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1904 static long get_msec(void)
1909 #ifdef MINIGLUT_NO_WINMM
1910 tm = GetTickCount();
1922 static void panic(const char *msg)
1924 const char *end = msg;
1926 sys_write(2, msg, end - msg);
1930 static void warn(const char *msg)
1932 const char *end = msg;
1934 sys_write(2, msg, end - msg);
1938 #ifdef MINIGLUT_USE_LIBC
1946 static void sys_exit(int status)
1951 static int sys_write(int fd, const void *buf, int count)
1953 return write(fd, buf, count);
1956 #else /* !MINIGLUT_USE_LIBC */
1960 static void sys_exit(int status)
1964 :: "a"(60), "D"(status));
1966 static int sys_write(int fd, const void *buf, int count)
1972 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1975 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1981 : "a"(96), "D"(tv), "S"(tz));
1984 #endif /* __x86_64__ */
1986 static void sys_exit(int status)
1990 :: "a"(1), "b"(status));
1992 static int sys_write(int fd, const void *buf, int count)
1998 : "a"(4), "b"(fd), "c"(buf), "d"(count));
2001 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
2007 : "a"(78), "b"(tv), "c"(tz));
2010 #endif /* __i386__ */
2011 #endif /* __linux__ */
2014 static void sys_exit(int status)
2016 ExitProcess(status);
2018 static int sys_write(int fd, const void *buf, int count)
2020 unsigned long wrsz = 0;
2022 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2023 if(!WriteFile(out, buf, count, &wrsz, 0)) {
2029 #endif /* !MINIGLUT_USE_LIBC */
2032 /* ----------------- primitives ------------------ */
2033 #ifdef MINIGLUT_USE_LIBC
2037 void mglut_sincos(float angle, float *sptr, float *cptr)
2043 float mglut_atan(float x)
2048 #else /* !MINIGLUT_USE_LIBC */
2051 void mglut_sincos(float angle, float *sptr, float *cptr)
2058 : "=m"(*sptr), "=m"(*cptr)
2063 float mglut_atan(float x)
2079 void mglut_sincos(float angle, float *sptr, float *cptr)
2092 float mglut_atan(float x)
2106 #pragma aux mglut_sincos = \
2108 "fstp dword ptr [edx]" \
2109 "fstp dword ptr [eax]" \
2110 parm[8087][eax][edx] \
2113 #pragma aux mglut_atan = \
2119 #endif /* __WATCOMC__ */
2121 #endif /* !MINIGLUT_USE_LIBC */
2123 #define PI 3.1415926536f
2125 void glutSolidSphere(float rad, int slices, int stacks)
2128 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2129 float du = 1.0f / (float)slices;
2130 float dv = 1.0f / (float)stacks;
2133 for(i=0; i<stacks; i++) {
2135 for(j=0; j<slices; j++) {
2137 for(k=0; k<4; k++) {
2138 gray = k ^ (k >> 1);
2139 s = gray & 1 ? u + du : u;
2140 t = gray & 2 ? v + dv : v;
2141 theta = s * PI * 2.0f;
2143 mglut_sincos(theta, &sintheta, &costheta);
2144 mglut_sincos(phi, &sinphi, &cosphi);
2145 x = sintheta * sinphi;
2146 y = costheta * sinphi;
2151 glNormal3f(x, y, z);
2152 glVertex3f(x * rad, y * rad, z * rad);
2159 void glutWireSphere(float rad, int slices, int stacks)
2161 glPushAttrib(GL_POLYGON_BIT);
2162 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2163 glutSolidSphere(rad, slices, stacks);
2167 void glutSolidCube(float sz)
2169 int i, j, idx, gray, flip, rotx;
2170 float vpos[3], norm[3];
2171 float rad = sz * 0.5f;
2174 for(i=0; i<6; i++) {
2177 idx = (~i & 2) - rotx;
2178 norm[0] = norm[1] = norm[2] = 0.0f;
2179 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
2181 vpos[idx] = norm[idx] * rad;
2182 for(j=0; j<4; j++) {
2183 gray = j ^ (j >> 1);
2184 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
2185 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
2186 glTexCoord2f(gray & 1, gray >> 1);
2193 void glutWireCube(float sz)
2195 glPushAttrib(GL_POLYGON_BIT);
2196 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2201 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
2204 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
2205 float du = 1.0f / (float)slices;
2206 float dv = 1.0f / (float)stacks;
2209 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
2210 mglut_sincos(phi, &sinphi, &cosphi);
2213 for(i=0; i<stacks; i++) {
2215 for(j=0; j<slices; j++) {
2217 for(k=0; k<4; k++) {
2218 gray = k ^ (k >> 1);
2219 s = gray & 2 ? u + du : u;
2220 t = gray & 1 ? v + dv : v;
2221 rad = rbot + (rtop - rbot) * t;
2222 theta = s * PI * 2.0f;
2223 mglut_sincos(theta, &sintheta, &costheta);
2225 x = sintheta * cosphi;
2226 y = costheta * cosphi;
2231 glNormal3f(x, y, z);
2232 glVertex3f(sintheta * rad, costheta * rad, t * height);
2239 void glutSolidCone(float base, float height, int slices, int stacks)
2241 draw_cylinder(base, 0, height, slices, stacks);
2244 void glutWireCone(float base, float height, int slices, int stacks)
2246 glPushAttrib(GL_POLYGON_BIT);
2247 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2248 glutSolidCone(base, height, slices, stacks);
2252 void glutSolidCylinder(float rad, float height, int slices, int stacks)
2254 draw_cylinder(rad, rad, height, slices, stacks);
2257 void glutWireCylinder(float rad, float height, int slices, int stacks)
2259 glPushAttrib(GL_POLYGON_BIT);
2260 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2261 glutSolidCylinder(rad, height, slices, stacks);
2265 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
2268 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
2269 float du = 1.0f / (float)rings;
2270 float dv = 1.0f / (float)sides;
2273 for(i=0; i<rings; i++) {
2275 for(j=0; j<sides; j++) {
2277 for(k=0; k<4; k++) {
2278 gray = k ^ (k >> 1);
2279 s = gray & 1 ? u + du : u;
2280 t = gray & 2 ? v + dv : v;
2281 theta = s * PI * 2.0f;
2282 phi = t * PI * 2.0f;
2283 mglut_sincos(theta, &sintheta, &costheta);
2284 mglut_sincos(phi, &sinphi, &cosphi);
2285 x = sintheta * sinphi;
2286 y = costheta * sinphi;
2291 glNormal3f(x, y, z);
2293 x = x * inner_rad + sintheta * outer_rad;
2294 y = y * inner_rad + costheta * outer_rad;
2296 glVertex3f(x, y, z);
2303 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
2305 glPushAttrib(GL_POLYGON_BIT);
2306 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2307 glutSolidTorus(inner_rad, outer_rad, sides, rings);
2311 #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index)
2312 #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts)
2314 #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16)
2316 #define PATCH_SUBDIV 7
2318 static float teapot_part_flip[] = {
2319 1, 1, 1, 1, /* rim flip */
2320 1, 1, 1, 1, /* body1 flip */
2321 1, 1, 1, 1, /* body2 flip */
2322 1, 1, 1, 1, /* lid patch 1 flip */
2323 1, 1, 1, 1, /* lid patch 2 flip */
2324 1, -1, /* handle 1 flip */
2325 1, -1, /* handle 2 flip */
2326 1, -1, /* spout 1 flip */
2327 1, -1, /* spout 2 flip */
2328 1, 1, 1, 1 /* bottom flip */
2331 static float teapot_part_rot[] = {
2332 0, 90, 180, 270, /* rim rotations */
2333 0, 90, 180, 270, /* body patch 1 rotations */
2334 0, 90, 180, 270, /* body patch 2 rotations */
2335 0, 90, 180, 270, /* lid patch 1 rotations */
2336 0, 90, 180, 270, /* lid patch 2 rotations */
2337 0, 0, /* handle 1 rotations */
2338 0, 0, /* handle 2 rotations */
2339 0, 0, /* spout 1 rotations */
2340 0, 0, /* spout 2 rotations */
2341 0, 90, 180, 270 /* bottom rotations */
2345 static int teapot_index[] = {
2347 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2348 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2349 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2350 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
2352 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2353 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2354 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2355 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
2357 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2358 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2359 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2360 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2362 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2363 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2364 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2365 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3,
2367 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2368 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2369 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2370 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
2372 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2373 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
2375 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2376 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67,
2378 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2379 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
2381 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2382 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
2384 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2385 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2386 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37,
2387 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37
2391 static float teapot_verts[][3] = {
2392 { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 },
2393 { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 },
2394 { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 },
2395 { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 },
2396 { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 },
2397 { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 },
2398 { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 },
2399 { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 },
2400 { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 },
2401 { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 },
2402 { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 },
2403 { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 },
2404 { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 },
2405 { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 },
2406 { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 },
2407 { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 },
2408 { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 },
2409 { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 },
2410 { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 },
2411 { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 },
2412 { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 },
2413 { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 },
2414 { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 },
2415 { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 },
2416 { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 },
2417 { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 },
2418 { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 },
2419 { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 },
2420 { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 },
2421 { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 },
2422 { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 },
2423 { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 },
2424 { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 },
2425 { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 },
2426 { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 },
2427 { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 },
2428 { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 },
2429 { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 },
2430 { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 },
2431 { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 },
2432 { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 },
2433 { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 },
2434 { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 },
2435 { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 },
2436 { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 },
2437 { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 },
2438 { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 },
2439 { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 },
2440 { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 },
2441 { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 },
2442 { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 },
2443 { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 },
2444 { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 },
2445 { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 },
2446 { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 },
2447 { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 },
2448 { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 },
2449 { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 },
2450 { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 },
2451 { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 },
2452 { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 },
2453 { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 },
2454 { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 },
2455 { 0.8400, -1.5000, 0.07500 }
2458 static void draw_patch(int *index, int flip, float scale);
2459 static float bernstein(int i, float x);
2461 void glutSolidTeapot(float size)
2467 for(i=0; i<NUM_TEAPOT_PATCHES; i++) {
2468 float flip = teapot_part_flip[i];
2469 float rot = teapot_part_rot[i];
2471 glMatrixMode(GL_MODELVIEW);
2473 glTranslatef(0, -3.15 * size * 0.5, 0);
2474 glRotatef(rot, 0, 1, 0);
2475 glScalef(1, 1, flip);
2476 glRotatef(-90, 1, 0, 0);
2478 draw_patch(teapot_index + i * 16, flip < 0.0 ? 1 : 0, size);
2484 void glutWireTeapot(float size)
2486 glPushAttrib(GL_POLYGON_BIT);
2487 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2488 glutSolidTeapot(size);
2493 static void bezier_patch(float *res, float *cp, float u, float v)
2497 res[0] = res[1] = res[2] = 0.0f;
2499 for(j=0; j<4; j++) {
2500 for(i=0; i<4; i++) {
2501 float bu = bernstein(i, u);
2502 float bv = bernstein(j, v);
2504 res[0] += cp[0] * bu * bv;
2505 res[1] += cp[1] * bu * bv;
2506 res[2] += cp[2] * bu * bv;
2513 static float rsqrt(float x)
2515 float xhalf = x * 0.5f;
2517 i = 0x5f3759df - (i >> 1);
2519 x = x * (1.5f - xhalf * x * x);
2524 #define CROSS(res, a, b) \
2526 (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \
2527 (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \
2528 (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \
2531 #define NORMALIZE(v) \
2533 float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
2541 static void bezier_patch_norm(float *res, float *cp, float u, float v)
2543 float tang[3], bitan[3], tmp[3];
2545 bezier_patch(tang, cp, u + DT, v);
2546 bezier_patch(tmp, cp, u - DT, v);
2551 bezier_patch(bitan, cp, u, v + DT);
2552 bezier_patch(tmp, cp, u, v - DT);
2557 CROSS(res, tang, bitan);
2563 static float bernstein(int i, float x)
2565 float invx = 1.0f - x;
2569 return invx * invx * invx;
2571 return 3.0f * x * invx * invx;
2573 return 3.0f * x * x * invx;
2582 static void draw_patch(int *index, int flip, float scale)
2584 static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}};
2585 static const float voffs[4] = {0, 1, 1, 0};
2591 float du = 1.0 / PATCH_SUBDIV;
2592 float dv = 1.0 / PATCH_SUBDIV;
2594 /* collect control points */
2595 for(i=0; i<16; i++) {
2596 cp[i * 3] = teapot_verts[index[i]][0];
2597 cp[i * 3 + 1] = teapot_verts[index[i]][1];
2598 cp[i * 3 + 2] = teapot_verts[index[i]][2];
2605 for(i=0; i<PATCH_SUBDIV; i++) {
2607 for(j=0; j<PATCH_SUBDIV; j++) {
2609 for(k=0; k<4; k++) {
2610 bezier_patch(pt, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2612 /* top/bottom normal hack */
2616 } else if(pt[2] < 0.00001) {
2620 bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv);
2625 glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale);