don't disable use of deprecated APIs unless asked to do so. add Meta case
[vte.git] / src / vtefc.c
1 /*
2  * Copyright (C) 2003,2004 Red Hat, Inc.
3  *
4  * This is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Library General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ident "$Id$"
20
21 #include "../config.h"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <gtk/gtk.h>
26 #include <pango/pango.h>
27 #include <fontconfig/fontconfig.h>
28 #include <glib.h>
29 #include "vtefc.h"
30 #include "vterdb.h"
31
32 static int
33 _vte_fc_weight_from_pango_weight(int weight)
34 {
35         /* Cut-and-pasted from Pango. */
36         if (weight < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_LIGHT) / 2)
37                 return FC_WEIGHT_LIGHT;
38         else if (weight < (PANGO_WEIGHT_NORMAL + 600) / 2)
39                 return FC_WEIGHT_MEDIUM;
40         else if (weight < (600 + PANGO_WEIGHT_BOLD) / 2)
41                 return FC_WEIGHT_DEMIBOLD;
42         else if (weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2)
43                 return FC_WEIGHT_BOLD;
44         else
45                 return FC_WEIGHT_BLACK;
46 }
47
48 static int
49 _vte_fc_slant_from_pango_style(PangoStyle style)
50 {
51         switch (style) {
52         case PANGO_STYLE_NORMAL:
53                 return FC_SLANT_ROMAN;
54                 break;
55         case PANGO_STYLE_ITALIC:
56                 return FC_SLANT_ITALIC;
57                 break;
58         case PANGO_STYLE_OBLIQUE:
59                 return FC_SLANT_OBLIQUE;
60                 break;
61         }
62         return FC_SLANT_ROMAN;
63 }
64
65 static int
66 _vte_fc_width_from_pango_stretch(PangoStretch stretch)
67 {
68         switch (stretch) {
69         case PANGO_STRETCH_ULTRA_CONDENSED:
70                 return 60;
71                 break;
72         case PANGO_STRETCH_EXTRA_CONDENSED:
73                 return 70;
74                 break;
75         case PANGO_STRETCH_CONDENSED:
76                 return 80;
77                 break;
78         case PANGO_STRETCH_SEMI_CONDENSED:
79                 return 90;
80                 break;
81         case PANGO_STRETCH_NORMAL:
82                 return 100;
83                 break;
84         case PANGO_STRETCH_SEMI_EXPANDED:
85                 return 105;
86                 break;
87         case PANGO_STRETCH_EXPANDED:
88                 return 120;
89                 break;
90         case PANGO_STRETCH_EXTRA_EXPANDED:
91                 return 150;
92                 break;
93         case PANGO_STRETCH_ULTRA_EXPANDED:
94                 return 200;
95                 break;
96         }
97         return 100;
98 }
99
100 static void
101 _vte_fc_transcribe_from_pango_font_description(GtkWidget *widget,
102                                                FcPattern *pattern,
103                                        const PangoFontDescription *font_desc)
104 {
105 #if GTK_CHECK_VERSION(2,2,0)
106         GdkScreen *screen;
107 #endif
108         const char *family = "monospace";
109         PangoLanguage *language;
110         double size = 10.0;
111         int pango_mask;
112         PangoContext *context;
113         PangoWeight weight;
114         PangoStyle style;
115         PangoStretch stretch;
116
117         if (font_desc == NULL) {
118                 return;
119         }
120
121         pango_mask = pango_font_description_get_set_fields(font_desc);
122
123         /* Set the family for the pattern, or use a sensible default. */
124         if (pango_mask & PANGO_FONT_MASK_FAMILY) {
125                 family = pango_font_description_get_family(font_desc);
126         }
127         FcPatternAddString(pattern, FC_FAMILY, family);
128
129         /* Set the font size for the pattern, or use a sensible default. */
130         if (pango_mask & PANGO_FONT_MASK_SIZE) {
131                 size = pango_font_description_get_size(font_desc);
132                 size /= PANGO_SCALE;
133         }
134         FcPatternAddDouble(pattern, FC_SIZE, size);
135
136         /* Set the language for the pattern. */
137 #if GTK_CHECK_VERSION(2,2,0)
138         if (gtk_widget_has_screen(widget)) {
139                 screen = gtk_widget_get_screen(widget);
140         } else {
141                 screen = gdk_display_get_default_screen(gtk_widget_get_display(widget));
142         }
143         context = gdk_pango_context_get_for_screen(screen);
144 #else
145         context = gdk_pango_context_get();
146 #endif
147         language = pango_context_get_language(context);
148         if (pango_language_to_string(language) != NULL) {
149                 FcPatternAddString(pattern, FC_LANG,
150                                    pango_language_to_string(language));
151         }
152
153         /* There aren't any fallbacks for these, so just omit them from the
154          * pattern if they're not set in the pango font. */
155         if (pango_mask & PANGO_FONT_MASK_WEIGHT) {
156                 weight = pango_font_description_get_weight(font_desc);
157                 FcPatternAddInteger(pattern, FC_WEIGHT,
158                                     _vte_fc_weight_from_pango_weight(weight));
159         }
160
161         if (pango_mask & PANGO_FONT_MASK_STRETCH) {
162                 stretch = pango_font_description_get_stretch(font_desc);
163                 FcPatternAddInteger(pattern, FC_WIDTH,
164                                     _vte_fc_width_from_pango_stretch(stretch));
165         }
166
167         if (pango_mask & PANGO_FONT_MASK_STYLE) {
168                 style = pango_font_description_get_style(font_desc);
169                 FcPatternAddInteger(pattern, FC_SLANT,
170                                     _vte_fc_slant_from_pango_style(style));
171         }
172
173         g_object_unref(G_OBJECT(context));
174 }
175
176 static void
177 _vte_fc_defaults_from_gtk(GtkWidget *widget, FcPattern *pattern)
178 {
179         GtkSettings *settings;
180 #if GTK_CHECK_VERSION(2,2,0)
181         GdkScreen *screen;
182 #endif
183         GObjectClass *klass;
184         int i, antialias = -1, hinting = -1, dpi = -1;
185         char *rgba = NULL, *hintstyle = NULL;
186
187         /* Add any defaults configured for GTK+. */
188 #if GTK_CHECK_VERSION(2,2,0)
189         if (gtk_widget_has_screen(widget)) {
190                 screen = gtk_widget_get_screen(widget);
191         } else {
192                 screen = gdk_display_get_default_screen(gtk_widget_get_display(widget));
193         }
194         settings = gtk_settings_get_for_screen(screen);
195 #else
196         settings = gtk_settings_get_default();
197 #endif
198         if (settings == NULL) {
199                 return;
200         }
201
202         /* Check that the properties we're looking at are defined. */
203         klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings));
204         if (g_object_class_find_property(klass, "gtk-xft-antialias") == NULL) {
205                 return;
206         }
207
208         /* Read the settings. */
209         g_object_get(G_OBJECT(settings),
210                      "gtk-xft-antialias", &antialias,
211                      "gtk-xft-dpi", &dpi,
212                      "gtk-xft-rgba", &rgba,
213                      "gtk-xft-hinting", &hinting,
214                      "gtk-xft-hintstyle", &hintstyle,
215                      NULL);
216
217         /* Pick up the antialiasing setting. */
218         if (antialias >= 0) {
219                 FcPatternDel(pattern, FC_ANTIALIAS);
220                 FcPatternAddBool(pattern, FC_ANTIALIAS, antialias > 0);
221         }
222
223         /* Pick up the configured DPI setting. */
224         if (dpi >= 0) {
225                 FcPatternDel(pattern, FC_DPI);
226                 FcPatternAddDouble(pattern, FC_DPI, dpi / 1024.0);
227         }
228
229         /* Pick up the configured subpixel rendering setting. */
230         if (rgba != NULL) {
231                 gboolean found;
232
233                 i = FC_RGBA_NONE;
234
235                 if (g_ascii_strcasecmp(rgba, "none") == 0) {
236                         i = FC_RGBA_NONE;
237                         found = TRUE;
238                 } else
239                 if (g_ascii_strcasecmp(rgba, "rgb") == 0) {
240                         i = FC_RGBA_RGB;
241                         found = TRUE;
242                 } else
243                 if (g_ascii_strcasecmp(rgba, "bgr") == 0) {
244                         i = FC_RGBA_BGR;
245                         found = TRUE;
246                 } else
247                 if (g_ascii_strcasecmp(rgba, "vrgb") == 0) {
248                         i = FC_RGBA_VRGB;
249                         found = TRUE;
250                 } else
251                 if (g_ascii_strcasecmp(rgba, "vbgr") == 0) {
252                         i = FC_RGBA_VBGR;
253                         found = TRUE;
254                 } else {
255                         found = FALSE;
256                 }
257                 if (found) {
258                         FcPatternDel(pattern, FC_RGBA);
259                         FcPatternAddInteger(pattern, FC_RGBA, i);
260                 }
261                 g_free(rgba);
262         }
263
264         /* Pick up the configured hinting setting. */
265         if (hinting >= 0) {
266                 FcPatternDel(pattern, FC_HINTING);
267                 FcPatternAddBool(pattern, FC_HINTING, hinting > 0);
268         }
269
270 #ifdef FC_HINT_STYLE
271         /* Pick up the default hinting style. */
272         if (hintstyle != NULL) {
273                 gboolean found;
274
275                 i = FC_HINT_NONE;
276
277                 if (g_ascii_strcasecmp(hintstyle, "hintnone") == 0) {
278                         i = FC_HINT_NONE;
279                         found = TRUE;
280                 } else
281                 if (g_ascii_strcasecmp(hintstyle, "hintslight") == 0) {
282                         i = FC_HINT_SLIGHT;
283                         found = TRUE;
284                 } else
285                 if (g_ascii_strcasecmp(hintstyle, "hintmedium") == 0) {
286                         i = FC_HINT_MEDIUM;
287                         found = TRUE;
288                 } else
289                 if (g_ascii_strcasecmp(hintstyle, "hintfull") == 0) {
290                         i = FC_HINT_FULL;
291                         found = TRUE;
292                 } else {
293                         found = FALSE;
294                 }
295                 if (found) {
296                         FcPatternDel(pattern, FC_HINT_STYLE);
297                         FcPatternAddInteger(pattern, FC_HINT_STYLE, i);
298                 }
299                 g_free(hintstyle);
300         }
301 #endif
302 }
303
304 static void
305 _vte_fc_defaults_from_rdb(GtkWidget *widget, FcPattern *pattern)
306 {
307         FcBool fcb;
308         double fcd;
309         int antialias = -1, hinting = -1, fci;
310         double dpi;
311         const char *rgba = NULL, *hintstyle = NULL;
312
313         /* Read the settings. */
314         hintstyle = _vte_rdb_get_hintstyle(widget);
315         rgba = _vte_rdb_get_rgba(widget);
316
317         /* Pick up the antialiasing setting. */
318         if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0,
319                              &fcb) == FcResultNoMatch) {
320                 antialias = _vte_rdb_get_antialias(widget);
321                 FcPatternAddBool(pattern, FC_ANTIALIAS, antialias);
322         }
323
324         /* Pick up the hinting setting. */
325         if (FcPatternGetBool(pattern, FC_HINTING, 0,
326                              &fcb) == FcResultNoMatch) {
327                 hinting = _vte_rdb_get_hinting(widget);
328                 FcPatternAddBool(pattern, FC_HINTING, hinting);
329         }
330
331         /* Pick up the configured DPI setting. */
332         if (FcPatternGetDouble(pattern, FC_DPI, 0,
333                                &fcd) == FcResultNoMatch) {
334                 dpi = _vte_rdb_get_dpi(widget);
335                 if (dpi >= 0) {
336                         FcPatternAddDouble(pattern, FC_DPI, dpi);
337                 }
338         }
339
340         /* Pick up the configured subpixel rendering setting. */
341         if (FcPatternGetInteger(pattern, FC_RGBA, 0,
342                                 &fci) == FcResultNoMatch) {
343                 rgba = _vte_rdb_get_rgba(widget);
344                 if (g_ascii_strcasecmp(rgba, "none") == 0) {
345                         FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_NONE);
346                 } else
347                 if (g_ascii_strcasecmp(rgba, "rgb") == 0) {
348                         FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_RGB);
349                 } else
350                 if (g_ascii_strcasecmp(rgba, "bgr") == 0) {
351                         FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_BGR);
352                 } else
353                 if (g_ascii_strcasecmp(rgba, "vrgb") == 0) {
354                         FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_VRGB);
355                 } else
356                 if (g_ascii_strcasecmp(rgba, "vbgr") == 0) {
357                         FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_VBGR);
358                 }
359         }
360
361 #ifdef FC_HINT_STYLE
362         /* Pick up the default hinting style. */
363         if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0,
364                                 &fci) == FcResultNoMatch) {
365                 hintstyle = _vte_rdb_get_hintstyle(widget);
366                 if (g_ascii_strcasecmp(hintstyle, "hintnone") == 0) {
367                         FcPatternAddInteger(pattern, FC_HINT_STYLE,
368                                             FC_HINT_NONE);
369                 } else
370                 if (g_ascii_strcasecmp(hintstyle, "hintslight") == 0) {
371                         FcPatternAddInteger(pattern, FC_HINT_STYLE,
372                                             FC_HINT_SLIGHT);
373                 } else
374                 if (g_ascii_strcasecmp(hintstyle, "hintmedium") == 0) {
375                         FcPatternAddInteger(pattern, FC_HINT_STYLE,
376                                             FC_HINT_MEDIUM);
377                 } else
378                 if (g_ascii_strcasecmp(hintstyle, "hintfull") == 0) {
379                         FcPatternAddInteger(pattern, FC_HINT_STYLE,
380                                             FC_HINT_FULL);
381                 }
382         }
383 #endif
384 }
385
386 /* Create a sorted set of fontconfig patterns from a Pango font description
387  * and append them to the array. */
388 gboolean
389 _vte_fc_patterns_from_pango_font_desc(GtkWidget *widget,
390                                       const PangoFontDescription *font_desc,
391                                       GArray *pattern_array,
392                                       _vte_fc_defaults_cb defaults_cb,
393                                       gpointer defaults_data)
394
395 {
396         FcPattern *pattern, *match, *tmp, *save;
397         FcFontSet *fontset;
398         FcResult result;
399         gboolean ret = FALSE;
400         int i;
401
402         g_return_val_if_fail(pattern_array != NULL, FALSE);
403
404         /* Create a scratch pattern. */
405         pattern = FcPatternCreate();
406
407         /* Transcribe what we can get from the Pango font description. */
408         _vte_fc_transcribe_from_pango_font_description(widget, pattern,
409                                                        font_desc);
410
411         /* Add any defaults specified in the configuration. */
412         FcConfigSubstitute(NULL, pattern, FcMatchPattern);
413
414         /* Add any defaults configured for GTK+. */
415         _vte_fc_defaults_from_gtk(widget, pattern);
416
417         /* Add defaults configured via the resource database. */
418         _vte_fc_defaults_from_rdb(widget, pattern);
419
420         /* Add any defaults which are hard-coded in fontconfig. */
421         FcDefaultSubstitute(pattern);
422
423         /* Add any defaults via a callback. */
424         if (defaults_cb != NULL) {
425                 defaults_cb(pattern, defaults_data);
426         }
427
428         /* Get a sorted list of patterns, duplicate them, and append them
429          * to the passed-in array. */
430         fontset = FcFontSort(NULL, pattern, FcTrue, NULL, &result);
431         if (fontset != NULL) {
432                 for (i = 0; i < fontset->nfont; i++) {
433                         tmp = FcFontRenderPrepare(NULL,
434                                                   pattern,
435                                                   fontset->fonts[i]);
436                         _vte_fc_defaults_from_gtk(widget, tmp);
437                         save = FcPatternDuplicate(tmp);
438                         FcPatternDestroy(tmp);
439                         g_array_append_val(pattern_array, save);
440                 }
441                 FcFontSetDestroy(fontset);
442                 ret = TRUE;
443         }
444
445         /* Last ditch effort. */
446         if (pattern_array->len == 0) {
447                 match = FcFontMatch(NULL, pattern, &result);
448                 if (result == FcResultMatch) {
449                         tmp = FcPatternDuplicate(match);
450                         _vte_fc_defaults_from_gtk(widget, tmp);
451                         save = FcPatternDuplicate(tmp);
452                         FcPatternDestroy(tmp);
453                         g_array_append_val(pattern_array, save);
454                         ret = TRUE;
455                 } else {
456                         ret = FALSE;
457                 }
458         }
459
460         FcPatternDestroy(pattern);
461
462         return ret;
463 }
464
465 void
466 _vte_fc_connect_settings_changes(GtkWidget *widget, GCallback *changed_cb)
467 {
468         GtkSettings *settings;
469         GObjectClass *klass;
470
471         /* Get the settings object used by the widget. */
472         settings = gtk_widget_get_settings(widget);
473         if (settings == NULL) {
474                 return;
475         }
476
477         /* Check that the properties we're looking at are defined. */
478         klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings));
479         if (g_object_class_find_property(klass, "gtk-xft-antialias") == NULL) {
480                 return;
481         }
482
483         /* Start listening for changes to the fontconfig settings. */
484         g_signal_connect(G_OBJECT(settings),
485                          "notify::gtk-xft-antialias",
486                          G_CALLBACK(changed_cb), widget);
487         g_signal_connect(G_OBJECT(settings),
488                          "notify::gtk-xft-hinting",
489                          G_CALLBACK(changed_cb), widget);
490         g_signal_connect(G_OBJECT(settings),
491                          "notify::gtk-xft-hintstyle",
492                          G_CALLBACK(changed_cb), widget);
493         g_signal_connect(G_OBJECT(settings),
494                          "notify::gtk-xft-rgba",
495                          G_CALLBACK(changed_cb), widget);
496         g_signal_connect(G_OBJECT(settings),
497                          "notify::gtk-xft-dpi",
498                          G_CALLBACK(changed_cb), widget);
499 }
500
501 void
502 _vte_fc_disconnect_settings_changes(GtkWidget *widget, GCallback *changed_cb)
503 {
504         GtkSettings *settings;
505
506         /* Get the settings object used by the widget. */
507         settings = gtk_widget_get_settings(widget);
508         if (settings == NULL) {
509                 return;
510         }
511
512         /* Stop listening for changes to the fontconfig settings. */
513         g_signal_handlers_disconnect_matched(G_OBJECT(settings),
514                                              G_SIGNAL_MATCH_FUNC |
515                                              G_SIGNAL_MATCH_DATA,
516                                              0, 0, NULL,
517                                              changed_cb,
518                                              widget);
519 }