sgl

view src/wsys_x11.c @ 6:0cb438c86b98

X11 sounds about ready
author John Tsiombikas <nuclear@siggraph.org>
date Fri, 13 May 2011 09:44:21 +0300
parents 0570e27e5ebc
children edbfc96fe80d
line source
1 #include <stdlib.h>
2 #include <X11/Xlib.h>
3 #include <GL/glx.h>
4 #include "sgl.h"
5 #include "wsys.h"
6 #include "log.h"
9 struct window {
10 Window win;
11 GLXContext ctx;
12 int width, height;
13 int mapped;
14 long evmask;
15 int redisp_pending;
16 struct window *next;
17 };
19 static int init(void);
20 static void shutdown(void);
22 /* video mode switching */
23 static int set_vidmode(int xsz, int ysz);
24 static int get_vidmode(int *xsz, int *ysz);
26 /* create/destroy windows */
27 static int create_window(int xsz, int ysz, unsigned int flags);
28 static void fill_attr(int *attr, unsigned int flags);
29 static void print_visual_info(XVisualInfo *vis);
30 static void close_window(int id);
32 /* window management */
33 static int set_active(int id);
34 static struct window *find_window(int id);
35 static int activate_window(struct window *win);
36 static int set_title(const char *str);
37 static void redisplay(void);
38 static void swap_buffers(void);
40 /* event handling and friends */
41 static void set_bits(long *mask, long bits);
42 static void clear_bits(long *mask, long bits);
43 static void set_event(int idx, int enable);
44 static int process_events(void);
45 static int handle_event(XEvent *xev);
46 static int translate_keysym(KeySym sym);
48 static struct wsys_module ws = {
49 "x11-glx", 0,
50 init,
51 shutdown,
52 set_vidmode,
53 get_vidmode,
54 create_window,
55 close_window,
56 set_active,
57 set_title,
58 redisplay,
59 swap_buffers,
60 set_event,
61 process_events,
62 0
63 };
65 static Display *dpy;
66 static Window root;
67 static int scr;
68 static Atom xa_wm_prot, xa_wm_del_win;
69 static struct window *winlist;
70 static struct window *active_win, *prev_active;
72 /* this is the only exported function, everything else should be static */
73 void sgl_register_x11(void)
74 {
75 sgl_register_module(&ws);
76 }
78 static int init(void)
79 {
80 if(dpy) {
81 sgl_log("warning: double init\n");
82 return 0;
83 }
85 winlist = 0;
86 active_win = prev_active = 0;
88 if(!(dpy = XOpenDisplay(0))) {
89 sgl_log("failed to open X display: %s\n", XDisplayName(0));
90 return -1;
91 }
92 scr = DefaultScreen(dpy);
93 root = RootWindow(dpy, scr);
95 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
96 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
98 return 0;
99 }
101 static void shutdown(void)
102 {
103 if(!dpy) {
104 return;
105 }
107 while(winlist) {
108 struct window *win = winlist;
109 winlist = winlist->next;
111 glXDestroyContext(dpy, win->ctx);
112 XDestroyWindow(dpy, win->win);
113 free(win);
114 }
115 XCloseDisplay(dpy);
116 dpy = 0;
117 }
119 static int set_vidmode(int xsz, int ysz)
120 {
121 /* TODO */
122 return 0;
123 }
125 static int get_vidmode(int *xsz, int *ysz)
126 {
127 /* TODO */
128 return 0;
129 }
131 static int create_window(int xsz, int ysz, unsigned int flags)
132 {
133 int attr[32];
134 Window win;
135 GLXContext ctx;
136 XVisualInfo *vis;
137 XClassHint chint;
138 XSetWindowAttributes xattr;
139 unsigned int attr_valid;
140 long evmask;
141 struct window *wnode;
142 void (*func)();
144 if(!(wnode = malloc(sizeof *wnode))) {
145 return -1;
146 }
148 fill_attr(attr, flags);
150 if(!(vis = glXChooseVisual(dpy, scr, attr))) {
151 sgl_log("failed to find a suitable visual\n");
152 free(wnode);
153 return -1;
154 }
155 print_visual_info(vis);
157 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
158 sgl_log("failed to create OpenGL context\n");
159 XFree(vis);
160 free(wnode);
161 return -1;
162 }
164 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
165 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
166 attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
168 if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
169 vis->visual, attr_valid, &xattr))) {
170 sgl_log("failed to create window\n");
171 glXDestroyContext(dpy, ctx);
172 XFree(vis);
173 free(wnode);
174 return -1;
175 }
176 XFree(vis);
178 evmask = StructureNotifyMask | VisibilityChangeMask;
179 XSelectInput(dpy, win, evmask);
181 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
183 set_title("OpenGL/X11");
185 chint.res_name = chint.res_class = "simplygl";
186 XSetClassHint(dpy, win, &chint);
188 XMapWindow(dpy, win);
190 wnode->win = win;
191 wnode->ctx = ctx;
192 wnode->width = xsz;
193 wnode->height = ysz;
194 wnode->mapped = 0;
195 wnode->evmask = evmask;
196 wnode->redisp_pending = 1;
197 wnode->next = winlist;
198 winlist = wnode;
200 if(!active_win) {
201 set_active(win);
202 } else {
203 activate_window(wnode);
204 }
206 if((func = sgl_get_callback(SGL_CREATE))) {
207 func(win);
208 }
209 if((func = sgl_get_callback(SGL_RESHAPE))) {
210 func(xsz, ysz);
211 }
212 activate_window(prev_active);
213 return win;
214 }
216 static void fill_attr(int *attr, unsigned int flags)
217 {
218 int i = 0;
220 attr[i++] = GLX_RGBA;
221 attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
222 attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
223 attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
225 if(flags & SGL_DOUBLE) {
226 attr[i++] = GLX_DOUBLEBUFFER;
227 }
228 if(flags & SGL_DEPTH) {
229 attr[i++] = GLX_DEPTH_SIZE;
230 attr[i++] = 1;
231 }
232 if(flags & SGL_STENCIL) {
233 attr[i++] = GLX_STENCIL_SIZE;
234 attr[i++] = 1;
235 }
236 if(flags & SGL_STEREO) {
237 attr[i++] = GLX_STEREO;
238 }
239 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
240 if(flags & SGL_MULTISAMPLE) {
241 attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
242 attr[i++] = 1;
243 attr[i++] = GLX_SAMPLES_ARB;
244 attr[i++] = 1;
245 }
246 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
247 attr[i] = None;
248 }
250 static void print_visual_info(XVisualInfo *vis)
251 {
252 int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
254 glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
255 glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
256 glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
257 glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
258 glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
259 glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
260 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
261 glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
262 if(aa) {
263 glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
264 } else {
265 samples = 1;
266 }
267 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
269 sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
270 sgl_log(" %s", stereo ? "stereo" : "mono");
271 sgl_log(" samples/pixel: %d\n", samples);
272 }
274 static void close_window(int id)
275 {
276 struct window dummy, *win, *prev;
277 sgl_close_callback_t close_func;
279 dummy.next = winlist;
281 prev = &dummy;
282 win = prev->next;
284 while(win) {
285 if(win->win == id) {
286 if(!(close_func = sgl_get_callback(SGL_CLOSE))) {
287 close_func(id);
288 }
289 glXDestroyContext(dpy, win->ctx);
290 XDestroyWindow(dpy, win->win);
291 prev->next = win->next;
292 free(win);
293 return;
294 }
295 win = win->next;
296 }
297 }
299 static int set_active(int id)
300 {
301 struct window *win = find_window(id);
302 if(!win) {
303 sgl_log("no such window: %d\n", id);
304 return -1;
305 }
306 /* only the user calls this, so don't revert this selection */
307 prev_active = win;
308 return activate_window(win);
309 }
311 static struct window *find_window(int id)
312 {
313 struct window *win = winlist;
315 while(win) {
316 if(win->win == id) {
317 return win;
318 }
319 win = win->next;
320 }
321 return 0;
322 }
324 static int activate_window(struct window *win)
325 {
326 if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
327 sgl_log("failed to activate window %d\n", (int)win->win);
328 return -1;
329 }
330 active_win = win;
331 return 0;
332 }
334 static int set_title(const char *str)
335 {
336 XTextProperty wm_name;
338 if(!str || !active_win) {
339 return -1;
340 }
341 XStringListToTextProperty((char**)&str, 1, &wm_name);
342 XSetWMName(dpy, active_win->win, &wm_name);
343 XSetWMIconName(dpy, active_win->win, &wm_name);
344 XFree(wm_name.value);
345 return 0;
346 }
348 static void redisplay(void)
349 {
350 active_win->redisp_pending = 1;
351 }
353 static void swap_buffers(void)
354 {
355 glXSwapBuffers(dpy, active_win->win);
356 }
358 static void set_bits(long *mask, long bits)
359 {
360 *mask |= bits;
361 }
363 static void clear_bits(long *mask, long bits)
364 {
365 *mask &= ~bits;
366 }
368 static void set_event(int idx, int enable)
369 {
370 void (*op)(long*, long);
371 op = enable ? set_bits : clear_bits;
373 switch(idx) {
374 case SGL_DISPLAY:
375 op(&active_win->evmask, ExposureMask);
376 break;
378 case SGL_KEYBOARD:
379 op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
380 break;
382 case SGL_MOUSE:
383 op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
384 break;
386 case SGL_MOTION:
387 op(&active_win->evmask, ButtonMotionMask);
388 break;
390 case SGL_PASSIVE:
391 op(&active_win->evmask, PointerMotionMask);
392 break;
394 default:
395 return;
396 }
398 XSelectInput(dpy, active_win->win, active_win->evmask);
399 }
401 static int process_events(void)
402 {
403 XEvent xev;
404 void (*func)();
405 struct window *win;
407 prev_active = active_win;
409 win = winlist;
410 while(win) {
411 if(win->redisp_pending && (func = sgl_get_callback(SGL_DISPLAY))) {
412 activate_window(win);
413 func();
414 win->redisp_pending = 0;
415 }
416 win = win->next;
417 }
419 func = sgl_get_callback(SGL_IDLE);
420 if(!func) {
421 XNextEvent(dpy, &xev);
422 XPutBackEvent(dpy, &xev);
423 }
425 /* process all pending events... */
426 while(XPending(dpy)) {
427 XNextEvent(dpy, &xev);
428 if(handle_event(&xev) == -1) {
429 return -1;
430 }
432 if(!dpy) {
433 return -1;
434 }
435 }
437 if(func) {
438 /* ... and then call the idle function */
439 func();
440 }
442 activate_window(prev_active);
443 return 0;
444 }
446 /* returns 0, or -1 when the last window gets closed */
447 static int handle_event(XEvent *xev)
448 {
449 int state;
450 struct window *win;
451 void (*func)();
453 if((win = find_window(xev->xany.window))) {
454 activate_window(win);
455 } else {
456 return 0;
457 }
459 switch(xev->type) {
460 case MapNotify:
461 active_win->mapped = 1;
462 break;
464 case UnmapNotify:
465 active_win->mapped = 0;
466 break;
468 case Expose:
469 if(active_win->mapped && xev->xexpose.count == 0) {
470 if((func = sgl_get_callback(SGL_DISPLAY))) {
471 func();
472 active_win->redisp_pending = 0;
473 }
474 }
475 break;
477 case MotionNotify:
478 if(xev->xmotion.state) {
479 func = sgl_get_callback(SGL_MOTION);
480 } else {
481 func = sgl_get_callback(SGL_PASSIVE);
482 }
483 if(func) {
484 func(0, xev->xmotion.x, xev->xmotion.y);
485 }
486 break;
488 case ButtonPress:
489 if(1) {
490 state = 1;
491 } else {
492 case ButtonRelease:
493 state = 0;
494 }
495 if((func = sgl_get_callback(SGL_MOUSE))) {
496 int bn = xev->xbutton.button - 1;
497 func(0, bn, state, xev->xbutton.x, xev->xbutton.y);
498 }
499 break;
501 case KeyPress:
502 if(1) {
503 state = 1;
504 } else {
505 case KeyRelease:
506 state = 0;
507 }
508 if((func = sgl_get_callback(SGL_KEYBOARD))) {
509 KeySym sym = XLookupKeysym(&xev->xkey, 0);
510 func(translate_keysym(sym), state);
511 }
512 break;
514 case ConfigureNotify:
515 if((func = sgl_get_callback(SGL_RESHAPE))) {
516 if(xev->xconfigure.width != active_win->width || xev->xconfigure.height != active_win->height) {
517 active_win->width = xev->xconfigure.width;
518 active_win->height = xev->xconfigure.height;
520 func(xev->xconfigure.width, xev->xconfigure.height);
521 }
522 }
523 break;
525 case ClientMessage:
526 if(xev->xclient.message_type == xa_wm_prot) {
527 if(xev->xclient.data.l[0] == xa_wm_del_win) {
528 close_window(active_win->win);
529 if(!active_win) {
530 return -1;
531 }
532 }
533 }
534 break;
536 default:
537 break;
538 }
540 return 0;
541 }
543 static int translate_keysym(KeySym sym)
544 {
545 if(sym < 256) {
546 return sym;
547 }
549 switch(sym) {
550 case XK_BackSpace:
551 return '\b';
552 case XK_Tab:
553 return '\t';
554 case XK_Linefeed:
555 return '\r';
556 case XK_Return:
557 return '\n';
558 case XK_Escape:
559 return 27;
560 default:
561 break;
562 }
563 return (int)sym;
564 }