1 /*-----------------------------------*-C-*-------------------------------------*
2 * File : xftacs.c
3 * Created : Tue 27 Dec 2005 09:59:55 PM CST
4 * Modified : Wed 12 Apr 2006 01:33:43 AM CDT
5 * Author : Gautam Iyer <gi1242@users.sourceforge.net>
6 *-----------------------------------------------------------------------------*
7 *
8 * All portions of code are copyright by their respective author/s.
9 *
10 * Copyright 2005-2006 Gautam Iyer <gi1242@users.sourceforge.net>
11 *
12 * This program is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the License, or (at your option) any later
15 * version.
16 *
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * this program; if not, write to the Free Software Foundation, Inc., 675 Mass
24 * Ave, Cambridge, MA 02139, USA.
25 *-----------------------------------------------------------------------------*
26 *
27 * DESCRIPTION
28 *
29 * Functions to draw VT100 / ACS graphics charecters. Xft functions do not
30 * draw text graphic charecters (like XDrawText), so we have to do it
31 * ourself. Partly plagurized with thanks from Thomas Dicky's xterm-204:
32 *
33 * http://invisible-island.net/xterm/xterm.html
34 *
35 * This file has nothing specific to rxvt. Instead of using XftDrawString,
36 * use xftDrawACSString. Draws the non-graphics chars using a regular Xft
37 * function, and graphics chars using XftGlyphs (if available) or line
38 * segments.
39 *
40 * Before your first call to xftDrawACSString, call xftInitACS(). After
41 * your last call to xftDrawACSString, call xftCloseACS().
42 *
43 * See the comments before each function definition for details.
44 *
45 * BUGS
46 *
47 * 1. Complex chars are drawn using a glyph from the Xft font. Not all
48 * fonts have these glyphs. Some have them in a different location than
49 * we expect.
50 *
51 * The fonts "Andale Mono", "Bitstream Vera Sans Mono" and "Courier New"
52 * HAVE the required glyphs at the positions below. The fonts "Courier",
53 * "Luxi Mono" and "Nimbus Mono L" DO NOT have the required glyphs.
54 *
55 *----------------------------------------------------------------------------*/
56
57 #include "../config.h"
58 #include "rxvt.h"
59
60 #include <xftacs.h>
61
62
63 /*
64 * Maximum number of glyphs to be drawn per call to XftDrawGlyphs.
65 */
66 #define MAX_GLYPHS (32)
67
68 /*
69 * Global variables that need to be inited on startup.
70 */
71 GC acsGc=0; /* GC used for all drawing requests */
72 Pixmap sPmap=0; /* Stippled pixmap used for tiling */
73
74 /*
75 * The grid is arbitrary, enough resolution that nothing's lost in
76 * initialization.
77 */
78 #define BOX_HIGH 60
79 #define BOX_WIDE 60
80
81 #define MID_HIGH (BOX_HIGH/2)
82 #define MID_WIDE (BOX_WIDE/2)
83
84 #define CHR_WIDE ((9*BOX_WIDE)/10)
85 #define CHR_HIGH ((9*BOX_HIGH)/10)
86
87 /*
88 * ...since we'll scale the values anyway.
89 */
90 #define SCALE_X(n) n = (n * (font_width-1)) / (BOX_WIDE-1)
91 #define SCALE_Y(n) n = (n * (font_height-1)) / (BOX_HIGH-1)
92
93 #define SEG(x0,y0,x1,y1) x0,y0, x1,y1
94
95 /*
96 * XError handler for xftInitACS. This only sets sPmap to 0, and complains.
97 */
98 int
acsXErrorHandler(Display * dpy,XErrorEvent * event)99 acsXErrorHandler( __attribute__((unused)) Display *dpy,
100 __attribute__((unused)) XErrorEvent *event)
101 {
102
103 sPmap = 0;
104
105 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "Could not create pixmap\n"));
106 return 0;
107 }
108
109 /*
110 * Initialize sPmap. Call this before calling xftDrawACSString(). If d or depth
111 * are 0, then default values are used.
112 *
113 * You will probabaly get a BadMatch error if you try using xftDrawACSString on
114 * a drawable of a different depth.
115 */
116 void
xftInitACS(Display * dpy,Drawable d,unsigned depth)117 xftInitACS( Display *dpy, Drawable d, unsigned depth)
118 {
119 int (*oldXerrorHandler)( Display *, XErrorEvent *);
120
121 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "Initing sPmap\n"));
122
123 if(d == 0) d = DefaultRootWindow( dpy);
124 if(depth == 0) depth=DefaultDepth( dpy, DefaultScreen( dpy));
125
126 #ifdef DEBUG
127 if( sPmap != 0)
128 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "sPmap not null in xftInitACS"));
129 #endif
130
131 acsGc = XCreateGC( dpy, d, 0, NULL);
132
133 oldXerrorHandler = XSetErrorHandler( (XErrorHandler) acsXErrorHandler);
134 sPmap = XCreatePixmap( dpy, d, 2, 2, depth);
135 XSetErrorHandler( oldXerrorHandler);
136
137 XSetTile( dpy, acsGc, sPmap);
138 }
139
140 /*
141 * Free sPmap. Call this after your last call to xftDrawACSString, or you WILL
142 * have a memory leak.
143 */
144 void
xftCloseACS(Display * dpy)145 xftCloseACS( Display *dpy)
146 {
147 if( sPmap )
148 {
149 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "freeing sPmap\n"));
150
151 XFreePixmap( dpy, sPmap);
152 sPmap = 0;
153 }
154 #ifdef DEBUG
155 else
156 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "sPmap already null in xftCloseACS"));
157 #endif
158
159 XFreeGC( dpy, acsGc);
160 }
161
162 /*
163 * INTERNAL USE ONLY. Draw a ACS graphics character on screen at x,y. Like XFT
164 * functions, we do not clear the background before drawing.
165 *
166 * (x,y) is the bottom left corner of the character to draw.
167 *
168 * WARNING: If any char in *str has ascii value >= 32, then this function will
169 * get stuck in an infinite loop.
170 */
171 void
xftDrawACSChars(Display * dpy,Drawable d,GC gc,XftDraw * draw,const XftColor * color,XftFont * pub,int x,int y,const unsigned char * str,int len)172 xftDrawACSChars(
173 Display *dpy, Drawable d, GC gc,
174 XftDraw *draw, const XftColor *color, XftFont *pub,
175 int x, int y, const unsigned char *str, int len)
176 {
177
178 /*
179 * Line segments to draw line like chars.
180 */
181 static const short glyph_ht[] = {
182 SEG( 0, 0, 0, 5*MID_HIGH/6), /* H */
183 SEG(6*BOX_WIDE/10, 0, 6*BOX_WIDE/10,5*MID_HIGH/6),
184 SEG( 0, 5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
185 SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */
186 SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH),
187 -1
188 }, glyph_ff[] = {
189 SEG( 0, 0, 6*BOX_WIDE/10, 0), /* F */
190 SEG( 0, 5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
191 SEG( 0, 0, 0*BOX_WIDE/3, 5*MID_HIGH/6),
192 SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */
193 SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
194 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH),
195 -1
196 }, glyph_lf[] = {
197 SEG( 0, 0, 0, 5*MID_HIGH/6), /* L */
198 SEG( 0, 5*MID_HIGH/6, 6*BOX_WIDE/10,5*MID_HIGH/6),
199 SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */
200 SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
201 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH),
202 -1
203 }, glyph_nl[] = {
204 SEG( 0, 5*MID_HIGH/6, 0, 0), /* N */
205 SEG( 0, 0, 5*BOX_WIDE/6, 5*MID_HIGH/6),
206 SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6, 0),
207 SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH), /* L */
208 SEG(1*BOX_WIDE/3, CHR_HIGH, CHR_WIDE, CHR_HIGH),
209 -1
210 }, glyph_vt[] = {
211 SEG( 0, 0, 5*BOX_WIDE/12,5*MID_HIGH/6), /* V */
212 SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6, 0),
213 SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */
214 SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH),
215 -1
216 }, lower_right_corner[] =
217 {
218 SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH),
219 SEG( MID_WIDE, MID_HIGH, MID_WIDE, 0),
220 -1
221 }, upper_right_corner[] =
222 {
223 SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH),
224 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
225 -1
226 }, upper_left_corner[] =
227 {
228 SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH),
229 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
230 -1
231 }, lower_left_corner[] =
232 {
233 SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH),
234 SEG( MID_WIDE, MID_WIDE, BOX_WIDE, MID_HIGH),
235 -1
236 }, cross[] =
237 {
238 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
239 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
240 -1
241 }, left_tee[] =
242 {
243 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
244 SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH),
245 -1
246 }, right_tee[] =
247 {
248 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
249 SEG( MID_WIDE, MID_HIGH, 0, MID_HIGH),
250 -1
251 }, bottom_tee[] =
252 {
253 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
254 SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH),
255 -1
256 }, top_tee[] =
257 {
258 SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
259 SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
260 -1
261 }, vertical_line[] =
262 {
263 SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
264 -1
265 };
266
267 /*
268 * Pointer to line-segment structure.
269 */
270 static const short *lines[] = {
271 NULL, /* 00 (unused) */
272 NULL, /* 01 diamond */
273 NULL, /* 02 box */
274 glyph_ht, /* 03 HT */
275 glyph_ff, /* 04 FF */
276 NULL, /* 05 CR (not drawn) */
277 glyph_lf, /* 06 LF */
278 NULL, /* 07 degrees (small circle) */
279 NULL, /* 08 plus or minus*/
280 glyph_nl, /* 09 */
281 glyph_vt, /* 0A */
282 lower_right_corner, /* 0B */
283 upper_right_corner, /* 0C */
284 upper_left_corner, /* 0D */
285 lower_left_corner, /* 0E */
286 cross, /* 0F */
287 NULL, /* 10 overline */
288 NULL, /* 11 topline */
289 NULL, /* 12 midline */
290 NULL, /* 13 botline */
291 NULL, /* 14 underline */
292 left_tee, /* 15 */
293 right_tee, /* 16 */
294 bottom_tee, /* 17 */
295 top_tee, /* 18 */
296 vertical_line, /* 19 */
297 NULL, /* 1A leq */
298 NULL, /* 1B geq */
299 NULL, /* 1C pi */
300 NULL, /* 1D neq */
301 NULL, /* 1E pound */
302 NULL, /* 1F bullet */
303 };
304
305 /*
306 * Character number in XftFont (if any).
307 */
308 static const FT_UInt xftCharNo[] = {
309 0, /* 00 (unused) */
310 0, /* 01 diamond */
311 0, /* 02 box */
312 0, /* 03 HT */
313 0, /* 04 FF */
314 0x8b, /* 05 CR (drawn as (c) ) */
315 0, /* 06 LF */
316 0x83, /* 07 degrees (small circle) */
317 0x93, /* 08 plus or minus*/
318 0, /* 09 */
319 0, /* 0A */
320 0, /* 0B */
321 0, /* 0C */
322 0, /* 0D */
323 0, /* 0E */
324 0, /* 0F */
325 0, /* 10 overline */
326 0, /* 11 topline */
327 0, /* 12 midline */
328 0, /* 13 botline */
329 0, /* 14 underline */
330 0, /* 15 */
331 0, /* 16 */
332 0, /* 17 */
333 0, /* 18 */
334 0, /* 19 */
335 0x94, /* 1A leq */
336 0x95, /* 1B geq */
337 0x9b, /* 1C pi */
338 0x8f, /* 1D neq */
339 0x85, /* 1E pound */
340 0x87 /* 1F bullet */
341 };
342
343 unsigned font_width = pub->max_advance_width;
344 unsigned font_height = pub->ascent + pub->descent;
345 const short *p;
346 FT_UInt glyphs[MAX_GLYPHS];
347
348 int ytop = y - pub->ascent; /* (x, ytop) is the top left corner */
349
350 /*
351 * Update fill styles in acsGc
352 */
353 XSetLineAttributes(dpy, acsGc, (font_height > 16) ? font_height / 16 : 1,
354 LineSolid, CapProjecting, JoinMiter);
355 XCopyGC( dpy, gc, GCForeground | GCBackground, acsGc);
356
357 /*
358 * Draw the characters. A few (ones with curves / shading) need to be
359 * treated specially. The rest can be drawn with the segments in lines[n].
360 */
361 while( 1 )
362 {
363 /*
364 * If even one value in str[] is >= 32, this loop will never terminate.
365 * This is checked before calling, so no need to recheck here.
366 *
367 * We use such "contorted" code to optimize for speed. Since this is
368 * done several times while refreshing the screen, we don't want to slow
369 * things down.
370 */
371 int n;
372
373 /*
374 * Find max contiguous block of chars which are present in the Xft font,
375 * and use XftDrawGlyphs to draw them.
376 */
377 for( n=0; n < len && n < MAX_GLYPHS && (glyphs[n] = xftCharNo[ *str ]);
378 n++, str++);
379 if( n )
380 {
381 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "(%d glyphs) ", n));
382
383 XftDrawGlyphs( draw, color, pub, x, y, glyphs, n);
384
385 x += n * font_width;
386 if( !(len -= n) ) break; /* !(len -= n) iff (len -= n) <= 0 */
387 }
388
389 /*
390 * Draw contiguous stippled box (0x02)
391 */
392 for( n=0; n < len && *str == 2; n++, str++);
393 if( n )
394 {
395 XGCValues values;
396
397 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "(%d boxes) ", n));
398
399 XGetGCValues( dpy, acsGc, GCForeground | GCBackground, &values);
400
401 XDrawPoint( dpy, sPmap, acsGc, 0, 0);
402 XDrawPoint( dpy, sPmap, acsGc, 1, 1);
403
404 XSetForeground( dpy, acsGc, values.background);
405 XDrawPoint( dpy, sPmap, acsGc, 0, 1);
406 XDrawPoint( dpy, sPmap, acsGc, 1, 0);
407
408 values.fill_style = FillTiled;
409 XChangeGC( dpy, acsGc, GCForeground | GCFillStyle, &values);
410
411 XFillRectangle( dpy, d, acsGc, x, ytop, n * font_width, font_height);
412
413 if( !(len -= n) ) break; /* !(len -= n) iff (len -= n) <= 0 */
414 x += n * font_width;
415 }
416
417 /*
418 * Draw contiguous horizontal lines.
419 */
420 if( *str >= 0x10 && *str <= 0x14)
421 {
422 unsigned char c = *str;
423 int xstart = x;
424 int ystart = ytop + ((c - 0x10) * (font_height-1)) / 4;
425
426 for( n=0; ++n < len && *(++str) == c; );
427
428 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "(%d hln)", n));
429 x += n * font_width;
430
431 XSetFillStyle( dpy, acsGc, FillSolid);
432 XDrawLine( dpy, d, acsGc, xstart, ystart, x-1, ystart);
433
434 if( !(len -= n)) break;
435 }
436
437 /*
438 * Contiguous drawing not possible for these cases.
439 */
440 if (*str == 1) /* Filled diamond */
441 {
442 XPoint points[4];
443 int npoints = 4, n;
444
445 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "(1 dmd) "));
446
447 points[0].x = CHR_WIDE/2 + (BOX_WIDE - CHR_WIDE) / 2;
448 points[0].y = 0;
449
450 points[1].x = (BOX_WIDE - CHR_WIDE) / 2;
451 points[1].y = CHR_HIGH/2 + (BOX_HIGH - CHR_HIGH) / 2;
452
453 points[2].x = points[0].x;
454 points[2].y = CHR_HIGH + (BOX_HIGH - CHR_HIGH) / 2;
455
456 points[3].x = CHR_WIDE + (BOX_WIDE - CHR_WIDE) / 2;
457 points[3].y = points[1].y;
458
459 for (n = 0; n < npoints; n++)
460 {
461 SCALE_X(points[n].x);
462 SCALE_Y(points[n].y);
463
464 points[n].x += x;
465 points[n].y += ytop;
466 }
467
468 XSetFillStyle( dpy, acsGc, FillSolid);
469 XFillPolygon( dpy, d, acsGc, points, npoints, Convex, CoordModeOrigin);
470
471 if( ! (--len)) break;
472 str++;
473 x += font_width;
474 }
475 else if( NOT_NULL(p = lines[*str]))
476 {
477 /*
478 * Draw character using segments in lines[*str]
479 */
480 int coord[4];
481 int n = 0;
482
483 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "(1 ldc) "));
484
485 XSetFillStyle( dpy, acsGc, FillSolid);
486 while (*p >= 0)
487 {
488 coord[n++] = *p++;
489 if (n == 4)
490 {
491 SCALE_X(coord[0]);
492 SCALE_Y(coord[1]);
493 SCALE_X(coord[2]);
494 SCALE_Y(coord[3]);
495 XDrawLine( dpy, d, acsGc,
496 x + coord[0], ytop + coord[1],
497 x + coord[2], ytop + coord[3]);
498 n = 0;
499 }
500 }
501
502 if( !(--len)) break;
503 str++;
504 x += font_width;
505 }
506 }
507 #ifdef DEBUG
508 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "\n"));
509 #endif
510 }
511
512 /*
513 * Draws an XFT string on screen. All characters below 32 are assumed to be ACS
514 * graphics characters and are drawn by hand. Ther rest are passed to
515 * xftdraw_string to be drawn by Xft. xftdraw_string should be XftDrawString8 /
516 * Utf8. Changing this to accept XftDrawString16 etc is not hard, but will bloat
517 * mrxvt (and isn't done here).
518 */
519 void
xftDrawACSString(Display * dpy,Drawable d,GC gc,void (* xftdraw_string)(),XftDraw * draw,const XftColor * color,XftFont * pub,int x,int y,const unsigned char * str,int len)520 xftDrawACSString ( Display *dpy, Drawable d, GC gc,
521 void (*xftdraw_string)(),
522 XftDraw *draw, const XftColor *color, XftFont *pub,
523 int x, int y, const unsigned char *str, int len)
524 {
525 const unsigned char *t = str;
526 int chars;
527
528 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, "Drawing %d(%d) %sACS characters.", len, STRLEN( str), ( xftdraw_string == XftDrawString8) ? "Utf8 " : ""));
529
530 while(len > 0)
531 {
532 /*
533 * Pass all non graphic chars to xftdraw_string.
534 */
535 for( chars=0; *t >= 32 && chars < len; chars++, t++);
536 if( chars)
537 {
538 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, " [%d chars]", chars));
539 xftdraw_string( draw, color, pub, x, y, str, chars);
540
541 x += chars * pub->max_advance_width;
542 str = t;
543 len -= chars;
544 }
545
546 /*
547 * Draw all ACS graphics chars by hand.
548 */
549 for( chars=0; *t < 32 && chars < len; chars++, t++);
550 if( chars)
551 {
552 rxvt_dbgmsg ((DBG_VERBOSE, DBG_XFTACS, " (%d glyphs)", chars));
553 xftDrawACSChars( dpy, d, gc, draw, color, pub, x, y, str, chars);
554
555 x += chars * pub->max_advance_width;
556 str = t;
557 len -= chars;
558 }
559 }
560 #ifdef DEBUG
561 rxvt_dbgmsg ((DBG_DEBUG, DBG_XFTACS, "\n"));
562 #endif
563 }
564 /*-------------------------- end-of-file (C source) --------------------------*/
565