MiniGLUT initial commit
[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 #include <stdlib.h>
20 #endif
21
22 #if defined(__unix__)
23
24 #include <X11/Xlib.h>
25 #include <X11/cursorfont.h>
26 #include <GL/glx.h>
27 #define BUILD_X11
28
29 #ifndef GLX_SAMPLE_BUFFERS_ARB
30 #define GLX_SAMPLE_BUFFERS_ARB  100000
31 #define GLX_SAMPLES_ARB                 100001
32 #endif
33 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
34 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB        0x20b2
35 #endif
36
37 static Display *dpy;
38 static Window win, root;
39 static int scr;
40 static GLXContext ctx;
41 static Atom xa_wm_proto, xa_wm_del_win;
42 static unsigned int evmask;
43
44 #elif defined(_WIN32)
45
46 #include <windows.h>
47 #define BUILD_WIN32
48
49 static HWND win;
50 static HDC dc;
51 static HGLRC ctx;
52
53 #else
54 #error unsupported platform
55 #endif
56
57 #include <GL/gl.h>
58 #include "miniglut.h"
59
60 struct ctx_info {
61         int rsize, gsize, bsize, asize;
62         int zsize, ssize;
63         int dblbuf;
64         int samples;
65         int stereo;
66         int srgb;
67 };
68
69 static void create_window(const char *title);
70 static void get_window_pos(Window win, int *x, int *y);
71 static void get_window_size(Window win, int *w, int *h);
72 static void get_screen_size(Window win, int *scrw, int *scrh);
73
74 static long get_msec(void);
75 static void panic(const char *msg);
76 static void sys_exit(int status);
77 static int sys_write(int fd, const void *buf, int count);
78
79
80 static int init_x, init_y, init_width = 256, init_height = 256;
81 static unsigned int init_mode;
82
83 static struct ctx_info ctx_info;
84 static int cur_cursor = GLUT_CURSOR_INHERIT;
85
86 static glut_cb cb_display;
87 static glut_cb cb_idle;
88 static glut_cb_reshape cb_reshape;
89 static glut_cb_state cb_vis, cb_entry;
90 static glut_cb_keyb cb_keydown, cb_keyup;
91 static glut_cb_special cb_skeydown, cb_skeyup;
92 static glut_cb_mouse cb_mouse;
93 static glut_cb_motion cb_motion, cb_passive;
94 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
95 static glut_cb_sbbutton cb_sball_button;
96
97 static int win_width, win_height;
98 static int mapped;
99 static int quit;
100 static int upd_pending, reshape_pending;
101 static int modstate;
102
103
104 void glutInit(int *argc, char **argv)
105 {
106 #ifdef BUILD_X11
107         if(!(dpy = XOpenDisplay(0))) {
108                 panic("Failed to connect to the X server\n");
109         }
110         scr = DefaultScreen(dpy);
111         root = RootWindow(dpy, scr);
112         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
113         xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
114
115         evmask = ExposureMask | StructureNotifyMask;
116 #endif
117 }
118
119 void glutInitWindowPosition(int x, int y)
120 {
121         init_x = x;
122         init_y = y;
123 }
124
125 void glutInitWindowSize(int xsz, int ysz)
126 {
127         init_width = xsz;
128         init_height = ysz;
129 }
130
131 void glutInitDisplayMode(unsigned int mode)
132 {
133         init_mode = mode;
134 }
135
136 void glutCreateWindow(const char *title)
137 {
138         create_window(title);
139 }
140
141 void glutExit(void)
142 {
143         quit = 1;
144 }
145
146 void glutMainLoop(void)
147 {
148         while(!quit) {
149                 glutMainLoopEvent();
150         }
151 }
152
153 void glutPostRedisplay(void)
154 {
155         upd_pending = 1;
156 }
157
158 #define UPD_EVMASK(x) \
159         do { \
160                 if(func) { \
161                         evmask |= x; \
162                 } else { \
163                         evmask &= ~(x); \
164                 } \
165                 if(win) XSelectInput(dpy, win, evmask); \
166         } while(0)
167
168
169 void glutIdleFunc(glut_cb func)
170 {
171         cb_idle = func;
172 }
173
174 void glutDisplayFunc(glut_cb func)
175 {
176         cb_display = func;
177 }
178
179 void glutReshapeFunc(glut_cb_reshape func)
180 {
181         cb_reshape = func;
182 }
183
184 void glutVisibilityFunc(glut_cb_state func)
185 {
186         cb_vis = func;
187 #ifdef BUILD_X11
188         UPD_EVMASK(VisibilityChangeMask);
189 #endif
190 }
191
192 void glutEntryFunc(glut_cb_state func)
193 {
194         cb_entry = func;
195 #ifdef BUILD_X11
196         UPD_EVMASK(EnterWindowMask | LeaveWindowMask);
197 #endif
198 }
199
200 void glutKeyboardFunc(glut_cb_keyb func)
201 {
202         cb_keydown = func;
203 #ifdef BUILD_X11
204         UPD_EVMASK(KeyPressMask);
205 #endif
206 }
207
208 void glutKeyboardUpFunc(glut_cb_keyb func)
209 {
210         cb_keyup = func;
211 #ifdef BUILD_X11
212         UPD_EVMASK(KeyReleaseMask);
213 #endif
214 }
215
216 void glutSpecialFunc(glut_cb_special func)
217 {
218         cb_skeydown = func;
219 #ifdef BUILD_X11
220         UPD_EVMASK(KeyPressMask);
221 #endif
222 }
223
224 void glutSpecialUpFunc(glut_cb_special func)
225 {
226         cb_skeyup = func;
227 #ifdef BUILD_X11
228         UPD_EVMASK(KeyReleaseMask);
229 #endif
230 }
231
232 void glutMouseFunc(glut_cb_mouse func)
233 {
234         cb_mouse = func;
235 #ifdef BUILD_X11
236         UPD_EVMASK(ButtonPressMask | ButtonReleaseMask);
237 #endif
238 }
239
240 void glutMotionFunc(glut_cb_motion func)
241 {
242         cb_motion = func;
243 #ifdef BUILD_X11
244         UPD_EVMASK(ButtonMotionMask);
245 #endif
246 }
247
248 void glutPassiveMotionFunc(glut_cb_motion func)
249 {
250         cb_passive = func;
251 #ifdef BUILD_X11
252         UPD_EVMASK(PointerMotionMask);
253 #endif
254 }
255
256 void glutSpaceballMotionFunc(glut_cb_sbmotion func)
257 {
258         cb_sball_motion = func;
259 }
260
261 void glutSpaceballRotateFunc(glut_cb_sbmotion func)
262 {
263         cb_sball_rotate = func;
264 }
265
266 void glutSpaceballBittonFunc(glut_cb_sbbutton func)
267 {
268         cb_sball_button = func;
269 }
270
271 int glutGet(unsigned int s)
272 {
273         int x, y;
274         switch(s) {
275         case GLUT_WINDOW_X:
276                 get_window_pos(win, &x, &y);
277                 return x;
278         case GLUT_WINDOW_Y:
279                 get_window_pos(win, &x, &y);
280                 return y;
281         case GLUT_WINDOW_WIDTH:
282                 get_window_size(win, &x, &y);
283                 return x;
284         case GLUT_WINDOW_HEIGHT:
285                 get_window_size(win, &x, &y);
286                 return y;
287         case GLUT_WINDOW_BUFFER_SIZE:
288                 return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize;
289         case GLUT_WINDOW_STENCIL_SIZE:
290                 return ctx_info.ssize;
291         case GLUT_WINDOW_DEPTH_SIZE:
292                 return ctx_info.zsize;
293         case GLUT_WINDOW_RED_SIZE:
294                 return ctx_info.rsize;
295         case GLUT_WINDOW_GREEN_SIZE:
296                 return ctx_info.gsize;
297         case GLUT_WINDOW_BLUE_SIZE:
298                 return ctx_info.bsize;
299         case GLUT_WINDOW_ALPHA_SIZE:
300                 return ctx_info.asize;
301         case GLUT_WINDOW_DOUBLEBUFFER:
302                 return ctx_info.dblbuf;
303         case GLUT_WINDOW_RGBA:
304                 return 1;
305         case GLUT_WINDOW_NUM_SAMPLES:
306                 return ctx_info.samples;
307         case GLUT_WINDOW_STEREO:
308                 return ctx_info.stereo;
309         case GLUT_WINDOW_SRGB:
310                 return ctx_info.srgb;
311         case GLUT_WINDOW_CURSOR:
312                 return cur_cursor;
313         case GLUT_SCREEN_WIDTH:
314                 get_screen_size(win, &x, &y);
315                 return x;
316         case GLUT_SCREEN_HEIGHT:
317                 get_screen_size(win, &x, &y);
318                 return y;
319         case GLUT_INIT_DISPLAY_MODE:
320                 return init_mode;
321         case GLUT_INIT_WINDOW_X:
322                 return init_x;
323         case GLUT_INIT_WINDOW_Y:
324                 return init_y;
325         case GLUT_INIT_WINDOW_WIDTH:
326                 return init_width;
327         case GLUT_INIT_WINDOW_HEIGHT:
328                 return init_height;
329         case GLUT_ELAPSED_TIME:
330                 return get_msec();
331         default:
332                 break;
333         }
334         return 0;
335 }
336
337 int glutGetModifiers(void)
338 {
339         return modstate;
340 }
341
342 static int is_space(int c)
343 {
344         return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r';
345 }
346
347 static const char *skip_space(const char *s)
348 {
349         while(*s && is_space(*s)) s++;
350         return s;
351 }
352
353 int glutExtensionSupported(char *ext)
354 {
355         const char *str, *eptr;
356
357         if(!(str = (const char*)glGetString(GL_EXTENSIONS))) {
358                 return 0;
359         }
360
361         while(*str) {
362                 str = skip_space(str);
363                 eptr = skip_space(ext);
364                 while(*str && !is_space(*str) && *eptr && *str == *eptr) {
365                         str++;
366                         eptr++;
367                 }
368                 if((!*str || is_space(*str)) && !*eptr) {
369                         return 1;
370                 }
371                 while(*str && !is_space(*str)) str++;
372         }
373
374         return 0;
375 }
376
377 /* TODO */
378 void glutSolidSphere(float rad)
379 {
380 }
381
382 void glutWireSphere(float rad)
383 {
384 }
385
386 void glutSolidCube(float sz)
387 {
388 }
389
390 void glutWireCube(float sz)
391 {
392 }
393
394 void glutSolidTorus(float inner_rad, float outer_rad, float sides, float rings)
395 {
396 }
397
398 void glutWireTorus(float inner_rad, float outer_rad, float sides, float rings)
399 {
400 }
401
402 void glutSolidTeapot(float size)
403 {
404 }
405
406 void glutWireTeapot(float size)
407 {
408 }
409
410
411 #ifdef BUILD_X11
412 static void handle_event(XEvent *ev);
413
414 void glutMainLoopEvent(void)
415 {
416         XEvent ev;
417
418         if(!cb_display) {
419                 panic("display callback not set");
420         }
421
422         for(;;) {
423                 if(!upd_pending && !cb_idle) {
424                         XNextEvent(dpy, &ev);
425                         handle_event(&ev);
426                         if(quit) return;
427                 }
428                 while(XPending(dpy)) {
429                         XNextEvent(dpy, &ev);
430                         handle_event(&ev);
431                         if(quit) return;
432                 }
433
434                 if(cb_idle) {
435                         cb_idle();
436                 }
437
438                 if(upd_pending && mapped) {
439                         upd_pending = 0;
440                         cb_display();
441                 }
442         }
443 }
444
445 static KeySym translate_keysym(KeySym sym)
446 {
447         switch(sym) {
448         case XK_Escape:
449                 return 27;
450         case XK_BackSpace:
451                 return '\b';
452         case XK_Linefeed:
453                 return '\r';
454         case XK_Return:
455                 return '\n';
456         case XK_Delete:
457                 return 127;
458         case XK_Tab:
459                 return '\t';
460         default:
461                 break;
462         }
463         return sym;
464 }
465
466 static void handle_event(XEvent *ev)
467 {
468         KeySym sym;
469
470         switch(ev->type) {
471         case MapNotify:
472                 mapped = 1;
473                 break;
474         case UnmapNotify:
475                 mapped = 0;
476                 break;
477         case ConfigureNotify:
478                 if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) {
479                         win_width = ev->xconfigure.width;
480                         win_height = ev->xconfigure.height;
481                         cb_reshape(ev->xconfigure.width, ev->xconfigure.height);
482                 }
483                 break;
484
485         case ClientMessage:
486                 if(ev->xclient.message_type == xa_wm_proto) {
487                         if(ev->xclient.data.l[0] == xa_wm_del_win) {
488                                 quit = 1;
489                         }
490                 }
491                 break;
492
493         case Expose:
494                 upd_pending = 1;
495                 break;
496
497         case KeyPress:
498         case KeyRelease:
499                 modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask);
500                 if(!(sym = XLookupKeysym(&ev->xkey, 0))) {
501                         break;
502                 }
503                 sym = translate_keysym(sym);
504                 if(sym < 256) {
505                         if(ev->type == KeyPress) {
506                                 if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y);
507                         } else {
508                                 if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y);
509                         }
510                 } else {
511                         if(ev->type == KeyPress) {
512                                 if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y);
513                         } else {
514                                 if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y);
515                         }
516                 }
517                 break;
518
519         case ButtonPress:
520         case ButtonRelease:
521                 modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask);
522                 if(cb_mouse) {
523                         int bn = ev->xbutton.button - Button1;
524                         cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP,
525                                         ev->xbutton.x, ev->xbutton.y);
526                 }
527                 break;
528
529         case MotionNotify:
530                 if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) {
531                         if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y);
532                 } else {
533                         if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y);
534                 }
535                 break;
536
537         case VisibilityNotify:
538                 if(cb_vis) {
539                         cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE);
540                 }
541                 break;
542         case EnterNotify:
543                 if(cb_entry) cb_entry(GLUT_ENTERED);
544                 break;
545         case LeaveNotify:
546                 if(cb_entry) cb_entry(GLUT_LEFT);
547                 break;
548         }
549 }
550
551 void glutSwapBuffers(void)
552 {
553         glXSwapBuffers(dpy, win);
554 }
555
556 void glutPositionWindow(int x, int y)
557 {
558         XMoveWindow(dpy, win, x, y);
559 }
560
561 void glutReshapeWindow(int xsz, int ysz)
562 {
563         XResizeWindow(dpy, win, xsz, ysz);
564 }
565
566 void glutFullScreen(void)
567 {
568         /* TODO */
569 }
570
571 void glutSetWindowTitle(const char *title)
572 {
573         XTextProperty tprop;
574         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
575                 return;
576         }
577         XSetWMName(dpy, win, &tprop);
578         XFree(tprop.value);
579 }
580
581 void glutSetIconTitle(const char *title)
582 {
583         XTextProperty tprop;
584         if(!XStringListToTextProperty((char**)&title, 1, &tprop)) {
585                 return;
586         }
587         XSetWMIconName(dpy, win, &tprop);
588         XFree(tprop.value);
589 }
590
591 void glutSetCursor(int cidx)
592 {
593         Cursor cur = None;
594
595         switch(cidx) {
596         case GLUT_CURSOR_LEFT_ARROW:
597                 cur = XCreateFontCursor(dpy, XC_left_ptr);
598                 break;
599         case GLUT_CURSOR_INHERIT:
600                 break;
601         case GLUT_CURSOR_NONE:
602                 /* TODO */
603         default:
604                 return;
605         }
606
607         XDefineCursor(dpy, win, cur);
608         cur_cursor = cidx;
609 }
610
611 static XVisualInfo *choose_visual(unsigned int mode)
612 {
613         XVisualInfo *vi;
614         int attr[32] = {
615                 GLX_RGBA,
616                 GLX_DOUBLEBUFFER,
617                 GLX_RED_SIZE, 4,
618                 GLX_GREEN_SIZE, 4,
619                 GLX_BLUE_SIZE, 4
620         };
621         int *aptr = attr + 8;
622         int *samples = 0;
623
624         if(mode & GLUT_ALPHA) {
625                 *aptr++ = GLX_ALPHA_SIZE;
626                 *aptr++ = 4;
627         }
628         if(mode & GLUT_DEPTH) {
629                 *aptr++ = GLX_DEPTH_SIZE;
630                 *aptr++ = 16;
631         }
632         if(mode & GLUT_STENCIL) {
633                 *aptr++ = GLX_STENCIL_SIZE;
634                 *aptr++ = 1;
635         }
636         if(mode & GLUT_ACCUM) {
637                 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
638                 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
639                 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
640         }
641         if(mode & GLUT_STEREO) {
642                 *aptr++ = GLX_STEREO;
643         }
644         if(mode & GLUT_SRGB) {
645                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
646         }
647         if(mode & GLUT_MULTISAMPLE) {
648                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
649                 *aptr++ = 1;
650                 *aptr++ = GLX_SAMPLES_ARB;
651                 samples = aptr;
652                 *aptr++ = 32;
653         }
654         *aptr++ = None;
655
656         if(!samples) {
657                 return glXChooseVisual(dpy, scr, attr);
658         }
659         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
660                 *samples >>= 1;
661                 if(!*samples) {
662                         aptr[-3] = None;
663                 }
664         }
665         return vi;
666 }
667
668 static void create_window(const char *title)
669 {
670         XSetWindowAttributes xattr;
671         XVisualInfo *vi;
672         unsigned int xattr_mask;
673         unsigned int mode = init_mode;
674
675         if(!(vi = choose_visual(mode))) {
676                 mode &= ~GLUT_SRGB;
677                 if(!(vi = choose_visual(mode))) {
678                         panic("Failed to find compatible visual\n");
679                 }
680         }
681
682         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
683                 XFree(vi);
684                 panic("Failed to create OpenGL context\n");
685         }
686
687         glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
688         glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
689         glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
690         glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
691         glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
692         glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
693         glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
694         glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
695         glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
696         glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
697
698         xattr.background_pixel = BlackPixel(dpy, scr);
699         xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
700         xattr_mask = CWBackPixel | CWColormap;
701         if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
702                         vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
703                 XFree(vi);
704                 glXDestroyContext(dpy, ctx);
705                 panic("Failed to create window\n");
706         }
707         XFree(vi);
708
709         XSelectInput(dpy, win, evmask);
710
711         glutSetWindowTitle(title);
712         glutSetIconTitle(title);
713         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
714         XMapWindow(dpy, win);
715
716         glXMakeCurrent(dpy, win, ctx);
717 }
718
719 static void get_window_pos(Window win, int *x, int *y)
720 {
721         XWindowAttributes wattr;
722         XGetWindowAttributes(dpy, win, &wattr);
723         *x = wattr.x;
724         *y = wattr.y;
725 }
726
727 static void get_window_size(Window win, int *w, int *h)
728 {
729         XWindowAttributes wattr;
730         XGetWindowAttributes(dpy, win, &wattr);
731         *w = wattr.width;
732         *h = wattr.height;
733 }
734
735 static void get_screen_size(Window win, int *scrw, int *scrh)
736 {
737         XWindowAttributes wattr;
738         XGetWindowAttributes(dpy, root, &wattr);
739         *scrw = wattr.width;
740         *scrh = wattr.height;
741 }
742 #endif
743
744 #if defined(__unix__) || defined(__APPLE__)
745 #include <sys/time.h>
746
747 static long get_msec(void)
748 {
749         static struct timeval tv0;
750         struct timeval tv;
751
752         gettimeofday(&tv, 0);
753         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
754                 tv0 = tv;
755                 return 0;
756         }
757         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
758 }
759 #endif
760 #ifdef _WIN32
761 static long get_msec(void)
762 {
763         static long t0;
764
765         if(!t0) {
766                 t0 = timeGetTime();
767                 return 0;
768         }
769         return timeGetTime() - t0;
770 }
771 #endif
772
773 static void panic(const char *msg)
774 {
775         const char *end = msg;
776         while(*end) end++;
777         sys_write(2, msg, end - msg);
778         sys_exit(1);
779 }
780
781
782 #ifdef MINIGLUT_USE_LIBC
783 static void sys_exit(int status)
784 {
785         exit(status);
786 }
787
788 static int sys_write(int fd, const void *buf, int count)
789 {
790         return write(fd, buf, count);
791 }
792 #else   /* !MINIGLUT_USE_LIBC */
793
794 #ifdef __linux__
795
796 #ifdef __x86_64__
797 static void sys_exit(int status)
798 {
799         asm volatile(
800                 "syscall\n\t"
801                 :: "a"(60), "D"(status)
802         );
803 }
804 static int sys_write(int fd, const void *buf, int count)
805 {
806         long res;
807         asm volatile(
808                 "syscall\n\t"
809                 : "=a"(res)
810                 : "a"(1), "D"(fd), "S"(buf), "d"(count)
811         );
812         return res;
813 }
814 #endif
815 #ifdef __i386__
816 static void sys_exit(int status)
817 {
818         asm volatile(
819                 "mov $1, %%eax\n\t"
820                 "int $0x80\n\t"
821                 :: "b"(status)
822                 : "eax"
823         );
824 }
825 static int sys_write(int fd, const void *buf, int count)
826 {
827         int res;
828         asm volatile(
829                 "mov $4, %%eax\n\t"
830                 "int $0x80\n\t"
831                 :: "b"(fd), "c"(buf), "d"(count));
832         return res;
833 }
834 #endif
835
836 #endif  /* __linux__ */
837
838 #ifdef _WIN32
839 static int sys_exit(int status)
840 {
841         ExitProcess(status);
842 }
843 static int sys_write(int fd, const void *buf, int count)
844 {
845         int wrsz;
846
847         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
848         if(!WriteFile(out, buf, count, &wrsz, 0)) {
849                 return -1;
850         }
851         return wrsz;
852 }
853 #endif  /* _WIN32 */
854
855 #endif  /* !MINIGLUT_USE_LIBC */