]> icculus.org git repositories - mikachu/openbox.git/blob - src/Font.cc
extend the style format to use xft.(font|size|flags) to specify an xft font
[mikachu/openbox.git] / src / Font.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Font.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 #endif // HAVE_STDLIB_H
32 }
33
34 #include <iostream>
35 #include <algorithm>
36
37 using std::string;
38 using std::cerr;
39 using std::endl;
40
41 #include "i18n.hh"
42 #include "Font.hh"
43 #include "Util.hh"
44 #include "GCCache.hh"
45 #include "Color.hh"
46
47 bool        BFont::_antialias       = True;
48 string      BFont::_fallback_font   = "fixed";
49
50
51 #ifdef XFT
52 BFont::BFont(Display *d, BScreen *screen, const string &family, int size,
53              bool bold, bool italic) : _display(d),
54                                        _screen(screen),
55                                        _family(family),
56                                        _simplename(False),
57                                        _size(size),
58                                        _bold(bold),
59                                        _italic(italic),
60                                        _xftfont(0),
61                                        _font(0),
62                                        _fontset(0),
63                                        _fontset_extents(0) {
64   _valid = False;
65
66   _xftfont = XftFontOpen(_display, _screen->getScreenNumber(),
67                          XFT_FAMILY, XftTypeString,  _family.c_str(),
68                          XFT_SIZE,   XftTypeInteger, _size,
69                          XFT_WEIGHT, XftTypeInteger, (_bold ?
70                                                       XFT_WEIGHT_BOLD :
71                                                       XFT_WEIGHT_MEDIUM),
72                          XFT_SLANT,  XftTypeInteger, (_italic ?
73                                                       XFT_SLANT_ITALIC :
74                                                       XFT_SLANT_ROMAN),
75                          XFT_ANTIALIAS, XftTypeBool, _antialias,
76                          0);
77   if (! _xftfont)
78     return; // failure
79
80   _font = XLoadQueryFont(_display, buildXlfd().c_str()); 
81   if (! _font)
82     return; // failure
83
84   _valid = True;
85 }
86 #endif
87
88
89 BFont::BFont(Display *d, BScreen *screen, const string &xlfd) :
90                                        _display(d),
91                                        _screen(screen),
92 #ifdef    XFT
93                                        _xftfont(0),
94 #endif // XFT
95                                        _font(0),
96                                        _fontset(0),
97                                        _fontset_extents(0) {
98   string int_xlfd;
99   if (xlfd.empty())
100     int_xlfd = _fallback_font;
101   else
102     int_xlfd = xlfd;
103   
104   if ((_valid = createXFont(int_xlfd)))
105     return; // success
106
107   if (int_xlfd != _fallback_font) {
108     // try the fallback
109     cerr << "BFont::BFont(): couldn't load font '" << _family << "'" << endl <<
110       "Falling back to default '" << _fallback_font << "'" << endl;
111
112     if ((_valid = createXFont(_fallback_font)))
113       return; // success
114   }
115
116   cerr << "BFont::BFont(): couldn't load font '" << _family << "'" << endl <<
117     "Giving up!" << endl;
118   return; // failure
119 }
120
121
122 bool BFont::createXFont(const std::string &xlfd) {
123   /*
124      Even though this is only used for font sets (multibyte), it is still parsed
125      out so that the bold/italic/etc information is still available from the
126      class when using non-multibyte.
127
128      This is where _simplename, _bold, _italic, and _size are initialized, since
129      they are not initialized in the constructor. This needs to occur before
130      calling any Xlfd-building functions.
131   */
132   if (! parseXlfd(xlfd))
133     return False;
134
135   if (i18n.multibyte()) {
136     char **missing, *def = "-";
137     int nmissing;
138  
139     _fontset = XCreateFontSet(_display, buildMultibyteXlfd().c_str(),
140                               &missing, &nmissing, &def);
141     if (nmissing) XFreeStringList(missing);
142     if (_fontset)
143       _fontset_extents = XExtentsOfFontSet(_fontset);
144     else
145       return False;
146
147     assert(_fontset_extents);
148   }
149     
150   _font = XLoadQueryFont(_display, xlfd.c_str());
151   if (! _font)
152     return False;
153   return True;
154 }
155
156
157 BFont::~BFont(void) {
158 #ifdef    XFT
159   if (_xftfont)
160     XftFontClose(_display, _xftfont);
161 #endif // XFT
162
163   if (i18n.multibyte() && _fontset)
164     XFreeFontSet(_display, _fontset);
165   if (_font)
166     XFreeFont(_display, _font);
167 }
168
169
170 /*
171  * Takes _family, _size, _bold, _italic, etc and builds them into a full XLFD.
172  */
173 string BFont::buildXlfd(void) const {
174   if (_simplename) 
175     return _family;
176
177   string weight = _bold ? "bold" : "medium";
178   string slant = _italic ? "i" : "r";
179   string sizestr= _size ? itostring(_size * 10) : "*";
180
181   return "-*-" + _family + "-" + weight + "-" + slant + "-*-*-*-" + sizestr +
182       "-*-*-*-*-*-*";
183 }
184
185
186 /*
187  * Takes _family, _size, _bold, _italic, etc and builds them into a full XLFD.
188  */
189 string BFont::buildMultibyteXlfd(void) const {
190   string weight = _bold ? "bold" : "medium";
191   string slant = _italic ? "i" : "r";
192   string sizestr= _size ? itostring(_size) : "*";
193
194   return _family + ','
195     + "-*-*-" + weight + "-" + slant + "-*-*-*-" + sizestr +
196       "-*-*-*-*-*-*" + ','
197     + "-*-*-*-*-*-*-*-" + sizestr + "-*-*-*-*-*-*" + ',' +
198     + "*";
199 }
200
201
202 /*
203  * Takes a full X font name and parses it out so we know if we're bold, our
204  * size, etc.
205  */
206 bool BFont::parseXlfd(const string &xlfd) {
207   if (xlfd.empty() || xlfd[0] != '-') {
208     _family = xlfd;
209     _simplename = True;
210     _bold = False;
211     _italic = False;
212     _size = 0;
213   } else {
214     _simplename = False;
215     string weight,
216            slant,
217            sizestr;
218     int i = 0;
219
220     string::const_iterator it = xlfd.begin(), end = xlfd.end();
221     while(1) {
222       string::const_iterator tmp = it;   // current string.begin()
223       it = std::find(tmp, end, '-');     // look for comma between tmp and end
224       if (i == 2) _family = string(tmp, it); // s[tmp:it]
225       if (i == 3) weight = string(tmp, it);
226       if (i == 4) slant = string(tmp, it);
227       if (i == 7 && string(tmp, it) != "*") sizestr = string(tmp, it);
228       if (sizestr.empty() &&
229           i == 8 && string(tmp, it) != "*") sizestr = string(tmp, it);
230       if (it == end || i >= 8)
231         break;
232       ++it;
233       ++i;
234     }
235     if (i < 3)  // no name even! can't parse that
236       return False;
237     _bold = weight == "bold" || weight == "demibold";
238     _italic = slant == "i" || slant == "o";
239     _size = atoi(sizestr.c_str()) / 10;
240   }
241   
242   // min/max size restrictions for sanity, but 0 is the font's "default size"
243   if (_size && _size < 3)
244     _size = 3;
245   else if (_size > 97)
246     _size = 97;
247
248   return True;
249 }
250
251
252 void BFont::drawString(Drawable d, int x, int y, const BColor &color,
253                        const string &string) const {
254   assert(_valid);
255
256 #ifdef    XFT
257   if (_xftfont) {
258     XftDraw *draw = XftDrawCreate(_display, d, _screen->getVisual(),
259                                   _screen->getColormap());
260     assert(draw);
261
262     XftColor c;
263     c.color.red = color.red() | color.red() << 8;
264     c.color.green = color.green() | color.green() << 8;
265     c.color.blue = color.blue() | color.blue() << 8;
266     c.color.alpha = 0xff | 0xff << 8; // no transparency in BColor yet
267     c.pixel = color.pixel();
268     
269     XftDrawStringUtf8(draw, &c, _xftfont, x, _xftfont->ascent + y,
270                       (XftChar8 *) string.c_str(), string.size());
271
272     XftDrawDestroy(draw);
273     return;
274   }
275 #endif // XFT
276
277   BGCCache *_cache = color.display()->gcCache();
278   BGCCacheItem *_item = _cache->find(color, _font, GXcopy, ClipByChildren);
279
280   assert(_cache);
281   assert(_item);
282
283   if (i18n.multibyte())
284     XmbDrawString(_display, d, _fontset, _item->gc(),
285                   x, y - _fontset_extents->max_ink_extent.y,
286                   string.c_str(), string.size());
287   else
288     XDrawString(_display, d, _item->gc(),
289                 x, _font->ascent + y,
290                 string.c_str(), string.size());
291
292   _cache->release(_item);
293 }
294
295
296 unsigned int BFont::measureString(const string &string) const {
297   assert(_valid);
298
299 #ifdef    XFT
300   if (_xftfont) {
301     XGlyphInfo info;
302     XftTextExtentsUtf8(_display, _xftfont, (XftChar8 *) string.c_str(),
303                        string.size(), &info);
304     return info.xOff;
305   }
306 #endif // XFT
307
308   if (i18n.multibyte()) {
309     XRectangle ink, logical;
310     XmbTextExtents(_fontset, string.c_str(), string.size(), &ink, &logical);
311     return logical.width;
312   } else {
313     return XTextWidth(_font, string.c_str(), string.size());
314   }
315 }
316
317
318 unsigned int BFont::height(void) const {
319   assert(_valid);
320
321 #ifdef    XFT
322   if (_xftfont)
323     return _xftfont->height;
324 #endif // XFT
325
326   if (i18n.multibyte())
327     return _fontset_extents->max_ink_extent.height;
328   else
329     return _font->ascent + _font->descent;
330 }
331
332
333 unsigned int BFont::maxCharWidth(void) const {
334   assert(_valid);
335
336 #ifdef    XFT
337   if (_xftfont)
338     return _xftfont->max_advance_width;
339 #endif // XFT
340
341   if (i18n.multibyte())
342     return _fontset_extents->max_logical_extent.width;
343   else
344     return _font->max_bounds.rbearing - _font->min_bounds.lbearing;
345 }