Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the Do What The Fuck You Want To
  8. * Public License, Version 2, as published by Sam Hocevar. See
  9. * http://sam.zoy.org/wtfpl/COPYING for more details.
  10. */
  11. /** \file driver_x11.c
  12. * \version \$Id$
  13. * \author Sam Hocevar <sam@zoy.org>
  14. * \brief X11 driver
  15. *
  16. * This file contains the libcaca X11 input and output driver
  17. */
  18. #include "config.h"
  19. #if defined(USE_X11)
  20. #include <X11/Xlib.h>
  21. #include <X11/Xutil.h>
  22. #include <X11/keysym.h>
  23. #if defined(HAVE_X11_XKBLIB_H)
  24. # include <X11/XKBlib.h>
  25. #endif
  26. #include <stdio.h> /* BUFSIZ */
  27. #include <stdlib.h>
  28. #include "caca.h"
  29. #include "caca_internals.h"
  30. #include "cucul.h"
  31. #include "cucul_internals.h"
  32. /*
  33. * Local functions
  34. */
  35. static int x11_error_handler(Display *, XErrorEvent *);
  36. struct driver_private
  37. {
  38. Display *dpy;
  39. Window window;
  40. Pixmap pixmap;
  41. GC gc;
  42. long int event_mask;
  43. int font_width, font_height;
  44. int colors[16];
  45. Font font;
  46. XFontStruct *font_struct;
  47. int font_offset;
  48. Cursor pointer;
  49. #if defined(HAVE_X11_XKBLIB_H)
  50. Bool autorepeat;
  51. #endif
  52. };
  53. static int x11_init_graphics(caca_t *kk)
  54. {
  55. static int const x11_palette[] =
  56. {
  57. /* Standard curses colours */
  58. 0x0, 0x0, 0x0,
  59. 0x0, 0x0, 0x8000,
  60. 0x0, 0x8000, 0x0,
  61. 0x0, 0x8000, 0x8000,
  62. 0x8000, 0x0, 0x0,
  63. 0x8000, 0x0, 0x8000,
  64. 0x8000, 0x8000, 0x0,
  65. 0x8000, 0x8000, 0x8000,
  66. /* Extra values for xterm-16color */
  67. 0x4000, 0x4000, 0x4000,
  68. 0x4000, 0x4000, 0xffff,
  69. 0x4000, 0xffff, 0x4000,
  70. 0x4000, 0xffff, 0xffff,
  71. 0xffff, 0x4000, 0x4000,
  72. 0xffff, 0x4000, 0xffff,
  73. 0xffff, 0xffff, 0x4000,
  74. 0xffff, 0xffff, 0xffff,
  75. };
  76. Colormap colormap;
  77. XSetWindowAttributes x11_winattr;
  78. int (*old_error_handler)(Display *, XErrorEvent *);
  79. char const *fonts[] = { NULL, "8x13bold", "fixed" }, **parser;
  80. char const *geometry;
  81. unsigned int width = 0, height = 0;
  82. int i;
  83. kk->drv.p = malloc(sizeof(struct driver_private));
  84. #if defined(HAVE_GETENV)
  85. geometry = getenv("CACA_GEOMETRY");
  86. if(geometry && *geometry)
  87. sscanf(geometry, "%ux%u", &width, &height);
  88. #endif
  89. if(width && height)
  90. _cucul_set_size(kk->qq, width, height);
  91. kk->drv.p->dpy = XOpenDisplay(NULL);
  92. if(kk->drv.p->dpy == NULL)
  93. return -1;
  94. #if defined(HAVE_GETENV)
  95. fonts[0] = getenv("CACA_FONT");
  96. if(fonts[0] && *fonts[0])
  97. parser = fonts;
  98. else
  99. #endif
  100. parser = fonts + 1;
  101. /* Ignore font errors */
  102. old_error_handler = XSetErrorHandler(x11_error_handler);
  103. /* Parse our font list */
  104. for( ; ; parser++)
  105. {
  106. if(!*parser)
  107. {
  108. XSetErrorHandler(old_error_handler);
  109. XCloseDisplay(kk->drv.p->dpy);
  110. return -1;
  111. }
  112. kk->drv.p->font = XLoadFont(kk->drv.p->dpy, *parser);
  113. if(!kk->drv.p->font)
  114. continue;
  115. kk->drv.p->font_struct = XQueryFont(kk->drv.p->dpy, kk->drv.p->font);
  116. if(!kk->drv.p->font_struct)
  117. {
  118. XUnloadFont(kk->drv.p->dpy, kk->drv.p->font);
  119. continue;
  120. }
  121. break;
  122. }
  123. /* Reset the default X11 error handler */
  124. XSetErrorHandler(old_error_handler);
  125. kk->drv.p->font_width = kk->drv.p->font_struct->max_bounds.width;
  126. kk->drv.p->font_height = kk->drv.p->font_struct->max_bounds.ascent
  127. + kk->drv.p->font_struct->max_bounds.descent;
  128. kk->drv.p->font_offset = kk->drv.p->font_struct->max_bounds.descent;
  129. colormap = DefaultColormap(kk->drv.p->dpy, DefaultScreen(kk->drv.p->dpy));
  130. for(i = 0; i < 16; i++)
  131. {
  132. XColor color;
  133. color.red = x11_palette[i * 3];
  134. color.green = x11_palette[i * 3 + 1];
  135. color.blue = x11_palette[i * 3 + 2];
  136. XAllocColor(kk->drv.p->dpy, colormap, &color);
  137. kk->drv.p->colors[i] = color.pixel;
  138. }
  139. x11_winattr.backing_store = Always;
  140. x11_winattr.background_pixel = kk->drv.p->colors[0];
  141. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  142. kk->drv.p->window =
  143. XCreateWindow(kk->drv.p->dpy, DefaultRootWindow(kk->drv.p->dpy), 0, 0,
  144. kk->qq->width * kk->drv.p->font_width,
  145. kk->qq->height * kk->drv.p->font_height,
  146. 0, 0, InputOutput, 0,
  147. CWBackingStore | CWBackPixel | CWEventMask,
  148. &x11_winattr);
  149. XStoreName(kk->drv.p->dpy, kk->drv.p->window, "caca for X");
  150. XSelectInput(kk->drv.p->dpy, kk->drv.p->window, StructureNotifyMask);
  151. XMapWindow(kk->drv.p->dpy, kk->drv.p->window);
  152. kk->drv.p->gc = XCreateGC(kk->drv.p->dpy, kk->drv.p->window, 0, NULL);
  153. XSetForeground(kk->drv.p->dpy, kk->drv.p->gc, kk->drv.p->colors[15]);
  154. XSetFont(kk->drv.p->dpy, kk->drv.p->gc, kk->drv.p->font);
  155. for(;;)
  156. {
  157. XEvent xevent;
  158. XNextEvent(kk->drv.p->dpy, &xevent);
  159. if (xevent.type == MapNotify)
  160. break;
  161. }
  162. #if defined(HAVE_X11_XKBLIB_H)
  163. /* Disable autorepeat */
  164. XkbSetDetectableAutoRepeat(kk->drv.p->dpy, True, &kk->drv.p->autorepeat);
  165. if(!kk->drv.p->autorepeat)
  166. XAutoRepeatOff(kk->drv.p->dpy);
  167. #endif
  168. kk->drv.p->event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
  169. | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
  170. | ExposureMask;
  171. XSelectInput(kk->drv.p->dpy, kk->drv.p->window, kk->drv.p->event_mask);
  172. XSync(kk->drv.p->dpy, False);
  173. kk->drv.p->pixmap = XCreatePixmap(kk->drv.p->dpy, kk->drv.p->window,
  174. kk->qq->width * kk->drv.p->font_width,
  175. kk->qq->height * kk->drv.p->font_height,
  176. DefaultDepth(kk->drv.p->dpy,
  177. DefaultScreen(kk->drv.p->dpy)));
  178. kk->drv.p->pointer = None;
  179. return 0;
  180. }
  181. static int x11_end_graphics(caca_t *kk)
  182. {
  183. XSync(kk->drv.p->dpy, False);
  184. #if defined(HAVE_X11_XKBLIB_H)
  185. if(!kk->drv.p->autorepeat)
  186. XAutoRepeatOn(kk->drv.p->dpy);
  187. #endif
  188. XFreePixmap(kk->drv.p->dpy, kk->drv.p->pixmap);
  189. XFreeFont(kk->drv.p->dpy, kk->drv.p->font_struct);
  190. XFreeGC(kk->drv.p->dpy, kk->drv.p->gc);
  191. XUnmapWindow(kk->drv.p->dpy, kk->drv.p->window);
  192. XDestroyWindow(kk->drv.p->dpy, kk->drv.p->window);
  193. XCloseDisplay(kk->drv.p->dpy);
  194. free(kk->drv.p);
  195. return 0;
  196. }
  197. static int x11_set_window_title(caca_t *kk, char const *title)
  198. {
  199. XStoreName(kk->drv.p->dpy, kk->drv.p->window, title);
  200. return 0;
  201. }
  202. static unsigned int x11_get_window_width(caca_t *kk)
  203. {
  204. return kk->qq->width * kk->drv.p->font_width;
  205. }
  206. static unsigned int x11_get_window_height(caca_t *kk)
  207. {
  208. return kk->qq->height * kk->drv.p->font_height;
  209. }
  210. static void x11_display(caca_t *kk)
  211. {
  212. unsigned int x, y, len;
  213. /* First draw the background colours. Splitting the process in two
  214. * loops like this is actually slightly faster. */
  215. for(y = 0; y < kk->qq->height; y++)
  216. {
  217. for(x = 0; x < kk->qq->width; x += len)
  218. {
  219. uint32_t *attr = kk->qq->attr + x + y * kk->qq->width;
  220. uint8_t bg = _cucul_argb32_to_ansi4bg(*attr);
  221. len = 1;
  222. while(x + len < kk->qq->width
  223. && _cucul_argb32_to_ansi4bg(attr[len]) == bg)
  224. len++;
  225. XSetForeground(kk->drv.p->dpy, kk->drv.p->gc,
  226. kk->drv.p->colors[_cucul_argb32_to_ansi4bg(*attr)]);
  227. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap, kk->drv.p->gc,
  228. x * kk->drv.p->font_width, y * kk->drv.p->font_height,
  229. len * kk->drv.p->font_width, kk->drv.p->font_height);
  230. }
  231. }
  232. /* Then print the foreground characters */
  233. for(y = 0; y < kk->qq->height; y++)
  234. {
  235. unsigned int yoff = (y + 1) * kk->drv.p->font_height
  236. - kk->drv.p->font_offset;
  237. uint32_t *chars = kk->qq->chars + y * kk->qq->width;
  238. for(x = 0; x < kk->qq->width; x++, chars++)
  239. {
  240. uint32_t *attr = kk->qq->attr + x + y * kk->qq->width;
  241. /* Skip spaces */
  242. if(*chars == 0x00000020)
  243. continue;
  244. XSetForeground(kk->drv.p->dpy, kk->drv.p->gc,
  245. kk->drv.p->colors[_cucul_argb32_to_ansi4fg(*attr)]);
  246. /* Plain ASCII, no problem. */
  247. if(*chars > 0x00000020 && *chars < 0x00000080)
  248. {
  249. char c = (uint8_t)*chars;
  250. XDrawString(kk->drv.p->dpy, kk->drv.p->pixmap, kk->drv.p->gc,
  251. x * kk->drv.p->font_width, yoff, &c, 1);
  252. continue;
  253. }
  254. /* We want to be able to print a few special Unicode characters
  255. * such as the CP437 gradients and half blocks. For unknown
  256. * characters, just print '?'. */
  257. switch(*chars)
  258. {
  259. case 0x00002580: /* ▀ */
  260. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap,
  261. kk->drv.p->gc,
  262. x * kk->drv.p->font_width,
  263. y * kk->drv.p->font_height,
  264. kk->drv.p->font_width,
  265. kk->drv.p->font_height / 2);
  266. break;
  267. case 0x00002584: /* ▄ */
  268. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap,
  269. kk->drv.p->gc,
  270. x * kk->drv.p->font_width,
  271. (y + 1) * kk->drv.p->font_height
  272. - kk->drv.p->font_height / 2,
  273. kk->drv.p->font_width,
  274. kk->drv.p->font_height / 2);
  275. break;
  276. case 0x00002588: /* █ */
  277. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap,
  278. kk->drv.p->gc,
  279. x * kk->drv.p->font_width,
  280. y * kk->drv.p->font_height,
  281. kk->drv.p->font_width,
  282. kk->drv.p->font_height);
  283. break;
  284. case 0x0000258c: /* ▌ */
  285. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap,
  286. kk->drv.p->gc,
  287. x * kk->drv.p->font_width,
  288. y * kk->drv.p->font_height,
  289. kk->drv.p->font_width / 2,
  290. kk->drv.p->font_height);
  291. break;
  292. case 0x00002590: /* ▐ */
  293. XFillRectangle(kk->drv.p->dpy, kk->drv.p->pixmap,
  294. kk->drv.p->gc,
  295. (x + 1) * kk->drv.p->font_width
  296. - kk->drv.p->font_width / 2,
  297. y * kk->drv.p->font_height,
  298. kk->drv.p->font_width / 2,
  299. kk->drv.p->font_height);
  300. break;
  301. case 0x00002593: /* ▓ */
  302. case 0x00002592: /* ▒ */
  303. case 0x00002591: /* ░ */
  304. {
  305. /* FIXME: this sucks utterly */
  306. int i, j, k = *chars - 0x00002591;
  307. for(j = kk->drv.p->font_height; j--; )
  308. for(i = kk->drv.p->font_width; i--; )
  309. {
  310. if(((i + 2 * (j & 1)) & 3) > k)
  311. continue;
  312. XDrawPoint(kk->drv.p->dpy, kk->drv.p->pixmap,
  313. kk->drv.p->gc,
  314. x * kk->drv.p->font_width + i,
  315. y * kk->drv.p->font_height + j);
  316. }
  317. break;
  318. }
  319. default:
  320. {
  321. char c;
  322. c = '?';
  323. XDrawString(kk->drv.p->dpy, kk->drv.p->pixmap,
  324. kk->drv.p->gc,
  325. x * kk->drv.p->font_width, yoff, &c, 1);
  326. break;
  327. }
  328. }
  329. }
  330. }
  331. XCopyArea(kk->drv.p->dpy, kk->drv.p->pixmap, kk->drv.p->window,
  332. kk->drv.p->gc, 0, 0,
  333. kk->qq->width * kk->drv.p->font_width,
  334. kk->qq->height * kk->drv.p->font_height,
  335. 0, 0);
  336. XFlush(kk->drv.p->dpy);
  337. }
  338. static void x11_handle_resize(caca_t *kk)
  339. {
  340. Pixmap new_pixmap;
  341. new_pixmap = XCreatePixmap(kk->drv.p->dpy, kk->drv.p->window,
  342. kk->resize.w * kk->drv.p->font_width,
  343. kk->resize.h * kk->drv.p->font_height,
  344. DefaultDepth(kk->drv.p->dpy,
  345. DefaultScreen(kk->drv.p->dpy)));
  346. XCopyArea(kk->drv.p->dpy, kk->drv.p->pixmap, new_pixmap,
  347. kk->drv.p->gc, 0, 0,
  348. kk->resize.w * kk->drv.p->font_width,
  349. kk->resize.h * kk->drv.p->font_height, 0, 0);
  350. XFreePixmap(kk->drv.p->dpy, kk->drv.p->pixmap);
  351. kk->drv.p->pixmap = new_pixmap;
  352. }
  353. static int x11_get_event(caca_t *kk, struct caca_event *ev)
  354. {
  355. XEvent xevent;
  356. char key;
  357. while(XCheckWindowEvent(kk->drv.p->dpy, kk->drv.p->window,
  358. kk->drv.p->event_mask, &xevent) == True)
  359. {
  360. KeySym keysym;
  361. /* Expose event */
  362. if(xevent.type == Expose)
  363. {
  364. XCopyArea(kk->drv.p->dpy, kk->drv.p->pixmap,
  365. kk->drv.p->window, kk->drv.p->gc, 0, 0,
  366. kk->qq->width * kk->drv.p->font_width,
  367. kk->qq->height * kk->drv.p->font_height, 0, 0);
  368. continue;
  369. }
  370. /* Resize event */
  371. if(xevent.type == ConfigureNotify)
  372. {
  373. unsigned int w, h;
  374. w = (xevent.xconfigure.width + kk->drv.p->font_width / 3)
  375. / kk->drv.p->font_width;
  376. h = (xevent.xconfigure.height + kk->drv.p->font_height / 3)
  377. / kk->drv.p->font_height;
  378. if(!w || !h || (w == kk->qq->width && h == kk->qq->height))
  379. continue;
  380. kk->resize.w = w;
  381. kk->resize.h = h;
  382. kk->resize.resized = 1;
  383. continue;
  384. }
  385. /* Check for mouse motion events */
  386. if(xevent.type == MotionNotify)
  387. {
  388. unsigned int newx = xevent.xmotion.x / kk->drv.p->font_width;
  389. unsigned int newy = xevent.xmotion.y / kk->drv.p->font_height;
  390. if(newx >= kk->qq->width)
  391. newx = kk->qq->width - 1;
  392. if(newy >= kk->qq->height)
  393. newy = kk->qq->height - 1;
  394. if(kk->mouse.x == newx && kk->mouse.y == newy)
  395. continue;
  396. kk->mouse.x = newx;
  397. kk->mouse.y = newy;
  398. ev->type = CACA_EVENT_MOUSE_MOTION;
  399. ev->data.mouse.x = kk->mouse.x;
  400. ev->data.mouse.y = kk->mouse.y;
  401. return 1;
  402. }
  403. /* Check for mouse press and release events */
  404. if(xevent.type == ButtonPress)
  405. {
  406. ev->type = CACA_EVENT_MOUSE_PRESS;
  407. ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  408. return 1;
  409. }
  410. if(xevent.type == ButtonRelease)
  411. {
  412. ev->type = CACA_EVENT_MOUSE_RELEASE;
  413. ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  414. return 1;
  415. }
  416. /* Check for key press and release events */
  417. if(xevent.type == KeyPress)
  418. ev->type = CACA_EVENT_KEY_PRESS;
  419. else if(xevent.type == KeyRelease)
  420. ev->type = CACA_EVENT_KEY_RELEASE;
  421. else
  422. continue;
  423. if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
  424. {
  425. ev->data.key.c = key;
  426. ev->data.key.ucs4 = key;
  427. ev->data.key.utf8[0] = key;
  428. ev->data.key.utf8[1] = '\0';
  429. return 1;
  430. }
  431. keysym = XKeycodeToKeysym(kk->drv.p->dpy, xevent.xkey.keycode, 0);
  432. switch(keysym)
  433. {
  434. case XK_F1: ev->data.key.c = CACA_KEY_F1; break;
  435. case XK_F2: ev->data.key.c = CACA_KEY_F2; break;
  436. case XK_F3: ev->data.key.c = CACA_KEY_F3; break;
  437. case XK_F4: ev->data.key.c = CACA_KEY_F4; break;
  438. case XK_F5: ev->data.key.c = CACA_KEY_F5; break;
  439. case XK_F6: ev->data.key.c = CACA_KEY_F6; break;
  440. case XK_F7: ev->data.key.c = CACA_KEY_F7; break;
  441. case XK_F8: ev->data.key.c = CACA_KEY_F8; break;
  442. case XK_F9: ev->data.key.c = CACA_KEY_F9; break;
  443. case XK_F10: ev->data.key.c = CACA_KEY_F10; break;
  444. case XK_F11: ev->data.key.c = CACA_KEY_F11; break;
  445. case XK_F12: ev->data.key.c = CACA_KEY_F12; break;
  446. case XK_F13: ev->data.key.c = CACA_KEY_F13; break;
  447. case XK_F14: ev->data.key.c = CACA_KEY_F14; break;
  448. case XK_F15: ev->data.key.c = CACA_KEY_F15; break;
  449. case XK_Left: ev->data.key.c = CACA_KEY_LEFT; break;
  450. case XK_Right: ev->data.key.c = CACA_KEY_RIGHT; break;
  451. case XK_Up: ev->data.key.c = CACA_KEY_UP; break;
  452. case XK_Down: ev->data.key.c = CACA_KEY_DOWN; break;
  453. default: ev->type = CACA_EVENT_NONE; return 0;
  454. }
  455. ev->data.key.ucs4 = 0;
  456. ev->data.key.utf8[0] = '\0';
  457. return 1;
  458. }
  459. ev->type = CACA_EVENT_NONE;
  460. return 0;
  461. }
  462. static void x11_set_mouse(caca_t *kk, int flags)
  463. {
  464. Cursor no_ptr;
  465. Pixmap bm_no;
  466. XColor black, dummy;
  467. Colormap colormap;
  468. static char const empty[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  469. if(flags)
  470. {
  471. XDefineCursor(kk->drv.p->dpy,kk->drv.p->window, 0);
  472. return;
  473. }
  474. colormap = DefaultColormap(kk->drv.p->dpy, DefaultScreen(kk->drv.p->dpy));
  475. if(!XAllocNamedColor(kk->drv.p->dpy, colormap, "black", &black, &dummy))
  476. {
  477. return;
  478. }
  479. bm_no = XCreateBitmapFromData(kk->drv.p->dpy, kk->drv.p->window,
  480. empty, 8, 8);
  481. no_ptr = XCreatePixmapCursor(kk->drv.p->dpy, bm_no, bm_no,
  482. &black, &black, 0, 0);
  483. XDefineCursor(kk->drv.p->dpy, kk->drv.p->window, no_ptr);
  484. XFreeCursor(kk->drv.p->dpy, no_ptr);
  485. if(bm_no != None)
  486. XFreePixmap(kk->drv.p->dpy, bm_no);
  487. XFreeColors(kk->drv.p->dpy, colormap, &black.pixel, 1, 0);
  488. XSync(kk->drv.p->dpy, False);
  489. }
  490. /*
  491. * XXX: following functions are local
  492. */
  493. static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
  494. {
  495. /* Ignore the error */
  496. return 0;
  497. }
  498. /*
  499. * Driver initialisation
  500. */
  501. int x11_install(caca_t *kk)
  502. {
  503. #if defined(HAVE_GETENV)
  504. if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
  505. return -1;
  506. #endif
  507. kk->drv.driver = CACA_DRIVER_X11;
  508. kk->drv.init_graphics = x11_init_graphics;
  509. kk->drv.end_graphics = x11_end_graphics;
  510. kk->drv.set_window_title = x11_set_window_title;
  511. kk->drv.get_window_width = x11_get_window_width;
  512. kk->drv.get_window_height = x11_get_window_height;
  513. kk->drv.display = x11_display;
  514. kk->drv.handle_resize = x11_handle_resize;
  515. kk->drv.get_event = x11_get_event;
  516. kk->drv.set_mouse = x11_set_mouse;
  517. return 0;
  518. }
  519. #endif /* USE_X11 */