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 unsigned int evmask;
43 static int have_netwm_fullscr(void);
50 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
52 static HINSTANCE hinst;
58 #error unsupported platform
64 int rsize, gsize, bsize, asize;
72 static void create_window(const char *title);
73 static void get_window_pos(int *x, int *y);
74 static void get_window_size(int *w, int *h);
75 static void get_screen_size(int *scrw, int *scrh);
77 static long get_msec(void);
78 static void panic(const char *msg);
79 static void sys_exit(int status);
80 static int sys_write(int fd, const void *buf, int count);
83 static int init_x = -1, init_y, init_width = 256, init_height = 256;
84 static unsigned int init_mode;
86 static struct ctx_info ctx_info;
87 static int cur_cursor = GLUT_CURSOR_INHERIT;
89 static glut_cb cb_display;
90 static glut_cb cb_idle;
91 static glut_cb_reshape cb_reshape;
92 static glut_cb_state cb_vis, cb_entry;
93 static glut_cb_keyb cb_keydown, cb_keyup;
94 static glut_cb_special cb_skeydown, cb_skeyup;
95 static glut_cb_mouse cb_mouse;
96 static glut_cb_motion cb_motion, cb_passive;
97 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
98 static glut_cb_sbbutton cb_sball_button;
100 static int fullscreen;
101 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
103 static int win_width, win_height;
106 static int upd_pending;
110 void glutInit(int *argc, char **argv)
113 if(!(dpy = XOpenDisplay(0))) {
114 panic("Failed to connect to the X server\n");
116 scr = DefaultScreen(dpy);
117 root = RootWindow(dpy, scr);
118 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
119 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
120 xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
121 if(have_netwm_fullscr()) {
122 xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
123 xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
126 evmask = ExposureMask | StructureNotifyMask;
132 hinst = GetModuleHandle(0);
134 wc.cbSize = sizeof wc;
135 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
136 wc.hCursor = LoadCursor(0, IDC_ARROW);
137 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
138 wc.hInstance = hinst;
139 wc.lpfnWndProc = handle_message;
140 wc.lpszClassName = "MiniGLUT";
141 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
142 if(!RegisterClassEx(&wc)) {
143 panic("Failed to register \"MiniGLUT\" window class\n");
147 get_screen_size(&init_x, &init_y);
154 void glutInitWindowPosition(int x, int y)
160 void glutInitWindowSize(int xsz, int ysz)
166 void glutInitDisplayMode(unsigned int mode)
171 void glutCreateWindow(const char *title)
173 create_window(title);
181 void glutMainLoop(void)
188 void glutPostRedisplay(void)
193 #define UPD_EVMASK(x) \
200 if(win) XSelectInput(dpy, win, evmask); \
204 void glutIdleFunc(glut_cb func)
209 void glutDisplayFunc(glut_cb func)
214 void glutReshapeFunc(glut_cb_reshape func)
219 void glutVisibilityFunc(glut_cb_state func)
223 UPD_EVMASK(VisibilityChangeMask);
227 void glutEntryFunc(glut_cb_state func)
231 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
235 void glutKeyboardFunc(glut_cb_keyb func)
239 UPD_EVMASK(KeyPressMask);
243 void glutKeyboardUpFunc(glut_cb_keyb func)
247 UPD_EVMASK(KeyReleaseMask);
251 void glutSpecialFunc(glut_cb_special func)
255 UPD_EVMASK(KeyPressMask);
259 void glutSpecialUpFunc(glut_cb_special func)
263 UPD_EVMASK(KeyReleaseMask);
267 void glutMouseFunc(glut_cb_mouse func)
271 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
275 void glutMotionFunc(glut_cb_motion func)
279 UPD_EVMASK(ButtonMotionMask);
283 void glutPassiveMotionFunc(glut_cb_motion func)
287 UPD_EVMASK(PointerMotionMask);
291 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
293 cb_sball_motion = func;
296 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
298 cb_sball_rotate = func;
301 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
303 cb_sball_button = func;
306 int glutGet(unsigned int s)
311 get_window_pos(&x, &y);
314 get_window_pos(&x, &y);
316 case GLUT_WINDOW_WIDTH:
317 get_window_size(&x, &y);
319 case GLUT_WINDOW_HEIGHT:
320 get_window_size(&x, &y);
322 case GLUT_WINDOW_BUFFER_SIZE:
323 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
324 case GLUT_WINDOW_STENCIL_SIZE:
325 return ctx_info.ssize;
326 case GLUT_WINDOW_DEPTH_SIZE:
327 return ctx_info.zsize;
328 case GLUT_WINDOW_RED_SIZE:
329 return ctx_info.rsize;
330 case GLUT_WINDOW_GREEN_SIZE:
331 return ctx_info.gsize;
332 case GLUT_WINDOW_BLUE_SIZE:
333 return ctx_info.bsize;
334 case GLUT_WINDOW_ALPHA_SIZE:
335 return ctx_info.asize;
336 case GLUT_WINDOW_DOUBLEBUFFER:
337 return ctx_info.dblbuf;
338 case GLUT_WINDOW_RGBA:
340 case GLUT_WINDOW_NUM_SAMPLES:
341 return ctx_info.samples;
342 case GLUT_WINDOW_STEREO:
343 return ctx_info.stereo;
344 case GLUT_WINDOW_SRGB:
345 return ctx_info.srgb;
346 case GLUT_WINDOW_CURSOR:
348 case GLUT_SCREEN_WIDTH:
349 get_screen_size(&x, &y);
351 case GLUT_SCREEN_HEIGHT:
352 get_screen_size(&x, &y);
354 case GLUT_INIT_DISPLAY_MODE:
356 case GLUT_INIT_WINDOW_X:
358 case GLUT_INIT_WINDOW_Y:
360 case GLUT_INIT_WINDOW_WIDTH:
362 case GLUT_INIT_WINDOW_HEIGHT:
364 case GLUT_ELAPSED_TIME:
372 int glutGetModifiers(void)
377 static int is_space(int c)
379 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
382 static const char *skip_space(const char *s)
384 while(*s && is_space(*s)) s++;
388 int glutExtensionSupported(char *ext)
390 const char *str, *eptr;
392 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
397 str = skip_space(str);
398 eptr = skip_space(ext);
399 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
403 if((!*str || is_space(*str)) && !*eptr) {
406 while(*str && !is_space(*str)) str++;
413 /* --------------- UNIX/X11 implementation ----------------- */
415 static void handle_event(XEvent *ev);
417 void glutMainLoopEvent(void)
422 panic("display callback not set");
425 if(!upd_pending && !cb_idle) {
426 XNextEvent(dpy, &ev);
430 while(XPending(dpy)) {
431 XNextEvent(dpy, &ev);
440 if(upd_pending && mapped) {
446 static KeySym translate_keysym(KeySym sym)
467 static void handle_event(XEvent *ev)
478 case ConfigureNotify:
479 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
480 win_width = ev->xconfigure.width;
481 win_height = ev->xconfigure.height;
482 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
487 if(ev->xclient.message_type == xa_wm_proto) {
488 if(ev->xclient.data.l[0] == xa_wm_del_win) {
500 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
501 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
504 sym = translate_keysym(sym);
506 if(ev->type == KeyPress) {
507 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
509 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
512 if(ev->type == KeyPress) {
513 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
515 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
522 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
524 int bn = ev->xbutton.button - Button1;
525 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
526 ev->xbutton.x, ev->xbutton.y);
531 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
532 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
534 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
538 case VisibilityNotify:
540 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
544 if(cb_entry) cb_entry(GLUT_ENTERED);
547 if(cb_entry) cb_entry(GLUT_LEFT);
552 void glutSwapBuffers(void)
554 glXSwapBuffers(dpy, win);
559 unsigned long functions;
560 unsigned long decorations;
562 unsigned long status;
565 #define MWM_HINTS_DECORATIONS 2
566 #define MWM_DECOR_ALL 1
568 static void set_fullscreen_mwm(int fs)
570 struct mwm_hints hints;
573 hints.decorations = 0;
574 hints.flags = MWM_HINTS_DECORATIONS;
575 XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32,
576 PropModeReplace, (unsigned char*)&hints, 5);
578 XDeleteProperty(dpy, win, xa_motif_wm_hints);
582 static int have_netwm_fullscr(void)
586 unsigned long i, count, rem;
588 Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False);
591 XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType,
592 &type, &fmt, &count, &rem, (unsigned char**)prop);
594 for(i=0; i<count; i++) {
595 if(prop[i] == xa_net_wm_state_fullscr) {
605 static void set_fullscreen_ewmh(int fs)
607 XClientMessageEvent msg = {0};
609 msg.type = ClientMessage;
611 msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */
613 msg.data.l[0] = fs ? 1 : 0;
614 msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */
616 msg.data.l[3] = 1; /* source regular application */
617 XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg);
620 static void set_fullscreen(int fs)
622 if(fullscreen == fs) return;
624 if(xa_net_wm_state && xa_net_wm_state_fullscr) {
625 set_fullscreen_ewmh(fs);
627 } else if(xa_motif_wm_hints) {
628 set_fullscreen_mwm(fs);
633 void glutPositionWindow(int x, int y)
636 XMoveWindow(dpy, win, x, y);
639 void glutReshapeWindow(int xsz, int ysz)
642 XResizeWindow(dpy, win, xsz, ysz);
645 void glutFullScreen(void)
650 void glutSetWindowTitle(const char *title)
653 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
656 XSetWMName(dpy, win, &tprop);
660 void glutSetIconTitle(const char *title)
663 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
666 XSetWMIconName(dpy, win, &tprop);
670 void glutSetCursor(int cidx)
675 case GLUT_CURSOR_LEFT_ARROW:
676 cur = XCreateFontCursor(dpy, XC_left_ptr);
678 case GLUT_CURSOR_INHERIT:
680 case GLUT_CURSOR_NONE:
686 XDefineCursor(dpy, win, cur);
690 static XVisualInfo *choose_visual(unsigned int mode)
697 if(mode & GLUT_DOUBLE) {
698 *aptr++ = GLX_DOUBLEBUFFER;
701 if(mode & GLUT_INDEX) {
702 *aptr++ = GLX_BUFFER_SIZE;
706 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
707 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
708 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
710 if(mode & GLUT_ALPHA) {
711 *aptr++ = GLX_ALPHA_SIZE;
714 if(mode & GLUT_DEPTH) {
715 *aptr++ = GLX_DEPTH_SIZE;
718 if(mode & GLUT_STENCIL) {
719 *aptr++ = GLX_STENCIL_SIZE;
722 if(mode & GLUT_ACCUM) {
723 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
724 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
725 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
727 if(mode & GLUT_STEREO) {
728 *aptr++ = GLX_STEREO;
730 if(mode & GLUT_SRGB) {
731 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
733 if(mode & GLUT_MULTISAMPLE) {
734 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
736 *aptr++ = GLX_SAMPLES_ARB;
743 return glXChooseVisual(dpy, scr, attr);
745 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
754 static void create_window(const char *title)
756 XSetWindowAttributes xattr = {0};
758 unsigned int xattr_mask;
759 unsigned int mode = init_mode;
761 if(!(vi = choose_visual(mode))) {
763 if(!(vi = choose_visual(mode))) {
764 panic("Failed to find compatible visual\n");
768 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
770 panic("Failed to create OpenGL context\n");
773 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
774 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
775 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
776 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
777 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
778 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
779 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
780 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
781 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
782 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
784 xattr.background_pixel = BlackPixel(dpy, scr);
785 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
786 xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel;
787 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
788 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
790 glXDestroyContext(dpy, ctx);
791 panic("Failed to create window\n");
795 XSelectInput(dpy, win, evmask);
797 glutSetWindowTitle(title);
798 glutSetIconTitle(title);
799 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
800 XMapWindow(dpy, win);
802 glXMakeCurrent(dpy, win, ctx);
805 static void get_window_pos(int *x, int *y)
807 XWindowAttributes wattr;
808 XGetWindowAttributes(dpy, win, &wattr);
813 static void get_window_size(int *w, int *h)
815 XWindowAttributes wattr;
816 XGetWindowAttributes(dpy, win, &wattr);
821 static void get_screen_size(int *scrw, int *scrh)
823 XWindowAttributes wattr;
824 XGetWindowAttributes(dpy, root, &wattr);
826 *scrh = wattr.height;
828 #endif /* BUILD_X11 */
831 /* --------------- windows implementation ----------------- */
833 static int reshape_pending;
835 static void update_modkeys(void);
836 static int translate_vkey(int vkey);
837 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
839 #ifdef MINIGLUT_WINMAIN
840 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
843 char *argv[] = { "miniglut.exe", 0 };
844 return main(argc, argv);
848 void glutMainLoopEvent(void)
853 panic("display callback not set");
856 if(reshape_pending && cb_reshape) {
858 get_window_size(&win_width, &win_height);
859 cb_reshape(win_width, win_height);
862 if(!upd_pending && !cb_idle) {
863 GetMessage(&msg, 0, 0, 0);
864 TranslateMessage(&msg);
865 DispatchMessage(&msg);
868 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
869 TranslateMessage(&msg);
870 DispatchMessage(&msg);
878 if(upd_pending && mapped) {
884 void glutSwapBuffers(void)
889 void glutPositionWindow(int x, int y)
892 unsigned int flags = SWP_SHOWWINDOW;
895 rect.left = prev_win_x;
896 rect.top = prev_win_y;
897 rect.right = rect.left + prev_win_width;
898 rect.bottom = rect.top + prev_win_height;
899 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
901 flags |= SWP_FRAMECHANGED;
903 GetWindowRect(win, &rect);
905 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
908 void glutReshapeWindow(int xsz, int ysz)
911 unsigned int flags = SWP_SHOWWINDOW;
914 rect.left = prev_win_x;
915 rect.top = prev_win_y;
916 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
918 flags |= SWP_FRAMECHANGED;
920 GetWindowRect(win, &rect);
922 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
925 void glutFullScreen(void)
928 int scr_width, scr_height;
930 if(fullscreen) return;
932 GetWindowRect(win, &rect);
933 prev_win_x = rect.left;
934 prev_win_y = rect.top;
935 prev_win_width = rect.right - rect.left;
936 prev_win_height = rect.bottom - rect.top;
938 get_screen_size(&scr_width, &scr_height);
940 SetWindowLong(win, GWL_STYLE, 0);
941 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
946 void glutSetWindowTitle(const char *title)
948 SetWindowText(win, title);
951 void glutSetIconTitle(const char *title)
955 void glutSetCursor(int cidx)
958 case GLUT_CURSOR_NONE:
961 case GLUT_CURSOR_INHERIT:
962 case GLUT_CURSOR_LEFT_ARROW:
964 SetCursor(LoadCursor(0, IDC_ARROW));
970 static void create_window(const char *title)
973 PIXELFORMATDESCRIPTOR pfd = {0};
978 rect.right = init_x + init_width;
979 rect.bottom = init_y + init_height;
980 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
982 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, rect.left, rect.top,
983 rect.right - rect.left, rect.bottom - rect.top, 0, 0, hinst, 0))) {
984 panic("Failed to create window\n");
988 pfd.nSize = sizeof pfd;
990 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
991 if(init_mode & GLUT_STEREO) {
992 pfd.dwFlags |= PFD_STEREO;
994 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
996 if(init_mode & GLUT_ALPHA) {
999 if(init_mode & GLUT_ACCUM) {
1000 pfd.cAccumBits = 24;
1002 if(init_mode & GLUT_DEPTH) {
1003 pfd.cDepthBits = 24;
1005 if(init_mode & GLUT_STENCIL) {
1006 pfd.cStencilBits = 8;
1008 pfd.iLayerType = PFD_MAIN_PLANE;
1010 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1011 panic("Failed to find suitable pixel format\n");
1013 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1014 panic("Failed to set the selected pixel format\n");
1016 if(!(ctx = wglCreateContext(dc))) {
1017 panic("Failed to create the OpenGL context\n");
1019 wglMakeCurrent(dc, ctx);
1021 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1022 ctx_info.rsize = pfd.cRedBits;
1023 ctx_info.gsize = pfd.cGreenBits;
1024 ctx_info.bsize = pfd.cBlueBits;
1025 ctx_info.asize = pfd.cAlphaBits;
1026 ctx_info.zsize = pfd.cDepthBits;
1027 ctx_info.ssize = pfd.cStencilBits;
1028 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1029 ctx_info.samples = 1; /* TODO */
1030 ctx_info.srgb = 0; /* TODO */
1033 SetForegroundWindow(win);
1036 reshape_pending = 1;
1039 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1041 static int mouse_x, mouse_y;
1046 if(win) DestroyWindow(win);
1050 wglMakeCurrent(dc, 0);
1051 wglDeleteContext(ctx);
1058 ValidateRect(win, 0);
1062 x = lparam & 0xffff;
1064 if(x != win_width && y != win_height) {
1068 reshape_pending = 0;
1069 cb_reshape(win_width, win_height);
1076 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1082 key = translate_vkey(wparam);
1085 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1089 cb_skeydown(key, mouse_x, mouse_y);
1097 key = translate_vkey(wparam);
1100 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1104 cb_skeyup(key, mouse_x, mouse_y);
1109 case WM_LBUTTONDOWN:
1110 handle_mbutton(0, 1, wparam, lparam);
1112 case WM_MBUTTONDOWN:
1113 handle_mbutton(1, 1, wparam, lparam);
1115 case WM_RBUTTONDOWN:
1116 handle_mbutton(2, 1, wparam, lparam);
1119 handle_mbutton(0, 0, wparam, lparam);
1122 handle_mbutton(1, 0, wparam, lparam);
1125 handle_mbutton(2, 0, wparam, lparam);
1129 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1130 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1132 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1138 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1142 return DefWindowProc(win, msg, wparam, lparam);
1148 static void update_modkeys(void)
1150 if(GetKeyState(VK_SHIFT) & 0x8000) {
1151 modstate |= GLUT_ACTIVE_SHIFT;
1153 modstate &= ~GLUT_ACTIVE_SHIFT;
1155 if(GetKeyState(VK_CONTROL) & 0x8000) {
1156 modstate |= GLUT_ACTIVE_CTRL;
1158 modstate &= ~GLUT_ACTIVE_CTRL;
1160 if(GetKeyState(VK_MENU) & 0x8000) {
1161 modstate |= GLUT_ACTIVE_ALT;
1163 modstate &= ~GLUT_ACTIVE_ALT;
1167 static int translate_vkey(int vkey)
1170 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1171 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1172 case VK_END: return GLUT_KEY_END;
1173 case VK_HOME: return GLUT_KEY_HOME;
1174 case VK_LEFT: return GLUT_KEY_LEFT;
1175 case VK_UP: return GLUT_KEY_UP;
1176 case VK_RIGHT: return GLUT_KEY_RIGHT;
1177 case VK_DOWN: return GLUT_KEY_DOWN;
1182 if(vkey >= 'A' && vkey <= 'Z') {
1184 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1185 vkey -= VK_F1 + GLUT_KEY_F1;
1191 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1198 x = lparam & 0xffff;
1200 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1204 static void get_window_pos(int *x, int *y)
1207 GetWindowRect(win, &rect);
1212 static void get_window_size(int *w, int *h)
1215 GetClientRect(win, &rect);
1216 *w = rect.right - rect.left;
1217 *h = rect.bottom - rect.top;
1220 static void get_screen_size(int *scrw, int *scrh)
1222 *scrw = GetSystemMetrics(SM_CXSCREEN);
1223 *scrh = GetSystemMetrics(SM_CYSCREEN);
1225 #endif /* BUILD_WIN32 */
1227 #if defined(__unix__) || defined(__APPLE__)
1228 #include <sys/time.h>
1230 #ifdef MINIGLUT_USE_LIBC
1231 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1233 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1236 static long get_msec(void)
1238 static struct timeval tv0;
1241 sys_gettimeofday(&tv, 0);
1242 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1246 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1250 static long get_msec(void)
1255 #ifdef MINIGLUT_NO_WINMM
1256 tm = GetTickCount();
1268 static void panic(const char *msg)
1270 const char *end = msg;
1272 sys_write(2, msg, end - msg);
1277 #ifdef MINIGLUT_USE_LIBC
1278 static void sys_exit(int status)
1283 static int sys_write(int fd, const void *buf, int count)
1285 return write(fd, buf, count);
1288 #else /* !MINIGLUT_USE_LIBC */
1292 static void sys_exit(int status)
1296 :: "a"(60), "D"(status));
1298 static int sys_write(int fd, const void *buf, int count)
1304 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1307 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1313 : "a"(96), "D"(tv), "S"(tz));
1316 #endif /* __x86_64__ */
1318 static void sys_exit(int status)
1322 :: "a"(1), "b"(status));
1324 static int sys_write(int fd, const void *buf, int count)
1330 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1333 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1339 : "a"(78), "b"(tv), "c"(tz));
1342 #endif /* __i386__ */
1343 #endif /* __linux__ */
1346 static void sys_exit(int status)
1348 ExitProcess(status);
1350 static int sys_write(int fd, const void *buf, int count)
1352 unsigned long wrsz = 0;
1354 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1355 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1361 #endif /* !MINIGLUT_USE_LIBC */
1364 /* ----------------- primitives ------------------ */
1365 #ifdef MINIGLUT_USE_LIBC
1369 #define mglut_sincos(a, s, c) \
1375 #define mglut_atan(x) atan(x)
1377 #else /* !MINIGLUT_USE_LIBC */
1380 static void mglut_sincos(float angle, float *sptr, float *cptr)
1387 : "=m"(*sptr), "=m"(*cptr)
1392 static float mglut_atan(float x)
1408 static void mglut_sincos(float angle, float *sptr, float *cptr)
1421 static float mglut_atan(float x)
1435 #pragma aux mglut_sincos = \
1437 "fstp dword ptr [edx]" \
1438 "fstp dword ptr [eax]" \
1439 parm[8087][eax][edx] \
1442 #pragma aux mglut_atan = \
1448 #endif /* __WATCOMC__ */
1450 #endif /* !MINIGLUT_USE_LIBC */
1452 #define PI 3.1415926536f
1454 void glutSolidSphere(float rad, int slices, int stacks)
1457 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1458 float du = 1.0f / (float)slices;
1459 float dv = 1.0f / (float)stacks;
1462 for(i=0; i<stacks; i++) {
1464 for(j=0; j<slices; j++) {
1466 for(k=0; k<4; k++) {
1467 gray = k ^ (k >> 1);
1468 s = gray & 1 ? u + du : u;
1469 t = gray & 2 ? v + dv : v;
1470 theta = s * PI * 2.0f;
1472 mglut_sincos(theta, &sintheta, &costheta);
1473 mglut_sincos(phi, &sinphi, &cosphi);
1474 x = sintheta * sinphi;
1475 y = costheta * sinphi;
1480 glNormal3f(x, y, z);
1481 glVertex3f(x * rad, y * rad, z * rad);
1488 void glutWireSphere(float rad, int slices, int stacks)
1490 glPushAttrib(GL_POLYGON_BIT);
1491 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1492 glutSolidSphere(rad, slices, stacks);
1496 void glutSolidCube(float sz)
1498 int i, j, idx, gray, flip, rotx;
1499 float vpos[3], norm[3];
1500 float rad = sz * 0.5f;
1503 for(i=0; i<6; i++) {
1506 idx = (~i & 2) - rotx;
1507 norm[0] = norm[1] = norm[2] = 0.0f;
1508 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
1510 vpos[idx] = norm[idx] * rad;
1511 for(j=0; j<4; j++) {
1512 gray = j ^ (j >> 1);
1513 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
1514 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
1515 glTexCoord2f(gray & 1, gray >> 1);
1522 void glutWireCube(float sz)
1524 glPushAttrib(GL_POLYGON_BIT);
1525 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1530 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
1533 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
1534 float du = 1.0f / (float)slices;
1535 float dv = 1.0f / (float)stacks;
1538 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
1539 mglut_sincos(phi, &sinphi, &cosphi);
1542 for(i=0; i<stacks; i++) {
1544 for(j=0; j<slices; j++) {
1546 for(k=0; k<4; k++) {
1547 gray = k ^ (k >> 1);
1548 s = gray & 2 ? u + du : u;
1549 t = gray & 1 ? v + dv : v;
1550 rad = rbot + (rtop - rbot) * t;
1551 theta = s * PI * 2.0f;
1552 mglut_sincos(theta, &sintheta, &costheta);
1554 x = sintheta * cosphi;
1555 y = costheta * cosphi;
1560 glNormal3f(x, y, z);
1561 glVertex3f(sintheta * rad, costheta * rad, t * height);
1568 void glutSolidCone(float base, float height, int slices, int stacks)
1570 draw_cylinder(base, 0, height, slices, stacks);
1573 void glutWireCone(float base, float height, int slices, int stacks)
1575 glPushAttrib(GL_POLYGON_BIT);
1576 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1577 glutSolidCone(base, height, slices, stacks);
1581 void glutSolidCylinder(float rad, float height, int slices, int stacks)
1583 draw_cylinder(rad, rad, height, slices, stacks);
1586 void glutWireCylinder(float rad, float height, int slices, int stacks)
1588 glPushAttrib(GL_POLYGON_BIT);
1589 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1590 glutSolidCylinder(rad, height, slices, stacks);
1594 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
1597 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
1598 float du = 1.0f / (float)rings;
1599 float dv = 1.0f / (float)sides;
1602 for(i=0; i<rings; i++) {
1604 for(j=0; j<sides; j++) {
1606 for(k=0; k<4; k++) {
1607 gray = k ^ (k >> 1);
1608 s = gray & 1 ? u + du : u;
1609 t = gray & 2 ? v + dv : v;
1610 theta = s * PI * 2.0f;
1611 phi = t * PI * 2.0f;
1612 mglut_sincos(theta, &sintheta, &costheta);
1613 mglut_sincos(phi, &sinphi, &cosphi);
1614 x = sintheta * sinphi;
1615 y = costheta * sinphi;
1620 glNormal3f(x, y, z);
1622 x = x * inner_rad + sintheta * outer_rad;
1623 y = y * inner_rad + costheta * outer_rad;
1625 glVertex3f(x, y, z);
1632 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
1634 glPushAttrib(GL_POLYGON_BIT);
1635 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1636 glutSolidTorus(inner_rad, outer_rad, sides, rings);
1640 void glutSolidTeapot(float size)
1644 void glutWireTeapot(float size)