ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/defaultfont.C
Revision: 1.27
Committed: Wed Mar 3 00:20:33 2004 UTC (20 years, 3 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.26: +23 -12 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*--------------------------------*-C-*---------------------------------*;
2 * File: defaultfont.C
3 *----------------------------------------------------------------------*
4 * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
5 * - original version.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *---------------------------------------------------------------------*/
21
22 #include "../config.h"
23 #include "rxvt.h"
24 #include "defaultfont.h"
25
26 #include <cstdlib>
27
28 #define DISPLAY r->display->display
29 #define GC r->TermWin.gc
30
31 const struct rxvt_fallback_font {
32 codeset cs;
33 const char *name;
34 } fallback_fonts[] = {
35 { CS_ISO8859_1, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-1" },
36 { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-15" },
37 { CS_ISO8859_15, "-*-*-*-r-*--*-*-*-*-c-*-fcd8859-15" },
38
39 #if ENCODING_EU
40 // cyrillic
41 { CS_KOI8_R, "-*-*-*-r-*--*-*-*-*-c-*-koi8-r" },
42 { CS_KOI8_U, "-*-*-*-r-*--*-*-*-*-c-*-koi8-u" },
43
44 { CS_ISO8859_2, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-2" },
45 { CS_ISO8859_3, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-3" },
46 { CS_ISO8859_4, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-4" },
47 { CS_ISO8859_5, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-5" },
48 { CS_ISO8859_6, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-6" },
49 { CS_ISO8859_7, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-7" },
50 { CS_ISO8859_8, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-8" },
51 { CS_ISO8859_9, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-9" },
52 { CS_ISO8859_10, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-10" },
53 { CS_ISO8859_11, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-11" },
54 { CS_ISO8859_13, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-13" },
55 { CS_ISO8859_14, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-14" },
56 { CS_ISO8859_16, "-*-*-*-r-*--*-*-*-*-c-*-iso8859-16" },
57 #endif
58
59 // japanese
60 #if ENCODING_JP || ENCODING_JP_EXT
61 # if XFT
62 // prefer xft for complex scripts
63 { CS_UNICODE, "xft:Kochi Gothic:antialias=false" },
64 # endif
65 { CS_JIS0201_1976_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0201*-0" },
66 { CS_JIS0208_1983_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0208*-0" },
67 { CS_JIS0212_1990_0, "-*-mincho-*-r-*--*-*-*-*-c-*-jisx0212*-0" },
68 #endif
69
70 #if ENCODING_CN || ENCODING_CN_EXT
71 # if XFT
72 { CS_BIG5_EXT, "xft:AR PL Mingti2L Big5" },
73 { CS_BIG5_EXT, "xft:AR PL KaitiM Big5" },
74 { CS_GB2312_1980_0, "xft:AR PL KaitiM GB" },
75 { CS_GB2312_1980_0, "xft:AR PL SungtiL GB" },
76 # endif
77 { CS_BIG5_EXT, "-*-*-*-*-*-*-*-*-*-*-c-*-big5*-0" },
78 { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-gb2312*-0" },
79 { CS_CNS11643_1992_1, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-1" },
80 { CS_CNS11643_1992_2, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-2" },
81 { CS_CNS11643_1992_3, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-3" },
82 { CS_CNS11643_1992_4, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-4" },
83 { CS_CNS11643_1992_5, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-5" },
84 { CS_CNS11643_1992_6, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-6" },
85 { CS_CNS11643_1992_7, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-7" },
86 { CS_CNS11643_1992_F, "-*-*-*-*-*-*-*-*-*-*-c-*-cns11643*-f" },
87 #endif
88
89 #if XFT
90 { CS_UNICODE, "xft:Andale Mono" },
91 { CS_UNICODE, "xft:Arial Unicode MS" },
92 #endif
93 { CS_UNICODE, "-*-lucidatypewriter-*-*-*-*-*-*-*-*-m-*-iso10646-1" },
94 { CS_UNICODE, "xft:FreeMono" },
95 { CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" },
96 { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" },
97 { CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" },
98
99 { CS_UNKNOWN, 0 }
100 };
101
102 /////////////////////////////////////////////////////////////////////////////
103
104 #if XFT
105 rxvt_drawable::~rxvt_drawable ()
106 {
107 if (xftdrawable)
108 XftDrawDestroy (xftdrawable);
109 }
110
111 rxvt_drawable::operator XftDraw *()
112 {
113 if (!xftdrawable)
114 xftdrawable = XftDrawCreate (display->display, drawable, display->visual, display->cmap);
115
116 return xftdrawable;
117 }
118 #endif
119
120 /////////////////////////////////////////////////////////////////////////////
121
122 static void *enc_buf;
123 static uint32_t enc_len;
124
125 static inline void *
126 get_enc_buf (uint32_t len)
127 {
128 if (len > enc_len)
129 {
130 free (enc_buf);
131 enc_buf = malloc (len);
132 }
133
134 return enc_buf;
135 }
136
137 static const char *
138 enc_char (const text_t *text, uint32_t len, codeset cs, bool &zero)
139 {
140 uint8_t *buf = (uint8_t *)get_enc_buf (len);
141
142 while (len--)
143 {
144 uint32_t c = FROM_UNICODE (cs, *text++);
145
146 if (c == NOCHAR)
147 {
148 c = 0;
149 zero = true;
150 }
151
152 *buf++ = c;
153 }
154
155 return (const char *)enc_buf;
156 }
157
158 static const XChar2b *
159 enc_xchar2b (const text_t *text, uint32_t len, codeset cs, bool &zero)
160 {
161 XChar2b *buf = (XChar2b *)get_enc_buf (len * sizeof (XChar2b));
162
163 while (len--)
164 {
165 uint32_t c = FROM_UNICODE (cs, *text++);
166
167 if (c == NOCHAR)
168 {
169 c = 0;
170 zero = true;
171 }
172
173 buf->byte1 = c >> 8;
174 buf->byte2 = c;
175 buf++;
176 }
177
178 return (XChar2b *)enc_buf;
179 }
180
181 /////////////////////////////////////////////////////////////////////////////
182
183 void
184 rxvt_font::clear_rect (rxvt_drawable &d, int x, int y, int w, int h, int color)
185 {
186 if (color == Color_bg)
187 XClearArea (d.display->display, d, x, y, w, h, FALSE);
188 else if (color >= 0)
189 {
190 #if XFT
191 XftDrawRect (d, &r->PixColors[color].c, x, y, w, h);
192 #else
193 XSetForeground (d.display->display, GC, r->PixColors[color]);
194 XFillRectangle (d.display->display, d, GC, x, y, w, h);
195 #endif
196 }
197 }
198
199 static const char *linedraw_cmds[128] = {
200 "1hH", "2hH", "1vV", "2vV",
201 0, 0, 0, 0,
202 0, 0, 0, 0,
203 "1HV", "2H1V", "1H2V", "2HV",
204
205 // 2510
206 "1hV", "2h1V", "1h2V", "2hV",
207 "1Hv", "2H1v", "1H2v", "2Hv",
208 "1hv", "2h1v", "1h2v", "2hv",
209 "1HvV", "2H1vV", "1HV2v", "1Hv2V",
210
211 // 2520
212 "1H2vV", "2Hv1V", "2HV1v", "2HvV",
213 "1hvV", "2h1vV", "1hV2v", "1hv2V",
214 "1h2vV", "2hv1V", "1v2hV", "2hvV",
215 "1hHV", "2h1HV", "2H1hV", "2hH1V",
216
217 // 2530
218 "1hH2V", "2hV1H", "1h2HV", "2hHV",
219 "1hHv", "1vH2h", "1hv2H", "1v2hH",
220 "1hH2v", "1H2hv", "1h2Hv", "2hHv",
221 "1hHvV", "1vVH2h", "1hvV2H", "1vV2hH",
222
223 // 2540
224 "1hHV2v", "1hHv2V", "1hH2vV", "1HV2hv",
225 "1hV2Hv", "1Hv2hV", "1hv2HV", "1V2hHv",
226 "1v2hHV", "1H2hvV", "1h2HvV", "2hHvV",
227 0, 0, 0, 0,
228
229 // 2550
230 0, 0, 0, 0,
231 0, 0, 0, 0,
232 0, 0, 0, 0,
233 0, 0, 0, 0,
234
235 // 2560
236 0, 0, 0, 0,
237 0, 0, 0, 0,
238 0, 0, 0, 0,
239 0, 0, 0, 0,
240
241 // 2570
242 0, "1a", "1b", "1ab",
243 "1h", "1v", "1H", "1V",
244 "2h", "2v", "2H", "2V",
245 "1h2H", "1v2V", "1H2h", "1V2v"
246
247 // to be done
248 };
249
250 struct rxvt_font_default : rxvt_font {
251 rxvt_fontprop properties ()
252 {
253 rxvt_fontprop p;
254
255 p.width = p.height = 1;
256 p.weight = rxvt_fontprop::medium;
257 p.slant = rxvt_fontprop::roman;
258
259 return p;
260 }
261
262 bool load (const rxvt_fontprop &prop)
263 {
264 width = 1; height = 1;
265 ascent = 1; descent = 0;
266
267 return true;
268 }
269
270 bool has_codepoint (uint32_t unicode)
271 {
272 if (unicode <= 0x001f)
273 return true;
274 if (unicode >= 0x0080 && unicode <= 0x009f)
275 return true;
276
277 if (unicode >= 0x2500 && unicode <= 0x257f
278 && linedraw_cmds[unicode - 0x2500])
279 return true;
280
281 switch (unicode)
282 {
283 case ZERO_WIDTH_CHAR:
284 return true;
285 }
286
287 return false;
288 }
289
290 void draw (rxvt_drawable &d, int x, int y,
291 const text_t *text, int len,
292 int fg, int bg);
293 };
294
295 void
296 rxvt_font_default::draw (rxvt_drawable &d, int x, int y,
297 const text_t *text, int len,
298 int fg, int bg)
299 {
300 clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
301
302 XSetForeground (d.display->display, GC, r->PixColors[fg]);
303
304 while (len--)
305 {
306 text_t t = *text++;
307
308 // is it in our linedrawing table?
309 if (t >= 0x2500 & t <= 0x2580 && linedraw_cmds[t - 0x2500])
310 {
311 const char *p = linedraw_cmds[t - 0x2500];
312
313 int x0 = x, x1 = x + r->TermWin.fwidth / 2, x2 = x + r->TermWin.fwidth - 1;
314 int y0 = y, y1 = y + r->TermWin.fheight / 2, y2 = y + r->TermWin.fheight - 1;
315
316 XGCValues gcv;
317
318 while (*p)
319 {
320 switch (*p++)
321 {
322 case '1':
323 gcv.line_width = 0;
324 XChangeGC (d.display->display, GC, GCLineWidth, &gcv);
325 break;
326
327 case '2':
328 gcv.line_width = 2;
329 XChangeGC (d.display->display, GC, GCLineWidth, &gcv);
330 break;
331
332 case 'h': XDrawLine (d.display->display, d, GC, x0, y1, x1, y1); break;
333 case 'H': XDrawLine (d.display->display, d, GC, x1, y1, x2, y1); break;
334 case 'v': XDrawLine (d.display->display, d, GC, x1, y0, x1, y1); break;
335 case 'V': XDrawLine (d.display->display, d, GC, x1, y1, x1, y2); break;
336 case 'a': XDrawLine (d.display->display, d, GC, x0, y2, x2, y0); break;
337 case 'b': XDrawLine (d.display->display, d, GC, x0, y0, x2, y2); break;
338 }
339 }
340
341 gcv.line_width = 0;
342 XChangeGC (d.display->display, GC, GCLineWidth, &gcv);
343 }
344 else
345 switch (t)
346 {
347 case ZERO_WIDTH_CHAR:
348 break;
349 default:
350 int w = 0;
351 while (len > 0 && *text == NOCHAR)
352 {
353 ++text;
354 --len;
355 w += r->TermWin.fwidth;
356 }
357
358 XDrawRectangle (d.display->display, d, GC, x + 2, y + 2,
359 w + r->TermWin.fwidth - 5, r->TermWin.fheight - 5);
360 x += w;
361 }
362
363 x += r->TermWin.fwidth;
364 }
365 }
366
367 /////////////////////////////////////////////////////////////////////////////
368
369 struct rxvt_font_x11 : rxvt_font {
370 rxvt_font_x11 () { f = 0; }
371
372 void clear ();
373
374 rxvt_fontprop properties ();
375
376 bool load (const rxvt_fontprop &prop);
377
378 bool has_codepoint (uint32_t unicode);
379
380 void draw (rxvt_drawable &d, int x, int y,
381 const text_t *text, int len,
382 int fg, int bg);
383
384 XFontStruct *f;
385 codeset cs;
386 bool enc2b, encm;
387
388 const char *get_property (XFontStruct *f, const char *property, const char *repl) const;
389 bool set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth);
390 bool set_properties (rxvt_fontprop &p, XFontStruct *f);
391 bool set_properties (rxvt_fontprop &p, const char *name);
392 };
393
394 const char *
395 rxvt_font_x11::get_property (XFontStruct *f, const char *property, const char *repl) const
396 {
397 unsigned long value;
398
399 if (XGetFontProperty (f, XInternAtom (DISPLAY, property, 0), &value))
400 return XGetAtomName (DISPLAY, value);
401 else
402 return repl;
403 }
404
405 rxvt_fontprop
406 rxvt_font_x11::properties ()
407 {
408 rxvt_fontprop p;
409 set_properties (p, f);
410 return p;
411 }
412
413 bool
414 rxvt_font_x11::set_properties (rxvt_fontprop &p, int height, const char *weight, const char *slant, int avgwidth)
415 {
416 p.width = avgwidth ? (avgwidth + 1) / 10 : (height + 1) / 2;
417 p.height = height;
418 p.weight = *weight == 'B' || *weight == 'b' ? rxvt_fontprop::bold : rxvt_fontprop::medium;
419 p.slant = *slant == 'r' || *slant == 'R' ? rxvt_fontprop::roman : rxvt_fontprop::italic;
420
421 return true;
422 }
423
424 bool
425 rxvt_font_x11::set_properties (rxvt_fontprop &p, XFontStruct *f)
426 {
427 const char *weight = get_property (f, "WEIGHT_NAME", "medium");
428 const char *slant = get_property (f, "SLANT", "r");
429
430 unsigned long height;
431 if (!XGetFontProperty (f, XInternAtom (DISPLAY, "PIXEL_SIZE", 0), &height))
432 return false;
433
434 unsigned long avgwidth;
435 if (!XGetFontProperty (f, XInternAtom (DISPLAY, "AVERAGE_WIDTH", 0), &avgwidth))
436 avgwidth = 0;
437
438 return set_properties (p, height, weight, slant, avgwidth);
439 }
440
441 bool
442 rxvt_font_x11::set_properties (rxvt_fontprop &p, const char *name)
443 {
444 int slashes = 0;
445 const char *comp[12];
446
447 for (const char *c = name; *c; c++)
448 if (*c == '-')
449 {
450 comp[slashes++] = c + 1;
451 if (slashes >= 13)
452 break;
453 }
454
455 /* can we short-circuit the costly XLoadQueryFont? */
456 if (slashes >= 13
457 && (*comp[ 6] >= '1' && *comp[ 6] <= '9')
458 && (*comp[11] >= '0' && *comp[11] <= '9'))
459 return set_properties (p, atoi (comp[6]), comp[2], comp[3], atoi (comp[11]));
460
461 XFontStruct *f = XLoadQueryFont (DISPLAY, name);
462
463 if (f)
464 {
465 // the font should really exist now. if not, we have a problem
466 // (e.g. if the user did xset fp rehash just when we were searching fonts).
467 // in that case, just return garbage.
468 bool ret = set_properties (p, f);
469 XFreeFont (DISPLAY, f);
470 return ret;
471 }
472 else
473 return false;
474 }
475
476 // fix the size of scalable fonts
477 static void
478 fix_scalable (char *buf, const char *name, const rxvt_fontprop &prop)
479 {
480 int slashes = 0;
481 const char *size;
482
483 for (const char *c = name; *c; c++)
484 if (*c == '-')
485 {
486 if (slashes == 6)
487 size = c + 1;
488
489 if (++slashes >= 13)
490 break;
491 }
492
493 if (slashes >= 13 && size[0] == '0')
494 {
495 strncpy (buf, name, size - name);
496 buf += size - name;
497 buf += sprintf (buf, "%d", prop.height);
498 strcpy (buf, size + 1);
499 }
500 else
501 strcpy (buf, name);
502 }
503
504 bool
505 rxvt_font_x11::load (const rxvt_fontprop &prop)
506 {
507 clear ();
508
509 char **list;
510 int count;
511 list = XListFonts (DISPLAY, name, 512, &count);
512 set_name (0);
513
514 if (!list)
515 return false;
516
517 int bestdiff = 0x7fffffff;
518 for (int i = 0; i < count; i++)
519 {
520 rxvt_fontprop p;
521 char fname[1024];
522 fix_scalable (fname, list[i], prop);
523
524 if (!set_properties (p, fname))
525 continue;
526
527 if (p.height > prop.height) // weed out too large fonts
528 continue;
529
530 int diff = (prop.height - p.height) * 32
531 + abs (prop.weight - p.weight)
532 + abs (prop.slant - p.slant );
533
534 if (!name // compare against best found so far
535 || diff < bestdiff)
536 {
537 set_name (strdup (fname));
538 bestdiff = diff;
539 }
540 }
541
542 XFreeFontNames (list);
543
544 if (!name)
545 return false;
546
547 f = XLoadQueryFont (DISPLAY, name);
548
549 if (!f)
550 return false;
551
552 const char *registry = get_property (f, "CHARSET_REGISTRY", 0);
553 const char *encoding = get_property (f, "CHARSET_ENCODING", 0);
554
555 if (registry && encoding)
556 {
557 char charset[64];
558 snprintf (charset, 64, "%s-%s", registry, encoding);
559
560 cs = codeset_from_name (charset);
561 }
562 else
563 {
564 const char *charset = get_property (f, "FONT", 0);
565
566 if (!charset)
567 charset = name;
568
569 int count = 13;
570 while (*charset)
571 if (*charset++ == '-' && !--count)
572 break;
573
574 cs = codeset_from_name (charset);
575 }
576
577 if (cs == CS_UNICODE)
578 cs = CS_UNICODE_16; // X11 can have a max. of 65536 chars per font
579
580 encm = f->min_byte1 != 0 || f->max_byte1 != 0;
581 enc2b = encm || f->max_char_or_byte2 > 255;
582
583 ascent = f->ascent;
584 descent = f->descent;
585 height = ascent + descent;
586
587 slow = false;
588
589 if (f->min_bounds.width == f->max_bounds.width)
590 width = f->min_bounds.width;
591 else if (f->per_char == NULL)
592 width = f->max_bounds.width;
593 else
594 {
595 slow = true;
596
597 int N = f->max_char_or_byte2 - f->min_char_or_byte2;
598
599 if (encm)
600 N += (f->max_byte1 - f->min_byte1)
601 * (f->max_char_or_byte2 - f->min_char_or_byte2 + 1);
602
603 while (N)
604 {
605 if (f->per_char[N].width > width)
606 width = f->per_char[N].width;
607
608 --N;
609 }
610 }
611
612 if (cs == CS_UNKNOWN)
613 {
614 fprintf (stderr, "unable to deduce codeset, ignoring font '%s'\n", name);
615
616 clear ();
617
618 return false;
619 }
620
621 return true;
622 }
623
624 void
625 rxvt_font_x11::clear ()
626 {
627 if (f)
628 {
629 XFreeFont (DISPLAY, f);
630 f = 0;
631 }
632 }
633
634 bool
635 rxvt_font_x11::has_codepoint (uint32_t unicode)
636 {
637 uint32_t ch = FROM_UNICODE (cs, unicode);
638
639 if (ch == NOCHAR)
640 return false;
641
642 /* check wether the character exists in _this_ font. horrible. */
643 XCharStruct *xcs;
644
645 if (encm)
646 {
647 unsigned char byte1 = ch >> 8;
648 unsigned char byte2 = ch & 255;
649
650 if (byte1 < f->min_byte1 || byte1 > f->max_byte1
651 || byte2 < f->min_char_or_byte2 || byte2 > f->max_char_or_byte2)
652 return false;
653
654 if (!f->per_char)
655 return true;
656
657 int D = f->max_char_or_byte2 - f->min_char_or_byte2 + 1;
658 int N = (byte1 - f->min_byte1) * D + byte2 - f->min_char_or_byte2;
659
660 xcs = f->per_char + N;
661 }
662 else
663 {
664 if (ch < f->min_char_or_byte2 || ch > f->max_char_or_byte2)
665 return false;
666
667 if (!f->per_char)
668 return true;
669
670 xcs = f->per_char + (ch - f->min_char_or_byte2);
671 }
672
673 if (xcs->lbearing == 0 && xcs->rbearing == 0 && xcs->width == 0
674 && xcs->ascent == 0 && xcs->descent == 0)
675 return false;
676
677 return true;
678 }
679
680 void
681 rxvt_font_x11::draw (rxvt_drawable &d, int x, int y,
682 const text_t *text, int len,
683 int fg, int bg)
684 {
685 // this looks like a mess /.
686 // and it is a mess /.
687 // yet we are trying to be perfect /.
688 // but the result still isn't perfect /.
689
690 bool slow = this->slow
691 || width != r->TermWin.fwidth
692 || height != r->TermWin.fheight;
693
694 int base = r->TermWin.fbase;
695
696 XGCValues v;
697 v.foreground = r->PixColors[fg];
698 v.background = r->PixColors[bg];
699 v.font = f->fid;
700
701 if (enc2b)
702 {
703 const XChar2b *xc = enc_xchar2b (text, len, cs, slow);
704
705 if (bg == Color_bg && !slow)
706 {
707 XChangeGC (d.display->display, GC, GCForeground | GCBackground | GCFont, &v);
708 XDrawImageString16 (d.display->display, d, GC, x, y + base, xc, len);
709 }
710 else
711 {
712 clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
713
714 XChangeGC (d.display->display, GC, GCForeground | GCFont, &v);
715
716 if (slow)
717 {
718 do
719 {
720 if (xc->byte1 || xc->byte2)
721 XDrawString16 (d.display->display, d, GC, x, y + base, xc, 1);
722
723 x += r->TermWin.fwidth;
724 xc++; len--;
725 }
726 while (len);
727 }
728 else
729 XDrawString16 (d.display->display, d, GC, x, y + base, xc, len);
730 }
731 }
732 else
733 {
734 const char *xc = enc_char (text, len, cs, slow);
735
736 if (bg == Color_bg && !slow)
737 {
738 XChangeGC (d.display->display, GC, GCForeground | GCBackground | GCFont, &v);
739 XDrawImageString (d.display->display, d, GC, x, y + base, xc, len);
740 }
741 else
742 {
743 clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
744
745 XChangeGC (d.display->display, GC, GCForeground | GCFont, &v);
746
747 if (slow)
748 {
749 do
750 {
751 if (*xc)
752 XDrawString (d.display->display, d, GC, x, y + base, xc, 1);
753
754 x += r->TermWin.fwidth;
755 xc++; len--;
756 }
757 while (len);
758 }
759 else
760 XDrawString (d.display->display, d, GC, x, y + base, xc, len);
761 }
762 }
763 }
764
765 /////////////////////////////////////////////////////////////////////////////
766
767 #if XFT
768 #if 0
769 #define UNIBITS 21
770 //#define SWATHBITS (UNIBITS / 2 + 3) // minimum size for "full" tables
771 #define SWATHBITS 8
772 #endif
773
774 struct rxvt_font_xft : rxvt_font {
775 rxvt_font_xft () { f = 0; }
776
777 void clear ();
778
779 rxvt_fontprop properties ();
780
781 bool load (const rxvt_fontprop &prop);
782
783 void draw (rxvt_drawable &d, int x, int y,
784 const text_t *text, int len,
785 int fg, int bg);
786
787 bool has_codepoint (uint32_t unicode);
788
789 protected:
790 XftFont *f;
791 };
792
793 void
794 rxvt_font_xft::clear ()
795 {
796 if (f)
797 {
798 XftFontClose (DISPLAY, f);
799 f = 0;
800 }
801 }
802
803 rxvt_fontprop
804 rxvt_font_xft::properties ()
805 {
806 rxvt_fontprop p;
807
808 FT_Face face = XftLockFace (f);
809
810 p.width = width; p.height = height;
811 p.weight = face->style_flags & FT_STYLE_FLAG_BOLD ? rxvt_fontprop::bold : rxvt_fontprop::medium;
812 p.slant = face->style_flags & FT_STYLE_FLAG_ITALIC ? rxvt_fontprop::italic : rxvt_fontprop::roman;
813
814 XftUnlockFace (f);
815
816 return p;
817 }
818
819 bool
820 rxvt_font_xft::load (const rxvt_fontprop &prop)
821 {
822 #if 0
823 for (int i = 0; i < SWATHCOUNT; i++)
824 cvr[i] = 0;
825 #endif
826
827 clear ();
828
829 FcPattern *p = FcNameParse ((FcChar8 *) name);
830
831 if (!p)
832 return false;
833
834 FcValue v;
835
836 if (FcPatternGet (p, FC_WEIGHT, 0, &v) != FcResultMatch)
837 FcPatternAddInteger (p, FC_WEIGHT, prop.weight);
838
839 if (FcPatternGet (p, FC_SLANT, 0, &v) != FcResultMatch)
840 FcPatternAddInteger (p, FC_SLANT, prop.slant);
841
842 #if 0 // clipping unfortunately destroys our precious double-width-characters
843 // clip width, we can't do better, or can we?
844 if (FcPatternGet (p, FC_CHAR_WIDTH, 0, &v) != FcResultMatch)
845 FcPatternAddInteger (p, FC_CHAR_WIDTH, prop.width);
846 #endif
847
848 //FcPatternAddBool (p, FC_MINSPACE, 1);
849
850 XftResult result;
851 FcPattern *match = XftFontMatch (DISPLAY, DefaultScreen (DISPLAY), p, &result);
852
853 FcPatternDestroy (p);
854
855 if (!match)
856 return false;
857
858 f = XftFontOpenPattern (DISPLAY, match);
859
860 if (!f)
861 {
862 FcPatternDestroy (match);
863 return false;
864 }
865
866 FT_Face face = XftLockFace (f);
867
868 slow = !FT_IS_FIXED_WIDTH (face);
869
870 int ftheight = 0;
871
872 for (;;)
873 {
874 XGlyphInfo g1, g2;
875 FcChar8 c;
876
877 c = 'i'; XftTextExtents8 (DISPLAY, f, &c, 1, &g1);
878 c = 'W'; XftTextExtents8 (DISPLAY, f, &c, 1, &g2);
879
880 if (g1.xOff != g2.xOff) // don't simply trust the font
881 slow = true;
882
883 width = g2.xOff;
884 ascent = (face->size->metrics.ascender + 63) >> 6;
885 descent = (-face->size->metrics.descender + 63) >> 6;
886 height = ascent + descent;
887
888 if (height <= prop.height || !prop.height)
889 break;
890
891 if (ftheight)
892 {
893 // take smaller steps near the end
894 if (height > prop.height + 1) ftheight++;
895 if (height > prop.height + 2) ftheight++;
896 if (height > prop.height + 3) ftheight++;
897
898 FT_Set_Pixel_Sizes (face, 0, ftheight -= height - prop.height);
899 }
900 else
901 FT_Set_Pixel_Sizes (face, 0, ftheight = prop.height);
902 }
903
904 XftUnlockFace (f);
905
906 return true;
907 }
908
909 bool
910 rxvt_font_xft::has_codepoint (uint32_t unicode)
911 {
912 return XftCharExists (DISPLAY, f, unicode);
913 }
914
915 void
916 rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
917 const text_t *text, int len,
918 int fg, int bg)
919 {
920 clear_rect (d, x, y, r->TermWin.fwidth * len, r->TermWin.fheight, bg);
921
922 if (!slow && width == r->TermWin.fwidth && 0)
923 {
924 if (sizeof (text_t) == sizeof (FcChar16))
925 XftDrawString16 (d, &r->PixColors[fg].c, f, x, y + r->TermWin.fbase, (const FcChar16 *)text, len);
926 else
927 XftDrawString32 (d, &r->PixColors[fg].c, f, x, y + r->TermWin.fbase, (const FcChar32 *)text, len);
928 }
929 else
930 {
931 while (len)
932 {
933 if (*text != NOCHAR && *text != ' ')
934 {
935 int fwidth = r->TermWin.fwidth;
936 if (len >= 2 && text[1] == NOCHAR)
937 fwidth *= 2;
938
939 XGlyphInfo extents;
940 if (sizeof (text_t) == sizeof (FcChar16))
941 {
942 XftTextExtents16 (d.display->display, f, (const FcChar16 *)text, 1, &extents);
943 XftDrawString16 (d, &r->PixColors[fg].c, f, x + extents.x + (fwidth - extents.width) / 2,
944 y + r->TermWin.fbase, (const FcChar16 *)text, 1);
945 }
946 else
947 {
948 XGlyphInfo extents;
949 XftTextExtents32 (d.display->display, f, (const FcChar32 *)text, 1, &extents);
950 XftDrawString32 (d, &r->PixColors[fg].c, f, x + extents.x + (fwidth - extents.width) / 2,
951 y + r->TermWin.fbase, (const FcChar32 *)text, 1);
952 }
953 }
954
955 x += r->TermWin.fwidth;
956 text++;
957 len--;
958 }
959 }
960 }
961 #endif
962
963 /////////////////////////////////////////////////////////////////////////////
964
965 rxvt_fontset::rxvt_fontset (rxvt_t r)
966 : r (r)
967 {
968 clear ();
969 }
970
971 rxvt_fontset::~rxvt_fontset ()
972 {
973 clear ();
974 }
975
976 void
977 rxvt_fontset::clear ()
978 {
979 for (rxvt_font **i = fonts.begin (); i != fonts.end (); i++)
980 FONT_UNREF (*i);
981
982 fonts.clear ();
983 base_id = 0;
984 base_prop.height = 0x7fffffff;
985 base_prop.weight = rxvt_fontprop::medium;
986 base_prop.slant = rxvt_fontprop::roman;
987
988 fallback = fallback_fonts;
989 }
990
991 rxvt_font *
992 rxvt_fontset::new_font (const char *name, codeset cs)
993 {
994 rxvt_font *f;
995
996 if (!name || !*name)
997 {
998 name = "";
999 f = new rxvt_font_default;
1000 }
1001 #if XFT
1002 else if (!strncmp (name, "xft:", 4))
1003 {
1004 name += 4;
1005 f = new rxvt_font_xft;
1006 }
1007 #endif
1008 else if (!strncmp (name, "x:", 2))
1009 {
1010 name += 2;
1011 f = new rxvt_font_x11;
1012 }
1013 else
1014 f = new rxvt_font_x11;
1015
1016 f->set_term (r);
1017 f->set_name (strdup (name));
1018
1019 f->cs = cs;
1020 f->loaded = false;
1021
1022 return f;
1023 }
1024
1025 /////////////////////////////////////////////////////////////////////////////
1026
1027 void
1028 rxvt_fontset::add_fonts (const char *desc)
1029 {
1030 if (desc)
1031 {
1032 char buf[512];
1033 const char *end;
1034
1035 do
1036 {
1037 while (*desc <= ' ') desc++;
1038
1039 if (*desc == '[')
1040 {
1041 fprintf (stderr, "extra font parameters not yet supported, skipping.\n");
1042
1043 //const char *extra = desc++; // not yet used
1044
1045 desc = strchr (desc, ']');
1046
1047 if (!desc)
1048 {
1049 fprintf (stderr, "ERROR: opening '[' without closing ']' in font specification.\n");
1050 break;
1051 }
1052
1053 desc++;
1054 while (*desc <= ' ') desc++;
1055 }
1056
1057 end = strchr (desc, ',');
1058 if (!end)
1059 end = desc + strlen (desc);
1060
1061 if (end - desc < 511)
1062 {
1063 strncpy (buf, desc, end - desc);
1064 buf[end - desc] = 0;
1065
1066 fonts.push_back (new_font (buf, CS_UNICODE));
1067 }
1068
1069 desc = end + 1;
1070 }
1071 while (*end);
1072 }
1073 }
1074
1075 bool
1076 rxvt_fontset::realize_font (int i)
1077 {
1078 if (fonts[i]->loaded)
1079 return true;
1080
1081 fonts[i]->loaded = true;
1082
1083 if (!fonts[i]->load (base_prop))
1084 {
1085 fonts[i]->cs = CS_UNKNOWN;
1086 return false;
1087 }
1088
1089 return true;
1090 }
1091
1092 bool
1093 rxvt_fontset::populate (const char *desc)
1094 {
1095 clear ();
1096
1097 fonts.push_back (new_font (0, CS_UNICODE));
1098 realize_font (0);
1099
1100 add_fonts (desc);
1101
1102 if (!base_id)
1103 base_id = 1;
1104
1105 // we currently need a base-font, no matter what
1106 if ((int)fonts.size () <= base_id || !realize_font (base_id))
1107 {
1108 puts ("unable to load specified font (s), falling back to 'fixed'\n");
1109 add_fonts ("fixed");
1110 base_id = fonts.size () - 1;
1111 }
1112
1113 if ((int)fonts.size () <= base_id || !realize_font (base_id))
1114 return false;
1115
1116 base_prop = fonts[base_id]->properties ();
1117
1118 return true;
1119 }
1120
1121 int
1122 rxvt_fontset::find_font (uint32_t unicode)
1123 {
1124 for (unsigned int i = 0; i < fonts.size (); i++)
1125 {
1126 rxvt_font *f = fonts[i];
1127
1128 if (!f->loaded)
1129 {
1130 if (FROM_UNICODE (f->cs, unicode) == NOCHAR)
1131 goto next_font;
1132
1133 if (!realize_font (i))
1134 goto next_font;
1135 }
1136
1137 if (f->cs != CS_UNKNOWN && f->has_codepoint (unicode))
1138 return i;
1139
1140 next_font:
1141 if (i == fonts.size () - 1 && fallback->name)
1142 {
1143 fonts.push_back (new_font (fallback->name, fallback->cs));
1144 fallback++;
1145 i = 0;
1146 }
1147 }
1148
1149 return 0; /* we must return SOME font */
1150 }
1151
1152
1153