fixed keyboard handling on windows
[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 #ifdef MINIGLUT_USE_LIBC
19 #define _GNU_SOURCE
20 #include <stdlib.h>
21 #include <math.h>
22 #else
23 static void mglut_sincosf(float angle, float *sptr, float *cptr);
24 static float mglut_atan(float x);
25 #endif
26
27 #define PI      3.1415926536f
28
29 #if defined(__unix__)
30
31 #include <X11/Xlib.h>
32 #include <X11/cursorfont.h>
33 #include <GL/glx.h>
34 #define BUILD_X11
35
36 #ifndef GLX_SAMPLE_BUFFERS_ARB
37 #define GLX_SAMPLE_BUFFERS_ARB  100000
38 #define GLX_SAMPLES_ARB                 100001
39 #endif
40 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
41 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20b2
42 #endif
43
44 static Display *dpy;
45 static Window win, root;
46 static int scr;
47 static GLXContext ctx;
48 static Atom xa_wm_proto, xa_wm_del_win;
49 static unsigned int evmask;
50
51 #elif defined(_WIN32)
52
53 #include <windows.h>
54 #define BUILD_WIN32
55
56 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
57
58 static HINSTANCE hinst;
59 static HWND win;
60 static HDC dc;
61 static HGLRC ctx;
62
63 #else
64 #error unsupported platform
65 #endif
66
67 #include <GL/gl.h>
68 #include "miniglut.h"
69
70 struct ctx_info {
71         int rsize, gsize, bsize, asize;
72         int zsize, ssize;
73         int dblbuf;
74         int samples;
75         int stereo;
76         int srgb;
77 };
78
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);
83
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);
88
89
90 static int init_x = -1, init_y, init_width = 256, init_height = 256;
91 static unsigned int init_mode;
92
93 static struct ctx_info ctx_info;
94 static int cur_cursor = GLUT_CURSOR_INHERIT;
95
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;
106
107 static int fullscreen;
108 static int prev_win_x, prev_win_y, prev_win_width, prev_win_height;
109
110 static int win_width, win_height;
111 static int mapped;
112 static int quit;
113 static int upd_pending;
114 static int modstate;
115
116
117 void glutInit(int *argc, char **argv)
118 {
119 #ifdef BUILD_X11
120         if(!(dpy = XOpenDisplay(0))) {
121                 panic("Failed to connect to the X server\n");
122         }
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);
127
128         evmask = ExposureMask | StructureNotifyMask;
129 #endif
130 #ifdef BUILD_WIN32
131         WNDCLASSEX wc = {0};
132
133         hinst = GetModuleHandle(0);
134
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");
145         }
146
147         if(init_x == -1) {
148                 get_screen_size(&init_x, &init_y);
149                 init_x >>= 3;
150                 init_y >>= 3;
151         }
152 #endif
153 }
154
155 void glutInitWindowPosition(int x, int y)
156 {
157         init_x = x;
158         init_y = y;
159 }
160
161 void glutInitWindowSize(int xsz, int ysz)
162 {
163         init_width = xsz;
164         init_height = ysz;
165 }
166
167 void glutInitDisplayMode(unsigned int mode)
168 {
169         init_mode = mode;
170 }
171
172 void glutCreateWindow(const char *title)
173 {
174         create_window(title);
175 }
176
177 void glutExit(void)
178 {
179         quit = 1;
180 }
181
182 void glutMainLoop(void)
183 {
184         while(!quit) {
185                 glutMainLoopEvent();
186         }
187 }
188
189 void glutPostRedisplay(void)
190 {
191         upd_pending = 1;
192 }
193
194 #define UPD_EVMASK(x) \
195         do { \
196                 if(func) { \
197                         evmask |= x; \
198                 } else { \
199                         evmask &= ~(x); \
200                 } \
201                 if(win) XSelectInput(dpy, win, evmask); \
202         } while(0)
203
204
205 void glutIdleFunc(glut_cb func)
206 {
207         cb_idle = func;
208 }
209
210 void glutDisplayFunc(glut_cb func)
211 {
212         cb_display = func;
213 }
214
215 void glutReshapeFunc(glut_cb_reshape func)
216 {
217         cb_reshape = func;
218 }
219
220 void glutVisibilityFunc(glut_cb_state func)
221 {
222         cb_vis = func;
223 #ifdef BUILD_X11
224         UPD_EVMASK(VisibilityChangeMask);
225 #endif
226 }
227
228 void glutEntryFunc(glut_cb_state func)
229 {
230         cb_entry = func;
231 #ifdef BUILD_X11
232         UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
233 #endif
234 }
235
236 void glutKeyboardFunc(glut_cb_keyb func)
237 {
238         cb_keydown = func;
239 #ifdef BUILD_X11
240         UPD_EVMASK(KeyPressMask);
241 #endif
242 }
243
244 void glutKeyboardUpFunc(glut_cb_keyb func)
245 {
246         cb_keyup = func;
247 #ifdef BUILD_X11
248         UPD_EVMASK(KeyReleaseMask);
249 #endif
250 }
251
252 void glutSpecialFunc(glut_cb_special func)
253 {
254         cb_skeydown = func;
255 #ifdef BUILD_X11
256         UPD_EVMASK(KeyPressMask);
257 #endif
258 }
259
260 void glutSpecialUpFunc(glut_cb_special func)
261 {
262         cb_skeyup = func;
263 #ifdef BUILD_X11
264         UPD_EVMASK(KeyReleaseMask);
265 #endif
266 }
267
268 void glutMouseFunc(glut_cb_mouse func)
269 {
270         cb_mouse = func;
271 #ifdef BUILD_X11
272         UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
273 #endif
274 }
275
276 void glutMotionFunc(glut_cb_motion func)
277 {
278         cb_motion = func;
279 #ifdef BUILD_X11
280         UPD_EVMASK(ButtonMotionMask);
281 #endif
282 }
283
284 void glutPassiveMotionFunc(glut_cb_motion func)
285 {
286         cb_passive = func;
287 #ifdef BUILD_X11
288         UPD_EVMASK(PointerMotionMask);
289 #endif
290 }
291
292 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
293 {
294         cb_sball_motion = func;
295 }
296
297 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
298 {
299         cb_sball_rotate = func;
300 }
301
302 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
303 {
304         cb_sball_button = func;
305 }
306
307 int glutGet(unsigned int s)
308 {
309         int x, y;
310         switch(s) {
311         case GLUT_WINDOW_X:
312                 get_window_pos(&x, &y);
313                 return x;
314         case GLUT_WINDOW_Y:
315                 get_window_pos(&x, &y);
316                 return y;
317         case GLUT_WINDOW_WIDTH:
318                 get_window_size(&x, &y);
319                 return x;
320         case GLUT_WINDOW_HEIGHT:
321                 get_window_size(&x, &y);
322                 return 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:
340                 return 1;
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:
348                 return cur_cursor;
349         case GLUT_SCREEN_WIDTH:
350                 get_screen_size(&x, &y);
351                 return x;
352         case GLUT_SCREEN_HEIGHT:
353                 get_screen_size(&x, &y);
354                 return y;
355         case GLUT_INIT_DISPLAY_MODE:
356                 return init_mode;
357         case GLUT_INIT_WINDOW_X:
358                 return init_x;
359         case GLUT_INIT_WINDOW_Y:
360                 return init_y;
361         case GLUT_INIT_WINDOW_WIDTH:
362                 return init_width;
363         case GLUT_INIT_WINDOW_HEIGHT:
364                 return init_height;
365         case GLUT_ELAPSED_TIME:
366                 return get_msec();
367         default:
368                 break;
369         }
370         return 0;
371 }
372
373 int glutGetModifiers(void)
374 {
375         return modstate;
376 }
377
378 static int is_space(int c)
379 {
380         return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
381 }
382
383 static const char *skip_space(const char *s)
384 {
385         while(*s && is_space(*s)) s++;
386         return s;
387 }
388
389 int glutExtensionSupported(char *ext)
390 {
391         const char *str, *eptr;
392
393         if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
394                 return 0;
395         }
396
397         while(*str) {
398                 str = skip_space(str);
399                 eptr = skip_space(ext);
400                 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
401                         str++;
402                         eptr++;
403                 }
404                 if((!*str || is_space(*str)) && !*eptr) {
405                         return 1;
406                 }
407                 while(*str && !is_space(*str)) str++;
408         }
409
410         return 0;
411 }
412
413 /* TODO */
414 void glutSolidSphere(float rad, int slices, int stacks)
415 {
416         int i, j, k, gray;
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;
420
421         glBegin(GL_QUADS);
422         for(i=0; i<stacks; i++) {
423                 v = i * dv;
424                 for(j=0; j<slices; j++) {
425                         u = j * du;
426                         for(k=0; k<4; k++) {
427                                 gray = k ^ (k >> 1);
428                                 s = gray & 1 ? u + du : u;
429                                 t = gray & 2 ? v + dv : v;
430                                 theta = s * PI * 2.0f;
431                                 phi = t * PI;
432                                 mglut_sincosf(theta, &sintheta, &costheta);
433                                 mglut_sincosf(phi, &sinphi, &cosphi);
434                                 x = sintheta * sinphi;
435                                 y = costheta * sinphi;
436                                 z = cosphi;
437
438                                 glColor3f(s, t, 1);
439                                 glTexCoord2f(s, t);
440                                 glNormal3f(x, y, z);
441                                 glVertex3f(x * rad, y * rad, z * rad);
442                         }
443                 }
444         }
445         glEnd();
446 }
447
448 void glutWireSphere(float rad, int slices, int stacks)
449 {
450         glPushAttrib(GL_POLYGON_BIT);
451         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
452         glutSolidSphere(rad, slices, stacks);
453         glPopAttrib();
454 }
455
456 void glutSolidCube(float sz)
457 {
458         int i, j, idx, gray, flip, rotx;
459         float vpos[3], norm[3];
460         float rad = sz * 0.5f;
461
462         glBegin(GL_QUADS);
463         for(i=0; i<6; i++) {
464                 flip = i & 1;
465                 rotx = i >> 2;
466                 idx = (~i & 2) - rotx;
467                 norm[0] = norm[1] = norm[2] = 0.0f;
468                 norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1;
469                 glNormal3fv(norm);
470                 vpos[idx] = norm[idx] * rad;
471                 for(j=0; j<4; j++) {
472                         gray = j ^ (j >> 1);
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);
476                         glVertex3fv(vpos);
477                 }
478         }
479         glEnd();
480 }
481
482 void glutWireCube(float sz)
483 {
484         glPushAttrib(GL_POLYGON_BIT);
485         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
486         glutSolidCube(sz);
487         glPopAttrib();
488 }
489
490 static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks)
491 {
492         int i, j, k, gray;
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;
496
497         rad = rbot - rtop;
498         phi = mglut_atan((rad < 0 ? -rad : rad) / height);
499         mglut_sincosf(phi, &sinphi, &cosphi);
500
501         glBegin(GL_QUADS);
502         for(i=0; i<stacks; i++) {
503                 v = i * dv;
504                 for(j=0; j<slices; j++) {
505                         u = j * du;
506                         for(k=0; k<4; k++) {
507                                 gray = k ^ (k >> 1);
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);
513
514                                 x = sintheta * cosphi;
515                                 y = costheta * cosphi;
516                                 z = sinphi;
517
518                                 glColor3f(s, t, 1);
519                                 glTexCoord2f(s, t);
520                                 glNormal3f(x, y, z);
521                                 glVertex3f(sintheta * rad, costheta * rad, t * height);
522                         }
523                 }
524         }
525         glEnd();
526 }
527
528 void glutSolidCone(float base, float height, int slices, int stacks)
529 {
530         draw_cylinder(base, 0, height, slices, stacks);
531 }
532
533 void glutWireCone(float base, float height, int slices, int stacks)
534 {
535         glPushAttrib(GL_POLYGON_BIT);
536         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
537         glutSolidCone(base, height, slices, stacks);
538         glPopAttrib();
539 }
540
541 void glutSolidCylinder(float rad, float height, int slices, int stacks)
542 {
543         draw_cylinder(rad, rad, height, slices, stacks);
544 }
545
546 void glutWireCylinder(float rad, float height, int slices, int stacks)
547 {
548         glPushAttrib(GL_POLYGON_BIT);
549         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
550         glutSolidCylinder(rad, height, slices, stacks);
551         glPopAttrib();
552 }
553
554 void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings)
555 {
556         int i, j, k, gray;
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;
560
561         glBegin(GL_QUADS);
562         for(i=0; i<rings; i++) {
563                 u = i * du;
564                 for(j=0; j<sides; j++) {
565                         v = j * dv;
566                         for(k=0; k<4; k++) {
567                                 gray = k ^ (k >> 1);
568                                 s = gray & 1 ? u + du : u;
569                                 t = gray & 2 ? v + dv : v;
570                                 theta = s * PI * 2.0f;
571                                 phi = t * PI * 2.0f;
572                                 mglut_sincosf(theta, &sintheta, &costheta);
573                                 mglut_sincosf(phi, &sinphi, &cosphi);
574                                 x = sintheta * sinphi;
575                                 y = costheta * sinphi;
576                                 z = cosphi;
577
578                                 glColor3f(s, t, 1);
579                                 glTexCoord2f(s, t);
580                                 glNormal3f(x, y, z);
581
582                                 x = x * inner_rad + sintheta * outer_rad;
583                                 y = y * inner_rad + costheta * outer_rad;
584                                 z *= inner_rad;
585                                 glVertex3f(x, y, z);
586                         }
587                 }
588         }
589         glEnd();
590 }
591
592 void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings)
593 {
594         glPushAttrib(GL_POLYGON_BIT);
595         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
596         glutSolidTorus(inner_rad, outer_rad, sides, rings);
597         glPopAttrib();
598 }
599
600 void glutSolidTeapot(float size)
601 {
602 }
603
604 void glutWireTeapot(float size)
605 {
606 }
607
608
609
610 /* --------------- UNIX/X11 implementation ----------------- */
611 #ifdef BUILD_X11
612 static void handle_event(XEvent *ev);
613
614 void glutMainLoopEvent(void)
615 {
616         XEvent ev;
617
618         if(!cb_display) {
619                 panic("display callback not set");
620         }
621
622         if(!upd_pending && !cb_idle) {
623                 XNextEvent(dpy, &ev);
624                 handle_event(&ev);
625                 if(quit) return;
626         }
627         while(XPending(dpy)) {
628                 XNextEvent(dpy, &ev);
629                 handle_event(&ev);
630                 if(quit) return;
631         }
632
633         if(cb_idle) {
634                 cb_idle();
635         }
636
637         if(upd_pending && mapped) {
638                 upd_pending = 0;
639                 cb_display();
640         }
641 }
642
643 static KeySym translate_keysym(KeySym sym)
644 {
645         switch(sym) {
646         case XK_Escape:
647                 return 27;
648         case XK_BackSpace:
649                 return '\b';
650         case XK_Linefeed:
651                 return '\r';
652         case XK_Return:
653                 return '\n';
654         case XK_Delete:
655                 return 127;
656         case XK_Tab:
657                 return '\t';
658         default:
659                 break;
660         }
661         return sym;
662 }
663
664 static void handle_event(XEvent *ev)
665 {
666         KeySym sym;
667
668         switch(ev->type) {
669         case MapNotify:
670                 mapped = 1;
671                 break;
672         case UnmapNotify:
673                 mapped = 0;
674                 break;
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);
680                 }
681                 break;
682
683         case ClientMessage:
684                 if(ev->xclient.message_type == xa_wm_proto) {
685                         if(ev->xclient.data.l[0] == xa_wm_del_win) {
686                                 quit = 1;
687                         }
688                 }
689                 break;
690
691         case Expose:
692                 upd_pending = 1;
693                 break;
694
695         case KeyPress:
696         case KeyRelease:
697                 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
698                 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
699                         break;
700                 }
701                 sym = translate_keysym(sym);
702                 if(sym < 256) {
703                         if(ev->type == KeyPress) {
704                                 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
705                         } else {
706                                 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
707                         }
708                 } else {
709                         if(ev->type == KeyPress) {
710                                 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
711                         } else {
712                                 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
713                         }
714                 }
715                 break;
716
717         case ButtonPress:
718         case ButtonRelease:
719                 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
720                 if(cb_mouse) {
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);
724                 }
725                 break;
726
727         case MotionNotify:
728                 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
729                         if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
730                 } else {
731                         if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
732                 }
733                 break;
734
735         case VisibilityNotify:
736                 if(cb_vis) {
737                         cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
738                 }
739                 break;
740         case EnterNotify:
741                 if(cb_entry) cb_entry(GLUT_ENTERED);
742                 break;
743         case LeaveNotify:
744                 if(cb_entry) cb_entry(GLUT_LEFT);
745                 break;
746         }
747 }
748
749 void glutSwapBuffers(void)
750 {
751         glXSwapBuffers(dpy, win);
752 }
753
754 void glutPositionWindow(int x, int y)
755 {
756         XMoveWindow(dpy, win, x, y);
757 }
758
759 void glutReshapeWindow(int xsz, int ysz)
760 {
761         XResizeWindow(dpy, win, xsz, ysz);
762 }
763
764 void glutFullScreen(void)
765 {
766         /* TODO */
767 }
768
769 void glutSetWindowTitle(const char *title)
770 {
771         XTextProperty tprop;
772         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
773                 return;
774         }
775         XSetWMName(dpy, win, &tprop);
776         XFree(tprop.value);
777 }
778
779 void glutSetIconTitle(const char *title)
780 {
781         XTextProperty tprop;
782         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
783                 return;
784         }
785         XSetWMIconName(dpy, win, &tprop);
786         XFree(tprop.value);
787 }
788
789 void glutSetCursor(int cidx)
790 {
791         Cursor cur = None;
792
793         switch(cidx) {
794         case GLUT_CURSOR_LEFT_ARROW:
795                 cur = XCreateFontCursor(dpy, XC_left_ptr);
796                 break;
797         case GLUT_CURSOR_INHERIT:
798                 break;
799         case GLUT_CURSOR_NONE:
800                 /* TODO */
801         default:
802                 return;
803         }
804
805         XDefineCursor(dpy, win, cur);
806         cur_cursor = cidx;
807 }
808
809 static XVisualInfo *choose_visual(unsigned int mode)
810 {
811         XVisualInfo *vi;
812         int attr[32];
813         int *aptr = attr;
814         int *samples = 0;
815
816         if(mode & GLUT_DOUBLE) {
817                 *aptr++ = GLX_DOUBLEBUFFER;
818         }
819
820         if(mode & GLUT_INDEX) {
821                 *aptr++ = GLX_BUFFER_SIZE;
822                 *aptr++ = 1;
823         } else {
824                 *aptr++ = GLX_RGBA;
825                 *aptr++ = GLX_RED_SIZE; *aptr++ = 4;
826                 *aptr++ = GLX_GREEN_SIZE; *aptr++ = 4;
827                 *aptr++ = GLX_BLUE_SIZE; *aptr++ = 4;
828         }
829         if(mode & GLUT_ALPHA) {
830                 *aptr++ = GLX_ALPHA_SIZE;
831                 *aptr++ = 4;
832         }
833         if(mode & GLUT_DEPTH) {
834                 *aptr++ = GLX_DEPTH_SIZE;
835                 *aptr++ = 16;
836         }
837         if(mode & GLUT_STENCIL) {
838                 *aptr++ = GLX_STENCIL_SIZE;
839                 *aptr++ = 1;
840         }
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;
845         }
846         if(mode & GLUT_STEREO) {
847                 *aptr++ = GLX_STEREO;
848         }
849         if(mode & GLUT_SRGB) {
850                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
851         }
852         if(mode & GLUT_MULTISAMPLE) {
853                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
854                 *aptr++ = 1;
855                 *aptr++ = GLX_SAMPLES_ARB;
856                 samples = aptr;
857                 *aptr++ = 32;
858         }
859         *aptr++ = None;
860
861         if(!samples) {
862                 return glXChooseVisual(dpy, scr, attr);
863         }
864         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
865                 *samples >>= 1;
866                 if(!*samples) {
867                         aptr[-3] = None;
868                 }
869         }
870         return vi;
871 }
872
873 static void create_window(const char *title)
874 {
875         XSetWindowAttributes xattr;
876         XVisualInfo *vi;
877         unsigned int xattr_mask;
878         unsigned int mode = init_mode;
879
880         if(!(vi = choose_visual(mode))) {
881                 mode &= ~GLUT_SRGB;
882                 if(!(vi = choose_visual(mode))) {
883                         panic("Failed to find compatible visual\n");
884                 }
885         }
886
887         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
888                 XFree(vi);
889                 panic("Failed to create OpenGL context\n");
890         }
891
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);
902
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))) {
908                 XFree(vi);
909                 glXDestroyContext(dpy, ctx);
910                 panic("Failed to create window\n");
911         }
912         XFree(vi);
913
914         XSelectInput(dpy, win, evmask);
915
916         glutSetWindowTitle(title);
917         glutSetIconTitle(title);
918         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
919         XMapWindow(dpy, win);
920
921         glXMakeCurrent(dpy, win, ctx);
922 }
923
924 static void get_window_pos(int *x, int *y)
925 {
926         XWindowAttributes wattr;
927         XGetWindowAttributes(dpy, win, &wattr);
928         *x = wattr.x;
929         *y = wattr.y;
930 }
931
932 static void get_window_size(int *w, int *h)
933 {
934         XWindowAttributes wattr;
935         XGetWindowAttributes(dpy, win, &wattr);
936         *w = wattr.width;
937         *h = wattr.height;
938 }
939
940 static void get_screen_size(int *scrw, int *scrh)
941 {
942         XWindowAttributes wattr;
943         XGetWindowAttributes(dpy, root, &wattr);
944         *scrw = wattr.width;
945         *scrh = wattr.height;
946 }
947 #endif  /* BUILD_X11 */
948
949
950 /* --------------- windows implementation ----------------- */
951 #ifdef BUILD_WIN32
952 static int reshape_pending;
953
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);
957
958 #ifdef MINIGLUT_WINMAIN
959 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd)
960 {
961         int argc = 1;
962         char *argv[] = { "miniglut.exe", 0 };
963         return main(argc, argv);
964 }
965 #endif
966
967 void glutMainLoopEvent(void)
968 {
969         MSG msg;
970
971         if(!cb_display) {
972                 panic("display callback not set");
973         }
974
975         if(reshape_pending && cb_reshape) {
976                 reshape_pending = 0;
977                 get_window_size(&win_width, &win_height);
978                 cb_reshape(win_width, win_height);
979         }
980
981         if(!upd_pending && !cb_idle) {
982                 GetMessage(&msg, 0, 0, 0);
983                 TranslateMessage(&msg);
984                 DispatchMessage(&msg);
985                 if(quit) return;
986         }
987         while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
988                 TranslateMessage(&msg);
989                 DispatchMessage(&msg);
990                 if(quit) return;
991         }
992
993         if(cb_idle) {
994                 cb_idle();
995         }
996
997         if(upd_pending && mapped) {
998                 upd_pending = 0;
999                 cb_display();
1000         }
1001 }
1002
1003 void glutSwapBuffers(void)
1004 {
1005         SwapBuffers(dc);
1006 }
1007
1008 void glutPositionWindow(int x, int y)
1009 {
1010         RECT rect;
1011         unsigned int flags = SWP_SHOWWINDOW;
1012
1013         if(fullscreen) {
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);
1019                 fullscreen = 0;
1020                 flags |= SWP_FRAMECHANGED;
1021         } else {
1022                 GetWindowRect(win, &rect);
1023         }
1024         SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags);
1025 }
1026
1027 void glutReshapeWindow(int xsz, int ysz)
1028 {
1029         RECT rect;
1030         unsigned int flags = SWP_SHOWWINDOW;
1031
1032         if(fullscreen) {
1033                 rect.left = prev_win_x;
1034                 rect.top = prev_win_y;
1035                 SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW);
1036                 fullscreen = 0;
1037                 flags |= SWP_FRAMECHANGED;
1038         } else {
1039                 GetWindowRect(win, &rect);
1040         }
1041         SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags);
1042 }
1043
1044 void glutFullScreen(void)
1045 {
1046         RECT rect;
1047         int scr_width, scr_height;
1048
1049         if(fullscreen) return;
1050
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;
1056
1057         get_screen_size(&scr_width, &scr_height);
1058
1059         SetWindowLong(win, GWL_STYLE, 0);
1060         SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW);
1061
1062         fullscreen = 1;
1063 }
1064
1065 void glutSetWindowTitle(const char *title)
1066 {
1067         SetWindowText(win, title);
1068 }
1069
1070 void glutSetIconTitle(const char *title)
1071 {
1072 }
1073
1074 void glutSetCursor(int cidx)
1075 {
1076         switch(cidx) {
1077         case GLUT_CURSOR_NONE:
1078                 ShowCursor(0);
1079                 break;
1080         case GLUT_CURSOR_INHERIT:
1081         case GLUT_CURSOR_LEFT_ARROW:
1082         default:
1083                 SetCursor(LoadCursor(0, IDC_ARROW));
1084                 ShowCursor(1);
1085         }
1086 }
1087
1088
1089 static void create_window(const char *title)
1090 {
1091         int pixfmt;
1092         PIXELFORMATDESCRIPTOR pfd = {0};
1093         RECT rect;
1094
1095         rect.left = init_x;
1096         rect.top = init_y;
1097         rect.right = init_x + init_width;
1098         rect.bottom = init_y + init_height;
1099         AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
1100
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");
1104         }
1105         dc = GetDC(win);
1106
1107         pfd.nSize = sizeof pfd;
1108         pfd.nVersion = 1;
1109         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1110         if(init_mode & GLUT_STEREO) {
1111                 pfd.dwFlags |= PFD_STEREO;
1112         }
1113         pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1114         pfd.cColorBits = 24;
1115         if(init_mode & GLUT_ALPHA) {
1116                 pfd.cAlphaBits = 8;
1117         }
1118         if(init_mode & GLUT_ACCUM) {
1119                 pfd.cAccumBits = 24;
1120         }
1121         if(init_mode & GLUT_DEPTH) {
1122                 pfd.cDepthBits = 24;
1123         }
1124         if(init_mode & GLUT_STENCIL) {
1125                 pfd.cStencilBits = 8;
1126         }
1127         pfd.iLayerType = PFD_MAIN_PLANE;
1128
1129         if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1130                 panic("Failed to find suitable pixel format\n");
1131         }
1132         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1133                 panic("Failed to set the selected pixel format\n");
1134         }
1135         if(!(ctx = wglCreateContext(dc))) {
1136                 panic("Failed to create the OpenGL context\n");
1137         }
1138         wglMakeCurrent(dc, ctx);
1139
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 */
1150
1151         ShowWindow(win, 1);
1152         SetForegroundWindow(win);
1153         SetFocus(win);
1154         upd_pending = 1;
1155         reshape_pending = 1;
1156 }
1157
1158 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1159 {
1160         static int mouse_x, mouse_y;
1161         int x, y, key;
1162
1163         switch(msg) {
1164         case WM_CLOSE:
1165                 if(win) DestroyWindow(win);
1166                 break;
1167
1168         case WM_DESTROY:
1169                 wglMakeCurrent(dc, 0);
1170                 wglDeleteContext(ctx);
1171                 quit = 1;
1172                 PostQuitMessage(0);
1173                 break;
1174
1175         case WM_PAINT:
1176                 upd_pending = 1;
1177                 ValidateRect(win, 0);
1178                 break;
1179
1180         case WM_SIZE:
1181                 x = lparam & 0xffff;
1182                 y = lparam >> 16;
1183                 if(x != win_width && y != win_height) {
1184                         win_width = x;
1185                         win_height = y;
1186                         if(cb_reshape) {
1187                                 reshape_pending = 0;
1188                                 cb_reshape(win_width, win_height);
1189                         }
1190                 }
1191                 break;
1192
1193         case WM_SHOWWINDOW:
1194                 mapped = wparam;
1195                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1196                 break;
1197
1198         case WM_KEYDOWN:
1199         case WM_SYSKEYDOWN:
1200                 update_modkeys();
1201                 key = translate_vkey(wparam);
1202                 if(key < 256) {
1203                         if(cb_keydown) {
1204                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1205                         }
1206                 } else {
1207                         if(cb_skeydown) {
1208                                 cb_skeydown(key, mouse_x, mouse_y);
1209                         }
1210                 }
1211                 break;
1212
1213         case WM_KEYUP:
1214         case WM_SYSKEYUP:
1215                 update_modkeys();
1216                 key = translate_vkey(wparam);
1217                 if(key < 256) {
1218                         if(cb_keyup) {
1219                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1220                         }
1221                 } else {
1222                         if(cb_skeyup) {
1223                                 cb_skeyup(key, mouse_x, mouse_y);
1224                         }
1225                 }
1226                 break;
1227
1228         case WM_LBUTTONDOWN:
1229                 handle_mbutton(0, 1, wparam, lparam);
1230                 break;
1231         case WM_MBUTTONDOWN:
1232                 handle_mbutton(1, 1, wparam, lparam);
1233                 break;
1234         case WM_RBUTTONDOWN:
1235                 handle_mbutton(2, 1, wparam, lparam);
1236                 break;
1237         case WM_LBUTTONUP:
1238                 handle_mbutton(0, 0, wparam, lparam);
1239                 break;
1240         case WM_MBUTTONUP:
1241                 handle_mbutton(1, 0, wparam, lparam);
1242                 break;
1243         case WM_RBUTTONUP:
1244                 handle_mbutton(2, 0, wparam, lparam);
1245                 break;
1246
1247         case WM_MOUSEMOVE:
1248                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1249                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1250                 } else {
1251                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1252                 }
1253                 break;
1254
1255         case WM_SYSCOMMAND:
1256                 wparam &= 0xfff0;
1257                 if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) {
1258                         return 0;
1259                 }
1260         default:
1261                 return DefWindowProc(win, msg, wparam, lparam);
1262         }
1263
1264         return 0;
1265 }
1266
1267 static void update_modkeys(void)
1268 {
1269         if(GetKeyState(VK_SHIFT) & 0x8000) {
1270                 modstate |= GLUT_ACTIVE_SHIFT;
1271         } else {
1272                 modstate &= ~GLUT_ACTIVE_SHIFT;
1273         }
1274         if(GetKeyState(VK_CONTROL) & 0x8000) {
1275                 modstate |= GLUT_ACTIVE_CTRL;
1276         } else {
1277                 modstate &= ~GLUT_ACTIVE_CTRL;
1278         }
1279         if(GetKeyState(VK_MENU) & 0x8000) {
1280                 modstate |= GLUT_ACTIVE_ALT;
1281         } else {
1282                 modstate &= ~GLUT_ACTIVE_ALT;
1283         }
1284 }
1285
1286 static int translate_vkey(int vkey)
1287 {
1288         switch(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;
1297         default:
1298                 break;
1299         }
1300
1301         if(vkey >= 'A' && vkey <= 'Z') {
1302                 vkey += 32;
1303         } else if(vkey >= VK_F1 && vkey <= VK_F12) {
1304                 vkey -= VK_F1 + GLUT_KEY_F1;
1305         }
1306
1307         return vkey;
1308 }
1309
1310 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1311 {
1312         int x, y;
1313
1314         update_modkeys();
1315
1316         if(cb_mouse) {
1317                 x = lparam & 0xffff;
1318                 y = lparam >> 16;
1319                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1320         }
1321 }
1322
1323 static void get_window_pos(int *x, int *y)
1324 {
1325         RECT rect;
1326         GetWindowRect(win, &rect);
1327         *x = rect.left;
1328         *y = rect.top;
1329 }
1330
1331 static void get_window_size(int *w, int *h)
1332 {
1333         RECT rect;
1334         GetClientRect(win, &rect);
1335         *w = rect.right - rect.left;
1336         *h = rect.bottom - rect.top;
1337 }
1338
1339 static void get_screen_size(int *scrw, int *scrh)
1340 {
1341         *scrw = GetSystemMetrics(SM_CXSCREEN);
1342         *scrh = GetSystemMetrics(SM_CYSCREEN);
1343 }
1344 #endif  /* BUILD_WIN32 */
1345
1346 #if defined(__unix__) || defined(__APPLE__)
1347 #include <sys/time.h>
1348
1349 #ifdef MINIGLUT_USE_LIBC
1350 #define sys_gettimeofday(tv, tz)        gettimeofday(tv, tz)
1351 #else
1352 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
1353 #endif
1354
1355 static long get_msec(void)
1356 {
1357         static struct timeval tv0;
1358         struct timeval tv;
1359
1360         sys_gettimeofday(&tv, 0);
1361         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1362                 tv0 = tv;
1363                 return 0;
1364         }
1365         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1366 }
1367 #endif
1368 #ifdef _WIN32
1369 static long get_msec(void)
1370 {
1371         static long t0;
1372         long tm;
1373
1374 #ifdef MINIGLUT_NO_WINMM
1375         tm = GetTickCount();
1376 #else
1377         tm = timeGetTime();
1378 #endif
1379         if(!t0) {
1380                 t0 = tm;
1381                 return 0;
1382         }
1383         return tm - t0;
1384 }
1385 #endif
1386
1387 static void panic(const char *msg)
1388 {
1389         const char *end = msg;
1390         while(*end) end++;
1391         sys_write(2, msg, end - msg);
1392         sys_exit(1);
1393 }
1394
1395
1396 #ifdef MINIGLUT_USE_LIBC
1397 static void sys_exit(int status)
1398 {
1399         exit(status);
1400 }
1401
1402 static int sys_write(int fd, const void *buf, int count)
1403 {
1404         return write(fd, buf, count);
1405 }
1406
1407 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1408 {
1409         return gettimeofday(tv, tz);
1410 }
1411
1412 #else   /* !MINIGLUT_USE_LIBC */
1413
1414 #ifdef __GNUC__
1415 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1416 {
1417         asm volatile(
1418                 "flds %2\n\t"
1419                 "fsincos\n\t"
1420                 "fstps %1\n\t"
1421                 "fstps %0\n\t"
1422                 : "=m"(*sptr), "=m"(*cptr)
1423                 : "m"(angle)
1424         );
1425 }
1426
1427 static float mglut_atan(float x)
1428 {
1429         float res;
1430         asm volatile(
1431                 "flds %1\n\t"
1432                 "fld1\n\t"
1433                 "fpatan\n\t"
1434                 "fstps %0\n\t"
1435                 : "=m"(res)
1436                 : "m"(x)
1437         );
1438         return res;
1439 }
1440 #endif
1441
1442 #ifdef _MSC_VER
1443 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1444 {
1445         float s, c;
1446         __asm {
1447                 fld angle
1448                 fsincos
1449                 fstp c
1450                 fstp s
1451         }
1452         *sptr = s;
1453         *cptr = c;
1454 }
1455
1456 static float mglut_atan(float x)
1457 {
1458         float res;
1459         __asm {
1460                 fld x
1461                 fld1
1462                 fpatan
1463                 fstp res
1464         }
1465         return res;
1466 }
1467 #endif
1468
1469 #ifdef __WATCOMC__
1470 #pragma aux mglut_sincosf = \
1471         "fsincos" \
1472         "fstp dword ptr [edx]" \
1473         "fstp dword ptr [eax]" \
1474         parm[8087][eax][edx]    \
1475         modify[8087];
1476
1477 #pragma aux mglut_atan = \
1478         "fld1" \
1479         "fpatan" \
1480         parm[8087] \
1481         value[8087] \
1482         modify [8087];
1483 #endif
1484
1485 #ifdef __linux__
1486
1487 #ifdef __x86_64__
1488 static void sys_exit(int status)
1489 {
1490         asm volatile(
1491                 "syscall\n\t"
1492                 :: "a"(60), "D"(status));
1493 }
1494 static int sys_write(int fd, const void *buf, int count)
1495 {
1496         long res;
1497         asm volatile(
1498                 "syscall\n\t"
1499                 : "=a"(res)
1500                 : "a"(1), "D"(fd), "S"(buf), "d"(count));
1501         return res;
1502 }
1503 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1504 {
1505         int res;
1506         asm volatile(
1507                 "syscall\n\t"
1508                 : "=a"(res)
1509                 : "a"(96), "D"(tv), "S"(tz));
1510         return res;
1511 }
1512 #endif
1513 #ifdef __i386__
1514 static void sys_exit(int status)
1515 {
1516         asm volatile(
1517                 "int $0x80\n\t"
1518                 :: "a"(1), "b"(status));
1519 }
1520 static int sys_write(int fd, const void *buf, int count)
1521 {
1522         int res;
1523         asm volatile(
1524                 "int $0x80\n\t"
1525                 : "=a"(res)
1526                 : "a"(4), "b"(fd), "c"(buf), "d"(count));
1527         return res;
1528 }
1529 static int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
1530 {
1531         int res;
1532         asm volatile(
1533                 "int $0x80\n\t"
1534                 : "=a"(res)
1535                 : "a"(78), "b"(tv), "c"(tz));
1536         return res;
1537 }
1538 #endif
1539
1540 #endif  /* __linux__ */
1541
1542 #ifdef _WIN32
1543 static void sys_exit(int status)
1544 {
1545         ExitProcess(status);
1546 }
1547 static int sys_write(int fd, const void *buf, int count)
1548 {
1549         unsigned long wrsz = 0;
1550
1551         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1552         if(!WriteFile(out, buf, count, &wrsz, 0)) {
1553                 return -1;
1554         }
1555         return wrsz;
1556 }
1557 #endif  /* _WIN32 */
1558
1559 #endif  /* !MINIGLUT_USE_LIBC */