You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 19 години
преди 9 години
преди 15 години
преди 19 години
преди 15 години
преди 15 години
преди 15 години
преди 15 години
преди 15 години
преди 15 години
преди 19 години
преди 19 години
преди 19 години
преди 19 години
преди 19 години
преди 19 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2015 Sam Hocevar <sam@hocevar.net>
  4. * 2007 Ben Wiley Sittler <bsittler@gmail.com>
  5. * All Rights Reserved
  6. *
  7. * This library is free software. It comes without any warranty, to
  8. * the extent permitted by applicable law. You can redistribute it
  9. * and/or modify it under the terms of the Do What the Fuck You Want
  10. * to Public License, Version 2, as published by Sam Hocevar. See
  11. * http://www.wtfpl.net/ for more details.
  12. */
  13. /*
  14. * This file contains the libcaca X11 input and output driver
  15. */
  16. #include "config.h"
  17. #if defined(USE_X11)
  18. #include <X11/Xlib.h>
  19. #include <X11/Xutil.h>
  20. #include <X11/keysym.h>
  21. #if defined HAVE_X11_XKBLIB_H
  22. # include <X11/XKBlib.h>
  23. #endif
  24. #include <stdio.h> /* BUFSIZ */
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #if defined HAVE_LOCALE_H
  28. # include <locale.h>
  29. #endif
  30. #include "caca.h"
  31. #include "caca_internals.h"
  32. /*
  33. * Local functions
  34. */
  35. static int x11_error_handler(Display *, XErrorEvent *);
  36. static void x11_put_glyph(caca_display_t *, int, int, int, int, int,
  37. uint32_t, uint32_t);
  38. struct driver_private
  39. {
  40. Display *dpy;
  41. Window window;
  42. Pixmap pixmap;
  43. GC gc;
  44. long int event_mask;
  45. int font_width, font_height;
  46. int colors[4096];
  47. #if defined X_HAVE_UTF8_STRING
  48. XFontSet font_set;
  49. #endif
  50. Font font;
  51. XFontStruct *font_struct;
  52. int font_offset;
  53. Cursor pointer;
  54. Atom wm_protocols;
  55. Atom wm_delete_window;
  56. #if defined HAVE_X11_XKBLIB_H
  57. Bool autorepeat;
  58. #endif
  59. uint32_t max_char;
  60. int cursor_flags;
  61. int dirty_cursor_x, dirty_cursor_y;
  62. #if defined X_HAVE_UTF8_STRING
  63. XIM im;
  64. XIC ic;
  65. #endif
  66. };
  67. #define UNICODE_XLFD_SUFFIX "-iso10646-1"
  68. #define LATIN_1_XLFD_SUFFIX "-iso8859-1"
  69. static int x11_init_graphics(caca_display_t *dp)
  70. {
  71. Colormap colormap;
  72. XSetWindowAttributes x11_winattr;
  73. #if defined X_HAVE_UTF8_STRING
  74. XVaNestedList list;
  75. #endif
  76. int (*old_error_handler)(Display *, XErrorEvent *);
  77. char const *fonts[] = { NULL, "8x13bold", "fixed", NULL }, **parser;
  78. char const *geometry;
  79. int width = caca_get_canvas_width(dp->cv);
  80. int height = caca_get_canvas_height(dp->cv);
  81. int i;
  82. dp->drv.p = malloc(sizeof(struct driver_private));
  83. #if defined HAVE_GETENV
  84. geometry = getenv("CACA_GEOMETRY");
  85. if(geometry && *geometry)
  86. sscanf(geometry, "%ux%u", &width, &height);
  87. #endif
  88. caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height);
  89. dp->resize.allow = 1;
  90. caca_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
  91. width = caca_get_canvas_width(dp->cv);
  92. height = caca_get_canvas_height(dp->cv);
  93. dp->resize.allow = 0;
  94. #if defined HAVE_LOCALE_H
  95. setlocale(LC_ALL, "");
  96. #endif
  97. dp->drv.p->dpy = XOpenDisplay(NULL);
  98. if(dp->drv.p->dpy == NULL)
  99. return -1;
  100. #if defined HAVE_GETENV
  101. fonts[0] = getenv("CACA_FONT");
  102. if(fonts[0] && *fonts[0])
  103. parser = fonts;
  104. else
  105. #endif
  106. parser = fonts + 1;
  107. /* Ignore font errors */
  108. old_error_handler = XSetErrorHandler(x11_error_handler);
  109. /* Parse our font list */
  110. for( ; ; parser++)
  111. {
  112. #if defined X_HAVE_UTF8_STRING
  113. char **missing_charset_list;
  114. char *def_string;
  115. int missing_charset_count;
  116. #endif
  117. uint32_t font_max_char;
  118. if(!*parser)
  119. {
  120. XSetErrorHandler(old_error_handler);
  121. XCloseDisplay(dp->drv.p->dpy);
  122. return -1;
  123. }
  124. #if defined X_HAVE_UTF8_STRING
  125. dp->drv.p->font_set = XCreateFontSet(dp->drv.p->dpy, *parser,
  126. &missing_charset_list,
  127. &missing_charset_count,
  128. &def_string);
  129. if (missing_charset_list)
  130. XFreeStringList(missing_charset_list);
  131. if (!dp->drv.p->font_set || missing_charset_count)
  132. {
  133. char buf[BUFSIZ];
  134. if (dp->drv.p->font_set)
  135. XFreeFontSet(dp->drv.p->dpy, dp->drv.p->font_set);
  136. snprintf(buf, BUFSIZ - 1, "%s,*", *parser);
  137. dp->drv.p->font_set = XCreateFontSet(dp->drv.p->dpy, buf,
  138. &missing_charset_list,
  139. &missing_charset_count,
  140. &def_string);
  141. if (missing_charset_list)
  142. XFreeStringList(missing_charset_list);
  143. }
  144. if (dp->drv.p->font_set)
  145. break;
  146. #endif
  147. dp->drv.p->font = XLoadFont(dp->drv.p->dpy, *parser);
  148. if(!dp->drv.p->font)
  149. continue;
  150. dp->drv.p->font_struct = XQueryFont(dp->drv.p->dpy, dp->drv.p->font);
  151. if(!dp->drv.p->font_struct)
  152. {
  153. XUnloadFont(dp->drv.p->dpy, dp->drv.p->font);
  154. continue;
  155. }
  156. if((strlen(*parser) > sizeof(UNICODE_XLFD_SUFFIX))
  157. && !strcasecmp(*parser + strlen(*parser)
  158. - strlen(UNICODE_XLFD_SUFFIX), UNICODE_XLFD_SUFFIX))
  159. dp->drv.p->max_char = 0xffff;
  160. else if((strlen(*parser) > sizeof(LATIN_1_XLFD_SUFFIX))
  161. && !strcasecmp(*parser + strlen(*parser)
  162. - strlen(LATIN_1_XLFD_SUFFIX), LATIN_1_XLFD_SUFFIX))
  163. dp->drv.p->max_char = 0xff;
  164. else
  165. dp->drv.p->max_char = 0x7f;
  166. font_max_char =
  167. (dp->drv.p->font_struct->max_byte1 << 8)
  168. | dp->drv.p->font_struct->max_char_or_byte2;
  169. if(font_max_char && (font_max_char < dp->drv.p->max_char))
  170. dp->drv.p->max_char = font_max_char;
  171. break;
  172. }
  173. /* Reset the default X11 error handler */
  174. XSetErrorHandler(old_error_handler);
  175. /* Set font width to the largest character in the set */
  176. #if defined X_HAVE_UTF8_STRING
  177. if (dp->drv.p->font_set)
  178. {
  179. /* Do not trust the fontset, because some fonts may have
  180. * irrelevantly large glyphs. Pango and rxvt use the same method. */
  181. static wchar_t const test[] =
  182. {
  183. '0', '1', '8', 'a', 'd', 'x', 'm', 'y', 'g', 'W', 'X', '\'', '_',
  184. 0x00cd, 0x00e7, 0x00d5, 0x0114, 0x0177, /* Í ç Õ Ĕ ŷ */
  185. /* 0x0643, */ /* ﻙ */
  186. 0x304c, 0x672c, /* が本 */
  187. 0,
  188. };
  189. XRectangle ink, logical;
  190. dp->drv.p->font_width =
  191. (XmbTextEscapement(dp->drv.p->font_set, "CAca", 4) + 2) / 4;
  192. XwcTextExtents(dp->drv.p->font_set, test, sizeof(test) / sizeof(*test),
  193. &ink, &logical);
  194. dp->drv.p->font_height = ink.height;
  195. dp->drv.p->font_offset = ink.height + ink.y;
  196. }
  197. else
  198. #endif
  199. {
  200. dp->drv.p->font_width = 0;
  201. if(dp->drv.p->font_struct->per_char
  202. && !dp->drv.p->font_struct->min_byte1
  203. && dp->drv.p->font_struct->min_char_or_byte2 <= 0x21
  204. && dp->drv.p->font_struct->max_char_or_byte2 >= 0x7e)
  205. {
  206. for(i = 0x21; i < 0x7f; i++)
  207. {
  208. int cw = dp->drv.p->font_struct->per_char[i
  209. - dp->drv.p->font_struct->min_char_or_byte2].width;
  210. if(cw > dp->drv.p->font_width)
  211. dp->drv.p->font_width = cw;
  212. }
  213. }
  214. if(!dp->drv.p->font_width)
  215. dp->drv.p->font_width = dp->drv.p->font_struct->max_bounds.width;
  216. dp->drv.p->font_height = dp->drv.p->font_struct->max_bounds.ascent
  217. + dp->drv.p->font_struct->max_bounds.descent;
  218. dp->drv.p->font_offset = dp->drv.p->font_struct->max_bounds.descent;
  219. }
  220. colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
  221. for(i = 0x000; i < 0x1000; i++)
  222. {
  223. XColor color;
  224. color.red = ((i & 0xf00) >> 8) * 0x1111;
  225. color.green = ((i & 0x0f0) >> 4) * 0x1111;
  226. color.blue = (i & 0x00f) * 0x1111;
  227. XAllocColor(dp->drv.p->dpy, colormap, &color);
  228. dp->drv.p->colors[i] = color.pixel;
  229. }
  230. x11_winattr.backing_store = Always;
  231. x11_winattr.background_pixel = dp->drv.p->colors[0x000];
  232. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  233. dp->drv.p->window =
  234. XCreateWindow(dp->drv.p->dpy, DefaultRootWindow(dp->drv.p->dpy), 0, 0,
  235. width * dp->drv.p->font_width,
  236. height * dp->drv.p->font_height,
  237. 0, 0, InputOutput, 0,
  238. CWBackingStore | CWBackPixel | CWEventMask,
  239. &x11_winattr);
  240. dp->drv.p->wm_protocols =
  241. XInternAtom(dp->drv.p->dpy, "WM_PROTOCOLS", True);
  242. dp->drv.p->wm_delete_window =
  243. XInternAtom(dp->drv.p->dpy, "WM_DELETE_WINDOW", True);
  244. if(dp->drv.p->wm_protocols != None && dp->drv.p->wm_delete_window != None)
  245. XSetWMProtocols(dp->drv.p->dpy, dp->drv.p->window,
  246. &dp->drv.p->wm_delete_window, 1);
  247. XStoreName(dp->drv.p->dpy, dp->drv.p->window, "caca for X");
  248. XSelectInput(dp->drv.p->dpy, dp->drv.p->window, StructureNotifyMask);
  249. XMapWindow(dp->drv.p->dpy, dp->drv.p->window);
  250. dp->drv.p->gc = XCreateGC(dp->drv.p->dpy, dp->drv.p->window, 0, NULL);
  251. XSetForeground(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->colors[0x888]);
  252. #if defined X_HAVE_UTF8_STRING
  253. if (!dp->drv.p->font_set)
  254. #endif
  255. XSetFont(dp->drv.p->dpy, dp->drv.p->gc, dp->drv.p->font);
  256. for(;;)
  257. {
  258. XEvent xevent;
  259. XNextEvent(dp->drv.p->dpy, &xevent);
  260. if(xevent.type == MapNotify)
  261. break;
  262. }
  263. #if defined HAVE_X11_XKBLIB_H
  264. /* Disable autorepeat */
  265. XkbSetDetectableAutoRepeat(dp->drv.p->dpy, True, &dp->drv.p->autorepeat);
  266. if(!dp->drv.p->autorepeat)
  267. XAutoRepeatOff(dp->drv.p->dpy);
  268. #endif
  269. dp->drv.p->event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
  270. | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
  271. | ExposureMask;
  272. XSelectInput(dp->drv.p->dpy, dp->drv.p->window, dp->drv.p->event_mask);
  273. XSync(dp->drv.p->dpy, False);
  274. dp->drv.p->pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
  275. width * dp->drv.p->font_width,
  276. height * dp->drv.p->font_height,
  277. DefaultDepth(dp->drv.p->dpy,
  278. DefaultScreen(dp->drv.p->dpy)));
  279. dp->drv.p->pointer = None;
  280. dp->drv.p->cursor_flags = 0;
  281. dp->drv.p->dirty_cursor_x = -1;
  282. dp->drv.p->dirty_cursor_y = -1;
  283. #if defined X_HAVE_UTF8_STRING
  284. list = XVaCreateNestedList(0, XNFontSet, dp->drv.p->font_set, NULL);
  285. dp->drv.p->im = XOpenIM(dp->drv.p->dpy, NULL, NULL, NULL);
  286. if (dp->drv.p->im == NULL) {
  287. fprintf(stderr, "x11 driver error: unable to open input method\n");
  288. return -1;
  289. }
  290. dp->drv.p->ic = XCreateIC(dp->drv.p->im,
  291. XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
  292. XNClientWindow, dp->drv.p->window,
  293. XNPreeditAttributes, list,
  294. XNStatusAttributes, list,
  295. NULL);
  296. if (dp->drv.p->ic == NULL) {
  297. fprintf(stderr, "x11 driver error: unable to create input context\n");
  298. return -1;
  299. }
  300. #endif
  301. return 0;
  302. }
  303. static int x11_end_graphics(caca_display_t *dp)
  304. {
  305. XSync(dp->drv.p->dpy, False);
  306. #if defined HAVE_X11_XKBLIB_H
  307. if(!dp->drv.p->autorepeat)
  308. XAutoRepeatOn(dp->drv.p->dpy);
  309. #endif
  310. XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
  311. #if defined X_HAVE_UTF8_STRING
  312. if (dp->drv.p->font_set)
  313. XFreeFontSet(dp->drv.p->dpy, dp->drv.p->font_set);
  314. else
  315. #endif
  316. XFreeFont(dp->drv.p->dpy, dp->drv.p->font_struct);
  317. XFreeGC(dp->drv.p->dpy, dp->drv.p->gc);
  318. XUnmapWindow(dp->drv.p->dpy, dp->drv.p->window);
  319. XDestroyWindow(dp->drv.p->dpy, dp->drv.p->window);
  320. #if defined X_HAVE_UTF8_STRING
  321. XDestroyIC(dp->drv.p->ic);
  322. XCloseIM(dp->drv.p->im);
  323. #endif
  324. XCloseDisplay(dp->drv.p->dpy);
  325. free(dp->drv.p);
  326. return 0;
  327. }
  328. static int x11_set_display_title(caca_display_t *dp, char const *title)
  329. {
  330. XStoreName(dp->drv.p->dpy, dp->drv.p->window, title);
  331. return 0;
  332. }
  333. static int x11_get_display_width(caca_display_t const *dp)
  334. {
  335. return caca_get_canvas_width(dp->cv) * dp->drv.p->font_width;
  336. }
  337. static int x11_get_display_height(caca_display_t const *dp)
  338. {
  339. return caca_get_canvas_height(dp->cv) * dp->drv.p->font_height;
  340. }
  341. static void x11_display(caca_display_t *dp)
  342. {
  343. uint32_t const *cvchars = caca_get_canvas_chars(dp->cv);
  344. uint32_t const *cvattrs = caca_get_canvas_attrs(dp->cv);
  345. int width = caca_get_canvas_width(dp->cv);
  346. int height = caca_get_canvas_height(dp->cv);
  347. int x, y, i, len;
  348. /* XXX: the magic value -1 is used to handle the cursor area */
  349. for(i = -1; i < caca_get_dirty_rect_count(dp->cv); i++)
  350. {
  351. int dx, dy, dw, dh;
  352. /* Get the dirty rectangle coordinates, either from the previous
  353. * cursor position, or from the canvas's list. */
  354. if(i == -1)
  355. {
  356. if(dp->drv.p->dirty_cursor_x < 0 || dp->drv.p->dirty_cursor_y < 0
  357. || dp->drv.p->dirty_cursor_x >= width
  358. || dp->drv.p->dirty_cursor_y >= height)
  359. continue;
  360. dx = dp->drv.p->dirty_cursor_x;
  361. dy = dp->drv.p->dirty_cursor_y;
  362. dw = dh = 1;
  363. dp->drv.p->dirty_cursor_x = -1;
  364. dp->drv.p->dirty_cursor_y = -1;
  365. }
  366. else
  367. {
  368. caca_get_dirty_rect(dp->cv, i, &dx, &dy, &dw, &dh);
  369. }
  370. /* First draw the background colours. Splitting the process in two
  371. * loops like this is actually slightly faster. */
  372. for(y = dy; y < dy + dh; y++)
  373. {
  374. for(x = dx; x < dx + dw; x += len)
  375. {
  376. uint32_t const *attrs = cvattrs + x + y * width;
  377. uint16_t bg = caca_attr_to_rgb12_bg(*attrs);
  378. len = 1;
  379. while(x + len < dx + dw
  380. && caca_attr_to_rgb12_bg(attrs[len]) == bg)
  381. len++;
  382. XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  383. dp->drv.p->colors[bg]);
  384. XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap,
  385. dp->drv.p->gc,
  386. x * dp->drv.p->font_width,
  387. y * dp->drv.p->font_height,
  388. len * dp->drv.p->font_width,
  389. dp->drv.p->font_height);
  390. }
  391. }
  392. /* Then print the foreground characters */
  393. for(y = dy; y < dy + dh; y++)
  394. {
  395. int yoff = (y + 1) * dp->drv.p->font_height
  396. - dp->drv.p->font_offset;
  397. uint32_t const *chars = cvchars + dx + y * width;
  398. uint32_t const *attrs = cvattrs + dx + y * width;
  399. for(x = dx; x < dx + dw; x++, chars++, attrs++)
  400. {
  401. XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  402. dp->drv.p->colors[caca_attr_to_rgb12_fg(*attrs)]);
  403. x11_put_glyph(dp, x * dp->drv.p->font_width,
  404. y * dp->drv.p->font_height, yoff,
  405. dp->drv.p->font_width, dp->drv.p->font_height,
  406. *attrs, *chars);
  407. }
  408. }
  409. }
  410. /* Print the cursor if necessary. */
  411. if(dp->drv.p->cursor_flags)
  412. {
  413. XSetForeground(dp->drv.p->dpy, dp->drv.p->gc,
  414. dp->drv.p->colors[0xfff]);
  415. x = caca_wherex(dp->cv);
  416. y = caca_wherey(dp->cv);
  417. XFillRectangle(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->gc,
  418. x * dp->drv.p->font_width, y * dp->drv.p->font_height,
  419. dp->drv.p->font_width, dp->drv.p->font_height);
  420. /* Mark the area as dirty */
  421. dp->drv.p->dirty_cursor_x = x;
  422. dp->drv.p->dirty_cursor_y = y;
  423. }
  424. XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, dp->drv.p->window,
  425. dp->drv.p->gc, 0, 0,
  426. width * dp->drv.p->font_width,
  427. height * dp->drv.p->font_height,
  428. 0, 0);
  429. XFlush(dp->drv.p->dpy);
  430. }
  431. static void x11_handle_resize(caca_display_t *dp)
  432. {
  433. Pixmap new_pixmap;
  434. new_pixmap = XCreatePixmap(dp->drv.p->dpy, dp->drv.p->window,
  435. dp->resize.w * dp->drv.p->font_width,
  436. dp->resize.h * dp->drv.p->font_height,
  437. DefaultDepth(dp->drv.p->dpy,
  438. DefaultScreen(dp->drv.p->dpy)));
  439. XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap, new_pixmap,
  440. dp->drv.p->gc, 0, 0,
  441. dp->resize.w * dp->drv.p->font_width,
  442. dp->resize.h * dp->drv.p->font_height, 0, 0);
  443. XFreePixmap(dp->drv.p->dpy, dp->drv.p->pixmap);
  444. dp->drv.p->pixmap = new_pixmap;
  445. }
  446. static int x11_get_event(caca_display_t *dp, caca_privevent_t *ev)
  447. {
  448. int width = caca_get_canvas_width(dp->cv);
  449. int height = caca_get_canvas_height(dp->cv);
  450. XEvent xevent;
  451. char key;
  452. while(XCheckWindowEvent(dp->drv.p->dpy, dp->drv.p->window,
  453. dp->drv.p->event_mask, &xevent) == True)
  454. {
  455. KeySym keysym;
  456. #if defined X_HAVE_UTF8_STRING
  457. int len;
  458. #endif
  459. /* Expose event */
  460. if(xevent.type == Expose)
  461. {
  462. XCopyArea(dp->drv.p->dpy, dp->drv.p->pixmap,
  463. dp->drv.p->window, dp->drv.p->gc, 0, 0,
  464. width * dp->drv.p->font_width,
  465. height * dp->drv.p->font_height, 0, 0);
  466. continue;
  467. }
  468. /* Resize event */
  469. if(xevent.type == ConfigureNotify)
  470. {
  471. int w, h;
  472. w = (xevent.xconfigure.width + dp->drv.p->font_width / 3)
  473. / dp->drv.p->font_width;
  474. h = (xevent.xconfigure.height + dp->drv.p->font_height / 3)
  475. / dp->drv.p->font_height;
  476. if(!w || !h || (w == width && h == height))
  477. continue;
  478. dp->resize.w = w;
  479. dp->resize.h = h;
  480. dp->resize.resized = 1;
  481. continue;
  482. }
  483. /* Check for mouse motion events */
  484. if(xevent.type == MotionNotify)
  485. {
  486. int newx = xevent.xmotion.x / dp->drv.p->font_width;
  487. int newy = xevent.xmotion.y / dp->drv.p->font_height;
  488. if(newx >= width)
  489. newx = width - 1;
  490. if(newy >= height)
  491. newy = height - 1;
  492. if(dp->mouse.x == newx && dp->mouse.y == newy)
  493. continue;
  494. dp->mouse.x = newx;
  495. dp->mouse.y = newy;
  496. ev->type = CACA_EVENT_MOUSE_MOTION;
  497. ev->data.mouse.x = dp->mouse.x;
  498. ev->data.mouse.y = dp->mouse.y;
  499. return 1;
  500. }
  501. /* Check for mouse press and release events */
  502. if(xevent.type == ButtonPress)
  503. {
  504. ev->type = CACA_EVENT_MOUSE_PRESS;
  505. ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  506. return 1;
  507. }
  508. if(xevent.type == ButtonRelease)
  509. {
  510. ev->type = CACA_EVENT_MOUSE_RELEASE;
  511. ev->data.mouse.button = ((XButtonEvent *)&xevent)->button;
  512. return 1;
  513. }
  514. /* Check for key press and release events */
  515. if(xevent.type == KeyPress)
  516. ev->type = CACA_EVENT_KEY_PRESS;
  517. else if(xevent.type == KeyRelease)
  518. ev->type = CACA_EVENT_KEY_RELEASE;
  519. else
  520. continue;
  521. if(XFilterEvent(&xevent, None) == True)
  522. continue;
  523. #if defined X_HAVE_UTF8_STRING
  524. len = Xutf8LookupString(dp->drv.p->ic, &xevent.xkey,
  525. ev->data.key.utf8, 8, NULL, NULL);
  526. ev->data.key.utf8[len] = 0;
  527. if (len)
  528. {
  529. ev->data.key.utf32 = caca_utf8_to_utf32(ev->data.key.utf8, NULL);
  530. if(ev->data.key.utf32 <= 0xff)
  531. ev->data.key.ch = ev->data.key.utf32;
  532. else
  533. ev->data.key.ch = 0;
  534. return 1;
  535. }
  536. #endif
  537. if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
  538. {
  539. ev->data.key.ch = key;
  540. ev->data.key.utf32 = key;
  541. ev->data.key.utf8[0] = key;
  542. ev->data.key.utf8[1] = '\0';
  543. return 1;
  544. }
  545. keysym = XLookupKeysym(&xevent.xkey, 0);
  546. switch(keysym)
  547. {
  548. case XK_F1: ev->data.key.ch = CACA_KEY_F1; break;
  549. case XK_F2: ev->data.key.ch = CACA_KEY_F2; break;
  550. case XK_F3: ev->data.key.ch = CACA_KEY_F3; break;
  551. case XK_F4: ev->data.key.ch = CACA_KEY_F4; break;
  552. case XK_F5: ev->data.key.ch = CACA_KEY_F5; break;
  553. case XK_F6: ev->data.key.ch = CACA_KEY_F6; break;
  554. case XK_F7: ev->data.key.ch = CACA_KEY_F7; break;
  555. case XK_F8: ev->data.key.ch = CACA_KEY_F8; break;
  556. case XK_F9: ev->data.key.ch = CACA_KEY_F9; break;
  557. case XK_F10: ev->data.key.ch = CACA_KEY_F10; break;
  558. case XK_F11: ev->data.key.ch = CACA_KEY_F11; break;
  559. case XK_F12: ev->data.key.ch = CACA_KEY_F12; break;
  560. case XK_F13: ev->data.key.ch = CACA_KEY_F13; break;
  561. case XK_F14: ev->data.key.ch = CACA_KEY_F14; break;
  562. case XK_F15: ev->data.key.ch = CACA_KEY_F15; break;
  563. case XK_Left: ev->data.key.ch = CACA_KEY_LEFT; break;
  564. case XK_Right: ev->data.key.ch = CACA_KEY_RIGHT; break;
  565. case XK_Up: ev->data.key.ch = CACA_KEY_UP; break;
  566. case XK_Down: ev->data.key.ch = CACA_KEY_DOWN; break;
  567. case XK_KP_Page_Up:
  568. case XK_Page_Up: ev->data.key.ch = CACA_KEY_PAGEUP; break;
  569. case XK_KP_Page_Down:
  570. case XK_Page_Down: ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
  571. case XK_KP_Home:
  572. case XK_Home: ev->data.key.ch = CACA_KEY_HOME; break;
  573. case XK_KP_End:
  574. case XK_End: ev->data.key.ch = CACA_KEY_END; break;
  575. default: ev->type = CACA_EVENT_NONE; return 0;
  576. }
  577. ev->data.key.utf32 = 0;
  578. ev->data.key.utf8[0] = '\0';
  579. return 1;
  580. }
  581. while(XCheckTypedEvent(dp->drv.p->dpy, ClientMessage, &xevent))
  582. {
  583. if(xevent.xclient.message_type != dp->drv.p->wm_protocols)
  584. continue;
  585. if((Atom)xevent.xclient.data.l[0] == dp->drv.p->wm_delete_window)
  586. {
  587. ev->type = CACA_EVENT_QUIT;
  588. return 1;
  589. }
  590. }
  591. ev->type = CACA_EVENT_NONE;
  592. return 0;
  593. }
  594. static void x11_set_mouse(caca_display_t *dp, int flags)
  595. {
  596. Cursor no_ptr;
  597. Pixmap bm_no;
  598. XColor black, dummy;
  599. Colormap colormap;
  600. static char const empty[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  601. if(flags)
  602. {
  603. XDefineCursor(dp->drv.p->dpy,dp->drv.p->window, 0);
  604. return;
  605. }
  606. colormap = DefaultColormap(dp->drv.p->dpy, DefaultScreen(dp->drv.p->dpy));
  607. if(!XAllocNamedColor(dp->drv.p->dpy, colormap, "black", &black, &dummy))
  608. {
  609. return;
  610. }
  611. bm_no = XCreateBitmapFromData(dp->drv.p->dpy, dp->drv.p->window,
  612. empty, 8, 8);
  613. no_ptr = XCreatePixmapCursor(dp->drv.p->dpy, bm_no, bm_no,
  614. &black, &black, 0, 0);
  615. XDefineCursor(dp->drv.p->dpy, dp->drv.p->window, no_ptr);
  616. XFreeCursor(dp->drv.p->dpy, no_ptr);
  617. if(bm_no != None)
  618. XFreePixmap(dp->drv.p->dpy, bm_no);
  619. XFreeColors(dp->drv.p->dpy, colormap, &black.pixel, 1, 0);
  620. XSync(dp->drv.p->dpy, False);
  621. }
  622. static void x11_set_cursor(caca_display_t *dp, int flags)
  623. {
  624. dp->drv.p->cursor_flags = flags;
  625. }
  626. /*
  627. * XXX: following functions are local
  628. */
  629. static int x11_error_handler(Display *dpy, XErrorEvent *xevent)
  630. {
  631. /* Ignore the error */
  632. return 0;
  633. }
  634. static void x11_put_glyph(caca_display_t *dp, int x, int y, int yoff,
  635. int w, int h, uint32_t attr, uint32_t ch)
  636. {
  637. static uint8_t const udlr[] =
  638. {
  639. /* 0x2500 - 0x250f: ─ . │ . . . . . . . . . ┌ . . . */
  640. 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
  641. 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
  642. /* 0x2510 - 0x251f: ┐ . . . └ . . . ┘ . . . ├ . . . */
  643. 0x14, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
  644. 0x44, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
  645. /* 0x2520 - 0x252f: . . . . ┤ . . . . . . . ┬ . . . */
  646. 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
  647. 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
  648. /* 0x2530 - 0x253f: . . . . ┴ . . . . . . . ┼ . . . */
  649. 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
  650. 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
  651. /* 0x2540 - 0x254f: . . . . . . . . . . . . . . . . */
  652. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  653. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  654. /* 0x2550 - 0x255f: ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ */
  655. 0x0a, 0xa0, 0x12, 0x21, 0x22, 0x18, 0x24, 0x28,
  656. 0x42, 0x81, 0x82, 0x48, 0x84, 0x88, 0x52, 0xa1,
  657. /* 0x2560 - 0x256c: ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ */
  658. 0xa2, 0x58, 0xa4, 0xa8, 0x1a, 0x25, 0x2a, 0x4a,
  659. 0x85, 0x8a, 0x5a, 0xa5, 0xaa,
  660. };
  661. Display *dpy = dp->drv.p->dpy;
  662. Pixmap px = dp->drv.p->pixmap;
  663. GC gc = dp->drv.p->gc;
  664. int fw;
  665. /* Underline */
  666. if(attr & CACA_UNDERLINE)
  667. XFillRectangle(dpy, px, gc, x, y + h - 1, w, 1);
  668. /* Skip spaces and magic stuff */
  669. if(ch <= 0x00000020)
  670. return;
  671. if(ch == CACA_MAGIC_FULLWIDTH)
  672. return;
  673. fw = w;
  674. if(caca_utf32_is_fullwidth(ch))
  675. fw *= 2;
  676. /* We want to be able to print a few special Unicode characters
  677. * such as the CP437 gradients and half blocks. For unknown
  678. * characters, print what caca_utf32_to_ascii() returns. */
  679. if(ch >= 0x2500 && ch <= 0x256c && udlr[ch - 0x2500])
  680. {
  681. uint16_t D = udlr[ch - 0x2500];
  682. if(D & 0x04)
  683. XFillRectangle(dpy, px, gc, x, y + h / 2, fw / 2 + 1, 1);
  684. if(D & 0x01)
  685. XFillRectangle(dpy, px, gc,
  686. x + fw / 2, y + h / 2, (fw + 1) / 2, 1);
  687. if(D & 0x40)
  688. XFillRectangle(dpy, px, gc, x + fw / 2, y, 1, h / 2 + 1);
  689. if(D & 0x10)
  690. XFillRectangle(dpy, px, gc, x + fw / 2, y + h / 2, 1, (h + 1) / 2);
  691. #define STEPIF(a,b) (D&(a)?-1:(D&(b))?1:0)
  692. if(D & 0x08)
  693. {
  694. XFillRectangle(dpy, px, gc, x, y - 1 + h / 2,
  695. fw / 2 + 1 + STEPIF(0xc0,0x20), 1);
  696. XFillRectangle(dpy, px, gc, x, y + 1 + h / 2,
  697. fw / 2 + 1 + STEPIF(0x30,0x80), 1);
  698. }
  699. if(D & 0x02)
  700. {
  701. XFillRectangle(dpy, px, gc, x - STEPIF(0xc0,0x20) + fw / 2,
  702. y - 1 + h / 2, (fw + 1) / 2 + STEPIF(0xc0,0x20), 1);
  703. XFillRectangle(dpy, px, gc, x - STEPIF(0x30,0x80) + fw / 2,
  704. y + 1 + h / 2, (fw + 1) / 2 + STEPIF(0x30,0x80), 1);
  705. }
  706. if(D & 0x80)
  707. {
  708. XFillRectangle(dpy, px, gc, x - 1 + fw / 2, y,
  709. 1, h / 2 + 1 + STEPIF(0x0c,0x02));
  710. XFillRectangle(dpy, px, gc, x + 1 + fw / 2, y,
  711. 1, h / 2 + 1 + STEPIF(0x03,0x08));
  712. }
  713. if(D & 0x20)
  714. {
  715. XFillRectangle(dpy, px, gc, x - 1 + fw / 2,
  716. y - STEPIF(0x0c,0x02) + h / 2,
  717. 1, (h + 1) / 2 + STEPIF(0x0c,0x02));
  718. XFillRectangle(dpy, px, gc, x + 1 + fw / 2,
  719. y - STEPIF(0x03,0x08) + h / 2,
  720. 1, (h + 1) / 2 + STEPIF(0x03,0x08));
  721. }
  722. return;
  723. }
  724. switch(ch)
  725. {
  726. case 0x000000b7: /* · */
  727. case 0x00002219: /* ∙ */
  728. case 0x000030fb: /* ・ */
  729. XFillRectangle(dpy, px, gc, x + fw / 2 - 1, y + h / 2 - 1, 2, 2);
  730. return;
  731. case 0x00002261: /* ≡ */
  732. XFillRectangle(dpy, px, gc, x + 1, y - 2 + h / 2, fw - 1, 1);
  733. XFillRectangle(dpy, px, gc, x + 1, y + h / 2, fw - 1, 1);
  734. XFillRectangle(dpy, px, gc, x + 1, y + 2 + h / 2, fw - 1, 1);
  735. return;
  736. case 0x00002580: /* ▀ */
  737. XFillRectangle(dpy, px, gc, x, y, fw, h / 2);
  738. return;
  739. case 0x00002584: /* ▄ */
  740. XFillRectangle(dpy, px, gc, x, y + h - h / 2, fw, h / 2);
  741. return;
  742. case 0x00002588: /* █ */
  743. case 0x000025ae: /* ▮ */
  744. XFillRectangle(dpy, px, gc, x, y, fw, h);
  745. return;
  746. case 0x0000258c: /* ▌ */
  747. XFillRectangle(dpy, px, gc, x, y, fw / 2, h);
  748. return;
  749. case 0x00002590: /* ▐ */
  750. XFillRectangle(dpy, px, gc, x + fw - fw / 2, y, fw / 2, h);
  751. return;
  752. case 0x000025a0: /* ■ */
  753. case 0x000025ac: /* ▬ */
  754. XFillRectangle(dpy, px, gc, x, y + h / 4, fw, h / 2);
  755. return;
  756. case 0x00002593: /* ▓ */
  757. case 0x00002592: /* ▒ */
  758. case 0x00002591: /* ░ */
  759. {
  760. /* FIXME: this sucks utterly */
  761. int i, j, k = ch - 0x00002591;
  762. for(j = h; j--; )
  763. for(i = fw; i--; )
  764. {
  765. if(((i + 2 * (j & 1)) & 3) > k)
  766. continue;
  767. XDrawPoint(dpy, px, gc, x + i, y + j);
  768. }
  769. return;
  770. }
  771. case 0x000025cb: /* ○ */
  772. case 0x00002022: /* • */
  773. case 0x000025cf: /* ● */
  774. {
  775. int d, xo, yo;
  776. d = fw >> (~ch & 0x1); /* XXX: hack */
  777. if(h < fw)
  778. d = h;
  779. if(d < 1)
  780. d = 1;
  781. xo = (fw - d) / 2;
  782. yo = (h - d) / 2;
  783. if(ch == 0x000025cb)
  784. XDrawArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
  785. else
  786. XFillArc(dpy, px, gc, x + xo, y + yo, d, d, 0, 64 * 360);
  787. return;
  788. }
  789. }
  790. #if defined X_HAVE_UTF8_STRING
  791. if (dp->drv.p->font_set)
  792. {
  793. wchar_t wch = ch;
  794. XwcDrawString(dpy, px, dp->drv.p->font_set, gc, x, yoff, &wch, 1);
  795. }
  796. else
  797. #endif
  798. {
  799. XChar2b ch16;
  800. #if !defined X_HAVE_UTF8_STRING
  801. if(ch > dp->drv.p->max_char)
  802. {
  803. ch16.byte1 = 0;
  804. ch16.byte2 = caca_utf32_to_ascii(ch);
  805. }
  806. else
  807. #endif
  808. {
  809. ch16.byte1 = (uint8_t)(ch >> 8);
  810. ch16.byte2 = (uint8_t)ch;
  811. }
  812. XDrawString16(dpy, px, gc, x, yoff, &ch16, 1);
  813. }
  814. }
  815. /*
  816. * Driver initialisation
  817. */
  818. int x11_install(caca_display_t *dp)
  819. {
  820. #if defined HAVE_GETENV
  821. if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
  822. return -1;
  823. #endif
  824. dp->drv.id = CACA_DRIVER_X11;
  825. dp->drv.driver = "x11";
  826. dp->drv.init_graphics = x11_init_graphics;
  827. dp->drv.end_graphics = x11_end_graphics;
  828. dp->drv.set_display_title = x11_set_display_title;
  829. dp->drv.get_display_width = x11_get_display_width;
  830. dp->drv.get_display_height = x11_get_display_height;
  831. dp->drv.display = x11_display;
  832. dp->drv.handle_resize = x11_handle_resize;
  833. dp->drv.get_event = x11_get_event;
  834. dp->drv.set_mouse = x11_set_mouse;
  835. dp->drv.set_cursor = x11_set_cursor;
  836. return 0;
  837. }
  838. #endif /* USE_X11 */