windows port
[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 HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
66
67 static HINSTANCE hinst;
68 static HWND win;
69 static HDC dc;
70 static HGLRC ctx;
71
72 #else
73 #error unsupported platform
74 #endif
75
76 #include <GL/gl.h>
77 #include "miniglut.h"
78
79 struct ctx_info {
80         int rsize, gsize, bsize, asize;
81         int zsize, ssize;
82         int dblbuf;
83         int samples;
84         int stereo;
85         int srgb;
86 };
87
88 static void create_window(const char *title);
89 static void get_window_pos(int *x, int *y);
90 static void get_window_size(int *w, int *h);
91 static void get_screen_size(int *scrw, int *scrh);
92
93 static long get_msec(void);
94 static void panic(const char *msg);
95 static void sys_exit(int status);
96 static int sys_write(int fd, const void *buf, int count);
97
98
99 static int init_x, init_y, init_width = 256, init_height = 256;
100 static unsigned int init_mode;
101
102 static struct ctx_info ctx_info;
103 static int cur_cursor = GLUT_CURSOR_INHERIT;
104
105 static glut_cb cb_display;
106 static glut_cb cb_idle;
107 static glut_cb_reshape cb_reshape;
108 static glut_cb_state cb_vis, cb_entry;
109 static glut_cb_keyb cb_keydown, cb_keyup;
110 static glut_cb_special cb_skeydown, cb_skeyup;
111 static glut_cb_mouse cb_mouse;
112 static glut_cb_motion cb_motion, cb_passive;
113 static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate;
114 static glut_cb_sbbutton cb_sball_button;
115
116 static int win_width, win_height;
117 static int mapped;
118 static int quit;
119 static int upd_pending, reshape_pending;
120 static int modstate;
121
122
123 void glutInit(int *argc, char **argv)
124 {
125 #ifdef BUILD_X11
126         if(!(dpy = XOpenDisplay(0))) {
127                 panic("Failed to connect to the X server\n");
128         }
129         scr = DefaultScreen(dpy);
130         root = RootWindow(dpy, scr);
131         xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False);
132         xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
133
134         evmask = ExposureMask | StructureNotifyMask;
135 #endif
136 #ifdef BUILD_WIN32
137         WNDCLASSEX wc = {0};
138
139         hinst = GetModuleHandle(0);
140
141         wc.cbSize = sizeof wc;
142         wc.hbrBackground = GetStockObject(BLACK_BRUSH);
143         wc.hCursor = LoadCursor(0, IDC_ARROW);
144         wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
145         wc.hInstance = hinst;
146         wc.lpfnWndProc = handle_message;
147         wc.lpszClassName = "MiniGLUT";
148         wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
149         if(!RegisterClassEx(&wc)) {
150                 panic("Failed to register \"MiniGLUT\" window class\n");
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                 GLX_RGBA,
814                 GLX_DOUBLEBUFFER,
815                 GLX_RED_SIZE, 4,
816                 GLX_GREEN_SIZE, 4,
817                 GLX_BLUE_SIZE, 4
818         };
819         int *aptr = attr + 8;
820         int *samples = 0;
821
822         if(mode & GLUT_ALPHA) {
823                 *aptr++ = GLX_ALPHA_SIZE;
824                 *aptr++ = 4;
825         }
826         if(mode & GLUT_DEPTH) {
827                 *aptr++ = GLX_DEPTH_SIZE;
828                 *aptr++ = 16;
829         }
830         if(mode & GLUT_STENCIL) {
831                 *aptr++ = GLX_STENCIL_SIZE;
832                 *aptr++ = 1;
833         }
834         if(mode & GLUT_ACCUM) {
835                 *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1;
836                 *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1;
837                 *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1;
838         }
839         if(mode & GLUT_STEREO) {
840                 *aptr++ = GLX_STEREO;
841         }
842         if(mode & GLUT_SRGB) {
843                 *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
844         }
845         if(mode & GLUT_MULTISAMPLE) {
846                 *aptr++ = GLX_SAMPLE_BUFFERS_ARB;
847                 *aptr++ = 1;
848                 *aptr++ = GLX_SAMPLES_ARB;
849                 samples = aptr;
850                 *aptr++ = 32;
851         }
852         *aptr++ = None;
853
854         if(!samples) {
855                 return glXChooseVisual(dpy, scr, attr);
856         }
857         while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) {
858                 *samples >>= 1;
859                 if(!*samples) {
860                         aptr[-3] = None;
861                 }
862         }
863         return vi;
864 }
865
866 static void create_window(const char *title)
867 {
868         XSetWindowAttributes xattr;
869         XVisualInfo *vi;
870         unsigned int xattr_mask;
871         unsigned int mode = init_mode;
872
873         if(!(vi = choose_visual(mode))) {
874                 mode &= ~GLUT_SRGB;
875                 if(!(vi = choose_visual(mode))) {
876                         panic("Failed to find compatible visual\n");
877                 }
878         }
879
880         if(!(ctx = glXCreateContext(dpy, vi, 0, True))) {
881                 XFree(vi);
882                 panic("Failed to create OpenGL context\n");
883         }
884
885         glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize);
886         glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize);
887         glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize);
888         glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize);
889         glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize);
890         glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize);
891         glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf);
892         glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo);
893         glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples);
894         glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb);
895
896         xattr.background_pixel = BlackPixel(dpy, scr);
897         xattr.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
898         xattr_mask = CWBackPixel | CWColormap;
899         if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0,
900                         vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) {
901                 XFree(vi);
902                 glXDestroyContext(dpy, ctx);
903                 panic("Failed to create window\n");
904         }
905         XFree(vi);
906
907         XSelectInput(dpy, win, evmask);
908
909         glutSetWindowTitle(title);
910         glutSetIconTitle(title);
911         XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
912         XMapWindow(dpy, win);
913
914         glXMakeCurrent(dpy, win, ctx);
915 }
916
917 static void get_window_pos(int *x, int *y)
918 {
919         XWindowAttributes wattr;
920         XGetWindowAttributes(dpy, win, &wattr);
921         *x = wattr.x;
922         *y = wattr.y;
923 }
924
925 static void get_window_size(int *w, int *h)
926 {
927         XWindowAttributes wattr;
928         XGetWindowAttributes(dpy, win, &wattr);
929         *w = wattr.width;
930         *h = wattr.height;
931 }
932
933 static void get_screen_size(int *scrw, int *scrh)
934 {
935         XWindowAttributes wattr;
936         XGetWindowAttributes(dpy, root, &wattr);
937         *scrw = wattr.width;
938         *scrh = wattr.height;
939 }
940 #endif  /* BUILD_X11 */
941
942
943 /* --------------- windows implementation ----------------- */
944 #ifdef BUILD_WIN32
945 static void update_modkeys(void);
946 static int translate_vkey(int vkey);
947 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam);
948
949 void glutMainLoopEvent(void)
950 {
951         MSG msg;
952         
953         if(!cb_display) {
954                 panic("display callback not set");
955         }
956
957         if(reshape_pending && cb_reshape) {
958                 reshape_pending = 0;
959                 get_window_size(&win_width, &win_height);
960                 cb_reshape(win_width, win_height);
961         }
962
963         if(!upd_pending && !cb_idle) {
964                 GetMessage(&msg, 0, 0, 0);
965                 TranslateMessage(&msg);
966                 DispatchMessage(&msg);
967                 if(quit) return;
968         }
969         while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
970                 TranslateMessage(&msg);
971                 DispatchMessage(&msg);
972                 if(quit) return;
973         }
974
975         if(cb_idle) {
976                 cb_idle();
977         }
978
979         if(upd_pending && mapped) {
980                 upd_pending = 0;
981                 cb_display();
982         }
983 }
984
985 void glutSwapBuffers(void)
986 {
987         SwapBuffers(dc);
988 }
989
990 void glutPositionWindow(int x, int y)
991 {
992         RECT rect;
993         GetWindowRect(win, &rect);
994         MoveWindow(win, x, y, rect.right - rect.left, rect.bottom - rect.top, 1);
995 }
996
997 void glutReshapeWindow(int xsz, int ysz)
998 {
999         RECT rect;
1000         GetWindowRect(win, &rect);
1001         MoveWindow(win, rect.left, rect.top, xsz, ysz, 1);
1002 }
1003
1004 void glutFullScreen(void)
1005 {
1006         /* TODO */
1007 }
1008
1009 void glutSetWindowTitle(const char *title)
1010 {
1011         SetWindowText(win, title);
1012 }
1013
1014 void glutSetIconTitle(const char *title)
1015 {
1016 }
1017
1018 void glutSetCursor(int cidx)
1019 {
1020         switch(cidx) {
1021         case GLUT_CURSOR_NONE:
1022                 ShowCursor(0);
1023                 break;
1024         case GLUT_CURSOR_INHERIT:
1025         case GLUT_CURSOR_LEFT_ARROW:
1026         default:
1027                 SetCursor(LoadCursor(0, IDC_ARROW));
1028                 ShowCursor(1);
1029         }
1030 }
1031
1032
1033 static void create_window(const char *title)
1034 {
1035         int pixfmt;
1036         PIXELFORMATDESCRIPTOR pfd = {0};
1037
1038         if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x, init_y,
1039                                 init_width, init_height, 0, 0, hinst, 0))) {
1040                 panic("Failed to create window\n");
1041         }
1042         dc = GetDC(win);
1043
1044         pfd.nSize = sizeof pfd;
1045         pfd.nVersion = 1;
1046         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
1047         if(init_mode & GLUT_STEREO) {
1048                 pfd.dwFlags |= PFD_STEREO;
1049         }
1050         pfd.iPixelType = init_mode & GLUT_INDEX ? PFD_TYPE_COLORINDEX : PFD_TYPE_RGBA;
1051         pfd.cColorBits = 24;
1052         if(init_mode & GLUT_ALPHA) {
1053                 pfd.cAlphaBits = 8;
1054         }
1055         if(init_mode & GLUT_ACCUM) {
1056                 pfd.cAccumBits = 24;
1057         }
1058         if(init_mode & GLUT_DEPTH) {
1059                 pfd.cDepthBits = 24;
1060         }
1061         if(init_mode & GLUT_STENCIL) {
1062                 pfd.cStencilBits = 8;
1063         }
1064         pfd.iLayerType = PFD_MAIN_PLANE;
1065
1066         if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) {
1067                 panic("Failed to find suitable pixel format\n");
1068         }
1069         if(!SetPixelFormat(dc, pixfmt, &pfd)) {
1070                 panic("Failed to set the selected pixel format\n");
1071         }
1072         if(!(ctx = wglCreateContext(dc))) {
1073                 panic("Failed to create the OpenGL context\n");
1074         }
1075         wglMakeCurrent(dc, ctx);
1076
1077         DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd);
1078         ctx_info.rsize = pfd.cRedBits;
1079         ctx_info.gsize = pfd.cGreenBits;
1080         ctx_info.bsize = pfd.cBlueBits;
1081         ctx_info.asize = pfd.cAlphaBits;
1082         ctx_info.zsize = pfd.cDepthBits;
1083         ctx_info.ssize = pfd.cStencilBits;
1084         ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0;
1085         ctx_info.samples = 1;   /* TODO */
1086         ctx_info.srgb = 0;              /* TODO */
1087
1088         ShowWindow(win, 1);
1089         upd_pending = 1;
1090         reshape_pending = 1;
1091 }
1092
1093 static HRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
1094 {
1095         static int mouse_x, mouse_y;
1096         int key;
1097
1098         switch(msg) {
1099         case WM_CLOSE:
1100                 if(win) DestroyWindow(win);
1101                 break;
1102
1103         case WM_DESTROY:
1104                 wglMakeCurrent(dc, 0);
1105                 wglDeleteContext(ctx);
1106                 quit = 1;
1107                 PostQuitMessage(0);
1108                 break;
1109
1110         case WM_PAINT:
1111                 upd_pending = 1;
1112                 ValidateRect(win, 0);
1113                 break;
1114
1115         case WM_SIZE:
1116                 win_width = lparam & 0xffff;
1117                 win_height = lparam >> 16;
1118                 if(cb_reshape) {
1119                         reshape_pending = 0;
1120                         cb_reshape(win_width, win_height);
1121                 }
1122                 break;
1123
1124         case WM_SHOWWINDOW:
1125                 mapped = wparam;
1126                 if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE);
1127                 break;
1128
1129         case WM_KEYDOWN:
1130                 update_modkeys();
1131                 key = translate_vkey(wparam);
1132                 if(key < 256) {
1133                         if(cb_keydown) {
1134                                 cb_keydown((unsigned char)key, mouse_x, mouse_y);
1135                         }
1136                 } else {
1137                         if(cb_skeydown) {
1138                                 cb_skeydown(key, mouse_x, mouse_y);
1139                         }
1140                 }
1141                 break;
1142
1143         case WM_KEYUP:
1144                 update_modkeys();
1145                 key = translate_vkey(wparam);
1146                 if(key < 256) {
1147                         if(cb_keyup) {
1148                                 cb_keyup((unsigned char)key, mouse_x, mouse_y);
1149                         }
1150                 } else {
1151                         if(cb_skeyup) {
1152                                 cb_skeyup(key, mouse_x, mouse_y);
1153                         }
1154                 }
1155                 break;
1156
1157         case WM_LBUTTONDOWN:
1158                 handle_mbutton(0, 1, wparam, lparam);
1159                 break;
1160         case WM_MBUTTONDOWN:
1161                 handle_mbutton(1, 1, wparam, lparam);
1162                 break;
1163         case WM_RBUTTONDOWN:
1164                 handle_mbutton(2, 1, wparam, lparam);
1165                 break;
1166         case WM_LBUTTONUP:
1167                 handle_mbutton(0, 0, wparam, lparam);
1168                 break;
1169         case WM_MBUTTONUP:
1170                 handle_mbutton(1, 0, wparam, lparam);
1171                 break;
1172         case WM_RBUTTONUP:
1173                 handle_mbutton(2, 0, wparam, lparam);
1174                 break;
1175
1176         case WM_MOUSEMOVE:
1177                 if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1178                         if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16);
1179                 } else {
1180                         if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16);
1181                 }
1182                 break;
1183
1184         default:
1185                 return DefWindowProc(win, msg, wparam, lparam);
1186         }
1187
1188         return 0;
1189 }
1190
1191 static void update_modkeys(void)
1192 {
1193         if(GetKeyState(VK_SHIFT)) {
1194                 modstate |= GLUT_ACTIVE_SHIFT;
1195         } else {
1196                 modstate &= ~GLUT_ACTIVE_SHIFT;
1197         }
1198         if(GetKeyState(VK_CONTROL)) {
1199                 modstate |= GLUT_ACTIVE_CTRL;
1200         } else {
1201                 modstate &= ~GLUT_ACTIVE_CTRL;
1202         }
1203         if(GetKeyState(VK_MENU)) {
1204                 modstate |= GLUT_ACTIVE_ALT;
1205         } else {
1206                 modstate &= ~GLUT_ACTIVE_ALT;
1207         }
1208 }
1209
1210 static int translate_vkey(int vkey)
1211 {
1212         return vkey;    /* TODO */
1213 }
1214
1215 static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam)
1216 {
1217         int x, y;
1218
1219         update_modkeys();
1220
1221         if(cb_mouse) {
1222                 x = lparam & 0xffff;
1223                 y = lparam >> 16;
1224                 cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y);
1225         }
1226 }
1227
1228 static void get_window_pos(int *x, int *y)
1229 {
1230         RECT rect;
1231         GetWindowRect(win, &rect);
1232         *x = rect.left;
1233         *y = rect.top;
1234 }
1235
1236 static void get_window_size(int *w, int *h)
1237 {
1238         RECT rect;
1239         GetClientRect(win, &rect);
1240         *w = rect.right - rect.left;
1241         *h = rect.bottom - rect.top;
1242 }
1243
1244 static void get_screen_size(int *scrw, int *scrh)
1245 {
1246         *scrw = GetSystemMetrics(SM_CXSCREEN);
1247         *scrh = GetSystemMetrics(SM_CYSCREEN);
1248 }
1249 #endif  /* BUILD_WIN32 */
1250
1251 #if defined(__unix__) || defined(__APPLE__)
1252 #include <sys/time.h>
1253
1254 static long get_msec(void)
1255 {
1256         static struct timeval tv0;
1257         struct timeval tv;
1258
1259         gettimeofday(&tv, 0);
1260         if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
1261                 tv0 = tv;
1262                 return 0;
1263         }
1264         return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
1265 }
1266 #endif
1267 #ifdef _WIN32
1268 static long get_msec(void)
1269 {
1270         static long t0;
1271
1272         if(!t0) {
1273                 t0 = timeGetTime();
1274                 return 0;
1275         }
1276         return timeGetTime() - t0;
1277 }
1278 #endif
1279
1280 static void panic(const char *msg)
1281 {
1282         const char *end = msg;
1283         while(*end) end++;
1284         sys_write(2, msg, end - msg);
1285         sys_exit(1);
1286 }
1287
1288
1289 #ifdef MINIGLUT_USE_LIBC
1290 static void sys_exit(int status)
1291 {
1292         exit(status);
1293 }
1294
1295 static int sys_write(int fd, const void *buf, int count)
1296 {
1297         return write(fd, buf, count);
1298 }
1299
1300 #else   /* !MINIGLUT_USE_LIBC */
1301
1302 #if defined(__GNUC__) && defined(MINIGLUT_GCC_NO_BUILTIN)
1303 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1304 {
1305         asm volatile(
1306                 "flds %2\n\t"
1307                 "fsincos\n\t"
1308                 "fstps %1\n\t"
1309                 "fstps %0\n\t"
1310                 : "=m"(*sptr), "=m"(*cptr)
1311                 : "m"(angle)
1312         );
1313 }
1314
1315 static float mglut_atan(float x)
1316 {
1317         float res;
1318         asm volatile(
1319                 "flds %1\n\t"
1320                 "fld1\n\t"
1321                 "fpatan\n\t"
1322                 "fstps %0\n\t"
1323                 : "=m"(res)
1324                 : "m"(x)
1325         );
1326         return res;
1327 }
1328 #endif
1329
1330 #ifdef _MSC_VER
1331 static void mglut_sincosf(float angle, float *sptr, float *cptr)
1332 {
1333         float s, c;
1334         __asm {
1335                 fld angle
1336                 fsincos
1337                 fstp c
1338                 fstp s
1339         }
1340         *sptr = s;
1341         *cptr = c;
1342 }
1343
1344 static float mglut_atan(float x)
1345 {
1346         float res;
1347         __asm {
1348                 fld x
1349                 fld1
1350                 fpatan
1351                 fstp res
1352         }
1353         return res;
1354 }
1355 #endif
1356
1357 #ifdef __WATCOMC__
1358 #pragma aux mglut_sincosf = \
1359         "fsincos" \
1360         "fstp dword ptr [edx]" \
1361         "fstp dword ptr [eax]" \
1362         parm[8087][eax][edx]    \
1363         modify[8087];
1364
1365 #pragma aux mglut_atan = \
1366         "fld1" \
1367         "fpatan" \
1368         parm[8087] \
1369         value[8087] \
1370         modify [8087];
1371 #endif
1372
1373 #ifdef __linux__
1374
1375 #ifdef __x86_64__
1376 static void sys_exit(int status)
1377 {
1378         asm volatile(
1379                 "syscall\n\t"
1380                 :: "a"(60), "D"(status)
1381         );
1382 }
1383 static int sys_write(int fd, const void *buf, int count)
1384 {
1385         long res;
1386         asm volatile(
1387                 "syscall\n\t"
1388                 : "=a"(res)
1389                 : "a"(1), "D"(fd), "S"(buf), "d"(count)
1390         );
1391         return res;
1392 }
1393 #endif
1394 #ifdef __i386__
1395 static void sys_exit(int status)
1396 {
1397         asm volatile(
1398                 "mov $1, %%eax\n\t"
1399                 "int $0x80\n\t"
1400                 :: "b"(status)
1401                 : "eax"
1402         );
1403 }
1404 static int sys_write(int fd, const void *buf, int count)
1405 {
1406         int res;
1407         asm volatile(
1408                 "mov $4, %%eax\n\t"
1409                 "int $0x80\n\t"
1410                 :: "b"(fd), "c"(buf), "d"(count));
1411         return res;
1412 }
1413 #endif
1414
1415 #endif  /* __linux__ */
1416
1417 #ifdef _WIN32
1418 static void sys_exit(int status)
1419 {
1420         ExitProcess(status);
1421 }
1422 static int sys_write(int fd, const void *buf, int count)
1423 {
1424         int wrsz;
1425
1426         HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
1427         if(!WriteFile(out, buf, count, &wrsz, 0)) {
1428                 return -1;
1429         }
1430         return wrsz;
1431 }
1432 #endif  /* _WIN32 */
1433
1434 #endif  /* !MINIGLUT_USE_LIBC */