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 #ifdef MINIGLUT_USE_LIBC
25 #include <X11/cursorfont.h>
29 #ifndef GLX_SAMPLE_BUFFERS_ARB
30 #define GLX_SAMPLE_BUFFERS_ARB 100000
31 #define GLX_SAMPLES_ARB 100001
33 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
34 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
38 static Window win, root;
40 static GLXContext ctx;
41 static Atom xa_wm_proto, xa_wm_del_win;
42 static unsigned int evmask;
54 #error unsupported platform
61 int rsize, gsize, bsize, asize;
69 static void create_window(const char *title);
70 static void get_window_pos(Window win, int *x, int *y);
71 static void get_window_size(Window win, int *w, int *h);
72 static void get_screen_size(Window win, int *scrw, int *scrh);
74 static long get_msec(void);
75 static void panic(const char *msg);
76 static void sys_exit(int status);
77 static int sys_write(int fd, const void *buf, int count);
80 static int init_x, init_y, init_width = 256, init_height = 256;
81 static unsigned int init_mode;
83 static struct ctx_info ctx_info;
84 static int cur_cursor = GLUT_CURSOR_INHERIT;
86 static glut_cb cb_display;
87 static glut_cb cb_idle;
88 static glut_cb_reshape cb_reshape;
89 static glut_cb_state cb_vis, cb_entry;
90 static glut_cb_keyb cb_keydown, cb_keyup;
91 static glut_cb_special cb_skeydown, cb_skeyup;
92 static glut_cb_mouse cb_mouse;
93 static glut_cb_motion cb_motion, cb_passive;
94 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
95 static glut_cb_sbbutton cb_sball_button;
97 static int win_width, win_height;
100 static int upd_pending, reshape_pending;
104 void glutInit(int *argc, char **argv)
107 if(!(dpy = XOpenDisplay(0))) {
108 panic("Failed to connect to the X server\n");
110 scr = DefaultScreen(dpy);
111 root = RootWindow(dpy, scr);
112 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
113 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
115 evmask = ExposureMask | StructureNotifyMask;
119 void glutInitWindowPosition(int x, int y)
125 void glutInitWindowSize(int xsz, int ysz)
131 void glutInitDisplayMode(unsigned int mode)
136 void glutCreateWindow(const char *title)
138 create_window(title);
146 void glutMainLoop(void)
153 void glutPostRedisplay(void)
158 #define UPD_EVMASK(x) \
165 if(win) XSelectInput(dpy, win, evmask); \
169 void glutIdleFunc(glut_cb func)
174 void glutDisplayFunc(glut_cb func)
179 void glutReshapeFunc(glut_cb_reshape func)
184 void glutVisibilityFunc(glut_cb_state func)
188 UPD_EVMASK(VisibilityChangeMask);
192 void glutEntryFunc(glut_cb_state func)
196 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
200 void glutKeyboardFunc(glut_cb_keyb func)
204 UPD_EVMASK(KeyPressMask);
208 void glutKeyboardUpFunc(glut_cb_keyb func)
212 UPD_EVMASK(KeyReleaseMask);
216 void glutSpecialFunc(glut_cb_special func)
220 UPD_EVMASK(KeyPressMask);
224 void glutSpecialUpFunc(glut_cb_special func)
228 UPD_EVMASK(KeyReleaseMask);
232 void glutMouseFunc(glut_cb_mouse func)
236 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
240 void glutMotionFunc(glut_cb_motion func)
244 UPD_EVMASK(ButtonMotionMask);
248 void glutPassiveMotionFunc(glut_cb_motion func)
252 UPD_EVMASK(PointerMotionMask);
256 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
258 cb_sball_motion = func;
261 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
263 cb_sball_rotate = func;
266 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
268 cb_sball_button = func;
271 int glutGet(unsigned int s)
276 get_window_pos(win, &x, &y);
279 get_window_pos(win, &x, &y);
281 case GLUT_WINDOW_WIDTH:
282 get_window_size(win, &x, &y);
284 case GLUT_WINDOW_HEIGHT:
285 get_window_size(win, &x, &y);
287 case GLUT_WINDOW_BUFFER_SIZE:
288 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
289 case GLUT_WINDOW_STENCIL_SIZE:
290 return ctx_info.ssize;
291 case GLUT_WINDOW_DEPTH_SIZE:
292 return ctx_info.zsize;
293 case GLUT_WINDOW_RED_SIZE:
294 return ctx_info.rsize;
295 case GLUT_WINDOW_GREEN_SIZE:
296 return ctx_info.gsize;
297 case GLUT_WINDOW_BLUE_SIZE:
298 return ctx_info.bsize;
299 case GLUT_WINDOW_ALPHA_SIZE:
300 return ctx_info.asize;
301 case GLUT_WINDOW_DOUBLEBUFFER:
302 return ctx_info.dblbuf;
303 case GLUT_WINDOW_RGBA:
305 case GLUT_WINDOW_NUM_SAMPLES:
306 return ctx_info.samples;
307 case GLUT_WINDOW_STEREO:
308 return ctx_info.stereo;
309 case GLUT_WINDOW_SRGB:
310 return ctx_info.srgb;
311 case GLUT_WINDOW_CURSOR:
313 case GLUT_SCREEN_WIDTH:
314 get_screen_size(win, &x, &y);
316 case GLUT_SCREEN_HEIGHT:
317 get_screen_size(win, &x, &y);
319 case GLUT_INIT_DISPLAY_MODE:
321 case GLUT_INIT_WINDOW_X:
323 case GLUT_INIT_WINDOW_Y:
325 case GLUT_INIT_WINDOW_WIDTH:
327 case GLUT_INIT_WINDOW_HEIGHT:
329 case GLUT_ELAPSED_TIME:
337 int glutGetModifiers(void)
342 static int is_space(int c)
344 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
347 static const char *skip_space(const char *s)
349 while(*s && is_space(*s)) s++;
353 int glutExtensionSupported(char *ext)
355 const char *str, *eptr;
357 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
362 str = skip_space(str);
363 eptr = skip_space(ext);
364 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
368 if((!*str || is_space(*str)) && !*eptr) {
371 while(*str && !is_space(*str)) str++;
378 void glutSolidSphere(float rad)
382 void glutWireSphere(float rad)
386 void glutSolidCube(float sz)
390 void glutWireCube(float sz)
394 void glutSolidTorus(float inner_rad, float outer_rad, float sides, float rings)
398 void glutWireTorus(float inner_rad, float outer_rad, float sides, float rings)
402 void glutSolidTeapot(float size)
406 void glutWireTeapot(float size)
412 static void handle_event(XEvent *ev);
414 void glutMainLoopEvent(void)
419 panic("display callback not set");
423 if(!upd_pending && !cb_idle) {
424 XNextEvent(dpy, &ev);
428 while(XPending(dpy)) {
429 XNextEvent(dpy, &ev);
438 if(upd_pending && mapped) {
445 static KeySym translate_keysym(KeySym sym)
466 static void handle_event(XEvent *ev)
477 case ConfigureNotify:
478 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
479 win_width = ev->xconfigure.width;
480 win_height = ev->xconfigure.height;
481 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
486 if(ev->xclient.message_type == xa_wm_proto) {
487 if(ev->xclient.data.l[0] == xa_wm_del_win) {
499 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
500 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
503 sym = translate_keysym(sym);
505 if(ev->type == KeyPress) {
506 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
508 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
511 if(ev->type == KeyPress) {
512 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
514 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
521 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
523 int bn = ev->xbutton.button - Button1;
524 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
525 ev->xbutton.x, ev->xbutton.y);
530 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
531 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
533 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
537 case VisibilityNotify:
539 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
543 if(cb_entry) cb_entry(GLUT_ENTERED);
546 if(cb_entry) cb_entry(GLUT_LEFT);
551 void glutSwapBuffers(void)
553 glXSwapBuffers(dpy, win);
556 void glutPositionWindow(int x, int y)
558 XMoveWindow(dpy, win, x, y);
561 void glutReshapeWindow(int xsz, int ysz)
563 XResizeWindow(dpy, win, xsz, ysz);
566 void glutFullScreen(void)
571 void glutSetWindowTitle(const char *title)
574 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
577 XSetWMName(dpy, win, &tprop);
581 void glutSetIconTitle(const char *title)
584 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
587 XSetWMIconName(dpy, win, &tprop);
591 void glutSetCursor(int cidx)
596 case GLUT_CURSOR_LEFT_ARROW:
597 cur = XCreateFontCursor(dpy, XC_left_ptr);
599 case GLUT_CURSOR_INHERIT:
601 case GLUT_CURSOR_NONE:
607 XDefineCursor(dpy, win, cur);
611 static XVisualInfo *choose_visual(unsigned int mode)
621 int *aptr = attr + 8;
624 if(mode & GLUT_ALPHA) {
625 *aptr++ = GLX_ALPHA_SIZE;
628 if(mode & GLUT_DEPTH) {
629 *aptr++ = GLX_DEPTH_SIZE;
632 if(mode & GLUT_STENCIL) {
633 *aptr++ = GLX_STENCIL_SIZE;
636 if(mode & GLUT_ACCUM) {
637 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
638 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
639 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
641 if(mode & GLUT_STEREO) {
642 *aptr++ = GLX_STEREO;
644 if(mode & GLUT_SRGB) {
645 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
647 if(mode & GLUT_MULTISAMPLE) {
648 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
650 *aptr++ = GLX_SAMPLES_ARB;
657 return glXChooseVisual(dpy, scr, attr);
659 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
668 static void create_window(const char *title)
670 XSetWindowAttributes xattr;
672 unsigned int xattr_mask;
673 unsigned int mode = init_mode;
675 if(!(vi = choose_visual(mode))) {
677 if(!(vi = choose_visual(mode))) {
678 panic("Failed to find compatible visual\n");
682 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
684 panic("Failed to create OpenGL context\n");
687 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
688 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
689 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
690 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
691 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
692 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
693 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
694 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
695 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
696 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
698 xattr.background_pixel = BlackPixel(dpy, scr);
699 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
700 xattr_mask = CWBackPixel | CWColormap;
701 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
702 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
704 glXDestroyContext(dpy, ctx);
705 panic("Failed to create window\n");
709 XSelectInput(dpy, win, evmask);
711 glutSetWindowTitle(title);
712 glutSetIconTitle(title);
713 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
714 XMapWindow(dpy, win);
716 glXMakeCurrent(dpy, win, ctx);
719 static void get_window_pos(Window win, int *x, int *y)
721 XWindowAttributes wattr;
722 XGetWindowAttributes(dpy, win, &wattr);
727 static void get_window_size(Window win, int *w, int *h)
729 XWindowAttributes wattr;
730 XGetWindowAttributes(dpy, win, &wattr);
735 static void get_screen_size(Window win, int *scrw, int *scrh)
737 XWindowAttributes wattr;
738 XGetWindowAttributes(dpy, root, &wattr);
740 *scrh = wattr.height;
744 #if defined(__unix__) || defined(__APPLE__)
745 #include <sys/time.h>
747 static long get_msec(void)
749 static struct timeval tv0;
752 gettimeofday(&tv, 0);
753 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
757 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
761 static long get_msec(void)
769 return timeGetTime() - t0;
773 static void panic(const char *msg)
775 const char *end = msg;
777 sys_write(2, msg, end - msg);
782 #ifdef MINIGLUT_USE_LIBC
783 static void sys_exit(int status)
788 static int sys_write(int fd, const void *buf, int count)
790 return write(fd, buf, count);
792 #else /* !MINIGLUT_USE_LIBC */
797 static void sys_exit(int status)
801 :: "a"(60), "D"(status)
804 static int sys_write(int fd, const void *buf, int count)
810 : "a"(1), "D"(fd), "S"(buf), "d"(count)
816 static void sys_exit(int status)
825 static int sys_write(int fd, const void *buf, int count)
831 :: "b"(fd), "c"(buf), "d"(count));
836 #endif /* __linux__ */
839 static int sys_exit(int status)
843 static int sys_write(int fd, const void *buf, int count)
847 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
848 if(!WriteFile(out, buf, count, &wrsz, 0)) {
855 #endif /* !MINIGLUT_USE_LIBC */