25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

456 lines
14 KiB

  1. /*
  2. * libcaca 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. static int x11_init_graphics(caca_t *kk)
  37. {
  38. static int const x11_palette[] =
  39. {
  40. /* Standard curses colours */
  41. 0x0, 0x0, 0x0,
  42. 0x0, 0x0, 0x8000,
  43. 0x0, 0x8000, 0x0,
  44. 0x0, 0x8000, 0x8000,
  45. 0x8000, 0x0, 0x0,
  46. 0x8000, 0x0, 0x8000,
  47. 0x8000, 0x8000, 0x0,
  48. 0x8000, 0x8000, 0x8000,
  49. /* Extra values for xterm-16color */
  50. 0x4000, 0x4000, 0x4000,
  51. 0x4000, 0x4000, 0xffff,
  52. 0x4000, 0xffff, 0x4000,
  53. 0x4000, 0xffff, 0xffff,
  54. 0xffff, 0x4000, 0x4000,
  55. 0xffff, 0x4000, 0xffff,
  56. 0xffff, 0xffff, 0x4000,
  57. 0xffff, 0xffff, 0xffff,
  58. };
  59. Colormap colormap;
  60. XSetWindowAttributes x11_winattr;
  61. int (*old_error_handler)(Display *, XErrorEvent *);
  62. char const *fonts[] = { NULL, "8x13bold", "fixed" }, **parser;
  63. char const *geometry;
  64. unsigned int width = 0, height = 0;
  65. int i;
  66. geometry = getenv("CACA_GEOMETRY");
  67. if(geometry && *(geometry))
  68. sscanf(geometry, "%ux%u", &width, &height);
  69. if(width && height)
  70. cucul_set_size(kk->qq, width, height);
  71. kk->x11.dpy = XOpenDisplay(NULL);
  72. if(kk->x11.dpy == NULL)
  73. return -1;
  74. fonts[0] = getenv("CACA_FONT");
  75. if(fonts[0] && *fonts[0])
  76. parser = fonts;
  77. else
  78. parser = fonts + 1;
  79. /* Ignore font errors */
  80. old_error_handler = XSetErrorHandler(x11_error_handler);
  81. /* Parse our font list */
  82. for( ; ; parser++)
  83. {
  84. if(!*parser)
  85. {
  86. XSetErrorHandler(old_error_handler);
  87. XCloseDisplay(kk->x11.dpy);
  88. return -1;
  89. }
  90. kk->x11.font = XLoadFont(kk->x11.dpy, *parser);
  91. if(!kk->x11.font)
  92. continue;
  93. kk->x11.font_struct = XQueryFont(kk->x11.dpy, kk->x11.font);
  94. if(!kk->x11.font_struct)
  95. {
  96. XUnloadFont(kk->x11.dpy, kk->x11.font);
  97. continue;
  98. }
  99. break;
  100. }
  101. /* Reset the default X11 error handler */
  102. XSetErrorHandler(old_error_handler);
  103. kk->x11.font_width = kk->x11.font_struct->max_bounds.width;
  104. kk->x11.font_height = kk->x11.font_struct->max_bounds.ascent
  105. + kk->x11.font_struct->max_bounds.descent;
  106. kk->x11.font_offset = kk->x11.font_struct->max_bounds.descent;
  107. colormap = DefaultColormap(kk->x11.dpy, DefaultScreen(kk->x11.dpy));
  108. for(i = 0; i < 16; i++)
  109. {
  110. XColor color;
  111. color.red = x11_palette[i * 3];
  112. color.green = x11_palette[i * 3 + 1];
  113. color.blue = x11_palette[i * 3 + 2];
  114. XAllocColor(kk->x11.dpy, colormap, &color);
  115. kk->x11.colors[i] = color.pixel;
  116. }
  117. x11_winattr.backing_store = Always;
  118. x11_winattr.background_pixel = kk->x11.colors[0];
  119. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  120. kk->x11.window =
  121. XCreateWindow(kk->x11.dpy, DefaultRootWindow(kk->x11.dpy), 0, 0,
  122. kk->qq->width * kk->x11.font_width,
  123. kk->qq->height * kk->x11.font_height,
  124. 0, 0, InputOutput, 0,
  125. CWBackingStore | CWBackPixel | CWEventMask,
  126. &x11_winattr);
  127. XStoreName(kk->x11.dpy, kk->x11.window, "caca for X");
  128. XSelectInput(kk->x11.dpy, kk->x11.window, StructureNotifyMask);
  129. XMapWindow(kk->x11.dpy, kk->x11.window);
  130. kk->x11.gc = XCreateGC(kk->x11.dpy, kk->x11.window, 0, NULL);
  131. XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[15]);
  132. XSetFont(kk->x11.dpy, kk->x11.gc, kk->x11.font);
  133. for(;;)
  134. {
  135. XEvent xevent;
  136. XNextEvent(kk->x11.dpy, &xevent);
  137. if (xevent.type == MapNotify)
  138. break;
  139. }
  140. #if defined(HAVE_X11_XKBLIB_H)
  141. /* Disable autorepeat */
  142. XkbSetDetectableAutoRepeat(kk->x11.dpy, True, &kk->x11.autorepeat);
  143. if(!kk->x11.autorepeat)
  144. XAutoRepeatOff(kk->x11.dpy);
  145. #endif
  146. kk->x11.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
  147. | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
  148. | ExposureMask;
  149. XSelectInput(kk->x11.dpy, kk->x11.window, kk->x11.event_mask);
  150. XSync(kk->x11.dpy, False);
  151. kk->x11.pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window,
  152. kk->qq->width * kk->x11.font_width,
  153. kk->qq->height * kk->x11.font_height,
  154. DefaultDepth(kk->x11.dpy,
  155. DefaultScreen(kk->x11.dpy)));
  156. kk->x11.new_width = kk->x11.new_height = 0;
  157. return 0;
  158. }
  159. static int x11_end_graphics(caca_t *kk)
  160. {
  161. XSync(kk->x11.dpy, False);
  162. #if defined(HAVE_X11_XKBLIB_H)
  163. if(!kk->x11.autorepeat)
  164. XAutoRepeatOn(kk->x11.dpy);
  165. #endif
  166. XFreePixmap(kk->x11.dpy, kk->x11.pixmap);
  167. XFreeFont(kk->x11.dpy, kk->x11.font_struct);
  168. XFreeGC(kk->x11.dpy, kk->x11.gc);
  169. XUnmapWindow(kk->x11.dpy, kk->x11.window);
  170. XDestroyWindow(kk->x11.dpy, kk->x11.window);
  171. XCloseDisplay(kk->x11.dpy);
  172. return 0;
  173. }
  174. static int x11_set_window_title(caca_t *kk, char const *title)
  175. {
  176. XStoreName(kk->x11.dpy, kk->x11.window, title);
  177. return 0;
  178. }
  179. static unsigned int x11_get_window_width(caca_t *kk)
  180. {
  181. return kk->qq->width * kk->x11.font_width;
  182. }
  183. static unsigned int x11_get_window_height(caca_t *kk)
  184. {
  185. return kk->qq->height * kk->x11.font_height;
  186. }
  187. static void x11_display(caca_t *kk)
  188. {
  189. unsigned int x, y, len;
  190. /* First draw the background colours. Splitting the process in two
  191. * loops like this is actually slightly faster. */
  192. for(y = 0; y < kk->qq->height; y++)
  193. {
  194. for(x = 0; x < kk->qq->width; x += len)
  195. {
  196. uint8_t *attr = kk->qq->attr + x + y * kk->qq->width;
  197. len = 1;
  198. while(x + len < kk->qq->width
  199. && (attr[len] >> 4) == (attr[0] >> 4))
  200. len++;
  201. XSetForeground(kk->x11.dpy, kk->x11.gc,
  202. kk->x11.colors[attr[0] >> 4]);
  203. XFillRectangle(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc,
  204. x * kk->x11.font_width, y * kk->x11.font_height,
  205. len * kk->x11.font_width, kk->x11.font_height);
  206. }
  207. }
  208. /* Then print the foreground characters */
  209. for(y = 0; y < kk->qq->height; y++)
  210. {
  211. for(x = 0; x < kk->qq->width; x += len)
  212. {
  213. char buffer[BUFSIZ]; /* FIXME: use a smaller buffer */
  214. uint32_t *chars = kk->qq->chars + x + y * kk->qq->width;
  215. uint8_t *attr = kk->qq->attr + x + y * kk->qq->width;
  216. len = 1;
  217. /* Skip spaces */
  218. if(chars[0] == ' ')
  219. continue;
  220. buffer[0] = chars[0] & 0x7f;
  221. while(x + len < kk->qq->width
  222. && (attr[len] & 0xf) == (attr[0] & 0xf))
  223. {
  224. buffer[len] = chars[len] & 0x7f;
  225. len++;
  226. }
  227. XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[attr[0] & 0xf]);
  228. XDrawString(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc,
  229. x * kk->x11.font_width,
  230. (y + 1) * kk->x11.font_height - kk->x11.font_offset,
  231. buffer, len);
  232. }
  233. }
  234. XCopyArea(kk->x11.dpy, kk->x11.pixmap, kk->x11.window, kk->x11.gc, 0, 0,
  235. kk->qq->width * kk->x11.font_width,
  236. kk->qq->height * kk->x11.font_height,
  237. 0, 0);
  238. XFlush(kk->x11.dpy);
  239. }
  240. static void x11_handle_resize(caca_t *kk, unsigned int *new_width,
  241. unsigned int *new_height)
  242. {
  243. Pixmap new_pixmap;
  244. *new_width = kk->x11.new_width;
  245. *new_height = kk->x11.new_height;
  246. new_pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window,
  247. kk->qq->width * kk->x11.font_width,
  248. kk->qq->height * kk->x11.font_height,
  249. DefaultDepth(kk->x11.dpy,
  250. DefaultScreen(kk->x11.dpy)));
  251. XCopyArea(kk->x11.dpy, kk->x11.pixmap, new_pixmap, kk->x11.gc, 0, 0,
  252. kk->qq->width * kk->x11.font_width,
  253. kk->qq->height * kk->x11.font_height, 0, 0);
  254. XFreePixmap(kk->x11.dpy, kk->x11.pixmap);
  255. kk->x11.pixmap = new_pixmap;
  256. }
  257. static unsigned int x11_get_event(caca_t *kk)
  258. {
  259. unsigned int event = 0;
  260. XEvent xevent;
  261. char key;
  262. while(XCheckWindowEvent(kk->x11.dpy, kk->x11.window,
  263. kk->x11.event_mask, &xevent) == True)
  264. {
  265. KeySym keysym;
  266. /* Expose event */
  267. if(xevent.type == Expose)
  268. {
  269. XCopyArea(kk->x11.dpy, kk->x11.pixmap,
  270. kk->x11.window, kk->x11.gc, 0, 0,
  271. kk->qq->width * kk->x11.font_width,
  272. kk->qq->height * kk->x11.font_height, 0, 0);
  273. continue;
  274. }
  275. /* Resize event */
  276. if(xevent.type == ConfigureNotify)
  277. {
  278. unsigned int w, h;
  279. w = (xevent.xconfigure.width + kk->x11.font_width / 3)
  280. / kk->x11.font_width;
  281. h = (xevent.xconfigure.height + kk->x11.font_height / 3)
  282. / kk->x11.font_height;
  283. if(!w || !h || (w == kk->qq->width && h == kk->qq->height))
  284. continue;
  285. kk->x11.new_width = w;
  286. kk->x11.new_height = h;
  287. /* If we are already resizing, ignore the new signal */
  288. if(kk->resize)
  289. continue;
  290. kk->resize = 1;
  291. return CACA_EVENT_RESIZE;
  292. }
  293. /* Check for mouse motion events */
  294. if(xevent.type == MotionNotify)
  295. {
  296. unsigned int newx = xevent.xmotion.x / kk->x11.font_width;
  297. unsigned int newy = xevent.xmotion.y / kk->x11.font_height;
  298. if(newx >= kk->qq->width)
  299. newx = kk->qq->width - 1;
  300. if(newy >= kk->qq->height)
  301. newy = kk->qq->height - 1;
  302. if(kk->mouse_x == newx && kk->mouse_y == newy)
  303. continue;
  304. kk->mouse_x = newx;
  305. kk->mouse_y = newy;
  306. return CACA_EVENT_MOUSE_MOTION | (kk->mouse_x << 12) | kk->mouse_y;
  307. }
  308. /* Check for mouse press and release events */
  309. if(xevent.type == ButtonPress)
  310. return CACA_EVENT_MOUSE_PRESS
  311. | ((XButtonEvent *)&xevent)->button;
  312. if(xevent.type == ButtonRelease)
  313. return CACA_EVENT_MOUSE_RELEASE
  314. | ((XButtonEvent *)&xevent)->button;
  315. /* Check for key press and release events */
  316. if(xevent.type == KeyPress)
  317. event |= CACA_EVENT_KEY_PRESS;
  318. else if(xevent.type == KeyRelease)
  319. event |= CACA_EVENT_KEY_RELEASE;
  320. else
  321. continue;
  322. if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
  323. return event | key;
  324. keysym = XKeycodeToKeysym(kk->x11.dpy, xevent.xkey.keycode, 0);
  325. switch(keysym)
  326. {
  327. case XK_F1: return event | CACA_KEY_F1;
  328. case XK_F2: return event | CACA_KEY_F2;
  329. case XK_F3: return event | CACA_KEY_F3;
  330. case XK_F4: return event | CACA_KEY_F4;
  331. case XK_F5: return event | CACA_KEY_F5;
  332. case XK_F6: return event | CACA_KEY_F6;
  333. case XK_F7: return event | CACA_KEY_F7;
  334. case XK_F8: return event | CACA_KEY_F8;
  335. case XK_F9: return event | CACA_KEY_F9;
  336. case XK_F10: return event | CACA_KEY_F10;
  337. case XK_F11: return event | CACA_KEY_F11;
  338. case XK_F12: return event | CACA_KEY_F12;
  339. case XK_F13: return event | CACA_KEY_F13;
  340. case XK_F14: return event | CACA_KEY_F14;
  341. case XK_F15: return event | CACA_KEY_F15;
  342. case XK_Left: return event | CACA_KEY_LEFT;
  343. case XK_Right: return event | CACA_KEY_RIGHT;
  344. case XK_Up: return event | CACA_KEY_UP;
  345. case XK_Down: return event | CACA_KEY_DOWN;
  346. default: return CACA_EVENT_NONE;
  347. }
  348. }
  349. return CACA_EVENT_NONE;
  350. }
  351. /*
  352. * XXX: following functions are local
  353. */
  354. static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
  355. {
  356. /* Ignore the error */
  357. return 0;
  358. }
  359. /*
  360. * Driver initialisation
  361. */
  362. void x11_init_driver(caca_t *kk)
  363. {
  364. kk->driver.driver = CACA_DRIVER_X11;
  365. kk->driver.init_graphics = x11_init_graphics;
  366. kk->driver.end_graphics = x11_end_graphics;
  367. kk->driver.set_window_title = x11_set_window_title;
  368. kk->driver.get_window_width = x11_get_window_width;
  369. kk->driver.get_window_height = x11_get_window_height;
  370. kk->driver.display = x11_display;
  371. kk->driver.handle_resize = x11_handle_resize;
  372. kk->driver.get_event = x11_get_event;
  373. }
  374. #endif /* USE_X11 */