sgl

view src/wsys_x11.c @ 19:12ce0cef7ebf

foo
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 26 Jun 2011 02:30:37 +0300
parents 5efd62ff354a
children 5d38efd33da0
line source
1 /* SimplyGL window system module for X11/GLX */
2 /* link-with: -lX11 */
4 #include "config.h"
6 #ifdef USE_WSYS_MODULE_X11
8 #include <stdlib.h>
9 #include <ctype.h>
10 #include <X11/Xlib.h>
11 #include <GL/glx.h>
12 #include "sgl.h"
13 #include "wsys.h"
14 #include "log.h"
17 struct window {
18 Window win;
19 GLXContext ctx;
20 int width, height;
21 int mapped;
22 long evmask;
23 int redisp_pending;
24 struct window *next;
25 };
27 static int init(void);
28 static void shutdown(void);
30 /* video mode switching */
31 static int set_vidmode(int xsz, int ysz);
32 static int get_vidmode(int *xsz, int *ysz);
34 /* create/destroy windows */
35 static int create_window(int xsz, int ysz, unsigned int flags);
36 static void fill_attr(int *attr, unsigned int flags);
37 static void print_visual_info(XVisualInfo *vis);
38 static void close_window(int id);
40 /* window management */
41 static int set_active(int id);
42 static struct window *find_window(int id);
43 static int activate_window(struct window *win);
44 static int set_title(const char *str);
45 static void redisplay(void);
46 static void swap_buffers(void);
48 static int get_modifiers(void);
50 /* event handling and friends */
51 static void set_bits(long *mask, long bits);
52 static void clear_bits(long *mask, long bits);
53 static void set_event(int idx, int enable);
54 static int process_events(void);
55 static int handle_event(XEvent *xev);
56 static void process_key(KeySym sym, int state);
57 static int translate_keysym(KeySym sym);
59 static struct wsys_module ws = {
60 "x11-glx", 0,
61 init,
62 shutdown,
63 set_vidmode,
64 get_vidmode,
65 create_window,
66 close_window,
67 set_active,
68 set_title,
69 redisplay,
70 swap_buffers,
71 get_modifiers,
72 set_event,
73 process_events,
74 0
75 };
77 static Display *dpy;
78 static Window root;
79 static int scr;
80 static Atom xa_wm_prot, xa_wm_del_win;
81 static struct window *winlist;
82 static struct window *active_win, *prev_active;
83 static int modkeys;
85 /* this is the only exported function, everything else should be static */
86 void sgl_register_x11(void)
87 {
88 sgl_register_module(&ws);
89 }
91 static int init(void)
92 {
93 if(dpy) {
94 sgl_log("warning: double init\n");
95 return 0;
96 }
98 winlist = 0;
99 active_win = prev_active = 0;
101 if(!(dpy = XOpenDisplay(0))) {
102 sgl_log("failed to open X display: %s\n", XDisplayName(0));
103 return -1;
104 }
105 scr = DefaultScreen(dpy);
106 root = RootWindow(dpy, scr);
108 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
109 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
111 return 0;
112 }
114 static void shutdown(void)
115 {
116 if(!dpy) {
117 return;
118 }
120 while(winlist) {
121 struct window *win = winlist;
122 winlist = winlist->next;
124 glXDestroyContext(dpy, win->ctx);
125 XDestroyWindow(dpy, win->win);
126 free(win);
127 }
128 XCloseDisplay(dpy);
129 dpy = 0;
130 }
132 static int set_vidmode(int xsz, int ysz)
133 {
134 /* TODO */
135 return 0;
136 }
138 static int get_vidmode(int *xsz, int *ysz)
139 {
140 /* TODO */
141 return 0;
142 }
144 static int create_window(int xsz, int ysz, unsigned int flags)
145 {
146 int attr[32];
147 Window win;
148 GLXContext ctx;
149 XVisualInfo *vis;
150 XClassHint chint;
151 XSetWindowAttributes xattr;
152 unsigned int attr_valid;
153 long evmask;
154 struct window *wnode;
155 void (*func)();
157 if(!(wnode = malloc(sizeof *wnode))) {
158 return -1;
159 }
161 fill_attr(attr, flags);
163 if(!(vis = glXChooseVisual(dpy, scr, attr))) {
164 sgl_log("failed to find a suitable visual\n");
165 free(wnode);
166 return -1;
167 }
168 print_visual_info(vis);
170 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
171 sgl_log("failed to create OpenGL context\n");
172 XFree(vis);
173 free(wnode);
174 return -1;
175 }
177 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
178 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
179 attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
181 if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
182 vis->visual, attr_valid, &xattr))) {
183 sgl_log("failed to create window\n");
184 glXDestroyContext(dpy, ctx);
185 XFree(vis);
186 free(wnode);
187 return -1;
188 }
189 XFree(vis);
191 evmask = StructureNotifyMask | VisibilityChangeMask;
192 XSelectInput(dpy, win, evmask);
194 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
196 set_title("OpenGL/X11");
198 chint.res_name = chint.res_class = "simplygl";
199 XSetClassHint(dpy, win, &chint);
201 XMapWindow(dpy, win);
203 wnode->win = win;
204 wnode->ctx = ctx;
205 wnode->width = xsz;
206 wnode->height = ysz;
207 wnode->mapped = 0;
208 wnode->evmask = evmask;
209 wnode->redisp_pending = 1;
210 wnode->next = winlist;
211 winlist = wnode;
213 if(!active_win) {
214 set_active(win);
215 } else {
216 activate_window(wnode);
217 }
219 if((func = sgl_get_callback(SGL_CREATE))) {
220 func(win);
221 }
222 if((func = sgl_get_callback(SGL_RESHAPE))) {
223 func(xsz, ysz);
224 }
225 activate_window(prev_active);
226 return win;
227 }
229 static void fill_attr(int *attr, unsigned int flags)
230 {
231 int i = 0;
233 attr[i++] = GLX_RGBA;
234 attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
235 attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
236 attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
238 if(flags & SGL_DOUBLE) {
239 attr[i++] = GLX_DOUBLEBUFFER;
240 }
241 if(flags & SGL_DEPTH) {
242 attr[i++] = GLX_DEPTH_SIZE;
243 attr[i++] = 1;
244 }
245 if(flags & SGL_STENCIL) {
246 attr[i++] = GLX_STENCIL_SIZE;
247 attr[i++] = 1;
248 }
249 if(flags & SGL_STEREO) {
250 attr[i++] = GLX_STEREO;
251 }
252 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
253 if(flags & SGL_MULTISAMPLE) {
254 attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
255 attr[i++] = 1;
256 attr[i++] = GLX_SAMPLES_ARB;
257 attr[i++] = 1;
258 }
259 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
260 attr[i] = None;
261 }
263 static void print_visual_info(XVisualInfo *vis)
264 {
265 int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
267 glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
268 glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
269 glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
270 glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
271 glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
272 glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
273 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
274 glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
275 if(aa) {
276 glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
277 } else {
278 samples = 1;
279 }
280 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
282 sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
283 sgl_log(" %s", stereo ? "stereo" : "mono");
284 sgl_log(" samples/pixel: %d\n", samples);
285 }
287 static void close_window(int id)
288 {
289 struct window dummy, *win, *prev;
290 sgl_close_callback_t close_func;
292 dummy.next = winlist;
294 prev = &dummy;
295 win = prev->next;
297 while(win) {
298 if(win->win == id) {
299 if(!(close_func = sgl_get_callback(SGL_CLOSE))) {
300 close_func(id);
301 }
302 glXDestroyContext(dpy, win->ctx);
303 XDestroyWindow(dpy, win->win);
304 prev->next = win->next;
305 free(win);
306 return;
307 }
308 prev = win;
309 win = win->next;
310 }
311 }
313 static int set_active(int id)
314 {
315 struct window *win = find_window(id);
316 if(!win) {
317 sgl_log("no such window: %d\n", id);
318 return -1;
319 }
320 /* only the user calls this, so don't revert this selection */
321 prev_active = win;
322 return activate_window(win);
323 }
325 static struct window *find_window(int id)
326 {
327 struct window *win = winlist;
329 while(win) {
330 if(win->win == id) {
331 return win;
332 }
333 win = win->next;
334 }
335 return 0;
336 }
338 static int activate_window(struct window *win)
339 {
340 if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
341 sgl_log("failed to activate window %d\n", (int)win->win);
342 return -1;
343 }
344 active_win = win;
345 return 0;
346 }
348 static int set_title(const char *str)
349 {
350 XTextProperty wm_name;
352 if(!str || !active_win) {
353 return -1;
354 }
355 XStringListToTextProperty((char**)&str, 1, &wm_name);
356 XSetWMName(dpy, active_win->win, &wm_name);
357 XSetWMIconName(dpy, active_win->win, &wm_name);
358 XFree(wm_name.value);
359 return 0;
360 }
362 static void redisplay(void)
363 {
364 active_win->redisp_pending = 1;
365 }
367 static void swap_buffers(void)
368 {
369 glXSwapBuffers(dpy, active_win->win);
370 }
372 static int get_modifiers(void)
373 {
374 return modkeys;
375 }
377 static void set_bits(long *mask, long bits)
378 {
379 *mask |= bits;
380 }
382 static void clear_bits(long *mask, long bits)
383 {
384 *mask &= ~bits;
385 }
387 static void set_event(int idx, int enable)
388 {
389 void (*op)(long*, long);
390 op = enable ? set_bits : clear_bits;
392 switch(idx) {
393 case SGL_DISPLAY:
394 op(&active_win->evmask, ExposureMask);
395 break;
397 case SGL_KEYBOARD:
398 op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
399 break;
401 case SGL_MOUSE:
402 op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
403 break;
405 case SGL_MOTION:
406 op(&active_win->evmask, ButtonMotionMask);
407 break;
409 case SGL_PASSIVE:
410 op(&active_win->evmask, PointerMotionMask);
411 break;
413 default:
414 return;
415 }
417 XSelectInput(dpy, active_win->win, active_win->evmask);
418 }
420 static int process_events(void)
421 {
422 XEvent xev;
423 void (*func)();
424 struct window *win;
426 prev_active = active_win;
428 win = winlist;
429 while(win) {
430 if(win->redisp_pending && (func = sgl_get_callback(SGL_DISPLAY))) {
431 activate_window(win);
432 func();
433 win->redisp_pending = 0;
434 }
435 win = win->next;
436 }
438 func = sgl_get_callback(SGL_IDLE);
439 if(!func) {
440 XNextEvent(dpy, &xev);
441 XPutBackEvent(dpy, &xev);
442 }
444 /* process all pending events... */
445 while(XPending(dpy)) {
446 XNextEvent(dpy, &xev);
447 if(handle_event(&xev) == -1) {
448 return -1;
449 }
451 if(!dpy) {
452 return -1;
453 }
454 }
456 if(func) {
457 /* ... and then call the idle function */
458 func();
459 }
461 activate_window(prev_active);
462 return 0;
463 }
465 /* returns 0, or -1 when the last window gets closed */
466 static int handle_event(XEvent *xev)
467 {
468 int state;
469 struct window *win;
470 void (*func)();
471 KeySym sym;
473 if((win = find_window(xev->xany.window))) {
474 activate_window(win);
475 } else {
476 return 0;
477 }
479 switch(xev->type) {
480 case MapNotify:
481 active_win->mapped = 1;
482 break;
484 case UnmapNotify:
485 active_win->mapped = 0;
486 break;
488 case Expose:
489 if(active_win->mapped && xev->xexpose.count == 0) {
490 if((func = sgl_get_callback(SGL_DISPLAY))) {
491 func();
492 active_win->redisp_pending = 0;
493 }
494 }
495 break;
497 case MotionNotify:
498 if(xev->xmotion.state) {
499 func = sgl_get_callback(SGL_MOTION);
500 } else {
501 func = sgl_get_callback(SGL_PASSIVE);
502 }
503 if(func) {
504 func(0, xev->xmotion.x, xev->xmotion.y);
505 }
506 break;
508 case ButtonPress:
509 if(1) {
510 state = 1;
511 } else {
512 case ButtonRelease:
513 state = 0;
514 }
515 if((func = sgl_get_callback(SGL_MOUSE))) {
516 int bn = xev->xbutton.button - 1;
517 func(0, bn, state, xev->xbutton.x, xev->xbutton.y);
518 }
519 break;
521 case KeyPress:
522 if(1) {
523 state = 1;
524 } else {
525 case KeyRelease:
526 state = 0;
527 }
528 sym = XLookupKeysym(&xev->xkey, 0);
529 process_key(sym, state);
531 if((func = sgl_get_callback(SGL_KEYBOARD))) {
532 func(translate_keysym(sym), state);
533 }
534 break;
536 case ConfigureNotify:
537 if((func = sgl_get_callback(SGL_RESHAPE))) {
538 if(xev->xconfigure.width != active_win->width || xev->xconfigure.height != active_win->height) {
539 active_win->width = xev->xconfigure.width;
540 active_win->height = xev->xconfigure.height;
542 func(xev->xconfigure.width, xev->xconfigure.height);
543 }
544 }
545 break;
547 case ClientMessage:
548 if(xev->xclient.message_type == xa_wm_prot) {
549 if(xev->xclient.data.l[0] == xa_wm_del_win) {
550 close_window(active_win->win);
551 if(!active_win) {
552 return -1;
553 }
554 }
555 }
556 break;
558 default:
559 break;
560 }
562 return 0;
563 }
565 static void process_key(KeySym sym, int state)
566 {
567 switch(sym) {
568 case XK_Shift_L:
569 case XK_Shift_R:
570 modkeys = state ? (modkeys | SGL_MOD_SHIFT) : (modkeys & ~SGL_MOD_SHIFT);
571 break;
573 case XK_Control_L:
574 case XK_Control_R:
575 modkeys = state ? (modkeys | SGL_MOD_CONTROL) : (modkeys & ~SGL_MOD_CONTROL);
576 break;
578 case XK_Alt_L:
579 case XK_Alt_R:
580 modkeys = state ? (modkeys | SGL_MOD_ALT) : (modkeys & ~SGL_MOD_ALT);
581 break;
583 default:
584 break;
585 }
586 }
588 static int translate_keysym(KeySym sym)
589 {
590 if(sym < 256) {
591 if(isalpha(sym) && (modkeys & SGL_MOD_SHIFT)) {
592 sym = toupper(sym);
593 }
594 return sym;
595 }
597 switch(sym) {
598 case XK_BackSpace:
599 return '\b';
600 case XK_Tab:
601 return '\t';
602 case XK_Linefeed:
603 return '\r';
604 case XK_Return:
605 return '\n';
606 case XK_Escape:
607 return 27;
608 default:
609 break;
610 }
611 return (int)sym;
612 }
614 #else
615 int sgl_wsys_x11_silence_the_fucking_empty_file_warnings;
616 #endif /* USE_WSYS_MODULE_X11 */