sgl

annotate src/wsys_x11.c @ 6:0cb438c86b98

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