]> icculus.org git repositories - mikachu/openbox.git/blob - render/font.c
keep the default tint at 50, for theme compatibility
[mikachu/openbox.git] / render / font.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    font.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003        Ben Jansens
6    Copyright (c) 2003        Derek Foreman
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    See the COPYING file for a copy of the GNU General Public License.
19 */
20
21 #include "font.h"
22 #include "color.h"
23 #include "mask.h"
24 #include "theme.h"
25 #include "geom.h"
26 #include "instance.h"
27 #include "gettext.h"
28
29 #include <glib.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <locale.h>
33
34 #define OB_SHADOW "shadow"
35 #define OB_SHADOW_OFFSET "shadowoffset"
36 #define OB_SHADOW_ALPHA "shadowtint"
37
38 FcObjectType objs[] = {
39     { OB_SHADOW,        FcTypeBool    },
40     { OB_SHADOW_OFFSET, FcTypeInteger },
41     { OB_SHADOW_ALPHA,  FcTypeInteger  }
42 };
43
44 static gboolean started = FALSE;
45
46 static void font_startup(void)
47 {
48     if (!XftInit(0)) {
49         g_warning(_("Couldn't initialize Xft."));
50         exit(EXIT_FAILURE);
51     }
52
53     /* Here we are teaching xft about the shadow, shadowoffset & shadowtint */
54     FcNameRegisterObjectTypes(objs, (sizeof(objs) / sizeof(objs[0])));
55 }
56
57 static void measure_font(const RrInstance *inst, RrFont *f)
58 {
59     PangoFontMetrics *metrics;
60     gchar *locale, *p;
61
62     /* get the default language from the locale
63        (based on gtk_get_default_language in gtkmain.c) */
64     locale = g_strdup(setlocale(LC_CTYPE, NULL));
65     if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
66     if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
67
68     /* measure the ascent and descent */
69     metrics = pango_context_get_metrics(inst->pango, f->font_desc,
70                                         pango_language_from_string(locale));
71     f->ascent = pango_font_metrics_get_ascent(metrics);
72     f->descent = pango_font_metrics_get_descent(metrics);
73     pango_font_metrics_unref(metrics);
74
75     g_free(locale);
76 }
77
78 RrFont *RrFontOpen(const RrInstance *inst, gchar *name, gint size,
79                    RrFontWeight weight, RrFontSlant slant, gboolean shadow,
80                    gint shadowoffset, gchar shadowtint)
81 {
82     RrFont *out;
83     PangoWeight pweight;
84     PangoStyle pstyle;
85
86     if (!started) {
87         font_startup();
88         started = TRUE;
89     }
90
91     g_assert(shadowtint <= 100 && shadowtint >= -100);
92
93     out = g_new(RrFont, 1);
94     out->inst = inst;
95     out->ref = 1;
96     out->font_desc = pango_font_description_new();
97     out->layout = pango_layout_new(inst->pango);
98
99     switch (weight) {
100     case RR_FONTWEIGHT_LIGHT:     pweight = PANGO_WEIGHT_LIGHT;     break;
101     case RR_FONTWEIGHT_NORMAL:    pweight = PANGO_WEIGHT_NORMAL;    break;
102     case RR_FONTWEIGHT_SEMIBOLD:  pweight = PANGO_WEIGHT_SEMIBOLD;  break;
103     case RR_FONTWEIGHT_BOLD:      pweight = PANGO_WEIGHT_BOLD;      break;
104     case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
105     default: g_assert_not_reached();
106     }
107
108     switch (slant) {
109     case RR_FONTSLANT_NORMAL:  pstyle = PANGO_STYLE_NORMAL;    break;
110     case RR_FONTSLANT_ITALIC:  pstyle = PANGO_STYLE_ITALIC;    break;
111     case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE;   break;
112     default: g_assert_not_reached();
113     }
114
115     /* setup the font */
116     pango_font_description_set_family(out->font_desc, name);
117     pango_font_description_set_weight(out->font_desc, pweight);
118     pango_font_description_set_style(out->font_desc, pstyle);
119     pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
120
121     /* setup the shadow */
122     out->shadow = shadow;
123     out->offset = shadowoffset;
124     out->tint = shadowtint;
125
126     /* setup the layout */
127     pango_layout_set_font_description(out->layout, out->font_desc);
128     pango_layout_set_single_paragraph_mode(out->layout, TRUE);
129     pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
130
131     /* get the ascent and descent */
132     measure_font(inst, out);
133
134     return out;
135 }
136
137 RrFont *RrFontOpenDefault(const RrInstance *inst)
138 {
139     return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
140                       RrDefaultFontWeight, RrDefaultFontSlant,
141                       RrDefaultFontShadow, RrDefaultFontShadowOffset,
142                       RrDefaultFontShadowTint);
143 }
144
145 void RrFontRef(RrFont *f)
146 {
147     ++f->ref;
148 }
149
150 void RrFontClose(RrFont *f)
151 {
152     if (f) {
153         if (--f->ref < 1) {
154             g_object_unref(f->layout);
155             pango_font_description_free(f->font_desc);
156             g_free(f);
157         }
158     }
159 }
160
161 static void font_measure_full(const RrFont *f, const gchar *str,
162                               gint *x, gint *y)
163 {
164     PangoRectangle rect;
165
166     pango_layout_set_text(f->layout, str, -1);
167     pango_layout_set_width(f->layout, -1);
168     pango_layout_get_pixel_extents(f->layout, NULL, &rect);
169     *x = rect.width + (f->shadow ? ABS(f->offset) : 0);
170     *y = rect.height + (f->shadow ? ABS(f->offset) : 0);
171 }
172
173 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str)
174 {
175     RrSize *size;
176     size = g_new(RrSize, 1);
177     font_measure_full(f, str, &size->width, &size->height);
178     return size;
179 }
180
181 gint RrFontHeight(const RrFont *f)
182 {
183     return (f->ascent + f->descent) / PANGO_SCALE +
184         (f->shadow ? f->offset : 0);
185 }
186
187 static inline int font_calculate_baseline(RrFont *f, gint height)
188 {
189 /* For my own reference:
190  *   _________
191  *  ^space/2  ^height     ^baseline
192  *  v_________|_          |
193  *            | ^ascent   |   _           _
194  *            | |         |  | |_ _____ _| |_ _  _
195  *            | |         |  |  _/ -_) \ /  _| || |
196  *            | v_________v   \__\___/_\_\\__|\_, |
197  *            | ^descent                      |__/
198  *  __________|_v
199  *  ^space/2  |
200  *  V_________v
201  */
202     return (((height * PANGO_SCALE) /* height of the space in pango units */
203              - (f->ascent + f->descent)) /* minus space taken up by text */
204             / 2 /* divided by two -> half of the empty space (this is the top
205                    of the text) */
206             + f->ascent) /* now move down to the baseline */
207         / PANGO_SCALE; /* back to pixels */
208 }
209
210 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
211 {
212     gint x,y,w,h;
213     XftColor c;
214     gint mw;
215     PangoRectangle rect;
216
217     /* center the text vertically
218        We do this centering based on the 'baseline' since different fonts have
219        different top edges. It looks bad when the whole string is moved when 1
220        character from a non-default language is included in the string */
221     y = area->y +
222         font_calculate_baseline(t->font, area->height);
223
224     /* the +2 and -4 leave a small blank edge on the sides */
225     x = area->x + 2;
226     w = area->width - 4;
227     h = area->height;
228
229     pango_layout_set_text(t->font->layout, t->string, -1);
230     pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
231
232     pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
233     mw = rect.width;
234
235     /* pango_layout_set_alignment doesn't work with 
236        pango_xft_render_layout_line */
237     switch (t->justify) {
238     case RR_JUSTIFY_LEFT:
239         break;
240     case RR_JUSTIFY_RIGHT:
241         x += (w - mw);
242         break;
243     case RR_JUSTIFY_CENTER:
244         x += (w - mw) / 2;
245         break;
246     }
247
248     if (t->font->shadow) {
249         if (t->font->tint >= 0) {
250             c.color.red = 0;
251             c.color.green = 0;
252             c.color.blue = 0;
253             c.color.alpha = 0xffff * t->font->tint / 100;
254             c.pixel = BlackPixel(RrDisplay(t->font->inst),
255                                  RrScreen(t->font->inst));
256         } else {
257             c.color.red = 0xffff;
258             c.color.green = 0xffff;
259             c.color.blue = 0xffff;
260             c.color.alpha = 0xffff * -t->font->tint / 100;
261             c.pixel = WhitePixel(RrDisplay(t->font->inst),
262                                  RrScreen(t->font->inst));
263         }
264         /* see below... */
265         pango_xft_render_layout_line
266             (d, &c, pango_layout_get_line(t->font->layout, 0),
267              (x + t->font->offset) * PANGO_SCALE,
268              (y + t->font->offset) * PANGO_SCALE);
269     }
270
271     c.color.red = t->color->r | t->color->r << 8;
272     c.color.green = t->color->g | t->color->g << 8;
273     c.color.blue = t->color->b | t->color->b << 8;
274     c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
275     c.pixel = t->color->pixel;
276
277     /* layout_line() uses y to specify the baseline
278        The line doesn't need to be freed, it's a part of the layout */
279     pango_xft_render_layout_line
280         (d, &c, pango_layout_get_line(t->font->layout, 0),
281          x * PANGO_SCALE, y * PANGO_SCALE);
282 }