sgl

view src/wsys_x11.c @ 5:0570e27e5ebc

pretty much done with the basic functionality and GLX shit
author John Tsiombikas <nuclear@siggraph.org>
date Fri, 13 May 2011 07:49:47 +0300
parents 648f8604d2b2
children 0cb438c86b98
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;
143 if(!(wnode = malloc(sizeof *wnode))) {
144 return -1;
145 }
147 fill_attr(attr, flags);
149 if(!(vis = glXChooseVisual(dpy, scr, attr))) {
150 sgl_log("failed to find a suitable visual\n");
151 free(wnode);
152 return -1;
153 }
154 print_visual_info(vis);
156 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
157 sgl_log("failed to create OpenGL context\n");
158 XFree(vis);
159 free(wnode);
160 return -1;
161 }
163 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
164 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
165 attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
167 if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
168 vis->visual, attr_valid, &xattr))) {
169 sgl_log("failed to create window\n");
170 glXDestroyContext(dpy, ctx);
171 XFree(vis);
172 free(wnode);
173 return -1;
174 }
175 XFree(vis);
177 evmask = StructureNotifyMask | VisibilityChangeMask;
178 XSelectInput(dpy, win, evmask);
180 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
182 set_title("OpenGL/X11");
184 chint.res_name = chint.res_class = "simplygl";
185 XSetClassHint(dpy, win, &chint);
187 XMapWindow(dpy, win);
189 wnode->win = win;
190 wnode->ctx = ctx;
191 wnode->width = xsz;
192 wnode->height = ysz;
193 wnode->mapped = 0;
194 wnode->evmask = evmask;
195 wnode->redisp_pending = 1;
196 wnode->next = winlist;
197 winlist = wnode;
199 if(!active_win) {
200 set_active(win);
201 }
202 return win;
203 }
205 static void fill_attr(int *attr, unsigned int flags)
206 {
207 int i = 0;
209 attr[i++] = GLX_RGBA;
210 attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
211 attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
212 attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
214 if(flags & SGL_DOUBLE) {
215 attr[i++] = GLX_DOUBLEBUFFER;
216 }
217 if(flags & SGL_DEPTH) {
218 attr[i++] = GLX_DEPTH_SIZE;
219 attr[i++] = 1;
220 }
221 if(flags & SGL_STENCIL) {
222 attr[i++] = GLX_STENCIL_SIZE;
223 attr[i++] = 1;
224 }
225 if(flags & SGL_STEREO) {
226 attr[i++] = GLX_STEREO;
227 }
228 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
229 if(flags & SGL_MULTISAMPLE) {
230 attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
231 attr[i++] = 1;
232 attr[i++] = GLX_SAMPLES_ARB;
233 attr[i++] = 1;
234 }
235 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
236 attr[i] = None;
237 }
239 static void print_visual_info(XVisualInfo *vis)
240 {
241 int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
243 glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
244 glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
245 glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
246 glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
247 glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
248 glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
249 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
250 glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
251 if(aa) {
252 glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
253 } else {
254 samples = 1;
255 }
256 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
258 sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
259 sgl_log(" %s", stereo ? "stereo" : "mono");
260 sgl_log(" samples/pixel: %d\n", samples);
261 }
263 static void close_window(int id)
264 {
265 struct window dummy, *win, *prev;
266 dummy.next = winlist;
268 prev = &dummy;
269 win = prev->next;
271 while(win) {
272 if(win->win == id) {
273 glXDestroyContext(dpy, win->ctx);
274 XDestroyWindow(dpy, win->win);
275 prev->next = win->next;
276 free(win);
277 return;
278 }
279 win = win->next;
280 }
281 }
283 static int set_active(int id)
284 {
285 struct window *win = find_window(id);
286 if(!win) {
287 sgl_log("no such window: %d\n", id);
288 return -1;
289 }
290 /* only the user calls this, so don't revert this selection */
291 prev_active = win;
292 return activate_window(win);
293 }
295 static struct window *find_window(int id)
296 {
297 struct window *win = winlist;
299 while(win) {
300 if(win->win == id) {
301 return win;
302 }
303 win = win->next;
304 }
305 return 0;
306 }
308 static int activate_window(struct window *win)
309 {
310 if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
311 sgl_log("failed to activate window %d\n", (int)win->win);
312 return -1;
313 }
314 active_win = win;
315 return 0;
316 }
318 static int set_title(const char *str)
319 {
320 XTextProperty wm_name;
322 if(!str || !active_win) {
323 return -1;
324 }
325 XStringListToTextProperty((char**)&str, 1, &wm_name);
326 XSetWMName(dpy, active_win->win, &wm_name);
327 XSetWMIconName(dpy, active_win->win, &wm_name);
328 XFree(wm_name.value);
329 return 0;
330 }
332 static void redisplay(void)
333 {
334 active_win->redisp_pending = 1;
335 }
337 static void swap_buffers(void)
338 {
339 glXSwapBuffers(dpy, active_win->win);
340 }
342 static void set_bits(long *mask, long bits)
343 {
344 *mask |= bits;
345 }
347 static void clear_bits(long *mask, long bits)
348 {
349 *mask &= ~bits;
350 }
352 static void set_event(int idx, int enable)
353 {
354 void (*op)(long*, long);
355 op = enable ? set_bits : clear_bits;
357 switch(idx) {
358 case SGL_DISPLAY:
359 op(&active_win->evmask, ExposureMask);
360 break;
362 case SGL_KEYBOARD:
363 op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
364 break;
366 case SGL_MOUSE:
367 op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
368 break;
370 case SGL_MOTION:
371 op(&active_win->evmask, ButtonMotionMask);
372 break;
374 case SGL_PASSIVE:
375 op(&active_win->evmask, PointerMotionMask);
376 break;
378 default:
379 return;
380 }
382 XSelectInput(dpy, active_win->win, active_win->evmask);
383 }
385 static int process_events(void)
386 {
387 XEvent xev;
388 void (*func)();
389 struct window *win;
391 prev_active = active_win;
393 win = winlist;
394 while(win) {
395 if(win->redisp_pending && (func = sgl_get_callback(SGL_DISPLAY))) {
396 activate_window(win);
397 func();
398 win->redisp_pending = 0;
399 }
400 win = win->next;
401 }
403 func = sgl_get_callback(SGL_IDLE);
404 if(!func) {
405 XNextEvent(dpy, &xev);
406 XPutBackEvent(dpy, &xev);
407 }
409 /* process all pending events... */
410 while(XPending(dpy)) {
411 XNextEvent(dpy, &xev);
412 if(handle_event(&xev) == -1) {
413 return -1;
414 }
416 if(!dpy) {
417 return -1;
418 }
419 }
421 if(func) {
422 /* ... and then call the idle function */
423 func();
424 }
426 activate_window(prev_active);
427 return 0;
428 }
430 /* returns 0, or -1 when the last window gets closed */
431 static int handle_event(XEvent *xev)
432 {
433 int state;
434 struct window *win;
435 void (*func)();
437 if((win = find_window(xev->xany.window))) {
438 activate_window(win);
439 } else {
440 return 0;
441 }
443 switch(xev->type) {
444 case MapNotify:
445 active_win->mapped = 1;
446 break;
448 case UnmapNotify:
449 active_win->mapped = 0;
450 break;
452 case Expose:
453 if(active_win->mapped && xev->xexpose.count == 0) {
454 if((func = sgl_get_callback(SGL_DISPLAY))) {
455 func();
456 active_win->redisp_pending = 0;
457 }
458 }
459 break;
461 case MotionNotify:
462 if(xev->xmotion.state) {
463 func = sgl_get_callback(SGL_MOTION);
464 } else {
465 func = sgl_get_callback(SGL_PASSIVE);
466 }
467 if(func) {
468 func(xev->xmotion.x, xev->xmotion.y);
469 }
470 break;
472 case ButtonPress:
473 if(1) {
474 state = 1;
475 } else {
476 case ButtonRelease:
477 state = 0;
478 }
479 if((func = sgl_get_callback(SGL_MOUSE))) {
480 int bn = xev->xbutton.button - 1;
481 func(bn, state, xev->xbutton.x, xev->xbutton.y);
482 }
483 break;
485 case KeyPress:
486 if(1) {
487 state = 1;
488 } else {
489 case KeyRelease:
490 state = 0;
491 }
492 if((func = sgl_get_callback(SGL_KEYBOARD))) {
493 KeySym sym = XLookupKeysym(&xev->xkey, 0);
494 func(translate_keysym(sym), state);
495 }
496 break;
498 case ConfigureNotify:
499 if((func = sgl_get_callback(SGL_RESHAPE))) {
500 if(xev->xconfigure.width != active_win->width || xev->xconfigure.height != active_win->height) {
501 active_win->width = xev->xconfigure.width;
502 active_win->height = xev->xconfigure.height;
504 func(xev->xconfigure.width, xev->xconfigure.height);
505 }
506 }
507 break;
509 case ClientMessage:
510 if(xev->xclient.message_type == xa_wm_prot) {
511 if(xev->xclient.data.l[0] == xa_wm_del_win) {
512 close_window(active_win->win);
513 if(!active_win) {
514 return -1;
515 }
516 }
517 }
518 break;
520 default:
521 break;
522 }
524 return 0;
525 }
527 static int translate_keysym(KeySym sym)
528 {
529 if(sym < 256) {
530 return sym;
531 }
533 switch(sym) {
534 case XK_BackSpace:
535 return '\b';
536 case XK_Tab:
537 return '\t';
538 case XK_Linefeed:
539 return '\r';
540 case XK_Return:
541 return '\n';
542 case XK_Escape:
543 return 27;
544 default:
545 break;
546 }
547 return (int)sym;
548 }