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.

x11.c 30 KiB

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