sgl

diff 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 diff
     1.1 --- a/src/wsys_x11.c	Wed May 11 09:09:43 2011 +0300
     1.2 +++ b/src/wsys_x11.c	Thu May 12 11:04:10 2011 +0300
     1.3 @@ -1,20 +1,43 @@
     1.4 +#include <stdlib.h>
     1.5  #include <X11/Xlib.h>
     1.6  #include <GL/glx.h>
     1.7 +#include "sgl.h"
     1.8  #include "wsys.h"
     1.9 +#include "log.h"
    1.10  
    1.11  struct window {
    1.12  	Window win;
    1.13  	GLXContext ctx;
    1.14 +	long evmask;
    1.15  	struct window *next;
    1.16  };
    1.17  
    1.18  static int init(void);
    1.19  static void shutdown(void);
    1.20 -static int set_vidmode(int xsz, int ysz, int bpp);
    1.21 -static int get_vidmode(int *xsz, int *ysz, int *bpp);
    1.22 +
    1.23 +/* video mode switching */
    1.24 +static int set_vidmode(int xsz, int ysz);
    1.25 +static int get_vidmode(int *xsz, int *ysz);
    1.26 +
    1.27 +/* create/destroy windows */
    1.28  static int create_window(int xsz, int ysz, unsigned int flags);
    1.29 +static void fill_attr(int *attr, unsigned int flags);
    1.30 +static void print_visual_info(XVisualInfo *vis);
    1.31  static void close_window(int id);
    1.32  
    1.33 +/* window management */
    1.34 +static int set_active(int id);
    1.35 +static int set_title(const char *str);
    1.36 +static void redisplay(void);
    1.37 +static void swap_buffers(void);
    1.38 +
    1.39 +/* event handling and friends */
    1.40 +static void set_bits(long *mask, long bits);
    1.41 +static void clear_bits(long *mask, long bits);
    1.42 +static void set_event(int idx, int enable);
    1.43 +static int process_events(void);
    1.44 +
    1.45 +
    1.46  static struct wsys_module ws = {
    1.47  	"x11-glx", 0,
    1.48  	init,
    1.49 @@ -23,14 +46,23 @@
    1.50  	get_vidmode,
    1.51  	create_window,
    1.52  	close_window,
    1.53 +	set_active,
    1.54 +	set_title,
    1.55 +	redisplay,
    1.56 +	swap_buffers,
    1.57 +	set_event,
    1.58 +	process_events,
    1.59  	0
    1.60  };
    1.61  
    1.62  static Display *dpy;
    1.63 +static Window root;
    1.64  static int scr;
    1.65 +static Atom xa_wm_prot, xa_wm_del_win;
    1.66  static struct window *winlist;
    1.67 +static struct window *active_win;
    1.68  
    1.69 -
    1.70 +/* this is the only exported function, everything else should be static */
    1.71  void sgl_register_x11(void)
    1.72  {
    1.73  	sgl_register_module(&ws);
    1.74 @@ -41,9 +73,14 @@
    1.75  	winlist = 0;
    1.76  
    1.77  	if(!(dpy = XOpenDisplay(0))) {
    1.78 +		sgl_log("failed to open X display: %s\n", XDisplayName(0));
    1.79  		return -1;
    1.80  	}
    1.81  	scr = DefaultScreen(dpy);
    1.82 +	root = RootWindow(dpy, scr);
    1.83 +
    1.84 +	xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
    1.85 +	xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
    1.86  
    1.87  	return 0;
    1.88  }
    1.89 @@ -55,25 +92,151 @@
    1.90  		winlist = winlist->next;
    1.91  
    1.92  		glXDestroyContext(dpy, win->ctx);
    1.93 -		XCloseWindow(dpy, win->win);
    1.94 +		XDestroyWindow(dpy, win->win);
    1.95  		free(win);
    1.96  	}
    1.97  	XCloseDisplay(dpy);
    1.98  	dpy = 0;
    1.99  }
   1.100  
   1.101 -static int set_vidmode(int xsz, int ysz, int bpp)
   1.102 +static int set_vidmode(int xsz, int ysz)
   1.103  {
   1.104  	/* TODO */
   1.105 +	return 0;
   1.106  }
   1.107  
   1.108 -static int get_vidmode(int *xsz, int *ysz, int *bpp)
   1.109 +static int get_vidmode(int *xsz, int *ysz)
   1.110  {
   1.111  	/* TODO */
   1.112 +	return 0;
   1.113  }
   1.114  
   1.115  static int create_window(int xsz, int ysz, unsigned int flags)
   1.116  {
   1.117 +	int attr[32];
   1.118 +	Window win;
   1.119 +	GLXContext ctx;
   1.120 +	XVisualInfo *vis;
   1.121 +	XClassHint chint;
   1.122 +	XSetWindowAttributes xattr;
   1.123 +	unsigned int attr_valid;
   1.124 +	long evmask;
   1.125 +	struct window *wnode;
   1.126 +
   1.127 +	if(!(wnode = malloc(sizeof *wnode))) {
   1.128 +		return -1;
   1.129 +	}
   1.130 +
   1.131 +	fill_attr(attr, flags);
   1.132 +
   1.133 +	if(!(vis = glXChooseVisual(dpy, scr, attr))) {
   1.134 +		sgl_log("failed to find a suitable visual\n");
   1.135 +		free(wnode);
   1.136 +		return -1;
   1.137 +	}
   1.138 +	print_visual_info(vis);
   1.139 +
   1.140 +	if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
   1.141 +		sgl_log("failed to create OpenGL context\n");
   1.142 +		XFree(vis);
   1.143 +		free(wnode);
   1.144 +		return -1;
   1.145 +	}
   1.146 +
   1.147 +	xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
   1.148 +	xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
   1.149 +	attr_valid = CWColormap | CWBackPixel | CWBorderPixel;
   1.150 +
   1.151 +	if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
   1.152 +					vis->visual, attr_valid, &xattr))) {
   1.153 +		sgl_log("failed to create window\n");
   1.154 +		glXDestroyContext(dpy, ctx);
   1.155 +		XFree(vis);
   1.156 +		free(wnode);
   1.157 +		return -1;
   1.158 +	}
   1.159 +	XFree(vis);
   1.160 +
   1.161 +	evmask = StructureNotifyMask | VisibilityChangeMask;
   1.162 +	XSelectInput(dpy, win, evmask);
   1.163 +
   1.164 +	XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
   1.165 +
   1.166 +	set_title("OpenGL/X11");
   1.167 +
   1.168 +	chint.res_name = chint.res_class = "simplygl";
   1.169 +	XSetClassHint(dpy, win, &chint);
   1.170 +
   1.171 +	XMapWindow(dpy, win);
   1.172 +
   1.173 +	wnode->win = win;
   1.174 +	wnode->ctx = ctx;
   1.175 +	wnode->evmask = evmask;
   1.176 +	wnode->next = winlist;
   1.177 +	winlist->next = wnode;
   1.178 +
   1.179 +	if(!active_win) {
   1.180 +		set_active(win);
   1.181 +	}
   1.182 +	return win;
   1.183 +}
   1.184 +
   1.185 +static void fill_attr(int *attr, unsigned int flags)
   1.186 +{
   1.187 +	int i = 0;
   1.188 +
   1.189 +	attr[i++] = GLX_RGBA;
   1.190 +	attr[i++] = GLX_RED_SIZE; attr[i++] = 1;
   1.191 +	attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1;
   1.192 +	attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1;
   1.193 +
   1.194 +	if(flags & SGL_DOUBLE) {
   1.195 +		attr[i++] = GLX_DOUBLEBUFFER;
   1.196 +	}
   1.197 +	if(flags & SGL_DEPTH) {
   1.198 +		attr[i++] = GLX_DEPTH_SIZE;
   1.199 +		attr[i++] = 1;
   1.200 +	}
   1.201 +	if(flags & SGL_STENCIL) {
   1.202 +		attr[i++] = GLX_STENCIL_SIZE;
   1.203 +		attr[i++] = 1;
   1.204 +	}
   1.205 +	if(flags & SGL_STEREO) {
   1.206 +		attr[i++] = GLX_STEREO;
   1.207 +	}
   1.208 +#if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
   1.209 +	if(flags & SGL_MULTISAMPLE) {
   1.210 +		attr[i++] = GLX_SAMPLE_BUFFERS_ARB;
   1.211 +		attr[i++] = 1;
   1.212 +		attr[i++] = GLX_SAMPLES_ARB;
   1.213 +		attr[i++] = 1;
   1.214 +	}
   1.215 +#endif	/* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
   1.216 +	attr[i] = None;
   1.217 +}
   1.218 +
   1.219 +static void print_visual_info(XVisualInfo *vis)
   1.220 +{
   1.221 +	int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples;
   1.222 +
   1.223 +	glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits);
   1.224 +	glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits);
   1.225 +	glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits);
   1.226 +	glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits);
   1.227 +	glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits);
   1.228 +	glXGetConfig(dpy, vis, GLX_STEREO, &stereo);
   1.229 +#if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
   1.230 +	glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa);
   1.231 +	if(aa) {
   1.232 +		glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples);
   1.233 +	} else {
   1.234 +		samples = 1;
   1.235 +	}
   1.236 +#endif	/* defined GLX_VERSION_1_4 || GLX_ARB_multisample */
   1.237 +
   1.238 +	sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits);
   1.239 +	sgl_log(" %s", stereo ? "stereo" : "mono");
   1.240 +	sgl_log(" samples/pixel: %d\n", samples);
   1.241  }
   1.242  
   1.243  static void close_window(int id)
   1.244 @@ -87,7 +250,7 @@
   1.245  	while(win) {
   1.246  		if(win->win == id) {
   1.247  			glXDestroyContext(dpy, win->ctx);
   1.248 -			XCloseWindow(dpy, win->win);
   1.249 +			XDestroyWindow(dpy, win->win);
   1.250  			prev->next = win->next;
   1.251  			free(win);
   1.252  			return;
   1.253 @@ -95,3 +258,168 @@
   1.254  		win = win->next;
   1.255  	}
   1.256  }
   1.257 +
   1.258 +static int set_active(int id)
   1.259 +{
   1.260 +	struct window *win = winlist;
   1.261 +
   1.262 +	while(win) {
   1.263 +		if(win->win == id) {
   1.264 +			if(glXMakeCurrent(dpy, win->win, win->ctx) == False) {
   1.265 +				sgl_log("failed to activate window %d\n", id);
   1.266 +				return -1;
   1.267 +			}
   1.268 +			active_win = win;
   1.269 +			return 0;
   1.270 +		}
   1.271 +		win = win->next;
   1.272 +	}
   1.273 +
   1.274 +	sgl_log("no such window: %d\n", id);
   1.275 +	return -1;
   1.276 +}
   1.277 +
   1.278 +static int set_title(const char *str)
   1.279 +{
   1.280 +	XTextProperty wm_name;
   1.281 +
   1.282 +	if(!str || !active_win) {
   1.283 +		return -1;
   1.284 +	}
   1.285 +	XStringListToTextProperty((char**)&str, 1, &wm_name);
   1.286 +	XSetWMName(dpy, active_win->win, &wm_name);
   1.287 +	XSetWMIconName(dpy, active_win->win, &wm_name);
   1.288 +	XFree(wm_name.value);
   1.289 +	return 0;
   1.290 +}
   1.291 +
   1.292 +static void redisplay(void)
   1.293 +{
   1.294 +	active_win->redisp_pending = 1;
   1.295 +}
   1.296 +
   1.297 +static void swap_buffers(void)
   1.298 +{
   1.299 +	glXSwapBuffers(dpy, active_win->ctx);
   1.300 +}
   1.301 +
   1.302 +static void set_bits(long *mask, long bits)
   1.303 +{
   1.304 +	*mask |= bits;
   1.305 +}
   1.306 +
   1.307 +static void clear_bits(long *mask, long bits)
   1.308 +{
   1.309 +	*mask &= ~bits;
   1.310 +}
   1.311 +
   1.312 +static void set_event(int idx, int enable)
   1.313 +{
   1.314 +	void (*op)(long*, long);
   1.315 +	op = enable ? set_bits : clear_bits;
   1.316 +
   1.317 +	switch(idx) {
   1.318 +	case SGL_DISPLAY:
   1.319 +		op(&active_win->evmask, ExposureMask);
   1.320 +		break;
   1.321 +
   1.322 +	case SGL_KEYBOARD:
   1.323 +		op(&active_win->evmask, KeyPressMask | KeyReleaseMask);
   1.324 +		break;
   1.325 +
   1.326 +	case SGL_MOUSE:
   1.327 +		op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask);
   1.328 +		break;
   1.329 +
   1.330 +	case SGL_MOTION:
   1.331 +		op(&active_win->evmask, ButtonMotionMask);
   1.332 +		break;
   1.333 +
   1.334 +	case SGL_PASSIVE:
   1.335 +		op(&active_win->evmask, PointerMotionMask);
   1.336 +		break;
   1.337 +
   1.338 +	default:
   1.339 +		return;
   1.340 +	}
   1.341 +
   1.342 +	XSelectInput(dpy, active_win->win, active_win->evmask);
   1.343 +}
   1.344 +
   1.345 +static int process_events(void)
   1.346 +{
   1.347 +	XEvent xev;
   1.348 +	void (*func)();
   1.349 +	int state;
   1.350 +
   1.351 +	while(XPending(dpy)) {
   1.352 +		XNextEvent(dpy, &xev);
   1.353 +
   1.354 +		switch(xev.type) {
   1.355 +		case Expose:
   1.356 +			if(xev.xexpose.count == 0) {
   1.357 +				if((func = sgl_get_callback(SGL_DISPLAY))) {
   1.358 +					func();
   1.359 +					active_win->redisp_pending = 0;
   1.360 +				}
   1.361 +			}
   1.362 +			break;
   1.363 +
   1.364 +		case MotionNotify:
   1.365 +			if(xev.xmotion.state) {
   1.366 +				func = sgl_get_callback(SGL_MOTION);
   1.367 +			} else {
   1.368 +				func = sgl_get_callback(SGL_PASSIVE);
   1.369 +			}
   1.370 +			if(func) {
   1.371 +				func(xev.xmotion.x, xev.xmotion.y);
   1.372 +			}
   1.373 +			break;
   1.374 +
   1.375 +		case ButtonPress:
   1.376 +			if(1) {
   1.377 +				state = 1;
   1.378 +			} else {
   1.379 +		case ButtonRelease:
   1.380 +				state = 0;
   1.381 +			}
   1.382 +			if((func = sgl_get_callback(SGL_MOUSE))) {
   1.383 +				int bn = xev.xbutton.button - 1;
   1.384 +				func(bn, state, xev.xbutton.x, xev.xbutton.y);
   1.385 +			}
   1.386 +			break;
   1.387 +
   1.388 +		case KeyPress:
   1.389 +			if(1) {
   1.390 +				state = 1;
   1.391 +			} else {
   1.392 +		case KeyRelease:
   1.393 +				state = 0;
   1.394 +			}
   1.395 +			if((func = sgl_get_callback(SGL_KEYBOARD))) {
   1.396 +				KeySym sym = XLookupKeysym(xev.xkey, 0);
   1.397 +				func(sym, state);
   1.398 +				/* XXX */
   1.399 +			}
   1.400 +			break;
   1.401 +
   1.402 +		case ConfigureNotify:
   1.403 +			if((func = sgl_get_callback(SGL_RESHAPE))) {
   1.404 +				func(xev.xconfigure.width, xev.xconfigure.height);
   1.405 +			}
   1.406 +			break;
   1.407 +		}
   1.408 +
   1.409 +		case ClientMessage:
   1.410 +			if(xev.xclient.message_type == xa_wm_prot) {
   1.411 +				if(xev.xclient.data.l[0] == xa_wm_del_win) {
   1.412 +					close_window(active_win->win);
   1.413 +					if(!active_win) {
   1.414 +						return 1;
   1.415 +					}
   1.416 +				}
   1.417 +			}
   1.418 +			break;
   1.419 +	}
   1.420 +	return 0;
   1.421 +}