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
23 static void mglut_sincosf(float angle, float *sptr, float *cptr);
24 static float mglut_atan(float x);
27 #define PI 3.1415926536f
32 #include <X11/cursorfont.h>
36 #ifndef GLX_SAMPLE_BUFFERS_ARB
37 #define GLX_SAMPLE_BUFFERS_ARB 100000
38 #define GLX_SAMPLES_ARB 100001
40 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
41 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2
45 static Window win, root;
47 static GLXContext ctx;
48 static Atom xa_wm_proto, xa_wm_del_win;
49 static unsigned int evmask;
56 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
58 static HINSTANCE hinst;
64 #error unsupported platform
71 int rsize, gsize, bsize, asize;
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 sys_exit(int status);
87 static int sys_write(int fd, const void *buf, int count);
90 static int init_x = -1, init_y, init_width = 256, init_height = 256;
91 static unsigned int init_mode;
93 static struct ctx_info ctx_info;
94 static int cur_cursor = GLUT_CURSOR_INHERIT;
96 static glut_cb cb_display;
97 static glut_cb cb_idle;
98 static glut_cb_reshape cb_reshape;
99 static glut_cb_state cb_vis, cb_entry;
100 static glut_cb_keyb cb_keydown, cb_keyup;
101 static glut_cb_special cb_skeydown, cb_skeyup;
102 static glut_cb_mouse cb_mouse;
103 static glut_cb_motion cb_motion, cb_passive;
104 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
105 static glut_cb_sbbutton cb_sball_button;
107 static int fullscreen;
108 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
110 static int win_width, win_height;
113 static int upd_pending;
117 void glutInit(int *argc, char **argv)
120 if(!(dpy = XOpenDisplay(0))) {
121 panic("Failed to connect to the X server\n");
123 scr = DefaultScreen(dpy);
124 root = RootWindow(dpy, scr);
125 xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
126 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
128 evmask = ExposureMask | StructureNotifyMask;
133 hinst = GetModuleHandle(0);
135 wc.cbSize = sizeof wc;
136 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
137 wc.hCursor = LoadCursor(0, IDC_ARROW);
138 wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
139 wc.hInstance = hinst;
140 wc.lpfnWndProc = handle_message;
141 wc.lpszClassName = "MiniGLUT";
142 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
143 if(!RegisterClassEx(&wc)) {
144 panic("Failed to register \"MiniGLUT\" window class\n");
148 get_screen_size(&init_x, &init_y);
155 void glutInitWindowPosition(int x, int y)
161 void glutInitWindowSize(int xsz, int ysz)
167 void glutInitDisplayMode(unsigned int mode)
172 void glutCreateWindow(const char *title)
174 create_window(title);
182 void glutMainLoop(void)
189 void glutPostRedisplay(void)
194 #define UPD_EVMASK(x) \
201 if(win) XSelectInput(dpy, win, evmask); \
205 void glutIdleFunc(glut_cb func)
210 void glutDisplayFunc(glut_cb func)
215 void glutReshapeFunc(glut_cb_reshape func)
220 void glutVisibilityFunc(glut_cb_state func)
224 UPD_EVMASK(VisibilityChangeMask);
228 void glutEntryFunc(glut_cb_state func)
232 UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
236 void glutKeyboardFunc(glut_cb_keyb func)
240 UPD_EVMASK(KeyPressMask);
244 void glutKeyboardUpFunc(glut_cb_keyb func)
248 UPD_EVMASK(KeyReleaseMask);
252 void glutSpecialFunc(glut_cb_special func)
256 UPD_EVMASK(KeyPressMask);
260 void glutSpecialUpFunc(glut_cb_special func)
264 UPD_EVMASK(KeyReleaseMask);
268 void glutMouseFunc(glut_cb_mouse func)
272 UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
276 void glutMotionFunc(glut_cb_motion func)
280 UPD_EVMASK(ButtonMotionMask);
284 void glutPassiveMotionFunc(glut_cb_motion func)
288 UPD_EVMASK(PointerMotionMask);
292 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
294 cb_sball_motion = func;
297 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
299 cb_sball_rotate = func;
302 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
304 cb_sball_button = func;
307 int glutGet(unsigned int s)
312 get_window_pos(&x, &y);
315 get_window_pos(&x, &y);
317 case GLUT_WINDOW_WIDTH:
318 get_window_size(&x, &y);
320 case GLUT_WINDOW_HEIGHT:
321 get_window_size(&x, &y);
323 case GLUT_WINDOW_BUFFER_SIZE:
324 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
325 case GLUT_WINDOW_STENCIL_SIZE:
326 return ctx_info.ssize;
327 case GLUT_WINDOW_DEPTH_SIZE:
328 return ctx_info.zsize;
329 case GLUT_WINDOW_RED_SIZE:
330 return ctx_info.rsize;
331 case GLUT_WINDOW_GREEN_SIZE:
332 return ctx_info.gsize;
333 case GLUT_WINDOW_BLUE_SIZE:
334 return ctx_info.bsize;
335 case GLUT_WINDOW_ALPHA_SIZE:
336 return ctx_info.asize;
337 case GLUT_WINDOW_DOUBLEBUFFER:
338 return ctx_info.dblbuf;
339 case GLUT_WINDOW_RGBA:
341 case GLUT_WINDOW_NUM_SAMPLES:
342 return ctx_info.samples;
343 case GLUT_WINDOW_STEREO:
344 return ctx_info.stereo;
345 case GLUT_WINDOW_SRGB:
346 return ctx_info.srgb;
347 case GLUT_WINDOW_CURSOR:
349 case GLUT_SCREEN_WIDTH:
350 get_screen_size(&x, &y);
352 case GLUT_SCREEN_HEIGHT:
353 get_screen_size(&x, &y);
355 case GLUT_INIT_DISPLAY_MODE:
357 case GLUT_INIT_WINDOW_X:
359 case GLUT_INIT_WINDOW_Y:
361 case GLUT_INIT_WINDOW_WIDTH:
363 case GLUT_INIT_WINDOW_HEIGHT:
365 case GLUT_ELAPSED_TIME:
373 int glutGetModifiers(void)
378 static int is_space(int c)
380 return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
383 static const char *skip_space(const char *s)
385 while(*s && is_space(*s)) s++;
389 int glutExtensionSupported(char *ext)
391 const char *str, *eptr;
393 if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
398 str = skip_space(str);
399 eptr = skip_space(ext);
400 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
404 if((!*str || is_space(*str)) && !*eptr) {
407 while(*str && !is_space(*str)) str++;
414 void glutSolidSphere(float rad, int slices, int stacks)
417 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
418 float du = 1.0f / (float)slices;
419 float dv = 1.0f / (float)stacks;
422 for(i=0; i<stacks; i++) {
424 for(j=0; j<slices; j++) {
428 s = gray & 1 ? u + du : u;
429 t = gray & 2 ? v + dv : v;
430 theta = s * PI * 2.0f;
432 mglut_sincosf(theta, &sintheta, &costheta);
433 mglut_sincosf(phi, &sinphi, &cosphi);
434 x = sintheta * sinphi;
435 y = costheta * sinphi;
441 glVertex3f(x * rad, y * rad, z * rad);
448 void glutWireSphere(float rad, int slices, int stacks)
450 glPushAttrib(GL_POLYGON_BIT);
451 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
452 glutSolidSphere(rad, slices, stacks);
456 void glutSolidCube(float sz)
458 int i, j, idx, gray, flip, rotx;
459 float vpos[3], norm[3];
460 float rad = sz * 0.5f;
466 idx = (~i & 2) - rotx;
467 norm[0] = norm[1] = norm[2] = 0.0f;
468 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
470 vpos[idx] = norm[idx] * rad;
473 vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad;
474 vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad;
475 glTexCoord2f(gray & 1, gray >> 1);
482 void glutWireCube(float sz)
484 glPushAttrib(GL_POLYGON_BIT);
485 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
490 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
493 float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad;
494 float du = 1.0f / (float)slices;
495 float dv = 1.0f / (float)stacks;
498 phi = mglut_atan((rad < 0 ? -rad : rad) / height);
499 mglut_sincosf(phi, &sinphi, &cosphi);
502 for(i=0; i<stacks; i++) {
504 for(j=0; j<slices; j++) {
508 s = gray & 2 ? u + du : u;
509 t = gray & 1 ? v + dv : v;
510 rad = rbot + (rtop - rbot) * t;
511 theta = s * PI * 2.0f;
512 mglut_sincosf(theta, &sintheta, &costheta);
514 x = sintheta * cosphi;
515 y = costheta * cosphi;
521 glVertex3f(sintheta * rad, costheta * rad, t * height);
528 void glutSolidCone(float base, float height, int slices, int stacks)
530 draw_cylinder(base, 0, height, slices, stacks);
533 void glutWireCone(float base, float height, int slices, int stacks)
535 glPushAttrib(GL_POLYGON_BIT);
536 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
537 glutSolidCone(base, height, slices, stacks);
541 void glutSolidCylinder(float rad, float height, int slices, int stacks)
543 draw_cylinder(rad, rad, height, slices, stacks);
546 void glutWireCylinder(float rad, float height, int slices, int stacks)
548 glPushAttrib(GL_POLYGON_BIT);
549 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
550 glutSolidCylinder(rad, height, slices, stacks);
554 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
557 float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi;
558 float du = 1.0f / (float)rings;
559 float dv = 1.0f / (float)sides;
562 for(i=0; i<rings; i++) {
564 for(j=0; j<sides; j++) {
568 s = gray & 1 ? u + du : u;
569 t = gray & 2 ? v + dv : v;
570 theta = s * PI * 2.0f;
572 mglut_sincosf(theta, &sintheta, &costheta);
573 mglut_sincosf(phi, &sinphi, &cosphi);
574 x = sintheta * sinphi;
575 y = costheta * sinphi;
582 x = x * inner_rad + sintheta * outer_rad;
583 y = y * inner_rad + costheta * outer_rad;
592 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
594 glPushAttrib(GL_POLYGON_BIT);
595 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
596 glutSolidTorus(inner_rad, outer_rad, sides, rings);
600 void glutSolidTeapot(float size)
604 void glutWireTeapot(float size)
610 /* --------------- UNIX/X11 implementation ----------------- */
612 static void handle_event(XEvent *ev);
614 void glutMainLoopEvent(void)
619 panic("display callback not set");
622 if(!upd_pending && !cb_idle) {
623 XNextEvent(dpy, &ev);
627 while(XPending(dpy)) {
628 XNextEvent(dpy, &ev);
637 if(upd_pending && mapped) {
643 static KeySym translate_keysym(KeySym sym)
664 static void handle_event(XEvent *ev)
675 case ConfigureNotify:
676 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
677 win_width = ev->xconfigure.width;
678 win_height = ev->xconfigure.height;
679 cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
684 if(ev->xclient.message_type == xa_wm_proto) {
685 if(ev->xclient.data.l[0] == xa_wm_del_win) {
697 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
698 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
701 sym = translate_keysym(sym);
703 if(ev->type == KeyPress) {
704 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
706 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
709 if(ev->type == KeyPress) {
710 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
712 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
719 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
721 int bn = ev->xbutton.button - Button1;
722 cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
723 ev->xbutton.x, ev->xbutton.y);
728 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
729 if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
731 if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
735 case VisibilityNotify:
737 cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
741 if(cb_entry) cb_entry(GLUT_ENTERED);
744 if(cb_entry) cb_entry(GLUT_LEFT);
749 void glutSwapBuffers(void)
751 glXSwapBuffers(dpy, win);
754 void glutPositionWindow(int x, int y)
756 XMoveWindow(dpy, win, x, y);
759 void glutReshapeWindow(int xsz, int ysz)
761 XResizeWindow(dpy, win, xsz, ysz);
764 void glutFullScreen(void)
769 void glutSetWindowTitle(const char *title)
772 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
775 XSetWMName(dpy, win, &tprop);
779 void glutSetIconTitle(const char *title)
782 if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
785 XSetWMIconName(dpy, win, &tprop);
789 void glutSetCursor(int cidx)
794 case GLUT_CURSOR_LEFT_ARROW:
795 cur = XCreateFontCursor(dpy, XC_left_ptr);
797 case GLUT_CURSOR_INHERIT:
799 case GLUT_CURSOR_NONE:
805 XDefineCursor(dpy, win, cur);
809 static XVisualInfo *choose_visual(unsigned int mode)
816 if(mode & GLUT_DOUBLE) {
817 *aptr++ = GLX_DOUBLEBUFFER;
820 if(mode & GLUT_INDEX) {
821 *aptr++ = GLX_BUFFER_SIZE;
825 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
826 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
827 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
829 if(mode & GLUT_ALPHA) {
830 *aptr++ = GLX_ALPHA_SIZE;
833 if(mode & GLUT_DEPTH) {
834 *aptr++ = GLX_DEPTH_SIZE;
837 if(mode & GLUT_STENCIL) {
838 *aptr++ = GLX_STENCIL_SIZE;
841 if(mode & GLUT_ACCUM) {
842 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
843 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
844 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
846 if(mode & GLUT_STEREO) {
847 *aptr++ = GLX_STEREO;
849 if(mode & GLUT_SRGB) {
850 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
852 if(mode & GLUT_MULTISAMPLE) {
853 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
855 *aptr++ = GLX_SAMPLES_ARB;
862 return glXChooseVisual(dpy, scr, attr);
864 while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
873 static void create_window(const char *title)
875 XSetWindowAttributes xattr;
877 unsigned int xattr_mask;
878 unsigned int mode = init_mode;
880 if(!(vi = choose_visual(mode))) {
882 if(!(vi = choose_visual(mode))) {
883 panic("Failed to find compatible visual\n");
887 if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
889 panic("Failed to create OpenGL context\n");
892 glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
893 glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
894 glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
895 glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
896 glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
897 glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
898 glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
899 glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
900 glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
901 glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
903 xattr.background_pixel = BlackPixel(dpy, scr);
904 xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
905 xattr_mask = CWBackPixel | CWColormap;
906 if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
907 vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
909 glXDestroyContext(dpy, ctx);
910 panic("Failed to create window\n");
914 XSelectInput(dpy, win, evmask);
916 glutSetWindowTitle(title);
917 glutSetIconTitle(title);
918 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
919 XMapWindow(dpy, win);
921 glXMakeCurrent(dpy, win, ctx);
924 static void get_window_pos(int *x, int *y)
926 XWindowAttributes wattr;
927 XGetWindowAttributes(dpy, win, &wattr);
932 static void get_window_size(int *w, int *h)
934 XWindowAttributes wattr;
935 XGetWindowAttributes(dpy, win, &wattr);
940 static void get_screen_size(int *scrw, int *scrh)
942 XWindowAttributes wattr;
943 XGetWindowAttributes(dpy, root, &wattr);
945 *scrh = wattr.height;
947 #endif /* BUILD_X11 */
950 /* --------------- windows implementation ----------------- */
952 static int reshape_pending;
954 static void update_modkeys(void);
955 static int translate_vkey(int vkey);
956 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
958 #ifdef MINIGLUT_WINMAIN
959 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
962 char *argv[] = { "miniglut.exe", 0 };
963 return main(argc, argv);
967 void glutMainLoopEvent(void)
972 panic("display callback not set");
975 if(reshape_pending && cb_reshape) {
977 get_window_size(&win_width, &win_height);
978 cb_reshape(win_width, win_height);
981 if(!upd_pending && !cb_idle) {
982 GetMessage(&msg, 0, 0, 0);
983 TranslateMessage(&msg);
984 DispatchMessage(&msg);
987 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
988 TranslateMessage(&msg);
989 DispatchMessage(&msg);
997 if(upd_pending && mapped) {
1003 void glutSwapBuffers(void)
1008 void glutPositionWindow(int x, int y)
1011 unsigned int flags = SWP_SHOWWINDOW;
1014 rect.left = prev_win_x;
1015 rect.top = prev_win_y;
1016 rect.right = rect.left + prev_win_width;
1017 rect.bottom = rect.top + prev_win_height;
1018 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1020 flags |= SWP_FRAMECHANGED;
1022 GetWindowRect(win, &rect);
1024 SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1027 void glutReshapeWindow(int xsz, int ysz)
1030 unsigned int flags = SWP_SHOWWINDOW;
1033 rect.left = prev_win_x;
1034 rect.top = prev_win_y;
1035 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1037 flags |= SWP_FRAMECHANGED;
1039 GetWindowRect(win, &rect);
1041 SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1044 void glutFullScreen(void)
1047 int scr_width, scr_height;
1049 if(fullscreen) return;
1051 GetWindowRect(win, &rect);
1052 prev_win_x = rect.left;
1053 prev_win_y = rect.top;
1054 prev_win_width = rect.right - rect.left;
1055 prev_win_height = rect.bottom - rect.top;
1057 get_screen_size(&scr_width, &scr_height);
1059 SetWindowLong(win, GWL_STYLE, 0);
1060 SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1065 void glutSetWindowTitle(const char *title)
1067 SetWindowText(win, title);
1070 void glutSetIconTitle(const char *title)
1074 void glutSetCursor(int cidx)
1077 case GLUT_CURSOR_NONE:
1080 case GLUT_CURSOR_INHERIT:
1081 case GLUT_CURSOR_LEFT_ARROW:
1083 SetCursor(LoadCursor(0, IDC_ARROW));
1089 static void create_window(const char *title)
1092 PIXELFORMATDESCRIPTOR pfd = {0};
1097 rect.right = init_x + init_width;
1098 rect.bottom = init_y + init_height;
1099 AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1101 if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, rect.left, rect.top,
1102 rect.right - rect.left, rect.bottom - rect.top, 0, 0, hinst, 0))) {
1103 panic("Failed to create window\n");
1107 pfd.nSize = sizeof pfd;
1109 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1110 if(init_mode & GLUT_STEREO) {
1111 pfd.dwFlags |= PFD_STEREO;
1113 pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1114 pfd.cColorBits = 24;
1115 if(init_mode & GLUT_ALPHA) {
1118 if(init_mode & GLUT_ACCUM) {
1119 pfd.cAccumBits = 24;
1121 if(init_mode & GLUT_DEPTH) {
1122 pfd.cDepthBits = 24;
1124 if(init_mode & GLUT_STENCIL) {
1125 pfd.cStencilBits = 8;
1127 pfd.iLayerType = PFD_MAIN_PLANE;
1129 if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1130 panic("Failed to find suitable pixel format\n");
1132 if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1133 panic("Failed to set the selected pixel format\n");
1135 if(!(ctx = wglCreateContext(dc))) {
1136 panic("Failed to create the OpenGL context\n");
1138 wglMakeCurrent(dc, ctx);
1140 DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1141 ctx_info.rsize = pfd.cRedBits;
1142 ctx_info.gsize = pfd.cGreenBits;
1143 ctx_info.bsize = pfd.cBlueBits;
1144 ctx_info.asize = pfd.cAlphaBits;
1145 ctx_info.zsize = pfd.cDepthBits;
1146 ctx_info.ssize = pfd.cStencilBits;
1147 ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1148 ctx_info.samples = 1; /* TODO */
1149 ctx_info.srgb = 0; /* TODO */
1152 SetForegroundWindow(win);
1155 reshape_pending = 1;
1158 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1160 static int mouse_x, mouse_y;
1165 if(win) DestroyWindow(win);
1169 wglMakeCurrent(dc, 0);
1170 wglDeleteContext(ctx);
1177 ValidateRect(win, 0);
1181 x = lparam & 0xffff;
1183 if(x != win_width && y != win_height) {
1187 reshape_pending = 0;
1188 cb_reshape(win_width, win_height);
1195 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1201 key = translate_vkey(wparam);
1204 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1208 cb_skeydown(key, mouse_x, mouse_y);
1216 key = translate_vkey(wparam);
1219 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1223 cb_skeyup(key, mouse_x, mouse_y);
1228 case WM_LBUTTONDOWN:
1229 handle_mbutton(0, 1, wparam, lparam);
1231 case WM_MBUTTONDOWN:
1232 handle_mbutton(1, 1, wparam, lparam);
1234 case WM_RBUTTONDOWN:
1235 handle_mbutton(2, 1, wparam, lparam);
1238 handle_mbutton(0, 0, wparam, lparam);
1241 handle_mbutton(1, 0, wparam, lparam);
1244 handle_mbutton(2, 0, wparam, lparam);
1248 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1249 if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1251 if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1257 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1261 return DefWindowProc(win, msg, wparam, lparam);
1267 static void update_modkeys(void)
1269 if(GetKeyState(VK_SHIFT) & 0x8000) {
1270 modstate |= GLUT_ACTIVE_SHIFT;
1272 modstate &= ~GLUT_ACTIVE_SHIFT;
1274 if(GetKeyState(VK_CONTROL) & 0x8000) {
1275 modstate |= GLUT_ACTIVE_CTRL;
1277 modstate &= ~GLUT_ACTIVE_CTRL;
1279 if(GetKeyState(VK_MENU) & 0x8000) {
1280 modstate |= GLUT_ACTIVE_ALT;
1282 modstate &= ~GLUT_ACTIVE_ALT;
1286 static int translate_vkey(int vkey)
1289 case VK_PRIOR: return GLUT_KEY_PAGE_UP;
1290 case VK_NEXT: return GLUT_KEY_PAGE_DOWN;
1291 case VK_END: return GLUT_KEY_END;
1292 case VK_HOME: return GLUT_KEY_HOME;
1293 case VK_LEFT: return GLUT_KEY_LEFT;
1294 case VK_UP: return GLUT_KEY_UP;
1295 case VK_RIGHT: return GLUT_KEY_RIGHT;
1296 case VK_DOWN: return GLUT_KEY_DOWN;
1301 if(vkey >= 'A' && vkey <= 'Z') {
1303 } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1304 vkey -= VK_F1 + GLUT_KEY_F1;
1310 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1317 x = lparam & 0xffff;
1319 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1323 static void get_window_pos(int *x, int *y)
1326 GetWindowRect(win, &rect);
1331 static void get_window_size(int *w, int *h)
1334 GetClientRect(win, &rect);
1335 *w = rect.right - rect.left;
1336 *h = rect.bottom - rect.top;
1339 static void get_screen_size(int *scrw, int *scrh)
1341 *scrw = GetSystemMetrics(SM_CXSCREEN);
1342 *scrh = GetSystemMetrics(SM_CYSCREEN);
1344 #endif /* BUILD_WIN32 */
1346 #if defined(__unix__) || defined(__APPLE__)
1347 #include <sys/time.h>
1349 #ifdef MINIGLUT_USE_LIBC
1350 #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz)
1352 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1355 static long get_msec(void)
1357 static struct timeval tv0;
1360 sys_gettimeofday(&tv, 0);
1361 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1365 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1369 static long get_msec(void)
1374 #ifdef MINIGLUT_NO_WINMM
1375 tm = GetTickCount();
1387 static void panic(const char *msg)
1389 const char *end = msg;
1391 sys_write(2, msg, end - msg);
1396 #ifdef MINIGLUT_USE_LIBC
1397 static void sys_exit(int status)
1402 static int sys_write(int fd, const void *buf, int count)
1404 return write(fd, buf, count);
1407 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1409 return gettimeofday(tv, tz);
1412 #else /* !MINIGLUT_USE_LIBC */
1415 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1422 : "=m"(*sptr), "=m"(*cptr)
1427 static float mglut_atan(float x)
1443 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1456 static float mglut_atan(float x)
1470 #pragma aux mglut_sincosf = \
1472 "fstp dword ptr [edx]" \
1473 "fstp dword ptr [eax]" \
1474 parm[8087][eax][edx] \
1477 #pragma aux mglut_atan = \
1488 static void sys_exit(int status)
1492 :: "a"(60), "D"(status));
1494 static int sys_write(int fd, const void *buf, int count)
1500 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1503 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1509 : "a"(96), "D"(tv), "S"(tz));
1514 static void sys_exit(int status)
1518 :: "a"(1), "b"(status));
1520 static int sys_write(int fd, const void *buf, int count)
1526 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1529 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1535 : "a"(78), "b"(tv), "c"(tz));
1540 #endif /* __linux__ */
1543 static void sys_exit(int status)
1545 ExitProcess(status);
1547 static int sys_write(int fd, const void *buf, int count)
1549 unsigned long wrsz = 0;
1551 HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1552 if(!WriteFile(out, buf, count, &wrsz, 0)) {
1559 #endif /* !MINIGLUT_USE_LIBC */