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