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/>.
18 /*#define MINIGLUT_GCC_NO_BUILTIN*/
20 #ifdef MINIGLUT_USE_LIBC
26 #if defined(__GNUC__) && !defined(MINIGLUT_GCC_NO_BUILTIN)
27 #define mglut_sincosf(a, s, c) __builtin_sincosf(a, s, c)
28 #define mglut_atan(x) __builtin_atan(x)
30 static void mglut_sincosf(float angle, float *sptr, float *cptr);
31 static float mglut_atan(float x);
36 #define PI 3.1415926536f
41 #include <X11/cursorfont.h>
45 #ifndef GLX_SAMPLE_BUFFERS_ARB
46 #define GLX_SAMPLE_BUFFERS_ARB 100000
47 #define GLX_SAMPLES_ARB 100001
49 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
50 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
54 static Window win, root;
56 static GLXContext ctx;
57 static Atom xa_wm_proto, xa_wm_del_win;
58 static unsigned int evmask;
70 #error unsupported platform
77 int rsize, gsize, bsize, asize;
85 static void create_window(const char *title);
86 static void get_window_pos(Window win, int *x, int *y);
87 static void get_window_size(Window win, int *w, int *h);
88 static void get_screen_size(Window win, int *scrw, int *scrh);
90 static long get_msec(void);
91 static void panic(const char *msg);
92 static void sys_exit(int status);
93 static int sys_write(int fd, const void *buf, int count);
96 static int init_x, init_y, init_width = 256, init_height = 256;
97 static unsigned int init_mode;
99 static struct ctx_info ctx_info;
100 static int cur_cursor = GLUT_CURSOR_INHERIT;
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 win_width, win_height;
116 static int upd_pending, reshape_pending;
120 void glutInit(int *argc, char **argv)
123 if(!(dpy = XOpenDisplay(0))) {
124 panic("Failed to connect to the X server\n");
126 scr = DefaultScreen(dpy);
127 root = RootWindow(dpy, scr);
128 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
129 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
131 evmask = ExposureMask | StructureNotifyMask;
135 void glutInitWindowPosition(int x, int y)
141 void glutInitWindowSize(int xsz, int ysz)
147 void glutInitDisplayMode(unsigned int mode)
152 void glutCreateWindow(const char *title)
154 create_window(title);
162 void glutMainLoop(void)
169 void glutPostRedisplay(void)
174 #define UPD_EVMASK(x) \
181 if(win) XSelectInput(dpy, win, evmask); \
185 void glutIdleFunc(glut_cb func)
190 void glutDisplayFunc(glut_cb func)
195 void glutReshapeFunc(glut_cb_reshape func)
200 void glutVisibilityFunc(glut_cb_state func)
204 UPD_EVMASK(VisibilityChangeMask);
208 void glutEntryFunc(glut_cb_state func)
212 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
216 void glutKeyboardFunc(glut_cb_keyb func)
220 UPD_EVMASK(KeyPressMask);
224 void glutKeyboardUpFunc(glut_cb_keyb func)
228 UPD_EVMASK(KeyReleaseMask);
232 void glutSpecialFunc(glut_cb_special func)
236 UPD_EVMASK(KeyPressMask);
240 void glutSpecialUpFunc(glut_cb_special func)
244 UPD_EVMASK(KeyReleaseMask);
248 void glutMouseFunc(glut_cb_mouse func)
252 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
256 void glutMotionFunc(glut_cb_motion func)
260 UPD_EVMASK(ButtonMotionMask);
264 void glutPassiveMotionFunc(glut_cb_motion func)
268 UPD_EVMASK(PointerMotionMask);
272 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
274 cb_sball_motion = func;
277 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
279 cb_sball_rotate = func;
282 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
284 cb_sball_button = func;
287 int glutGet(unsigned int s)
292 get_window_pos(win, &x, &y);
295 get_window_pos(win, &x, &y);
297 case GLUT_WINDOW_WIDTH:
298 get_window_size(win, &x, &y);
300 case GLUT_WINDOW_HEIGHT:
301 get_window_size(win, &x, &y);
303 case GLUT_WINDOW_BUFFER_SIZE:
304 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
305 case GLUT_WINDOW_STENCIL_SIZE:
306 return ctx_info.ssize;
307 case GLUT_WINDOW_DEPTH_SIZE:
308 return ctx_info.zsize;
309 case GLUT_WINDOW_RED_SIZE:
310 return ctx_info.rsize;
311 case GLUT_WINDOW_GREEN_SIZE:
312 return ctx_info.gsize;
313 case GLUT_WINDOW_BLUE_SIZE:
314 return ctx_info.bsize;
315 case GLUT_WINDOW_ALPHA_SIZE:
316 return ctx_info.asize;
317 case GLUT_WINDOW_DOUBLEBUFFER:
318 return ctx_info.dblbuf;
319 case GLUT_WINDOW_RGBA:
321 case GLUT_WINDOW_NUM_SAMPLES:
322 return ctx_info.samples;
323 case GLUT_WINDOW_STEREO:
324 return ctx_info.stereo;
325 case GLUT_WINDOW_SRGB:
326 return ctx_info.srgb;
327 case GLUT_WINDOW_CURSOR:
329 case GLUT_SCREEN_WIDTH:
330 get_screen_size(win, &x, &y);
332 case GLUT_SCREEN_HEIGHT:
333 get_screen_size(win, &x, &y);
335 case GLUT_INIT_DISPLAY_MODE:
337 case GLUT_INIT_WINDOW_X:
339 case GLUT_INIT_WINDOW_Y:
341 case GLUT_INIT_WINDOW_WIDTH:
343 case GLUT_INIT_WINDOW_HEIGHT:
345 case GLUT_ELAPSED_TIME:
353 int glutGetModifiers(void)
358 static int is_space(int c)
360 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
363 static const char *skip_space(const char *s)
365 while(*s && is_space(*s)) s++;
369 int glutExtensionSupported(char *ext)
371 const char *str, *eptr;
373 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
378 str = skip_space(str);
379 eptr = skip_space(ext);
380 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
384 if((!*str || is_space(*str)) && !*eptr) {
387 while(*str && !is_space(*str)) str++;
394 void glutSolidSphere(float rad, int slices, int stacks)
397 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
398 float du = 1.0f / (float)slices;
399 float dv = 1.0f / (float)stacks;
402 for(i=0; i<stacks; i++) {
404 for(j=0; j<slices; j++) {
408 s = gray & 1 ? u + du : u;
409 t = gray & 2 ? v + dv : v;
410 theta = s * PI * 2.0f;
412 mglut_sincosf(theta, &sintheta, &costheta);
413 mglut_sincosf(phi, &sinphi, &cosphi);
414 x = sintheta * sinphi;
415 y = costheta * sinphi;
421 glVertex3f(x * rad, y * rad, z * rad);
428 void glutWireSphere(float rad, int slices, int stacks)
430 glPushAttrib(GL_POLYGON_BIT);
431 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
432 glutSolidSphere(rad, slices, stacks);
436 void glutSolidCube(float sz)
438 int i, j, idx, gray, flip, rotx;
439 float vpos[3], norm[3];
440 float rad = sz * 0.5f;
446 idx = (~i & 2) - rotx;
447 norm[0] = norm[1] = norm[2] = 0.0f;
448 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
450 vpos[idx] = norm[idx] * rad;
453 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
454 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
455 glTexCoord2f(gray & 1, gray >> 1);
462 void glutWireCube(float sz)
464 glPushAttrib(GL_POLYGON_BIT);
465 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
470 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
473 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
474 float du = 1.0f / (float)slices;
475 float dv = 1.0f / (float)stacks;
478 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
479 mglut_sincosf(phi, &sinphi, &cosphi);
482 for(i=0; i<stacks; i++) {
484 for(j=0; j<slices; j++) {
488 s = gray & 2 ? u + du : u;
489 t = gray & 1 ? v + dv : v;
490 rad = rbot + (rtop - rbot) * t;
491 theta = s * PI * 2.0f;
492 mglut_sincosf(theta, &sintheta, &costheta);
494 x = sintheta * cosphi;
495 y = costheta * cosphi;
501 glVertex3f(sintheta * rad, costheta * rad, t * height);
508 void glutSolidCone(float base, float height, int slices, int stacks)
510 draw_cylinder(base, 0, height, slices, stacks);
513 void glutWireCone(float base, float height, int slices, int stacks)
515 glPushAttrib(GL_POLYGON_BIT);
516 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
517 glutSolidCone(base, height, slices, stacks);
521 void glutSolidCylinder(float rad, float height, int slices, int stacks)
523 draw_cylinder(rad, rad, height, slices, stacks);
526 void glutWireCylinder(float rad, float height, int slices, int stacks)
528 glPushAttrib(GL_POLYGON_BIT);
529 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
530 glutSolidCylinder(rad, height, slices, stacks);
534 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
537 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
538 float du = 1.0f / (float)rings;
539 float dv = 1.0f / (float)sides;
542 for(i=0; i<rings; i++) {
544 for(j=0; j<sides; j++) {
548 s = gray & 1 ? u + du : u;
549 t = gray & 2 ? v + dv : v;
550 theta = s * PI * 2.0f;
552 mglut_sincosf(theta, &sintheta, &costheta);
553 mglut_sincosf(phi, &sinphi, &cosphi);
554 x = sintheta * sinphi;
555 y = costheta * sinphi;
562 x = x * inner_rad + sintheta * outer_rad;
563 y = y * inner_rad + costheta * outer_rad;
572 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
574 glPushAttrib(GL_POLYGON_BIT);
575 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
576 glutSolidTorus(inner_rad, outer_rad, sides, rings);
580 void glutSolidTeapot(float size)
584 void glutWireTeapot(float size)
590 static void handle_event(XEvent *ev);
592 void glutMainLoopEvent(void)
597 panic("display callback not set");
601 if(!upd_pending && !cb_idle) {
602 XNextEvent(dpy, &ev);
606 while(XPending(dpy)) {
607 XNextEvent(dpy, &ev);
616 if(upd_pending && mapped) {
623 static KeySym translate_keysym(KeySym sym)
644 static void handle_event(XEvent *ev)
655 case ConfigureNotify:
656 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
657 win_width = ev->xconfigure.width;
658 win_height = ev->xconfigure.height;
659 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
664 if(ev->xclient.message_type == xa_wm_proto) {
665 if(ev->xclient.data.l[0] == xa_wm_del_win) {
677 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
678 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
681 sym = translate_keysym(sym);
683 if(ev->type == KeyPress) {
684 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
686 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
689 if(ev->type == KeyPress) {
690 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
692 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
699 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
701 int bn = ev->xbutton.button - Button1;
702 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
703 ev->xbutton.x, ev->xbutton.y);
708 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
709 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
711 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
715 case VisibilityNotify:
717 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
721 if(cb_entry) cb_entry(GLUT_ENTERED);
724 if(cb_entry) cb_entry(GLUT_LEFT);
729 void glutSwapBuffers(void)
731 glXSwapBuffers(dpy, win);
734 void glutPositionWindow(int x, int y)
736 XMoveWindow(dpy, win, x, y);
739 void glutReshapeWindow(int xsz, int ysz)
741 XResizeWindow(dpy, win, xsz, ysz);
744 void glutFullScreen(void)
749 void glutSetWindowTitle(const char *title)
752 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
755 XSetWMName(dpy, win, &tprop);
759 void glutSetIconTitle(const char *title)
762 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
765 XSetWMIconName(dpy, win, &tprop);
769 void glutSetCursor(int cidx)
774 case GLUT_CURSOR_LEFT_ARROW:
775 cur = XCreateFontCursor(dpy, XC_left_ptr);
777 case GLUT_CURSOR_INHERIT:
779 case GLUT_CURSOR_NONE:
785 XDefineCursor(dpy, win, cur);
789 static XVisualInfo *choose_visual(unsigned int mode)
799 int *aptr = attr + 8;
802 if(mode & GLUT_ALPHA) {
803 *aptr++ = GLX_ALPHA_SIZE;
806 if(mode & GLUT_DEPTH) {
807 *aptr++ = GLX_DEPTH_SIZE;
810 if(mode & GLUT_STENCIL) {
811 *aptr++ = GLX_STENCIL_SIZE;
814 if(mode & GLUT_ACCUM) {
815 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
816 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
817 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
819 if(mode & GLUT_STEREO) {
820 *aptr++ = GLX_STEREO;
822 if(mode & GLUT_SRGB) {
823 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
825 if(mode & GLUT_MULTISAMPLE) {
826 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
828 *aptr++ = GLX_SAMPLES_ARB;
835 return glXChooseVisual(dpy, scr, attr);
837 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
846 static void create_window(const char *title)
848 XSetWindowAttributes xattr;
850 unsigned int xattr_mask;
851 unsigned int mode = init_mode;
853 if(!(vi = choose_visual(mode))) {
855 if(!(vi = choose_visual(mode))) {
856 panic("Failed to find compatible visual\n");
860 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
862 panic("Failed to create OpenGL context\n");
865 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
866 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
867 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
868 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
869 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
870 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
871 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
872 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
873 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
874 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
876 xattr.background_pixel = BlackPixel(dpy, scr);
877 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
878 xattr_mask = CWBackPixel | CWColormap;
879 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
880 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
882 glXDestroyContext(dpy, ctx);
883 panic("Failed to create window\n");
887 XSelectInput(dpy, win, evmask);
889 glutSetWindowTitle(title);
890 glutSetIconTitle(title);
891 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
892 XMapWindow(dpy, win);
894 glXMakeCurrent(dpy, win, ctx);
897 static void get_window_pos(Window win, int *x, int *y)
899 XWindowAttributes wattr;
900 XGetWindowAttributes(dpy, win, &wattr);
905 static void get_window_size(Window win, int *w, int *h)
907 XWindowAttributes wattr;
908 XGetWindowAttributes(dpy, win, &wattr);
913 static void get_screen_size(Window win, int *scrw, int *scrh)
915 XWindowAttributes wattr;
916 XGetWindowAttributes(dpy, root, &wattr);
918 *scrh = wattr.height;
922 #if defined(__unix__) || defined(__APPLE__)
923 #include <sys/time.h>
925 static long get_msec(void)
927 static struct timeval tv0;
930 gettimeofday(&tv, 0);
931 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
935 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
939 static long get_msec(void)
947 return timeGetTime() - t0;
951 static void panic(const char *msg)
953 const char *end = msg;
955 sys_write(2, msg, end - msg);
960 #ifdef MINIGLUT_USE_LIBC
961 static void sys_exit(int status)
966 static int sys_write(int fd, const void *buf, int count)
968 return write(fd, buf, count);
971 #else /* !MINIGLUT_USE_LIBC */
973 #if defined(__GNUC__) && defined(MINIGLUT_GCC_NO_BUILTIN)
974 static void mglut_sincosf(float angle, float *sptr, float *cptr)
981 : "=m"(*sptr), "=m"(*cptr)
986 static float mglut_atan(float x)
1002 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1015 static float mglut_atan(float x)
1029 #pragma aux mglut_sincosf = \
1031 "fstp dword ptr [edx]" \
1032 "fstp dword ptr [eax]" \
1033 parm[8087][eax][edx] \
1036 #pragma aux mglut_atan = \
1047 static void sys_exit(int status)
1051 :: "a"(60), "D"(status)
1054 static int sys_write(int fd, const void *buf, int count)
1060 : "a"(1), "D"(fd), "S"(buf), "d"(count)
1066 static void sys_exit(int status)
1075 static int sys_write(int fd, const void *buf, int count)
1081 :: "b"(fd), "c"(buf), "d"(count));
1086 #endif /* __linux__ */
1089 static int sys_exit(int status)
1091 ExitProcess(status);
1093 static int sys_write(int fd, const void *buf, int count)
1097 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1098 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1105 #endif /* !MINIGLUT_USE_LIBC */