2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
21 #include <X11/keysym.h>
22 #include <X11/cursorfont.h>
26 #ifndef GLX_SAMPLE_BUFFERS_ARB
27 #define GLX_SAMPLE_BUFFERS_ARB 100000
28 #define GLX_SAMPLES_ARB 100001
30 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
31 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
35 static Window win, root;
37 static GLXContext ctx;
38 static Atom xa_wm_proto, xa_wm_del_win;
39 static Atom xa_net_wm_state, xa_net_wm_state_fullscr;
40 static Atom xa_motif_wm_hints;
41 static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event;
42 static unsigned int evmask;
44 static int have_netwm_fullscr(void);
51 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
53 static HINSTANCE hinst;
59 #error unsupported platform
65 int rsize, gsize, bsize, asize;
73 static void cleanup(void);
74 static void create_window(const char *title);
75 static void get_window_pos(int *x, int *y);
76 static void get_window_size(int *w, int *h);
77 static void get_screen_size(int *scrw, int *scrh);
79 static long get_msec(void);
80 static void panic(const char *msg);
81 static void sys_exit(int status);
82 static int sys_write(int fd, const void *buf, int count);
85 static int init_x = -1, init_y, init_width = 256, init_height = 256;
86 static unsigned int init_mode;
88 static struct ctx_info ctx_info;
89 static int cur_cursor = GLUT_CURSOR_INHERIT;
91 static glut_cb cb_display;
92 static glut_cb cb_idle;
93 static glut_cb_reshape cb_reshape;
94 static glut_cb_state cb_vis, cb_entry;
95 static glut_cb_keyb cb_keydown, cb_keyup;
96 static glut_cb_special cb_skeydown, cb_skeyup;
97 static glut_cb_mouse cb_mouse;
98 static glut_cb_motion cb_motion, cb_passive;
99 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
100 static glut_cb_sbbutton cb_sball_button;
102 static int fullscreen;
103 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
105 static int win_width, win_height;
108 static int upd_pending;
111 void glutInit(int *argc, char **argv)
114 if(!(dpy = XOpenDisplay(0))) {
115 panic("Failed to connect to the X server\n");
117 scr = DefaultScreen(dpy);
118 root = RootWindow(dpy, scr);
119 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
120 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
121 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
122 if(have_netwm_fullscr()) {
123 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
124 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
127 xa_motion_event = XInternAtom(dpy, "MotionEvent", True);
128 xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True);
129 xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True);
130 xa_command_event = XInternAtom(dpy, "CommandEvent", True);
132 evmask = ExposureMask | StructureNotifyMask;
138 hinst = GetModuleHandle(0);
140 wc.cbSize = sizeof wc;
141 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
142 wc.hCursor = LoadCursor(0, IDC_ARROW);
143 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
144 wc.hInstance = hinst;
145 wc.lpfnWndProc = handle_message;
146 wc.lpszClassName = "MiniGLUT";
147 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
148 if(!RegisterClassEx(&wc)) {
149 panic("Failed to register \"MiniGLUT\" window class\n");
153 get_screen_size(&init_x, &init_y);
160 void glutInitWindowPosition(int x, int y)
166 void glutInitWindowSize(int xsz, int ysz)
172 void glutInitDisplayMode(unsigned int mode)
177 void glutCreateWindow(const char *title)
179 create_window(title);
187 void glutMainLoop(void)
194 void glutPostRedisplay(void)
199 #define UPD_EVMASK(x) \
206 if(win) XSelectInput(dpy, win, evmask); \
210 void glutIdleFunc(glut_cb func)
215 void glutDisplayFunc(glut_cb func)
220 void glutReshapeFunc(glut_cb_reshape func)
225 void glutVisibilityFunc(glut_cb_state func)
229 UPD_EVMASK(VisibilityChangeMask);
233 void glutEntryFunc(glut_cb_state func)
237 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
241 void glutKeyboardFunc(glut_cb_keyb func)
245 UPD_EVMASK(KeyPressMask);
249 void glutKeyboardUpFunc(glut_cb_keyb func)
253 UPD_EVMASK(KeyReleaseMask);
257 void glutSpecialFunc(glut_cb_special func)
261 UPD_EVMASK(KeyPressMask);
265 void glutSpecialUpFunc(glut_cb_special func)
269 UPD_EVMASK(KeyReleaseMask);
273 void glutMouseFunc(glut_cb_mouse func)
277 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
281 void glutMotionFunc(glut_cb_motion func)
285 UPD_EVMASK(ButtonMotionMask);
289 void glutPassiveMotionFunc(glut_cb_motion func)
293 UPD_EVMASK(PointerMotionMask);
297 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
299 cb_sball_motion = func;
302 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
304 cb_sball_rotate = func;
307 void glutSpaceballButtonFunc(glut_cb_sbbutton func)
309 cb_sball_button = func;
312 int glutGet(unsigned int s)
317 get_window_pos(&x, &y);
320 get_window_pos(&x, &y);
322 case GLUT_WINDOW_WIDTH:
323 get_window_size(&x, &y);
325 case GLUT_WINDOW_HEIGHT:
326 get_window_size(&x, &y);
328 case GLUT_WINDOW_BUFFER_SIZE:
329 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
330 case GLUT_WINDOW_STENCIL_SIZE:
331 return ctx_info.ssize;
332 case GLUT_WINDOW_DEPTH_SIZE:
333 return ctx_info.zsize;
334 case GLUT_WINDOW_RED_SIZE:
335 return ctx_info.rsize;
336 case GLUT_WINDOW_GREEN_SIZE:
337 return ctx_info.gsize;
338 case GLUT_WINDOW_BLUE_SIZE:
339 return ctx_info.bsize;
340 case GLUT_WINDOW_ALPHA_SIZE:
341 return ctx_info.asize;
342 case GLUT_WINDOW_DOUBLEBUFFER:
343 return ctx_info.dblbuf;
344 case GLUT_WINDOW_RGBA:
346 case GLUT_WINDOW_NUM_SAMPLES:
347 return ctx_info.samples;
348 case GLUT_WINDOW_STEREO:
349 return ctx_info.stereo;
350 case GLUT_WINDOW_SRGB:
351 return ctx_info.srgb;
352 case GLUT_WINDOW_CURSOR:
354 case GLUT_SCREEN_WIDTH:
355 get_screen_size(&x, &y);
357 case GLUT_SCREEN_HEIGHT:
358 get_screen_size(&x, &y);
360 case GLUT_INIT_DISPLAY_MODE:
362 case GLUT_INIT_WINDOW_X:
364 case GLUT_INIT_WINDOW_Y:
366 case GLUT_INIT_WINDOW_WIDTH:
368 case GLUT_INIT_WINDOW_HEIGHT:
370 case GLUT_ELAPSED_TIME:
378 int glutGetModifiers(void)
383 static int is_space(int c)
385 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
388 static const char *skip_space(const char *s)
390 while(*s && is_space(*s)) s++;
394 int glutExtensionSupported(char *ext)
396 const char *str, *eptr;
398 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
403 str = skip_space(str);
404 eptr = skip_space(ext);
405 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
409 if((!*str || is_space(*str)) && !*eptr) {
412 while(*str && !is_space(*str)) str++;
419 /* --------------- UNIX/X11 implementation ----------------- */
422 SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
424 SPNAV_EVENT_BUTTON /* includes both press and release */
427 struct spnav_event_motion {
435 struct spnav_event_button {
443 struct spnav_event_motion motion;
444 struct spnav_event_button button;
448 static void handle_event(XEvent *ev);
450 static int spnav_window(Window win);
451 static int spnav_event(const XEvent *xev, union spnav_event *event);
452 static int spnav_remove_events(int type);
455 void glutMainLoopEvent(void)
460 panic("display callback not set");
463 if(!upd_pending && !cb_idle) {
464 XNextEvent(dpy, &ev);
468 while(XPending(dpy)) {
469 XNextEvent(dpy, &ev);
478 if(upd_pending && mapped) {
489 static void cleanup(void)
493 glXMakeCurrent(dpy, 0, 0);
494 XDestroyWindow(dpy, win);
498 static KeySym translate_keysym(KeySym sym)
519 static void handle_event(XEvent *ev)
522 union spnav_event sev;
531 case ConfigureNotify:
532 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
533 win_width = ev->xconfigure.width;
534 win_height = ev->xconfigure.height;
535 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
540 if(ev->xclient.message_type == xa_wm_proto) {
541 if(ev->xclient.data.l[0] == xa_wm_del_win) {
545 if(spnav_event(ev, &sev)) {
547 case SPNAV_EVENT_MOTION:
548 if(cb_sball_motion) {
549 cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z);
551 if(cb_sball_rotate) {
552 cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz);
554 spnav_remove_events(SPNAV_EVENT_MOTION);
557 case SPNAV_EVENT_BUTTON:
558 if(cb_sball_button) {
559 cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP);
575 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
576 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
579 sym = translate_keysym(sym);
581 if(ev->type == KeyPress) {
582 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
584 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
587 if(ev->type == KeyPress) {
588 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
590 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
597 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
599 int bn = ev->xbutton.button - Button1;
600 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
601 ev->xbutton.x, ev->xbutton.y);
606 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
607 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
609 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
613 case VisibilityNotify:
615 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
619 if(cb_entry) cb_entry(GLUT_ENTERED);
622 if(cb_entry) cb_entry(GLUT_LEFT);
627 void glutSwapBuffers(void)
629 glXSwapBuffers(dpy, win);
633 * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it
634 * needs to resize the window to make it fullscreen. The way it does this is by
635 * querying the size of the root window (see get_screen_size), which in the
636 * case of multi-monitor setups will be the combined size of all monitors.
637 * This is problematic; the way to solve it is to use the XRandR extension, or
638 * the Xinerama extension, to figure out the dimensions of the correct video
639 * output, which would add potentially two extension support libraries to our
641 * Moreover, any X installation modern enough to support XR&R will almost
642 * certainly be running a window manager supporting the EHWM
643 * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely
644 * on manual resizing, and is used in preference if available, making this
645 * whole endeavor pointless.
646 * So I'll just leave it with set_fullscreen_mwm covering the entire
647 * multi-monitor area for now.
652 unsigned long functions;
653 unsigned long decorations;
655 unsigned long status;
658 #define MWM_HINTS_DECORATIONS 2
659 #define MWM_DECOR_ALL 1
661 static void set_fullscreen_mwm(int fs)
663 struct mwm_hints hints;
664 int scr_width, scr_height;
667 get_window_pos(&prev_win_x, &prev_win_y);
668 get_window_size(&prev_win_width, &prev_win_height);
669 get_screen_size(&scr_width, &scr_height);
671 hints.decorations = 0;
672 hints.flags = MWM_HINTS_DECORATIONS;
673 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
674 PropModeReplace, (unsigned char*)&hints, 5);
676 XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height);
678 XDeleteProperty(dpy, win, xa_motif_wm_hints);
679 XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height);
683 static int have_netwm_fullscr(void)
687 unsigned long i, count, rem;
689 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
692 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
693 &type, &fmt, &count, &rem, (unsigned char**)prop);
695 for(i=0; i<count; i++) {
696 if(prop[i] == xa_net_wm_state_fullscr) {
706 static void set_fullscreen_ewmh(int fs)
708 XClientMessageEvent msg = {0};
710 msg.type = ClientMessage;
712 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
714 msg.data.l[0] = fs ? 1 : 0;
715 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
717 msg.data.l[3] = 1; /* source regular application */
718 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
721 static void set_fullscreen(int fs)
723 if(fullscreen == fs) return;
725 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
726 set_fullscreen_ewmh(fs);
728 } else if(xa_motif_wm_hints) {
729 set_fullscreen_mwm(fs);
734 void glutPositionWindow(int x, int y)
737 XMoveWindow(dpy, win, x, y);
740 void glutReshapeWindow(int xsz, int ysz)
743 XResizeWindow(dpy, win, xsz, ysz);
746 void glutFullScreen(void)
751 void glutSetWindowTitle(const char *title)
754 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
757 XSetWMName(dpy, win, &tprop);
761 void glutSetIconTitle(const char *title)
764 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
767 XSetWMIconName(dpy, win, &tprop);
771 void glutSetCursor(int cidx)
776 case GLUT_CURSOR_LEFT_ARROW:
777 cur = XCreateFontCursor(dpy, XC_left_ptr);
779 case GLUT_CURSOR_INHERIT:
781 case GLUT_CURSOR_NONE:
787 XDefineCursor(dpy, win, cur);
791 static XVisualInfo *choose_visual(unsigned int mode)
798 if(mode & GLUT_DOUBLE) {
799 *aptr++ = GLX_DOUBLEBUFFER;
802 if(mode & GLUT_INDEX) {
803 *aptr++ = GLX_BUFFER_SIZE;
807 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
808 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
809 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
811 if(mode & GLUT_ALPHA) {
812 *aptr++ = GLX_ALPHA_SIZE;
815 if(mode & GLUT_DEPTH) {
816 *aptr++ = GLX_DEPTH_SIZE;
819 if(mode & GLUT_STENCIL) {
820 *aptr++ = GLX_STENCIL_SIZE;
823 if(mode & GLUT_ACCUM) {
824 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
825 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
826 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
828 if(mode & GLUT_STEREO) {
829 *aptr++ = GLX_STEREO;
831 if(mode & GLUT_SRGB) {
832 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
834 if(mode & GLUT_MULTISAMPLE) {
835 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
837 *aptr++ = GLX_SAMPLES_ARB;
844 return glXChooseVisual(dpy, scr, attr);
846 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
855 static void create_window(const char *title)
857 XSetWindowAttributes xattr = {0};
859 unsigned int xattr_mask;
860 unsigned int mode = init_mode;
862 if(!(vi = choose_visual(mode))) {
864 if(!(vi = choose_visual(mode))) {
865 panic("Failed to find compatible visual\n");
869 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
871 panic("Failed to create OpenGL context\n");
874 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
875 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
876 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
877 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
878 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
879 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
880 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
881 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
882 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
883 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
885 xattr.background_pixel = BlackPixel(dpy, scr);
886 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
887 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
888 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
889 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
891 glXDestroyContext(dpy, ctx);
892 panic("Failed to create window\n");
896 XSelectInput(dpy, win, evmask);
900 glutSetWindowTitle(title);
901 glutSetIconTitle(title);
902 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
903 XMapWindow(dpy, win);
905 glXMakeCurrent(dpy, win, ctx);
908 static void get_window_pos(int *x, int *y)
911 XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child);
914 static void get_window_size(int *w, int *h)
916 XWindowAttributes wattr;
917 XGetWindowAttributes(dpy, win, &wattr);
922 static void get_screen_size(int *scrw, int *scrh)
924 XWindowAttributes wattr;
925 XGetWindowAttributes(dpy, root, &wattr);
927 *scrh = wattr.height;
933 CMD_APP_WINDOW = 27695,
937 static Window get_daemon_window(Display *dpy);
938 static int catch_badwin(Display *dpy, XErrorEvent *err);
940 #define SPNAV_INITIALIZED (xa_motion_event)
942 static int spnav_window(Window win)
944 int (*prev_xerr_handler)(Display*, XErrorEvent*);
948 if(!SPNAV_INITIALIZED) {
952 if(!(daemon_win = get_daemon_window(dpy))) {
956 prev_xerr_handler = XSetErrorHandler(catch_badwin);
958 xev.type = ClientMessage;
959 xev.xclient.send_event = False;
960 xev.xclient.display = dpy;
961 xev.xclient.window = win;
962 xev.xclient.message_type = xa_command_event;
963 xev.xclient.format = 16;
964 xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16;
965 xev.xclient.data.s[1] = (unsigned int)win & 0xffff;
966 xev.xclient.data.s[2] = CMD_APP_WINDOW;
968 XSendEvent(dpy, daemon_win, False, 0, &xev);
971 XSetErrorHandler(prev_xerr_handler);
975 static Bool match_events(Display *dpy, XEvent *xev, char *arg)
977 int evtype = *(int*)arg;
979 if(xev->type != ClientMessage) {
983 if(xev->xclient.message_type == xa_motion_event) {
984 return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False;
986 if(xev->xclient.message_type == xa_button_press_event ||
987 xev->xclient.message_type == xa_button_release_event) {
988 return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False;
993 static int spnav_remove_events(int type)
997 while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) {
1003 static int spnav_event(const XEvent *xev, union spnav_event *event)
1008 xmsg_type = xev->xclient.message_type;
1010 if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event &&
1011 xmsg_type != xa_button_release_event) {
1015 if(xmsg_type == xa_motion_event) {
1016 event->type = SPNAV_EVENT_MOTION;
1017 event->motion.data = &event->motion.x;
1019 for(i=0; i<6; i++) {
1020 event->motion.data[i] = xev->xclient.data.s[i + 2];
1022 event->motion.period = xev->xclient.data.s[8];
1024 event->type = SPNAV_EVENT_BUTTON;
1025 event->button.press = xmsg_type == xa_button_press_event ? 1 : 0;
1026 event->button.bnum = xev->xclient.data.s[2];
1031 static int mglut_strcmp(const char *s1, const char *s2)
1033 while(*s1 && *s1 == *s2) {
1040 static Window get_daemon_window(Display *dpy)
1043 XTextProperty wname;
1046 unsigned long nitems, bytes_after;
1047 unsigned char *prop;
1049 XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType,
1050 &type, &fmt, &nitems, &bytes_after, &prop);
1055 win = *(Window*)prop;
1058 if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) {
1065 static int catch_badwin(Display *dpy, XErrorEvent *err)
1072 #endif /* BUILD_X11 */
1075 /* --------------- windows implementation ----------------- */
1077 static int reshape_pending;
1079 static void update_modkeys(void);
1080 static int translate_vkey(int vkey);
1081 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
1083 #ifdef MINIGLUT_WINMAIN
1084 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
1087 char *argv[] = { "miniglut.exe", 0 };
1088 return main(argc, argv);
1092 void glutMainLoopEvent(void)
1097 panic("display callback not set");
1100 if(reshape_pending && cb_reshape) {
1101 reshape_pending = 0;
1102 get_window_size(&win_width, &win_height);
1103 cb_reshape(win_width, win_height);
1106 if(!upd_pending && !cb_idle) {
1107 GetMessage(&msg, 0, 0, 0);
1108 TranslateMessage(&msg);
1109 DispatchMessage(&msg);
1112 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
1113 TranslateMessage(&msg);
1114 DispatchMessage(&msg);
1122 if(upd_pending && mapped) {
1128 static void cleanup(void)
1131 wglMakeCurrent(dc, 0);
1132 wglDeleteContext(ctx);
1133 UnregisterClass("MiniGLUT", hinst);
1137 void glutSwapBuffers(void)
1142 void glutPositionWindow(int x, int y)
1145 unsigned int flags = SWP_SHOWWINDOW;
1148 rect.left = prev_win_x;
1149 rect.top = prev_win_y;
1150 rect.right = rect.left + prev_win_width;
1151 rect.bottom = rect.top + prev_win_height;
1152 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1154 flags |= SWP_FRAMECHANGED;
1156 GetWindowRect(win, &rect);
1158 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1161 void glutReshapeWindow(int xsz, int ysz)
1164 unsigned int flags = SWP_SHOWWINDOW;
1167 rect.left = prev_win_x;
1168 rect.top = prev_win_y;
1169 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1171 flags |= SWP_FRAMECHANGED;
1173 GetWindowRect(win, &rect);
1175 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1178 void glutFullScreen(void)
1181 int scr_width, scr_height;
1183 if(fullscreen) return;
1185 GetWindowRect(win, &rect);
1186 prev_win_x = rect.left;
1187 prev_win_y = rect.top;
1188 prev_win_width = rect.right - rect.left;
1189 prev_win_height = rect.bottom - rect.top;
1191 get_screen_size(&scr_width, &scr_height);
1193 SetWindowLong(win, GWL_STYLE, 0);
1194 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1199 void glutSetWindowTitle(const char *title)
1201 SetWindowText(win, title);
1204 void glutSetIconTitle(const char *title)
1208 void glutSetCursor(int cidx)
1211 case GLUT_CURSOR_NONE:
1214 case GLUT_CURSOR_INHERIT:
1215 case GLUT_CURSOR_LEFT_ARROW:
1217 SetCursor(LoadCursor(0, IDC_ARROW));
1223 static void create_window(const char *title)
1226 PIXELFORMATDESCRIPTOR pfd = {0};
1231 rect.right = init_x + init_width;
1232 rect.bottom = init_y + init_height;
1233 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1235 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, rect.left, rect.top,
1236 rect.right - rect.left, rect.bottom - rect.top, 0, 0, hinst, 0))) {
1237 panic("Failed to create window\n");
1241 pfd.nSize = sizeof pfd;
1243 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1244 if(init_mode & GLUT_STEREO) {
1245 pfd.dwFlags |= PFD_STEREO;
1247 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1248 pfd.cColorBits = 24;
1249 if(init_mode & GLUT_ALPHA) {
1252 if(init_mode & GLUT_ACCUM) {
1253 pfd.cAccumBits = 24;
1255 if(init_mode & GLUT_DEPTH) {
1256 pfd.cDepthBits = 24;
1258 if(init_mode & GLUT_STENCIL) {
1259 pfd.cStencilBits = 8;
1261 pfd.iLayerType = PFD_MAIN_PLANE;
1263 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1264 panic("Failed to find suitable pixel format\n");
1266 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1267 panic("Failed to set the selected pixel format\n");
1269 if(!(ctx = wglCreateContext(dc))) {
1270 panic("Failed to create the OpenGL context\n");
1272 wglMakeCurrent(dc, ctx);
1274 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1275 ctx_info.rsize = pfd.cRedBits;
1276 ctx_info.gsize = pfd.cGreenBits;
1277 ctx_info.bsize = pfd.cBlueBits;
1278 ctx_info.asize = pfd.cAlphaBits;
1279 ctx_info.zsize = pfd.cDepthBits;
1280 ctx_info.ssize = pfd.cStencilBits;
1281 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1282 ctx_info.samples = 1; /* TODO */
1283 ctx_info.srgb = 0; /* TODO */
1286 SetForegroundWindow(win);
1289 reshape_pending = 1;
1292 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1294 static int mouse_x, mouse_y;
1299 if(win) DestroyWindow(win);
1310 ValidateRect(win, 0);
1314 x = lparam & 0xffff;
1316 if(x != win_width && y != win_height) {
1320 reshape_pending = 0;
1321 cb_reshape(win_width, win_height);
1328 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1334 key = translate_vkey(wparam);
1337 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1341 cb_skeydown(key, mouse_x, mouse_y);
1349 key = translate_vkey(wparam);
1352 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1356 cb_skeyup(key, mouse_x, mouse_y);
1361 case WM_LBUTTONDOWN:
1362 handle_mbutton(0, 1, wparam, lparam);
1364 case WM_MBUTTONDOWN:
1365 handle_mbutton(1, 1, wparam, lparam);
1367 case WM_RBUTTONDOWN:
1368 handle_mbutton(2, 1, wparam, lparam);
1371 handle_mbutton(0, 0, wparam, lparam);
1374 handle_mbutton(1, 0, wparam, lparam);
1377 handle_mbutton(2, 0, wparam, lparam);
1381 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1382 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1384 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1390 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1394 return DefWindowProc(win, msg, wparam, lparam);
1400 static void update_modkeys(void)
1402 if(GetKeyState(VK_SHIFT) & 0x8000) {
1403 modstate |= GLUT_ACTIVE_SHIFT;
1405 modstate &= ~GLUT_ACTIVE_SHIFT;
1407 if(GetKeyState(VK_CONTROL) & 0x8000) {
1408 modstate |= GLUT_ACTIVE_CTRL;
1410 modstate &= ~GLUT_ACTIVE_CTRL;
1412 if(GetKeyState(VK_MENU) & 0x8000) {
1413 modstate |= GLUT_ACTIVE_ALT;
1415 modstate &= ~GLUT_ACTIVE_ALT;
1419 static int translate_vkey(int vkey)
1422 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1423 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1424 case VK_END: return GLUT_KEY_END;
1425 case VK_HOME: return GLUT_KEY_HOME;
1426 case VK_LEFT: return GLUT_KEY_LEFT;
1427 case VK_UP: return GLUT_KEY_UP;
1428 case VK_RIGHT: return GLUT_KEY_RIGHT;
1429 case VK_DOWN: return GLUT_KEY_DOWN;
1434 if(vkey >= 'A' && vkey <= 'Z') {
1436 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1437 vkey -= VK_F1 + GLUT_KEY_F1;
1443 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1450 x = lparam & 0xffff;
1452 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1456 static void get_window_pos(int *x, int *y)
1459 GetWindowRect(win, &rect);
1464 static void get_window_size(int *w, int *h)
1467 GetClientRect(win, &rect);
1468 *w = rect.right - rect.left;
1469 *h = rect.bottom - rect.top;
1472 static void get_screen_size(int *scrw, int *scrh)
1474 *scrw = GetSystemMetrics(SM_CXSCREEN);
1475 *scrh = GetSystemMetrics(SM_CYSCREEN);
1477 #endif /* BUILD_WIN32 */
1479 #if defined(__unix__) || defined(__APPLE__)
1480 #include <sys/time.h>
1482 #ifdef MINIGLUT_USE_LIBC
1483 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1485 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1488 static long get_msec(void)
1490 static struct timeval tv0;
1493 sys_gettimeofday(&tv, 0);
1494 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1498 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1502 static long get_msec(void)
1507 #ifdef MINIGLUT_NO_WINMM
1508 tm = GetTickCount();
1520 static void panic(const char *msg)
1522 const char *end = msg;
1524 sys_write(2, msg, end - msg);
1529 #ifdef MINIGLUT_USE_LIBC
1530 static void sys_exit(int status)
1535 static int sys_write(int fd, const void *buf, int count)
1537 return write(fd, buf, count);
1540 #else /* !MINIGLUT_USE_LIBC */
1544 static void sys_exit(int status)
1548 :: "a"(60), "D"(status));
1550 static int sys_write(int fd, const void *buf, int count)
1556 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1559 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1565 : "a"(96), "D"(tv), "S"(tz));
1568 #endif /* __x86_64__ */
1570 static void sys_exit(int status)
1574 :: "a"(1), "b"(status));
1576 static int sys_write(int fd, const void *buf, int count)
1582 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1585 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1591 : "a"(78), "b"(tv), "c"(tz));
1594 #endif /* __i386__ */
1595 #endif /* __linux__ */
1598 static void sys_exit(int status)
1600 ExitProcess(status);
1602 static int sys_write(int fd, const void *buf, int count)
1604 unsigned long wrsz = 0;
1606 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1607 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1613 #endif /* !MINIGLUT_USE_LIBC */
1616 /* ----------------- primitives ------------------ */
1617 #ifdef MINIGLUT_USE_LIBC
1621 void mglut_sincos(float angle, float *sptr, float *cptr)
1627 float mglut_atan(float x)
1632 #else /* !MINIGLUT_USE_LIBC */
1635 void mglut_sincos(float angle, float *sptr, float *cptr)
1642 : "=m"(*sptr), "=m"(*cptr)
1647 float mglut_atan(float x)
1663 void mglut_sincos(float angle, float *sptr, float *cptr)
1676 float mglut_atan(float x)
1690 #pragma aux mglut_sincos = \
1692 "fstp dword ptr [edx]" \
1693 "fstp dword ptr [eax]" \
1694 parm[8087][eax][edx] \
1697 #pragma aux mglut_atan = \
1703 #endif /* __WATCOMC__ */
1705 #endif /* !MINIGLUT_USE_LIBC */
1707 #define PI 3.1415926536f
1709 void glutSolidSphere(float rad, int slices, int stacks)
1712 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1713 float du = 1.0f / (float)slices;
1714 float dv = 1.0f / (float)stacks;
1717 for(i=0; i<stacks; i++) {
1719 for(j=0; j<slices; j++) {
1721 for(k=0; k<4; k++) {
1722 gray = k ^ (k >> 1);
1723 s = gray & 1 ? u + du : u;
1724 t = gray & 2 ? v + dv : v;
1725 theta = s * PI * 2.0f;
1727 mglut_sincos(theta, &sintheta, &costheta);
1728 mglut_sincos(phi, &sinphi, &cosphi);
1729 x = sintheta * sinphi;
1730 y = costheta * sinphi;
1735 glNormal3f(x, y, z);
1736 glVertex3f(x * rad, y * rad, z * rad);
1743 void glutWireSphere(float rad, int slices, int stacks)
1745 glPushAttrib(GL_POLYGON_BIT);
1746 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1747 glutSolidSphere(rad, slices, stacks);
1751 void glutSolidCube(float sz)
1753 int i, j, idx, gray, flip, rotx;
1754 float vpos[3], norm[3];
1755 float rad = sz * 0.5f;
1758 for(i=0; i<6; i++) {
1761 idx = (~i & 2) - rotx;
1762 norm[0] = norm[1] = norm[2] = 0.0f;
1763 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
1765 vpos[idx] = norm[idx] * rad;
1766 for(j=0; j<4; j++) {
1767 gray = j ^ (j >> 1);
1768 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
1769 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
1770 glTexCoord2f(gray & 1, gray >> 1);
1777 void glutWireCube(float sz)
1779 glPushAttrib(GL_POLYGON_BIT);
1780 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1785 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
1788 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
1789 float du = 1.0f / (float)slices;
1790 float dv = 1.0f / (float)stacks;
1793 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
1794 mglut_sincos(phi, &sinphi, &cosphi);
1797 for(i=0; i<stacks; i++) {
1799 for(j=0; j<slices; j++) {
1801 for(k=0; k<4; k++) {
1802 gray = k ^ (k >> 1);
1803 s = gray & 2 ? u + du : u;
1804 t = gray & 1 ? v + dv : v;
1805 rad = rbot + (rtop - rbot) * t;
1806 theta = s * PI * 2.0f;
1807 mglut_sincos(theta, &sintheta, &costheta);
1809 x = sintheta * cosphi;
1810 y = costheta * cosphi;
1815 glNormal3f(x, y, z);
1816 glVertex3f(sintheta * rad, costheta * rad, t * height);
1823 void glutSolidCone(float base, float height, int slices, int stacks)
1825 draw_cylinder(base, 0, height, slices, stacks);
1828 void glutWireCone(float base, float height, int slices, int stacks)
1830 glPushAttrib(GL_POLYGON_BIT);
1831 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1832 glutSolidCone(base, height, slices, stacks);
1836 void glutSolidCylinder(float rad, float height, int slices, int stacks)
1838 draw_cylinder(rad, rad, height, slices, stacks);
1841 void glutWireCylinder(float rad, float height, int slices, int stacks)
1843 glPushAttrib(GL_POLYGON_BIT);
1844 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1845 glutSolidCylinder(rad, height, slices, stacks);
1849 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
1852 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1853 float du = 1.0f / (float)rings;
1854 float dv = 1.0f / (float)sides;
1857 for(i=0; i<rings; i++) {
1859 for(j=0; j<sides; j++) {
1861 for(k=0; k<4; k++) {
1862 gray = k ^ (k >> 1);
1863 s = gray & 1 ? u + du : u;
1864 t = gray & 2 ? v + dv : v;
1865 theta = s * PI * 2.0f;
1866 phi = t * PI * 2.0f;
1867 mglut_sincos(theta, &sintheta, &costheta);
1868 mglut_sincos(phi, &sinphi, &cosphi);
1869 x = sintheta * sinphi;
1870 y = costheta * sinphi;
1875 glNormal3f(x, y, z);
1877 x = x * inner_rad + sintheta * outer_rad;
1878 y = y * inner_rad + costheta * outer_rad;
1880 glVertex3f(x, y, z);
1887 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
1889 glPushAttrib(GL_POLYGON_BIT);
1890 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1891 glutSolidTorus(inner_rad, outer_rad, sides, rings);
1895 void glutSolidTeapot(float size)
1899 void glutWireTeapot(float size)