sgl

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