sgl

view src/wsys_x11.c @ 4:648f8604d2b2

cont. x11 module
author John Tsiombikas <nuclear@siggraph.org>
date Thu, 12 May 2011 11:04:10 +0300
parents 1b6c5dadb460
children 0570e27e5ebc
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"
8 struct window {
9 Window win;
10 GLXContext ctx;
11 long evmask;
12 struct window *next;
13 };
15 static int init(void);
16 static void shutdown(void);
18 /* video mode switching */
19 static int set_vidmode(int xsz, int ysz);
20 static int get_vidmode(int *xsz, int *ysz);
22 /* create/destroy windows */
23 static int create_window(int xsz, int ysz, unsigned int flags);
24 static void fill_attr(int *attr, unsigned int flags);
25 static void print_visual_info(XVisualInfo *vis);
26 static void close_window(int id);
28 /* window management */
29 static int set_active(int id);
30 static int set_title(const char *str);
31 static void redisplay(void);
32 static void swap_buffers(void);
34 /* event handling and friends */
35 static void set_bits(long *mask, long bits);
36 static void clear_bits(long *mask, long bits);
37 static void set_event(int idx, int enable);
38 static int process_events(void);
41 static struct wsys_module ws = {
42 "x11-glx", 0,
43 init,
44 shutdown,
45 set_vidmode,
46 get_vidmode,
47 create_window,
48 close_window,
49 set_active,
50 set_title,
51 redisplay,
52 swap_buffers,
53 set_event,
54 process_events,
55 0
56 };
58 static Display *dpy;
59 static Window root;
60 static int scr;
61 static Atom xa_wm_prot, xa_wm_del_win;
62 static struct window *winlist;
63 static struct window *active_win;
65 /* this is the only exported function, everything else should be static */
66 void sgl_register_x11(void)
67 {
68 sgl_register_module(&ws);
69 }
71 static int init(void)
72 {
73 winlist = 0;
75 if(!(dpy = XOpenDisplay(0))) {
76 sgl_log("failed to open X display: %s\n", XDisplayName(0));
77 return -1;
78 }
79 scr = DefaultScreen(dpy);
80 root = RootWindow(dpy, scr);
82 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
83 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
85 return 0;
86 }
88 static void shutdown(void)
89 {
90 while(winlist) {
91 struct window *win = winlist;
92 winlist = winlist->next;
94 glXDestroyContext(dpy, win->ctx);
95 XDestroyWindow(dpy, win->win);
96 free(win);
97 }
98 XCloseDisplay(dpy);
99 dpy = 0;
100 }
102 static int set_vidmode(int xsz, int ysz)
103 {
104 /* TODO */
105 return 0;
106 }
108 static int get_vidmode(int *xsz, int *ysz)
109 {
110 /* TODO */
111 return 0;
112 }
114 static int create_window(int xsz, int ysz, unsigned int flags)
115 {
116 int attr[32];
117 Window win;
118 GLXContext ctx;
119 XVisualInfo *vis;
120 XClassHint chint;
121 XSetWindowAttributes xattr;
122 unsigned int attr_valid;
123 long evmask;
124 struct window *wnode;
126 if(!(wnode = malloc(sizeof *wnode))) {
127 return -1;
128 }
130 fill_attr(attr, flags);
132 if(!(vis = glXChooseVisual(dpy, scr, attr))) {
133 sgl_log("failed to find a suitable visual\n");
134 free(wnode);
135 return -1;
136 }
137 print_visual_info(vis);
139 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
140 sgl_log("failed to create OpenGL context\n");
141 XFree(vis);
142 free(wnode);
143 return -1;
144 }
146 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
147 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
148 attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
150 if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
151 vis->visual, attr_valid, &xattr))) {
152 sgl_log("failed to create window\n");
153 glXDestroyContext(dpy, ctx);
154 XFree(vis);
155 free(wnode);
156 return -1;
157 }
158 XFree(vis);
160 evmask = StructureNotifyMask | VisibilityChangeMask;
161 XSelectInput(dpy, win, evmask);
163 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
165 set_title("OpenGL/X11");
167 chint.res_name = chint.res_class = "simplygl";
168 XSetClassHint(dpy, win, &chint);
170 XMapWindow(dpy, win);
172 wnode->win = win;
173 wnode->ctx = ctx;
174 wnode->evmask = evmask;
175 wnode->next = winlist;
176 winlist->next = wnode;
178 if(!active_win) {
179 set_active(win);
180 }
181 return win;
182 }
184 static void fill_attr(int *attr, unsigned int flags)
185 {
186 int i = 0;
188 attr[i++] = GLX_RGBA;
189 attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
190 attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
191 attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
193 if(flags & SGL_DOUBLE) {
194 attr[i++] = GLX_DOUBLEBUFFER;
195 }
196 if(flags & SGL_DEPTH) {
197 attr[i++] = GLX_DEPTH_SIZE;
198 attr[i++] = 1;
199 }
200 if(flags & SGL_STENCIL) {
201 attr[i++] = GLX_STENCIL_SIZE;
202 attr[i++] = 1;
203 }
204 if(flags & SGL_STEREO) {
205 attr[i++] = GLX_STEREO;
206 }
207 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
208 if(flags & SGL_MULTISAMPLE) {
209 attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
210 attr[i++] = 1;
211 attr[i++] = GLX_SAMPLES_ARB;
212 attr[i++] = 1;
213 }
214 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
215 attr[i] = None;
216 }
218 static void print_visual_info(XVisualInfo *vis)
219 {
220 int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
222 glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
223 glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
224 glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
225 glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
226 glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
227 glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
228 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
229 glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
230 if(aa) {
231 glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
232 } else {
233 samples = 1;
234 }
235 #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
237 sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
238 sgl_log(" %s", stereo ? "stereo" : "mono");
239 sgl_log(" samples/pixel: %d\n", samples);
240 }
242 static void close_window(int id)
243 {
244 struct window dummy, *win, *prev;
245 dummy.next = winlist;
247 prev = &dummy;
248 win = prev->next;
250 while(win) {
251 if(win->win == id) {
252 glXDestroyContext(dpy, win->ctx);
253 XDestroyWindow(dpy, win->win);
254 prev->next = win->next;
255 free(win);
256 return;
257 }
258 win = win->next;
259 }
260 }
262 static int set_active(int id)
263 {
264 struct window *win = winlist;
266 while(win) {
267 if(win->win == id) {
268 if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
269 sgl_log("failed to activate window %d\n", id);
270 return -1;
271 }
272 active_win = win;
273 return 0;
274 }
275 win = win->next;
276 }
278 sgl_log("no such window: %d\n", id);
279 return -1;
280 }
282 static int set_title(const char *str)
283 {
284 XTextProperty wm_name;
286 if(!str || !active_win) {
287 return -1;
288 }
289 XStringListToTextProperty((char**)&str, 1, &wm_name);
290 XSetWMName(dpy, active_win->win, &wm_name);
291 XSetWMIconName(dpy, active_win->win, &wm_name);
292 XFree(wm_name.value);
293 return 0;
294 }
296 static void redisplay(void)
297 {
298 active_win->redisp_pending = 1;
299 }
301 static void swap_buffers(void)
302 {
303 glXSwapBuffers(dpy, active_win->ctx);
304 }
306 static void set_bits(long *mask, long bits)
307 {
308 *mask |= bits;
309 }
311 static void clear_bits(long *mask, long bits)
312 {
313 *mask &= ~bits;
314 }
316 static void set_event(int idx, int enable)
317 {
318 void (*op)(long*, long);
319 op = enable ? set_bits : clear_bits;
321 switch(idx) {
322 case SGL_DISPLAY:
323 op(&active_win->evmask, ExposureMask);
324 break;
326 case SGL_KEYBOARD:
327 op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
328 break;
330 case SGL_MOUSE:
331 op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
332 break;
334 case SGL_MOTION:
335 op(&active_win->evmask, ButtonMotionMask);
336 break;
338 case SGL_PASSIVE:
339 op(&active_win->evmask, PointerMotionMask);
340 break;
342 default:
343 return;
344 }
346 XSelectInput(dpy, active_win->win, active_win->evmask);
347 }
349 static int process_events(void)
350 {
351 XEvent xev;
352 void (*func)();
353 int state;
355 while(XPending(dpy)) {
356 XNextEvent(dpy, &xev);
358 switch(xev.type) {
359 case Expose:
360 if(xev.xexpose.count == 0) {
361 if((func = sgl_get_callback(SGL_DISPLAY))) {
362 func();
363 active_win->redisp_pending = 0;
364 }
365 }
366 break;
368 case MotionNotify:
369 if(xev.xmotion.state) {
370 func = sgl_get_callback(SGL_MOTION);
371 } else {
372 func = sgl_get_callback(SGL_PASSIVE);
373 }
374 if(func) {
375 func(xev.xmotion.x, xev.xmotion.y);
376 }
377 break;
379 case ButtonPress:
380 if(1) {
381 state = 1;
382 } else {
383 case ButtonRelease:
384 state = 0;
385 }
386 if((func = sgl_get_callback(SGL_MOUSE))) {
387 int bn = xev.xbutton.button - 1;
388 func(bn, state, xev.xbutton.x, xev.xbutton.y);
389 }
390 break;
392 case KeyPress:
393 if(1) {
394 state = 1;
395 } else {
396 case KeyRelease:
397 state = 0;
398 }
399 if((func = sgl_get_callback(SGL_KEYBOARD))) {
400 KeySym sym = XLookupKeysym(xev.xkey, 0);
401 func(sym, state);
402 /* XXX */
403 }
404 break;
406 case ConfigureNotify:
407 if((func = sgl_get_callback(SGL_RESHAPE))) {
408 func(xev.xconfigure.width, xev.xconfigure.height);
409 }
410 break;
411 }
413 case ClientMessage:
414 if(xev.xclient.message_type == xa_wm_prot) {
415 if(xev.xclient.data.l[0] == xa_wm_del_win) {
416 close_window(active_win->win);
417 if(!active_win) {
418 return 1;
419 }
420 }
421 }
422 break;
423 }
424 return 0;
425 }