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.
 
 
 
 
 
 

344 lines
8.8 KiB

  1. /*
  2. * libcucul Unicode canvas 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 char.c
  12. * \version \$Id$
  13. * \author Sam Hocevar <sam@zoy.org>
  14. * \brief Character drawing
  15. *
  16. * This file contains character and string drawing functions.
  17. */
  18. #include "config.h"
  19. #if !defined(__KERNEL__)
  20. # include <stdio.h> /* BUFSIZ */
  21. # include <string.h>
  22. # include <stdlib.h>
  23. # include <stdarg.h>
  24. # if defined(HAVE_UNISTD_H)
  25. # include <unistd.h>
  26. # endif
  27. # if defined(HAVE_SIGNAL_H)
  28. # include <signal.h>
  29. # endif
  30. # if defined(HAVE_SYS_IOCTL_H)
  31. # include <sys/ioctl.h>
  32. # endif
  33. #endif
  34. #include "cucul.h"
  35. #include "cucul_internals.h"
  36. size_t utf8_strlen(const char *s);
  37. const char *utf8_skip(const char *s, size_t x);
  38. uint32_t utf8_to_utf32(const char *s);
  39. /** \brief Set the default colour pair.
  40. *
  41. * This function sets the default colour pair. String functions such as
  42. * caca_printf() and graphical primitive functions such as caca_draw_line()
  43. * will use these colour pairs.
  44. *
  45. * \param fgcolor The requested foreground colour.
  46. * \param bgcolor The requested background colour.
  47. */
  48. void cucul_set_color(cucul_t *qq, enum cucul_color fgcolor, enum cucul_color bgcolor)
  49. {
  50. if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
  51. return;
  52. qq->fgcolor = fgcolor;
  53. qq->bgcolor = bgcolor;
  54. }
  55. /** \brief Get the current foreground colour.
  56. *
  57. * This function returns the current foreground colour that was set with
  58. * cucul_set_color().
  59. *
  60. * \return The current foreground colour.
  61. */
  62. enum cucul_color cucul_get_fg_color(cucul_t *qq)
  63. {
  64. return qq->fgcolor;
  65. }
  66. /** \brief Get the current background colour.
  67. *
  68. * This function returns the current background colour that was set with
  69. * cucul_set_color().
  70. *
  71. * \return The current background colour.
  72. */
  73. enum cucul_color cucul_get_bg_color(cucul_t *qq)
  74. {
  75. return qq->bgcolor;
  76. }
  77. /** \brief Print an ASCII character.
  78. *
  79. * This function prints an ASCII character at the given coordinates, using
  80. * the default foreground and background values. If the coordinates are
  81. * outside the screen boundaries, nothing is printed. If the character
  82. * value is a non-printable character or is outside the ASCII range, it is
  83. * replaced with a space. To print a sequence of bytes forming an UTF-8
  84. * character, use cucul_putstr() instead.
  85. *
  86. * \param x X coordinate.
  87. * \param y Y coordinate.
  88. * \param c The character to print.
  89. */
  90. void cucul_putchar(cucul_t *qq, int x, int y, char c)
  91. {
  92. if(x < 0 || x >= (int)qq->width ||
  93. y < 0 || y >= (int)qq->height)
  94. return;
  95. if((unsigned char)c < 0x20 || (unsigned char)c > 0x7f)
  96. c = 0x20;
  97. qq->chars[x + y * qq->width] = c;
  98. qq->attr[x + y * qq->width] = (qq->bgcolor << 4) | qq->fgcolor;
  99. }
  100. /** \brief Print a Unicode character.
  101. *
  102. * FIXME: do we really want this function?
  103. *
  104. * This function prints a Unicode character (native-endian, 32 bits UCS-4,
  105. * also known as UTF-32) at the given coordinates, using the default
  106. * foreground and background values. If the coordinates are outside the
  107. * screen boundaries, nothing is printed. If the character is an invalid
  108. * Unicode character, it is replaced with a space.
  109. *
  110. * \param x X coordinate.
  111. * \param y Y coordinate.
  112. * \param c The character to print.
  113. */
  114. void cucul_putchar32(cucul_t *qq, int x, int y, unsigned long int c)
  115. {
  116. if(x < 0 || x >= (int)qq->width ||
  117. y < 0 || y >= (int)qq->height)
  118. return;
  119. if(c < 0x20 || c > 0x7f)
  120. c = 0x20;
  121. qq->chars[x + y * qq->width] = c;
  122. qq->attr[x + y * qq->width] = (qq->bgcolor << 4) | qq->fgcolor;
  123. }
  124. /** \brief Print a string.
  125. *
  126. * This function prints an UTF-8 string at the given coordinates, using the
  127. * default foreground and background values. The coordinates may be outside
  128. * the screen boundaries (eg. a negative Y coordinate) and the string will
  129. * be cropped accordingly if it is too long.
  130. *
  131. * \param x X coordinate.
  132. * \param y Y coordinate.
  133. * \param s The string to print.
  134. */
  135. void cucul_putstr(cucul_t *qq, int x, int y, char const *s)
  136. {
  137. uint32_t *chars;
  138. uint8_t *attr;
  139. unsigned int len;
  140. if(y < 0 || y >= (int)qq->height || x >= (int)qq->width)
  141. return;
  142. len = utf8_strlen(s);
  143. if(x < 0)
  144. {
  145. if(len < (unsigned int)-x)
  146. return;
  147. len -= -x;
  148. s = utf8_skip(s, -x);
  149. x = 0;
  150. }
  151. chars = qq->chars + x + y * qq->width;
  152. attr = qq->attr + x + y * qq->width;
  153. if(x + len >= qq->width)
  154. len = qq->width - x;
  155. while(len)
  156. {
  157. *chars++ = utf8_to_utf32(s);
  158. *attr++ = (qq->bgcolor << 4) | qq->fgcolor;
  159. s = utf8_skip(s, 1);
  160. len--;
  161. }
  162. }
  163. /** \brief Format a string.
  164. *
  165. * This function formats a string at the given coordinates, using the
  166. * default foreground and background values. The coordinates may be outside
  167. * the screen boundaries (eg. a negative Y coordinate) and the string will
  168. * be cropped accordingly if it is too long. The syntax of the format
  169. * string is the same as for the C printf() function.
  170. *
  171. * \param x X coordinate.
  172. * \param y Y coordinate.
  173. * \param format The format string to print.
  174. * \param ... Arguments to the format string.
  175. */
  176. void cucul_printf(cucul_t *qq, int x, int y, char const *format, ...)
  177. {
  178. char tmp[BUFSIZ];
  179. char *buf = tmp;
  180. va_list args;
  181. if(y < 0 || y >= (int)qq->height || x >= (int)qq->width)
  182. return;
  183. if(qq->width - x + 1 > BUFSIZ)
  184. buf = malloc(qq->width - x + 1);
  185. va_start(args, format);
  186. #if defined(HAVE_VSNPRINTF)
  187. vsnprintf(buf, qq->width - x + 1, format, args);
  188. #else
  189. vsprintf(buf, format, args);
  190. #endif
  191. buf[qq->width - x] = '\0';
  192. va_end(args);
  193. cucul_putstr(qq, x, y, buf);
  194. if(buf != tmp)
  195. free(buf);
  196. }
  197. /** \brief Get the screen.
  198. *
  199. * This function fills a byte array with the character values.
  200. */
  201. void cucul_get_screen(cucul_t *qq, char *buffer)
  202. {
  203. unsigned int x, y;
  204. for(y = 0; y < qq->height; y++)
  205. {
  206. for(x = 0; x < qq->width; x++)
  207. {
  208. *buffer++ = qq->attr[x + y * qq->width];
  209. *buffer++ = qq->chars[x + y * qq->width] & 0x7f; /* FIXME: ASCII */
  210. }
  211. }
  212. }
  213. /** \brief Clear the screen.
  214. *
  215. * This function clears the screen using a black background.
  216. */
  217. void cucul_clear(cucul_t *qq)
  218. {
  219. enum cucul_color oldfg = cucul_get_fg_color(qq);
  220. enum cucul_color oldbg = cucul_get_bg_color(qq);
  221. int y = qq->height;
  222. cucul_set_color(qq, CUCUL_COLOR_LIGHTGRAY, CUCUL_COLOR_BLACK);
  223. /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
  224. while(y--)
  225. cucul_putstr(qq, 0, y, qq->empty_line);
  226. cucul_set_color(qq, oldfg, oldbg);
  227. }
  228. /*
  229. * XXX: The following functions are local.
  230. */
  231. static const char trailing[256] =
  232. {
  233. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  234. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  235. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  236. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  237. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  238. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  239. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  240. 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
  241. };
  242. static const uint32_t offsets[6] =
  243. {
  244. 0x00000000UL, 0x00003080UL, 0x000E2080UL,
  245. 0x03C82080UL, 0xFA082080UL, 0x82082080UL
  246. };
  247. size_t utf8_strlen(const char *s)
  248. {
  249. int len = 0;
  250. const char *parser = s;
  251. while(*parser)
  252. {
  253. int i;
  254. int bytes = 1 + trailing[(int)(unsigned char)*parser];
  255. for(i = 1; i < bytes; i++)
  256. if(!parser[i])
  257. return len;
  258. parser += bytes;
  259. len++;
  260. }
  261. return len;
  262. }
  263. const char *utf8_skip(const char *s, size_t x)
  264. {
  265. const char *parser = s;
  266. while(x)
  267. {
  268. int i;
  269. int bytes = 1 + trailing[(int)(unsigned char)*parser];
  270. for(i = 1; i < bytes; i++)
  271. if(!parser[i])
  272. return parser;
  273. parser += bytes;
  274. x--;
  275. }
  276. return parser;
  277. }
  278. uint32_t utf8_to_utf32(const char *s)
  279. {
  280. int bytes = trailing[(int)(unsigned char)*s];
  281. uint32_t ret = 0;
  282. switch(bytes)
  283. {
  284. /* FIXME: do something for invalid sequences (4 and 5) */
  285. case 3: ret += (uint8_t)*s++; ret <<= 6;
  286. case 2: ret += (uint8_t)*s++; ret <<= 6;
  287. case 1: ret += (uint8_t)*s++; ret <<= 6;
  288. case 0: ret += (uint8_t)*s++;
  289. }
  290. ret -= offsets[bytes];
  291. return ret;
  292. }