removed X Window parameters to cross-platform functions
[miniglut] / miniglut.c
1 /*
2 MiniGLUT - minimal GLUT subset without dependencies
3 Copyright (C) 2020  John Tsiombikas <nuclear@member.fsf.org>
4
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.
9
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.
14
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/>.
17  */
18 /*#define MINIGLUT_GCC_NO_BUILTIN*/
19
20 #ifdef MINIGLUT_USE_LIBC
21 #define _GNU_SOURCE
22 #include <stdlib.h>
23 #include <math.h>
24 #else
25
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)
29 #else
30 static void mglut_sincosf(float angle, float *sptr, float *cptr);
31 static float mglut_atan(float x);
32 #endif
33
34 #endif
35
36 #define PI      3.1415926536f
37
38 #if defined(__unix__)
39
40 #include <X11/Xlib.h>
41 #include <X11/cursorfont.h>
42 #include <GL/glx.h>
43 #define BUILD_X11
44
45 #ifndef GLX_SAMPLE_BUFFERS_ARB
46 #define GLX_SAMPLE_BUFFERS_ARB  100000
47 #define GLX_SAMPLES_ARB                 100001
48 #endif
49 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
50 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20b2
51 #endif
52
53 static Display *dpy;
54 static Window win, root;
55 static int scr;
56 static GLXContext ctx;
57 static Atom xa_wm_proto, xa_wm_del_win;
58 static unsigned int evmask;
59
60 #elif defined(_WIN32)
61
62 #include <windows.h>
63 #define BUILD_WIN32
64
65 static HWND win;
66 static HDC dc;
67 static HGLRC ctx;
68
69 #else
70 #error unsupported platform
71 #endif
72
73 #include <GL/gl.h>
74 #include "miniglut.h"
75
76 struct ctx_info {
77         int rsize, gsize, bsize, asize;
78         int zsize, ssize;
79         int dblbuf;
80         int samples;
81         int stereo;
82         int srgb;
83 };
84
85 static void create_window(const char *title);
86 static void get_window_pos(int *x, int *y);
87 static void get_window_size(int *w, int *h);
88 static void get_screen_size(int *scrw, int *scrh);
89
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);
94
95
96 static int init_x, init_y, init_width = 256, init_height = 256;
97 static unsigned int init_mode;
98
99 static struct ctx_info ctx_info;
100 static int cur_cursor = GLUT_CURSOR_INHERIT;
101
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;
112
113 static int win_width, win_height;
114 static int mapped;
115 static int quit;
116 static int upd_pending, reshape_pending;
117 static int modstate;
118
119
120 void glutInit(int *argc, char **argv)
121 {
122 #ifdef BUILD_X11
123         if(!(dpy = XOpenDisplay(0))) {
124                 panic("Failed to connect to the X server\n");
125         }
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);
130
131         evmask = ExposureMask | StructureNotifyMask;
132 #endif
133 }
134
135 void glutInitWindowPosition(int x, int y)
136 {
137         init_x = x;
138         init_y = y;
139 }
140
141 void glutInitWindowSize(int xsz, int ysz)
142 {
143         init_width = xsz;
144         init_height = ysz;
145 }
146
147 void glutInitDisplayMode(unsigned int mode)
148 {
149         init_mode = mode;
150 }
151
152 void glutCreateWindow(const char *title)
153 {
154         create_window(title);
155 }
156
157 void glutExit(void)
158 {
159         quit = 1;
160 }
161
162 void glutMainLoop(void)
163 {
164         while(!quit) {
165                 glutMainLoopEvent();
166         }
167 }
168
169 void glutPostRedisplay(void)
170 {
171         upd_pending = 1;
172 }
173
174 #define UPD_EVMASK(x) \
175         do { \
176                 if(func) { \
177                         evmask |= x; \
178                 } else { \
179                         evmask &= ~(x); \
180                 } \
181                 if(win) XSelectInput(dpy, win, evmask); \
182         } while(0)
183
184
185 void glutIdleFunc(glut_cb func)
186 {
187         cb_idle = func;
188 }
189
190 void glutDisplayFunc(glut_cb func)
191 {
192         cb_display = func;
193 }
194
195 void glutReshapeFunc(glut_cb_reshape func)
196 {
197         cb_reshape = func;
198 }
199
200 void glutVisibilityFunc(glut_cb_state func)
201 {
202         cb_vis = func;
203 #ifdef BUILD_X11
204         UPD_EVMASK(VisibilityChangeMask);
205 #endif
206 }
207
208 void glutEntryFunc(glut_cb_state func)
209 {
210         cb_entry = func;
211 #ifdef BUILD_X11
212         UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
213 #endif
214 }
215
216 void glutKeyboardFunc(glut_cb_keyb func)
217 {
218         cb_keydown = func;
219 #ifdef BUILD_X11
220         UPD_EVMASK(KeyPressMask);
221 #endif
222 }
223
224 void glutKeyboardUpFunc(glut_cb_keyb func)
225 {
226         cb_keyup = func;
227 #ifdef BUILD_X11
228         UPD_EVMASK(KeyReleaseMask);
229 #endif
230 }
231
232 void glutSpecialFunc(glut_cb_special func)
233 {
234         cb_skeydown = func;
235 #ifdef BUILD_X11
236         UPD_EVMASK(KeyPressMask);
237 #endif
238 }
239
240 void glutSpecialUpFunc(glut_cb_special func)
241 {
242         cb_skeyup = func;
243 #ifdef BUILD_X11
244         UPD_EVMASK(KeyReleaseMask);
245 #endif
246 }
247
248 void glutMouseFunc(glut_cb_mouse func)
249 {
250         cb_mouse = func;
251 #ifdef BUILD_X11
252         UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
253 #endif
254 }
255
256 void glutMotionFunc(glut_cb_motion func)
257 {
258         cb_motion = func;
259 #ifdef BUILD_X11
260         UPD_EVMASK(ButtonMotionMask);
261 #endif
262 }
263
264 void glutPassiveMotionFunc(glut_cb_motion func)
265 {
266         cb_passive = func;
267 #ifdef BUILD_X11
268         UPD_EVMASK(PointerMotionMask);
269 #endif
270 }
271
272 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
273 {
274         cb_sball_motion = func;
275 }
276
277 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
278 {
279         cb_sball_rotate = func;
280 }
281
282 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
283 {
284         cb_sball_button = func;
285 }
286
287 int glutGet(unsigned int s)
288 {
289         int x, y;
290         switch(s) {
291         case GLUT_WINDOW_X:
292                 get_window_pos(&x, &y);
293                 return x;
294         case GLUT_WINDOW_Y:
295                 get_window_pos(&x, &y);
296                 return y;
297         case GLUT_WINDOW_WIDTH:
298                 get_window_size(&x, &y);
299                 return x;
300         case GLUT_WINDOW_HEIGHT:
301                 get_window_size(&x, &y);
302                 return 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:
320                 return 1;
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:
328                 return cur_cursor;
329         case GLUT_SCREEN_WIDTH:
330                 get_screen_size(&x, &y);
331                 return x;
332         case GLUT_SCREEN_HEIGHT:
333                 get_screen_size(&x, &y);
334                 return y;
335         case GLUT_INIT_DISPLAY_MODE:
336                 return init_mode;
337         case GLUT_INIT_WINDOW_X:
338                 return init_x;
339         case GLUT_INIT_WINDOW_Y:
340                 return init_y;
341         case GLUT_INIT_WINDOW_WIDTH:
342                 return init_width;
343         case GLUT_INIT_WINDOW_HEIGHT:
344                 return init_height;
345         case GLUT_ELAPSED_TIME:
346                 return get_msec();
347         default:
348                 break;
349         }
350         return 0;
351 }
352
353 int glutGetModifiers(void)
354 {
355         return modstate;
356 }
357
358 static int is_space(int c)
359 {
360         return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
361 }
362
363 static const char *skip_space(const char *s)
364 {
365         while(*s && is_space(*s)) s++;
366         return s;
367 }
368
369 int glutExtensionSupported(char *ext)
370 {
371         const char *str, *eptr;
372
373         if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
374                 return 0;
375         }
376
377         while(*str) {
378                 str = skip_space(str);
379                 eptr = skip_space(ext);
380                 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
381                         str++;
382                         eptr++;
383                 }
384                 if((!*str || is_space(*str)) && !*eptr) {
385                         return 1;
386                 }
387                 while(*str && !is_space(*str)) str++;
388         }
389
390         return 0;
391 }
392
393 /* TODO */
394 void glutSolidSphere(float rad, int slices, int stacks)
395 {
396         int i, j, k, gray;
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;
400
401         glBegin(GL_QUADS);
402         for(i=0; i<stacks; i++) {
403                 v = i * dv;
404                 for(j=0; j<slices; j++) {
405                         u = j * du;
406                         for(k=0; k<4; k++) {
407                                 gray = k ^ (k >> 1);
408                                 s = gray & 1 ? u + du : u;
409                                 t = gray & 2 ? v + dv : v;
410                                 theta = s * PI * 2.0f;
411                                 phi = t * PI;
412                                 mglut_sincosf(theta, &sintheta, &costheta);
413                                 mglut_sincosf(phi, &sinphi, &cosphi);
414                                 x = sintheta * sinphi;
415                                 y = costheta * sinphi;
416                                 z = cosphi;
417
418                                 glColor3f(s, t, 1);
419                                 glTexCoord2f(s, t);
420                                 glNormal3f(x, y, z);
421                                 glVertex3f(x * rad, y * rad, z * rad);
422                         }
423                 }
424         }
425         glEnd();
426 }
427
428 void glutWireSphere(float rad, int slices, int stacks)
429 {
430         glPushAttrib(GL_POLYGON_BIT);
431         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
432         glutSolidSphere(rad, slices, stacks);
433         glPopAttrib();
434 }
435
436 void glutSolidCube(float sz)
437 {
438         int i, j, idx, gray, flip, rotx;
439         float vpos[3], norm[3];
440         float rad = sz * 0.5f;
441
442         glBegin(GL_QUADS);
443         for(i=0; i<6; i++) {
444                 flip = i & 1;
445                 rotx = i >> 2;
446                 idx = (~i & 2) - rotx;
447                 norm[0] = norm[1] = norm[2] = 0.0f;
448                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
449                 glNormal3fv(norm);
450                 vpos[idx] = norm[idx] * rad;
451                 for(j=0; j<4; j++) {
452                         gray = j ^ (j >> 1);
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);
456                         glVertex3fv(vpos);
457                 }
458         }
459         glEnd();
460 }
461
462 void glutWireCube(float sz)
463 {
464         glPushAttrib(GL_POLYGON_BIT);
465         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
466         glutSolidCube(sz);
467         glPopAttrib();
468 }
469
470 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
471 {
472         int i, j, k, gray;
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;
476
477         rad = rbot - rtop;
478         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
479         mglut_sincosf(phi, &sinphi, &cosphi);
480
481         glBegin(GL_QUADS);
482         for(i=0; i<stacks; i++) {
483                 v = i * dv;
484                 for(j=0; j<slices; j++) {
485                         u = j * du;
486                         for(k=0; k<4; k++) {
487                                 gray = k ^ (k >> 1);
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);
493
494                                 x = sintheta * cosphi;
495                                 y = costheta * cosphi;
496                                 z = sinphi;
497
498                                 glColor3f(s, t, 1);
499                                 glTexCoord2f(s, t);
500                                 glNormal3f(x, y, z);
501                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
502                         }
503                 }
504         }
505         glEnd();
506 }
507
508 void glutSolidCone(float base, float height, int slices, int stacks)
509 {
510         draw_cylinder(base, 0, height, slices, stacks);
511 }
512
513 void glutWireCone(float base, float height, int slices, int stacks)
514 {
515         glPushAttrib(GL_POLYGON_BIT);
516         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
517         glutSolidCone(base, height, slices, stacks);
518         glPopAttrib();
519 }
520
521 void glutSolidCylinder(float rad, float height, int slices, int stacks)
522 {
523         draw_cylinder(rad, rad, height, slices, stacks);
524 }
525
526 void glutWireCylinder(float rad, float height, int slices, int stacks)
527 {
528         glPushAttrib(GL_POLYGON_BIT);
529         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
530         glutSolidCylinder(rad, height, slices, stacks);
531         glPopAttrib();
532 }
533
534 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
535 {
536         int i, j, k, gray;
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;
540
541         glBegin(GL_QUADS);
542         for(i=0; i<rings; i++) {
543                 u = i * du;
544                 for(j=0; j<sides; j++) {
545                         v = j * dv;
546                         for(k=0; k<4; k++) {
547                                 gray = k ^ (k >> 1);
548                                 s = gray & 1 ? u + du : u;
549                                 t = gray & 2 ? v + dv : v;
550                                 theta = s * PI * 2.0f;
551                                 phi = t * PI * 2.0f;
552                                 mglut_sincosf(theta, &sintheta, &costheta);
553                                 mglut_sincosf(phi, &sinphi, &cosphi);
554                                 x = sintheta * sinphi;
555                                 y = costheta * sinphi;
556                                 z = cosphi;
557
558                                 glColor3f(s, t, 1);
559                                 glTexCoord2f(s, t);
560                                 glNormal3f(x, y, z);
561
562                                 x = x * inner_rad + sintheta * outer_rad;
563                                 y = y * inner_rad + costheta * outer_rad;
564                                 z *= inner_rad;
565                                 glVertex3f(x, y, z);
566                         }
567                 }
568         }
569         glEnd();
570 }
571
572 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
573 {
574         glPushAttrib(GL_POLYGON_BIT);
575         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
576         glutSolidTorus(inner_rad, outer_rad, sides, rings);
577         glPopAttrib();
578 }
579
580 void glutSolidTeapot(float size)
581 {
582 }
583
584 void glutWireTeapot(float size)
585 {
586 }
587
588
589 #ifdef BUILD_X11
590 static void handle_event(XEvent *ev);
591
592 void glutMainLoopEvent(void)
593 {
594         XEvent ev;
595
596         if(!cb_display) {
597                 panic("display callback not set");
598         }
599
600         for(;;) {
601                 if(!upd_pending && !cb_idle) {
602                         XNextEvent(dpy, &ev);
603                         handle_event(&ev);
604                         if(quit) return;
605                 }
606                 while(XPending(dpy)) {
607                         XNextEvent(dpy, &ev);
608                         handle_event(&ev);
609                         if(quit) return;
610                 }
611
612                 if(cb_idle) {
613                         cb_idle();
614                 }
615
616                 if(upd_pending && mapped) {
617                         upd_pending = 0;
618                         cb_display();
619                 }
620         }
621 }
622
623 static KeySym translate_keysym(KeySym sym)
624 {
625         switch(sym) {
626         case XK_Escape:
627                 return 27;
628         case XK_BackSpace:
629                 return '\b';
630         case XK_Linefeed:
631                 return '\r';
632         case XK_Return:
633                 return '\n';
634         case XK_Delete:
635                 return 127;
636         case XK_Tab:
637                 return '\t';
638         default:
639                 break;
640         }
641         return sym;
642 }
643
644 static void handle_event(XEvent *ev)
645 {
646         KeySym sym;
647
648         switch(ev->type) {
649         case MapNotify:
650                 mapped = 1;
651                 break;
652         case UnmapNotify:
653                 mapped = 0;
654                 break;
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);
660                 }
661                 break;
662
663         case ClientMessage:
664                 if(ev->xclient.message_type == xa_wm_proto) {
665                         if(ev->xclient.data.l[0] == xa_wm_del_win) {
666                                 quit = 1;
667                         }
668                 }
669                 break;
670
671         case Expose:
672                 upd_pending = 1;
673                 break;
674
675         case KeyPress:
676         case KeyRelease:
677                 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
678                 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
679                         break;
680                 }
681                 sym = translate_keysym(sym);
682                 if(sym < 256) {
683                         if(ev->type == KeyPress) {
684                                 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
685                         } else {
686                                 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
687                         }
688                 } else {
689                         if(ev->type == KeyPress) {
690                                 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
691                         } else {
692                                 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
693                         }
694                 }
695                 break;
696
697         case ButtonPress:
698         case ButtonRelease:
699                 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
700                 if(cb_mouse) {
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);
704                 }
705                 break;
706
707         case MotionNotify:
708                 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
709                         if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
710                 } else {
711                         if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
712                 }
713                 break;
714
715         case VisibilityNotify:
716                 if(cb_vis) {
717                         cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
718                 }
719                 break;
720         case EnterNotify:
721                 if(cb_entry) cb_entry(GLUT_ENTERED);
722                 break;
723         case LeaveNotify:
724                 if(cb_entry) cb_entry(GLUT_LEFT);
725                 break;
726         }
727 }
728
729 void glutSwapBuffers(void)
730 {
731         glXSwapBuffers(dpy, win);
732 }
733
734 void glutPositionWindow(int x, int y)
735 {
736         XMoveWindow(dpy, win, x, y);
737 }
738
739 void glutReshapeWindow(int xsz, int ysz)
740 {
741         XResizeWindow(dpy, win, xsz, ysz);
742 }
743
744 void glutFullScreen(void)
745 {
746         /* TODO */
747 }
748
749 void glutSetWindowTitle(const char *title)
750 {
751         XTextProperty tprop;
752         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
753                 return;
754         }
755         XSetWMName(dpy, win, &tprop);
756         XFree(tprop.value);
757 }
758
759 void glutSetIconTitle(const char *title)
760 {
761         XTextProperty tprop;
762         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
763                 return;
764         }
765         XSetWMIconName(dpy, win, &tprop);
766         XFree(tprop.value);
767 }
768
769 void glutSetCursor(int cidx)
770 {
771         Cursor cur = None;
772
773         switch(cidx) {
774         case GLUT_CURSOR_LEFT_ARROW:
775                 cur = XCreateFontCursor(dpy, XC_left_ptr);
776                 break;
777         case GLUT_CURSOR_INHERIT:
778                 break;
779         case GLUT_CURSOR_NONE:
780                 /* TODO */
781         default:
782                 return;
783         }
784
785         XDefineCursor(dpy, win, cur);
786         cur_cursor = cidx;
787 }
788
789 static XVisualInfo *choose_visual(unsigned int mode)
790 {
791         XVisualInfo *vi;
792         int attr[32] = {
793                 GLX_RGBA,
794                 GLX_DOUBLEBUFFER,
795                 GLX_RED_SIZE, 4,
796                 GLX_GREEN_SIZE, 4,
797                 GLX_BLUE_SIZE, 4
798         };
799         int *aptr = attr + 8;
800         int *samples = 0;
801
802         if(mode & GLUT_ALPHA) {
803                 *aptr++ = GLX_ALPHA_SIZE;
804                 *aptr++ = 4;
805         }
806         if(mode & GLUT_DEPTH) {
807                 *aptr++ = GLX_DEPTH_SIZE;
808                 *aptr++ = 16;
809         }
810         if(mode & GLUT_STENCIL) {
811                 *aptr++ = GLX_STENCIL_SIZE;
812                 *aptr++ = 1;
813         }
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;
818         }
819         if(mode & GLUT_STEREO) {
820                 *aptr++ = GLX_STEREO;
821         }
822         if(mode & GLUT_SRGB) {
823                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
824         }
825         if(mode & GLUT_MULTISAMPLE) {
826                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
827                 *aptr++ = 1;
828                 *aptr++ = GLX_SAMPLES_ARB;
829                 samples = aptr;
830                 *aptr++ = 32;
831         }
832         *aptr++ = None;
833
834         if(!samples) {
835                 return glXChooseVisual(dpy, scr, attr);
836         }
837         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
838                 *samples >>= 1;
839                 if(!*samples) {
840                         aptr[-3] = None;
841                 }
842         }
843         return vi;
844 }
845
846 static void create_window(const char *title)
847 {
848         XSetWindowAttributes xattr;
849         XVisualInfo *vi;
850         unsigned int xattr_mask;
851         unsigned int mode = init_mode;
852
853         if(!(vi = choose_visual(mode))) {
854                 mode &= ~GLUT_SRGB;
855                 if(!(vi = choose_visual(mode))) {
856                         panic("Failed to find compatible visual\n");
857                 }
858         }
859
860         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
861                 XFree(vi);
862                 panic("Failed to create OpenGL context\n");
863         }
864
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);
875
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))) {
881                 XFree(vi);
882                 glXDestroyContext(dpy, ctx);
883                 panic("Failed to create window\n");
884         }
885         XFree(vi);
886
887         XSelectInput(dpy, win, evmask);
888
889         glutSetWindowTitle(title);
890         glutSetIconTitle(title);
891         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
892         XMapWindow(dpy, win);
893
894         glXMakeCurrent(dpy, win, ctx);
895 }
896
897 static void get_window_pos(int *x, int *y)
898 {
899         XWindowAttributes wattr;
900         XGetWindowAttributes(dpy, win, &wattr);
901         *x = wattr.x;
902         *y = wattr.y;
903 }
904
905 static void get_window_size(int *w, int *h)
906 {
907         XWindowAttributes wattr;
908         XGetWindowAttributes(dpy, win, &wattr);
909         *w = wattr.width;
910         *h = wattr.height;
911 }
912
913 static void get_screen_size(int *scrw, int *scrh)
914 {
915         XWindowAttributes wattr;
916         XGetWindowAttributes(dpy, root, &wattr);
917         *scrw = wattr.width;
918         *scrh = wattr.height;
919 }
920 #endif
921
922 #if defined(__unix__) || defined(__APPLE__)
923 #include <sys/time.h>
924
925 static long get_msec(void)
926 {
927         static struct timeval tv0;
928         struct timeval tv;
929
930         gettimeofday(&tv, 0);
931         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
932                 tv0 = tv;
933                 return 0;
934         }
935         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
936 }
937 #endif
938 #ifdef _WIN32
939 static long get_msec(void)
940 {
941         static long t0;
942
943         if(!t0) {
944                 t0 = timeGetTime();
945                 return 0;
946         }
947         return timeGetTime() - t0;
948 }
949 #endif
950
951 static void panic(const char *msg)
952 {
953         const char *end = msg;
954         while(*end) end++;
955         sys_write(2, msg, end - msg);
956         sys_exit(1);
957 }
958
959
960 #ifdef MINIGLUT_USE_LIBC
961 static void sys_exit(int status)
962 {
963         exit(status);
964 }
965
966 static int sys_write(int fd, const void *buf, int count)
967 {
968         return write(fd, buf, count);
969 }
970
971 #else   /* !MINIGLUT_USE_LIBC */
972
973 #if defined(__GNUC__) && defined(MINIGLUT_GCC_NO_BUILTIN)
974 static void mglut_sincosf(float angle, float *sptr, float *cptr)
975 {
976         asm volatile(
977                 "flds %2\n\t"
978                 "fsincos\n\t"
979                 "fstps %1\n\t"
980                 "fstps %0\n\t"
981                 : "=m"(*sptr), "=m"(*cptr)
982                 : "m"(angle)
983         );
984 }
985
986 static float mglut_atan(float x)
987 {
988         float res;
989         asm volatile(
990                 "flds %1\n\t"
991                 "fld1\n\t"
992                 "fpatan\n\t"
993                 "fstps %0\n\t"
994                 : "=m"(res)
995                 : "m"(x)
996         );
997         return res;
998 }
999 #endif
1000
1001 #ifdef _MSC_VER
1002 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1003 {
1004         float s, c;
1005         __asm {
1006                 fld angle
1007                 fsincos
1008                 fstp c
1009                 fstp s
1010         }
1011         *sptr = s;
1012         *cptr = c;
1013 }
1014
1015 static float mglut_atan(float x)
1016 {
1017         float res;
1018         __asm {
1019                 fld x
1020                 fld1
1021                 fpatan
1022                 fstp res
1023         }
1024         return res;
1025 }
1026 #endif
1027
1028 #ifdef __WATCOMC__
1029 #pragma aux mglut_sincosf = \
1030         "fsincos" \
1031         "fstp dword ptr [edx]" \
1032         "fstp dword ptr [eax]" \
1033         parm[8087][eax][edx]    \
1034         modify[8087];
1035
1036 #pragma aux mglut_atan = \
1037         "fld1" \
1038         "fpatan" \
1039         parm[8087] \
1040         value[8087] \
1041         modify [8087];
1042 #endif
1043
1044 #ifdef __linux__
1045
1046 #ifdef __x86_64__
1047 static void sys_exit(int status)
1048 {
1049         asm volatile(
1050                 "syscall\n\t"
1051                 :: "a"(60), "D"(status)
1052         );
1053 }
1054 static int sys_write(int fd, const void *buf, int count)
1055 {
1056         long res;
1057         asm volatile(
1058                 "syscall\n\t"
1059                 : "=a"(res)
1060                 : "a"(1), "D"(fd), "S"(buf), "d"(count)
1061         );
1062         return res;
1063 }
1064 #endif
1065 #ifdef __i386__
1066 static void sys_exit(int status)
1067 {
1068         asm volatile(
1069                 "mov $1, %%eax\n\t"
1070                 "int $0x80\n\t"
1071                 :: "b"(status)
1072                 : "eax"
1073         );
1074 }
1075 static int sys_write(int fd, const void *buf, int count)
1076 {
1077         int res;
1078         asm volatile(
1079                 "mov $4, %%eax\n\t"
1080                 "int $0x80\n\t"
1081                 :: "b"(fd), "c"(buf), "d"(count));
1082         return res;
1083 }
1084 #endif
1085
1086 #endif  /* __linux__ */
1087
1088 #ifdef _WIN32
1089 static int sys_exit(int status)
1090 {
1091         ExitProcess(status);
1092 }
1093 static int sys_write(int fd, const void *buf, int count)
1094 {
1095         int wrsz;
1096
1097         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1098         if(!WriteFile(out, buf, count, &wrsz, 0)) {
1099                 return -1;
1100         }
1101         return wrsz;
1102 }
1103 #endif  /* _WIN32 */
1104
1105 #endif  /* !MINIGLUT_USE_LIBC */