Commit | Line | Data |
---|---|---|
4ff155dd GM |
1 | /* Functions for handling font and other changes dynamically. |
2 | ||
73b0cd50 | 3 | Copyright (C) 2009-2011 Free Software Foundation, Inc. |
637fa988 JD |
4 | |
5 | This file is part of GNU Emacs. | |
6 | ||
7 | GNU Emacs 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 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
08a494a3 | 20 | #include <config.h> |
aaafe47a PE |
21 | |
22 | #include <float.h> | |
210af043 | 23 | #include <limits.h> |
637fa988 JD |
24 | #include <setjmp.h> |
25 | #include <fcntl.h> | |
26 | #include "lisp.h" | |
27 | #include "xterm.h" | |
28 | #include "xsettings.h" | |
29 | #include "frame.h" | |
b87dd913 | 30 | #include "keyboard.h" |
637fa988 JD |
31 | #include "blockinput.h" |
32 | #include "termhooks.h" | |
33 | #include "termopts.h" | |
34 | ||
35 | #include <X11/Xproto.h> | |
36 | ||
9851bfc5 JD |
37 | #define HAVE_GSETTINGS |
38 | #ifdef HAVE_GSETTINGS | |
39 | #include <glib.h> | |
40 | #else | |
637fa988 JD |
41 | #ifdef HAVE_GCONF |
42 | #include <gconf/gconf-client.h> | |
43 | #endif | |
9851bfc5 JD |
44 | #endif |
45 | ||
637fa988 JD |
46 | #ifdef HAVE_XFT |
47 | #include <X11/Xft/Xft.h> | |
48 | #endif | |
49 | ||
50 | static char *current_mono_font; | |
99852628 | 51 | static char *current_font; |
637fa988 | 52 | static struct x_display_info *first_dpyinfo; |
f904c0f9 JD |
53 | static Lisp_Object Qmonospace_font_name, Qfont_name, Qfont_render, |
54 | Qtool_bar_style; | |
f904c0f9 | 55 | static Lisp_Object current_tool_bar_style; |
637fa988 | 56 | |
637fa988 | 57 | static void |
971de7fb | 58 | store_config_changed_event (Lisp_Object arg, Lisp_Object display_name) |
637fa988 JD |
59 | { |
60 | struct input_event event; | |
61 | EVENT_INIT (event); | |
62 | event.kind = CONFIG_CHANGED_EVENT; | |
63 | event.frame_or_window = display_name; | |
64 | event.arg = arg; | |
65 | kbd_buffer_store_event (&event); | |
66 | } | |
67 | ||
9851bfc5 JD |
68 | static void |
69 | store_monospaced_changed (void) | |
70 | { | |
71 | if (first_dpyinfo != NULL) | |
72 | { | |
73 | /* Check if display still open */ | |
74 | struct x_display_info *dpyinfo; | |
75 | int found = 0; | |
76 | for (dpyinfo = x_display_list; !found && dpyinfo; dpyinfo = dpyinfo->next) | |
77 | found = dpyinfo == first_dpyinfo; | |
78 | ||
79 | if (found && use_system_font) | |
80 | store_config_changed_event (Qmonospace_font_name, | |
81 | XCAR (first_dpyinfo->name_list_element)); | |
82 | } | |
83 | } | |
84 | ||
85 | ||
86 | #ifdef HAVE_GSETTINGS | |
87 | ||
88 | #define EMACS_TYPE_SETTINGS (emacs_settings_get_type ()) | |
89 | #define EMACS_SETTINGS(obj) \ | |
90 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMACS_TYPE_SETTINGS, EmacsSettings)) | |
91 | #define EMACS_IS_SETTINGS(obj) \ | |
92 | (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMACS_TYPE_SETTINGS)) | |
93 | #define EMACS_SETTINGS_CLASS(klass) \ | |
94 | (G_TYPE_CHECK_CLASS_CAST ((klass), EMACS_TYPE_SETTINGS, EmacsSettingsClass)) | |
95 | #define EMACS_IS_SETTINGS_CLASS(klass) \ | |
96 | (G_TYPE_CHECK_CLASS_TYPE ((klass), EMACS_TYPE_SETTINGS)) | |
97 | #define EMACS_SETTINGS_GET_CLASS(obj) \ | |
98 | (G_TYPE_INSTANCE_GET_CLASS ((obj), EMACS_TYPE_SETTINGS, EmacsSettingsClass)) | |
99 | ||
100 | typedef struct _EmacsSettings EmacsSettings; | |
101 | typedef struct _EmacsSettingsClass EmacsSettingsClass; | |
102 | ||
103 | struct _EmacsSettings | |
104 | { | |
105 | GObject parent_instance; | |
106 | }; | |
107 | ||
108 | struct _EmacsSettingsClass | |
109 | { | |
110 | GObjectClass parent_class; | |
111 | }; | |
112 | ||
113 | /* will create emacs_settings_get_type and set emacs_settings_parent_class */ | |
114 | G_DEFINE_TYPE (EmacsSettings, emacs_settings, G_TYPE_OBJECT); | |
115 | ||
116 | static GObject * | |
117 | emacs_settings_constructor (GType gtype, | |
118 | guint n_properties, | |
119 | GObjectConstructParam *properties) | |
120 | { | |
121 | GObject *obj; | |
122 | ||
123 | /* Always chain up to the parent constructor */ | |
124 | obj = G_OBJECT_CLASS (emacs_settings_parent_class) | |
125 | ->constructor (gtype, n_properties, properties); | |
126 | ||
127 | /* update the object state depending on constructor properties */ | |
128 | ||
129 | return obj; | |
130 | } | |
131 | ||
132 | enum { PROP_MONO = 1, PROP_FONT }; | |
133 | ||
134 | static void | |
135 | emacs_settings_get_property (GObject *object, | |
136 | guint property_id, | |
137 | GValue *value, | |
138 | GParamSpec *pspec) | |
139 | { | |
140 | switch (property_id) | |
141 | { | |
142 | case PROP_MONO: | |
143 | g_value_set_string (value, current_mono_font); | |
144 | break; | |
145 | case PROP_FONT: | |
146 | g_value_set_string (value, current_font); | |
147 | break; | |
148 | } | |
149 | } | |
150 | ||
151 | static void | |
152 | emacs_settings_set_property (GObject *object, | |
153 | guint property_id, | |
154 | const GValue *value, | |
155 | GParamSpec *pspec) | |
156 | { | |
157 | const char *newfont; | |
158 | switch (property_id) | |
159 | { | |
160 | case PROP_MONO: | |
161 | xfree (current_mono_font); | |
162 | newfont = g_value_get_string (value); | |
163 | if (current_mono_font != NULL && strcmp (newfont, current_mono_font) == 0) | |
164 | return; /* No change. */ | |
165 | ||
166 | current_mono_font = xstrdup (newfont); | |
167 | store_monospaced_changed (); | |
168 | break; | |
169 | ||
170 | case PROP_FONT: | |
171 | xfree (current_font); | |
172 | current_font = xstrdup (g_value_get_string (value)); | |
173 | break; | |
174 | } | |
175 | } | |
176 | ||
177 | static void | |
178 | emacs_settings_class_init (EmacsSettingsClass *klass) | |
179 | { | |
180 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | |
181 | ||
182 | gobject_class->constructor = emacs_settings_constructor; | |
183 | gobject_class->set_property = emacs_settings_set_property; | |
184 | gobject_class->get_property = emacs_settings_get_property; | |
185 | ||
186 | g_object_class_install_property | |
187 | (gobject_class, | |
188 | PROP_MONO, | |
189 | g_param_spec_string ("monospace-font", | |
190 | "Monospace-font", | |
191 | "System monospace font", | |
192 | "", | |
193 | G_PARAM_READWRITE)); | |
194 | g_object_class_install_property | |
195 | (gobject_class, | |
196 | PROP_FONT, | |
197 | g_param_spec_string ("font", | |
198 | "Font", | |
199 | "System font", | |
200 | "", | |
201 | G_PARAM_READWRITE)); | |
202 | ||
203 | } | |
204 | ||
205 | static void | |
206 | emacs_settings_init (EmacsSettings *self) | |
207 | { | |
208 | } | |
209 | ||
210 | ||
211 | static GSettings *gsettings_client; | |
212 | static EmacsSettings *gsettings_obj; | |
213 | ||
214 | #else | |
215 | #ifdef HAVE_GCONF | |
216 | static GConfClient *gconf_client; | |
217 | #endif | |
218 | #endif | |
219 | ||
220 | ||
f904c0f9 JD |
221 | #define XSETTINGS_FONT_NAME "Gtk/FontName" |
222 | #define XSETTINGS_TOOL_BAR_STYLE "Gtk/ToolbarStyle" | |
0269ef77 | 223 | |
f904c0f9 JD |
224 | enum { |
225 | SEEN_AA = 0x01, | |
226 | SEEN_HINTING = 0x02, | |
227 | SEEN_RGBA = 0x04, | |
228 | SEEN_LCDFILTER = 0x08, | |
229 | SEEN_HINTSTYLE = 0x10, | |
230 | SEEN_DPI = 0x20, | |
231 | SEEN_FONT = 0x40, | |
232 | SEEN_TB_STYLE = 0x80, | |
233 | }; | |
af59aa6e | 234 | struct xsettings |
f904c0f9 JD |
235 | { |
236 | #ifdef HAVE_XFT | |
237 | FcBool aa, hinting; | |
238 | int rgba, lcdfilter, hintstyle; | |
239 | double dpi; | |
240 | #endif | |
241 | ||
242 | char *font; | |
243 | char *tb_style; | |
244 | ||
245 | unsigned seen; | |
246 | }; | |
247 | ||
9851bfc5 JD |
248 | #ifdef HAVE_GSETTINGS |
249 | #define GSETTINGS_SCHEMA "org.gnome.desktop.interface" | |
250 | #define SYSTEM_MONO_FONT "monospace-font-name" | |
251 | #define SYSTEM_FONT "font-name" | |
31a01b90 | 252 | |
9851bfc5 JD |
253 | #else |
254 | #ifdef HAVE_GCONF | |
255 | #define SYSTEM_MONO_FONT "/desktop/gnome/interface/monospace_font_name" | |
256 | #define SYSTEM_FONT "/desktop/gnome/interface/font_name" | |
31a01b90 | 257 | |
637fa988 JD |
258 | /* Callback called when something changed in GConf that we care about, |
259 | that is SYSTEM_MONO_FONT. */ | |
260 | ||
261 | static void | |
e4c8d29a J |
262 | something_changedCB (GConfClient *client, |
263 | guint cnxn_id, | |
264 | GConfEntry *entry, | |
265 | gpointer user_data) | |
637fa988 JD |
266 | { |
267 | GConfValue *v = gconf_entry_get_value (entry); | |
af59aa6e | 268 | |
637fa988 JD |
269 | if (!v) return; |
270 | if (v->type == GCONF_VALUE_STRING) | |
271 | { | |
272 | const char *value = gconf_value_get_string (v); | |
637fa988 | 273 | if (current_mono_font != NULL && strcmp (value, current_mono_font) == 0) |
872870b2 | 274 | return; /* No change. */ |
637fa988 JD |
275 | |
276 | xfree (current_mono_font); | |
277 | current_mono_font = xstrdup (value); | |
9851bfc5 | 278 | store_monospaced_changed (); |
637fa988 JD |
279 | } |
280 | } | |
9851bfc5 | 281 | |
637fa988 | 282 | #endif /* HAVE_GCONF */ |
9851bfc5 | 283 | #endif /* ! HAVE_GSETTINGS */ |
637fa988 JD |
284 | |
285 | #ifdef HAVE_XFT | |
286 | ||
21050de1 JD |
287 | /* Older fontconfig versions don't have FC_LCD_*. */ |
288 | #ifndef FC_LCD_NONE | |
289 | #define FC_LCD_NONE 0 | |
290 | #endif | |
291 | #ifndef FC_LCD_DEFAULT | |
292 | #define FC_LCD_DEFAULT 1 | |
293 | #endif | |
294 | #ifndef FC_LCD_FILTER | |
295 | #define FC_LCD_FILTER "lcdfilter" | |
296 | #endif | |
297 | ||
f904c0f9 JD |
298 | #endif /* HAVE_XFT */ |
299 | ||
637fa988 JD |
300 | /* Find the window that contains the XSETTINGS property values. */ |
301 | ||
302 | static void | |
971de7fb | 303 | get_prop_window (struct x_display_info *dpyinfo) |
637fa988 JD |
304 | { |
305 | Display *dpy = dpyinfo->display; | |
306 | ||
307 | XGrabServer (dpy); | |
308 | dpyinfo->xsettings_window = XGetSelectionOwner (dpy, | |
309 | dpyinfo->Xatom_xsettings_sel); | |
310 | if (dpyinfo->xsettings_window != None) | |
311 | /* Select events so we can detect if window is deleted or if settings | |
312 | are changed. */ | |
313 | XSelectInput (dpy, dpyinfo->xsettings_window, | |
314 | PropertyChangeMask|StructureNotifyMask); | |
315 | ||
316 | XUngrabServer (dpy); | |
317 | } | |
318 | ||
637fa988 JD |
319 | #define SWAP32(nr) (((nr) << 24) | (((nr) << 8) & 0xff0000) \ |
320 | | (((nr) >> 8) & 0xff00) | ((nr) >> 24)) | |
321 | #define SWAP16(nr) (((nr) << 8) | ((nr) >> 8)) | |
322 | #define PAD(nr) (((nr) + 3) & ~3) | |
323 | ||
324 | /* Parse xsettings and extract those that deal with Xft. | |
325 | See http://freedesktop.org/wiki/Specifications/XSettingsRegistry | |
326 | and http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html. | |
327 | ||
328 | Layout of prop. First is a header: | |
329 | ||
330 | bytes type what | |
331 | ------------------------------------ | |
332 | 1 CARD8 byte-order | |
333 | 3 unused | |
334 | 4 CARD32 SERIAL | |
335 | 4 CARD32 N_SETTINGS | |
336 | ||
337 | Then N_SETTINGS records, with header: | |
338 | ||
339 | bytes type what | |
340 | ------------------------------------ | |
341 | 1 SETTING_TYPE type (0 = integer, 1 = string, 2 RGB color). | |
342 | 1 unused | |
343 | 2 CARD16 n == name-length | |
344 | n STRING8 name | |
345 | p unused, p=pad_to_even_4(n) | |
346 | 4 CARD32 last-change-serial | |
347 | ||
348 | and then the value, For string: | |
af59aa6e | 349 | |
637fa988 JD |
350 | bytes type what |
351 | ------------------------------------ | |
352 | 4 CARD32 n = value-length | |
353 | n STRING8 value | |
354 | p unused, p=pad_to_even_4(n) | |
355 | ||
356 | For integer: | |
357 | ||
358 | bytes type what | |
359 | ------------------------------------ | |
360 | 4 INT32 value | |
361 | ||
362 | For RGB color: | |
363 | ||
364 | bytes type what | |
365 | ------------------------------------ | |
366 | 2 CARD16 red | |
367 | 2 CARD16 blue | |
368 | 2 CARD16 green | |
369 | 2 CARD16 alpha | |
370 | ||
3c055b77 | 371 | Returns non-zero if some Xft settings was seen, zero otherwise. |
637fa988 JD |
372 | */ |
373 | ||
374 | static int | |
e4c8d29a J |
375 | parse_settings (unsigned char *prop, |
376 | long unsigned int bytes, | |
377 | struct xsettings *settings) | |
637fa988 JD |
378 | { |
379 | Lisp_Object byteorder = Fbyteorder (); | |
380 | int my_bo = XFASTINT (byteorder) == 'B' ? MSBFirst : LSBFirst; | |
381 | int that_bo = prop[0]; | |
382 | CARD32 n_settings; | |
383 | int bytes_parsed = 0; | |
384 | int settings_seen = 0; | |
385 | int i = 0; | |
386 | ||
387 | /* First 4 bytes is a serial number, skip that. */ | |
388 | ||
389 | if (bytes < 12) return BadLength; | |
390 | memcpy (&n_settings, prop+8, 4); | |
391 | if (my_bo != that_bo) n_settings = SWAP32 (n_settings); | |
392 | bytes_parsed = 12; | |
393 | ||
394 | memset (settings, 0, sizeof (*settings)); | |
395 | ||
99852628 | 396 | while (bytes_parsed+4 < bytes && settings_seen < 7 |
637fa988 JD |
397 | && i < n_settings) |
398 | { | |
399 | int type = prop[bytes_parsed++]; | |
400 | CARD16 nlen; | |
401 | CARD32 vlen, ival = 0; | |
402 | char name[128]; /* The names we are looking for are not this long. */ | |
403 | char sval[128]; /* The values we are looking for are not this long. */ | |
99852628 | 404 | int want_this; |
637fa988 JD |
405 | int to_cpy; |
406 | ||
407 | sval[0] = '\0'; | |
408 | ++i; | |
409 | ++bytes_parsed; /* Padding */ | |
410 | ||
411 | memcpy (&nlen, prop+bytes_parsed, 2); | |
412 | bytes_parsed += 2; | |
413 | if (my_bo != that_bo) nlen = SWAP16 (nlen); | |
414 | if (bytes_parsed+nlen > bytes) return BadLength; | |
415 | to_cpy = nlen > 127 ? 127 : nlen; | |
416 | memcpy (name, prop+bytes_parsed, to_cpy); | |
417 | name[to_cpy] = '\0'; | |
418 | ||
419 | bytes_parsed += nlen; | |
420 | bytes_parsed = PAD (bytes_parsed); | |
421 | ||
422 | bytes_parsed += 4; /* Skip serial for this value */ | |
423 | if (bytes_parsed > bytes) return BadLength; | |
424 | ||
f904c0f9 JD |
425 | want_this = |
426 | #ifdef HAVE_XFT | |
427 | (nlen > 6 && strncmp (name, "Xft/", 4) == 0) | |
428 | || | |
429 | #endif | |
430 | (strcmp (XSETTINGS_FONT_NAME, name) == 0) | |
431 | || (strcmp (XSETTINGS_TOOL_BAR_STYLE, name) == 0); | |
637fa988 | 432 | |
af59aa6e | 433 | switch (type) |
637fa988 JD |
434 | { |
435 | case 0: /* Integer */ | |
436 | if (bytes_parsed+4 > bytes) return BadLength; | |
99852628 | 437 | if (want_this) |
637fa988 JD |
438 | { |
439 | memcpy (&ival, prop+bytes_parsed, 4); | |
440 | if (my_bo != that_bo) ival = SWAP32 (ival); | |
441 | } | |
442 | bytes_parsed += 4; | |
443 | break; | |
444 | ||
445 | case 1: /* String */ | |
446 | if (bytes_parsed+4 > bytes) return BadLength; | |
447 | memcpy (&vlen, prop+bytes_parsed, 4); | |
448 | bytes_parsed += 4; | |
449 | if (my_bo != that_bo) vlen = SWAP32 (vlen); | |
99852628 | 450 | if (want_this) |
637fa988 JD |
451 | { |
452 | to_cpy = vlen > 127 ? 127 : vlen; | |
453 | memcpy (sval, prop+bytes_parsed, to_cpy); | |
454 | sval[to_cpy] = '\0'; | |
455 | } | |
456 | bytes_parsed += vlen; | |
457 | bytes_parsed = PAD (bytes_parsed); | |
458 | break; | |
459 | ||
460 | case 2: /* RGB value */ | |
461 | /* No need to parse this */ | |
462 | if (bytes_parsed+8 > bytes) return BadLength; | |
af59aa6e | 463 | bytes_parsed += 8; /* 4 values (r, b, g, alpha), 2 bytes each. */ |
637fa988 JD |
464 | break; |
465 | ||
466 | default: /* Parse Error */ | |
467 | return BadValue; | |
468 | } | |
469 | ||
af59aa6e | 470 | if (want_this) |
637fa988 JD |
471 | { |
472 | ++settings_seen; | |
f904c0f9 JD |
473 | if (strcmp (name, XSETTINGS_FONT_NAME) == 0) |
474 | { | |
475 | settings->font = xstrdup (sval); | |
476 | settings->seen |= SEEN_FONT; | |
477 | } | |
478 | else if (strcmp (name, XSETTINGS_TOOL_BAR_STYLE) == 0) | |
479 | { | |
480 | settings->tb_style = xstrdup (sval); | |
481 | settings->seen |= SEEN_TB_STYLE; | |
482 | } | |
483 | #ifdef HAVE_XFT | |
484 | else if (strcmp (name, "Xft/Antialias") == 0) | |
3c055b77 JD |
485 | { |
486 | settings->seen |= SEEN_AA; | |
487 | settings->aa = ival != 0; | |
488 | } | |
637fa988 | 489 | else if (strcmp (name, "Xft/Hinting") == 0) |
3c055b77 JD |
490 | { |
491 | settings->seen |= SEEN_HINTING; | |
492 | settings->hinting = ival != 0; | |
493 | } | |
af59aa6e | 494 | # ifdef FC_HINT_STYLE |
637fa988 JD |
495 | else if (strcmp (name, "Xft/HintStyle") == 0) |
496 | { | |
3c055b77 | 497 | settings->seen |= SEEN_HINTSTYLE; |
637fa988 JD |
498 | if (strcmp (sval, "hintnone") == 0) |
499 | settings->hintstyle = FC_HINT_NONE; | |
500 | else if (strcmp (sval, "hintslight") == 0) | |
501 | settings->hintstyle = FC_HINT_SLIGHT; | |
502 | else if (strcmp (sval, "hintmedium") == 0) | |
503 | settings->hintstyle = FC_HINT_MEDIUM; | |
504 | else if (strcmp (sval, "hintfull") == 0) | |
505 | settings->hintstyle = FC_HINT_FULL; | |
3c055b77 JD |
506 | else |
507 | settings->seen &= ~SEEN_HINTSTYLE; | |
637fa988 | 508 | } |
af59aa6e | 509 | # endif |
637fa988 JD |
510 | else if (strcmp (name, "Xft/RGBA") == 0) |
511 | { | |
3c055b77 | 512 | settings->seen |= SEEN_RGBA; |
637fa988 JD |
513 | if (strcmp (sval, "none") == 0) |
514 | settings->rgba = FC_RGBA_NONE; | |
515 | else if (strcmp (sval, "rgb") == 0) | |
516 | settings->rgba = FC_RGBA_RGB; | |
517 | else if (strcmp (sval, "bgr") == 0) | |
518 | settings->rgba = FC_RGBA_BGR; | |
519 | else if (strcmp (sval, "vrgb") == 0) | |
520 | settings->rgba = FC_RGBA_VRGB; | |
521 | else if (strcmp (sval, "vbgr") == 0) | |
522 | settings->rgba = FC_RGBA_VBGR; | |
3c055b77 JD |
523 | else |
524 | settings->seen &= ~SEEN_RGBA; | |
637fa988 JD |
525 | } |
526 | else if (strcmp (name, "Xft/DPI") == 0) | |
3c055b77 JD |
527 | { |
528 | settings->seen |= SEEN_DPI; | |
529 | settings->dpi = (double)ival/1024.0; | |
530 | } | |
637fa988 JD |
531 | else if (strcmp (name, "Xft/lcdfilter") == 0) |
532 | { | |
3c055b77 | 533 | settings->seen |= SEEN_LCDFILTER; |
637fa988 JD |
534 | if (strcmp (sval, "none") == 0) |
535 | settings->lcdfilter = FC_LCD_NONE; | |
536 | else if (strcmp (sval, "lcddefault") == 0) | |
537 | settings->lcdfilter = FC_LCD_DEFAULT; | |
3c055b77 JD |
538 | else |
539 | settings->seen &= ~SEEN_LCDFILTER; | |
637fa988 | 540 | } |
f904c0f9 | 541 | #endif /* HAVE_XFT */ |
637fa988 JD |
542 | } |
543 | } | |
544 | ||
3c055b77 | 545 | return settings_seen; |
637fa988 JD |
546 | } |
547 | ||
548 | static int | |
971de7fb | 549 | read_settings (struct x_display_info *dpyinfo, struct xsettings *settings) |
637fa988 | 550 | { |
637fa988 JD |
551 | Atom act_type; |
552 | int act_form; | |
553 | unsigned long nitems, bytes_after; | |
554 | unsigned char *prop = NULL; | |
555 | Display *dpy = dpyinfo->display; | |
556 | int rc; | |
557 | ||
558 | x_catch_errors (dpy); | |
559 | rc = XGetWindowProperty (dpy, | |
560 | dpyinfo->xsettings_window, | |
561 | dpyinfo->Xatom_xsettings_prop, | |
562 | 0, LONG_MAX, False, AnyPropertyType, | |
563 | &act_type, &act_form, &nitems, &bytes_after, | |
564 | &prop); | |
565 | ||
566 | if (rc == Success && prop != NULL && act_form == 8 && nitems > 0 | |
567 | && act_type == dpyinfo->Xatom_xsettings_prop) | |
f904c0f9 | 568 | rc = parse_settings (prop, nitems, settings); |
637fa988 JD |
569 | |
570 | XFree (prop); | |
571 | ||
572 | x_uncatch_errors (); | |
573 | ||
3c055b77 | 574 | return rc != 0; |
637fa988 JD |
575 | } |
576 | ||
581e51e8 | 577 | |
637fa988 | 578 | static void |
e4c8d29a J |
579 | apply_xft_settings (struct x_display_info *dpyinfo, |
580 | int send_event_p, | |
581 | struct xsettings *settings) | |
637fa988 | 582 | { |
f904c0f9 | 583 | #ifdef HAVE_XFT |
637fa988 | 584 | FcPattern *pat; |
f904c0f9 | 585 | struct xsettings oldsettings; |
637fa988 JD |
586 | int changed = 0; |
587 | ||
637fa988 | 588 | memset (&oldsettings, 0, sizeof (oldsettings)); |
637fa988 JD |
589 | pat = FcPatternCreate (); |
590 | XftDefaultSubstitute (dpyinfo->display, | |
591 | XScreenNumberOfScreen (dpyinfo->screen), | |
592 | pat); | |
593 | FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); | |
594 | FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); | |
af59aa6e | 595 | # ifdef FC_HINT_STYLE |
637fa988 | 596 | FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &oldsettings.hintstyle); |
af59aa6e | 597 | # endif |
637fa988 JD |
598 | FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &oldsettings.lcdfilter); |
599 | FcPatternGetInteger (pat, FC_RGBA, 0, &oldsettings.rgba); | |
600 | FcPatternGetDouble (pat, FC_DPI, 0, &oldsettings.dpi); | |
601 | ||
f904c0f9 | 602 | if ((settings->seen & SEEN_AA) != 0 && oldsettings.aa != settings->aa) |
637fa988 JD |
603 | { |
604 | FcPatternDel (pat, FC_ANTIALIAS); | |
f904c0f9 | 605 | FcPatternAddBool (pat, FC_ANTIALIAS, settings->aa); |
637fa988 | 606 | ++changed; |
f904c0f9 | 607 | oldsettings.aa = settings->aa; |
637fa988 | 608 | } |
67477f30 | 609 | |
f904c0f9 JD |
610 | if ((settings->seen & SEEN_HINTING) != 0 |
611 | && oldsettings.hinting != settings->hinting) | |
637fa988 JD |
612 | { |
613 | FcPatternDel (pat, FC_HINTING); | |
f904c0f9 | 614 | FcPatternAddBool (pat, FC_HINTING, settings->hinting); |
637fa988 | 615 | ++changed; |
f904c0f9 | 616 | oldsettings.hinting = settings->hinting; |
637fa988 | 617 | } |
f904c0f9 | 618 | if ((settings->seen & SEEN_RGBA) != 0 && oldsettings.rgba != settings->rgba) |
637fa988 JD |
619 | { |
620 | FcPatternDel (pat, FC_RGBA); | |
f904c0f9 JD |
621 | FcPatternAddInteger (pat, FC_RGBA, settings->rgba); |
622 | oldsettings.rgba = settings->rgba; | |
637fa988 JD |
623 | ++changed; |
624 | } | |
67477f30 | 625 | |
a6eb20d8 | 626 | /* Older fontconfig versions don't have FC_LCD_FILTER. */ |
f904c0f9 JD |
627 | if ((settings->seen & SEEN_LCDFILTER) != 0 |
628 | && oldsettings.lcdfilter != settings->lcdfilter) | |
637fa988 JD |
629 | { |
630 | FcPatternDel (pat, FC_LCD_FILTER); | |
f904c0f9 | 631 | FcPatternAddInteger (pat, FC_LCD_FILTER, settings->lcdfilter); |
637fa988 | 632 | ++changed; |
f904c0f9 | 633 | oldsettings.lcdfilter = settings->lcdfilter; |
637fa988 | 634 | } |
67477f30 | 635 | |
af59aa6e | 636 | # ifdef FC_HINT_STYLE |
f904c0f9 JD |
637 | if ((settings->seen & SEEN_HINTSTYLE) != 0 |
638 | && oldsettings.hintstyle != settings->hintstyle) | |
637fa988 JD |
639 | { |
640 | FcPatternDel (pat, FC_HINT_STYLE); | |
f904c0f9 | 641 | FcPatternAddInteger (pat, FC_HINT_STYLE, settings->hintstyle); |
637fa988 | 642 | ++changed; |
f904c0f9 | 643 | oldsettings.hintstyle = settings->hintstyle; |
637fa988 | 644 | } |
af59aa6e | 645 | # endif |
67477f30 | 646 | |
f904c0f9 JD |
647 | if ((settings->seen & SEEN_DPI) != 0 && oldsettings.dpi != settings->dpi |
648 | && settings->dpi > 0) | |
637fa988 JD |
649 | { |
650 | Lisp_Object frame, tail; | |
651 | ||
652 | FcPatternDel (pat, FC_DPI); | |
f904c0f9 | 653 | FcPatternAddDouble (pat, FC_DPI, settings->dpi); |
637fa988 | 654 | ++changed; |
f904c0f9 | 655 | oldsettings.dpi = settings->dpi; |
af59aa6e | 656 | |
637fa988 | 657 | /* Change the DPI on this display and all frames on the display. */ |
f904c0f9 | 658 | dpyinfo->resy = dpyinfo->resx = settings->dpi; |
637fa988 JD |
659 | FOR_EACH_FRAME (tail, frame) |
660 | if (FRAME_X_P (XFRAME (frame)) | |
661 | && FRAME_X_DISPLAY_INFO (XFRAME (frame)) == dpyinfo) | |
f904c0f9 | 662 | XFRAME (frame)->resy = XFRAME (frame)->resx = settings->dpi; |
637fa988 JD |
663 | } |
664 | ||
665 | if (changed) | |
666 | { | |
aaafe47a PE |
667 | static char const format[] = |
668 | "Antialias: %d, Hinting: %d, RGBA: %d, LCDFilter: %d, " | |
669 | "Hintstyle: %d, DPI: %lf"; | |
670 | enum | |
671 | { | |
672 | d_formats = 5, | |
673 | d_growth = INT_BUFSIZE_BOUND (int) - sizeof "%d", | |
674 | lf_formats = 1, | |
675 | max_f_integer_digits = DBL_MAX_10_EXP + 1, | |
676 | f_precision = 6, | |
677 | lf_growth = (sizeof "-." + max_f_integer_digits + f_precision | |
678 | - sizeof "%lf") | |
679 | }; | |
680 | char buf[sizeof format + d_formats * d_growth + lf_formats * lf_growth]; | |
681 | ||
637fa988 JD |
682 | XftDefaultSet (dpyinfo->display, pat); |
683 | if (send_event_p) | |
f904c0f9 JD |
684 | store_config_changed_event (Qfont_render, |
685 | XCAR (dpyinfo->name_list_element)); | |
aaafe47a PE |
686 | sprintf (buf, format, oldsettings.aa, oldsettings.hinting, |
687 | oldsettings.rgba, oldsettings.lcdfilter, | |
688 | oldsettings.hintstyle, oldsettings.dpi); | |
689 | Vxft_settings = build_string (buf); | |
637fa988 JD |
690 | } |
691 | else | |
692 | FcPatternDestroy (pat); | |
f904c0f9 | 693 | #endif /* HAVE_XFT */ |
637fa988 JD |
694 | } |
695 | ||
f904c0f9 | 696 | static void |
971de7fb | 697 | read_and_apply_settings (struct x_display_info *dpyinfo, int send_event_p) |
f904c0f9 JD |
698 | { |
699 | struct xsettings settings; | |
700 | Lisp_Object dpyname = XCAR (dpyinfo->name_list_element); | |
701 | ||
702 | if (!read_settings (dpyinfo, &settings)) | |
703 | return; | |
704 | ||
705 | apply_xft_settings (dpyinfo, True, &settings); | |
706 | if (settings.seen & SEEN_TB_STYLE) | |
707 | { | |
708 | Lisp_Object style = Qnil; | |
709 | if (strcmp (settings.tb_style, "both") == 0) | |
710 | style = Qboth; | |
711 | else if (strcmp (settings.tb_style, "both-horiz") == 0) | |
712 | style = Qboth_horiz; | |
713 | else if (strcmp (settings.tb_style, "icons") == 0) | |
714 | style = Qimage; | |
715 | else if (strcmp (settings.tb_style, "text") == 0) | |
716 | style = Qtext; | |
717 | if (!NILP (style) && !EQ (style, current_tool_bar_style)) | |
718 | { | |
719 | current_tool_bar_style = style; | |
720 | if (send_event_p) | |
721 | store_config_changed_event (Qtool_bar_style, dpyname); | |
722 | } | |
baad03f0 | 723 | xfree (settings.tb_style); |
f904c0f9 JD |
724 | } |
725 | ||
726 | if (settings.seen & SEEN_FONT) | |
727 | { | |
af59aa6e | 728 | if (!current_font || strcmp (current_font, settings.font) != 0) |
f904c0f9 | 729 | { |
baad03f0 | 730 | xfree (current_font); |
f904c0f9 JD |
731 | current_font = settings.font; |
732 | if (send_event_p) | |
733 | store_config_changed_event (Qfont_name, dpyname); | |
734 | } | |
735 | else | |
baad03f0 | 736 | xfree (settings.font); |
f904c0f9 JD |
737 | } |
738 | } | |
637fa988 JD |
739 | |
740 | void | |
971de7fb | 741 | xft_settings_event (struct x_display_info *dpyinfo, XEvent *event) |
637fa988 | 742 | { |
637fa988 | 743 | int check_window_p = 0; |
f904c0f9 | 744 | int apply_settings = 0; |
637fa988 JD |
745 | |
746 | switch (event->type) | |
747 | { | |
748 | case DestroyNotify: | |
749 | if (dpyinfo->xsettings_window == event->xany.window) | |
750 | check_window_p = 1; | |
751 | break; | |
752 | ||
753 | case ClientMessage: | |
754 | if (event->xclient.message_type == dpyinfo->Xatom_xsettings_mgr | |
755 | && event->xclient.data.l[1] == dpyinfo->Xatom_xsettings_sel | |
756 | && event->xclient.window == dpyinfo->root_window) | |
757 | check_window_p = 1; | |
758 | break; | |
759 | ||
760 | case PropertyNotify: | |
761 | if (event->xproperty.window == dpyinfo->xsettings_window | |
762 | && event->xproperty.state == PropertyNewValue | |
763 | && event->xproperty.atom == dpyinfo->Xatom_xsettings_prop) | |
f904c0f9 | 764 | apply_settings = 1; |
637fa988 JD |
765 | break; |
766 | } | |
767 | ||
f904c0f9 | 768 | |
637fa988 JD |
769 | if (check_window_p) |
770 | { | |
771 | dpyinfo->xsettings_window = None; | |
772 | get_prop_window (dpyinfo); | |
773 | if (dpyinfo->xsettings_window != None) | |
f904c0f9 | 774 | apply_settings = 1; |
637fa988 | 775 | } |
f904c0f9 JD |
776 | |
777 | if (apply_settings) | |
778 | read_and_apply_settings (dpyinfo, True); | |
637fa988 JD |
779 | } |
780 | ||
781 | ||
9851bfc5 JD |
782 | static void |
783 | init_gsettings (void) | |
784 | { | |
785 | #ifdef HAVE_GSETTINGS | |
786 | GVariant *val; | |
787 | #ifdef HAVE_G_TYPE_INIT | |
788 | g_type_init (); | |
789 | #endif | |
790 | ||
791 | gsettings_client = g_settings_new (GSETTINGS_SCHEMA); | |
792 | if (!gsettings_client) return; | |
793 | g_object_ref_sink (G_OBJECT (gsettings_client)); | |
794 | ||
795 | gsettings_obj = g_object_new (EMACS_TYPE_SETTINGS, NULL); | |
796 | if (!gsettings_obj) | |
797 | { | |
798 | g_object_unref (G_OBJECT (gsettings_client)); | |
799 | return; | |
800 | } | |
801 | g_object_ref_sink (G_OBJECT (gsettings_obj)); | |
802 | ||
803 | val = g_settings_get_value (gsettings_client, SYSTEM_MONO_FONT); | |
804 | if (val) | |
805 | { | |
806 | g_variant_ref_sink (val); | |
807 | if (g_variant_is_of_type (val, G_VARIANT_TYPE_STRING)) | |
808 | current_mono_font = xstrdup (g_variant_get_string (val, NULL)); | |
809 | g_variant_unref (val); | |
810 | } | |
811 | ||
812 | g_settings_bind (gsettings_client, SYSTEM_MONO_FONT, gsettings_obj, | |
813 | "monospace-font", G_SETTINGS_BIND_GET); | |
814 | g_settings_bind (gsettings_client, SYSTEM_FONT, gsettings_obj, | |
815 | "font", G_SETTINGS_BIND_GET); | |
816 | #endif /* HAVE_GSETTINGS */ | |
817 | } | |
818 | ||
819 | ||
637fa988 | 820 | static void |
971de7fb | 821 | init_gconf (void) |
637fa988 | 822 | { |
9851bfc5 | 823 | #if defined (HAVE_GCONF) && defined (HAVE_XFT) && ! defined (HAVE_GSETTINGS) |
637fa988 | 824 | char *s; |
637fa988 | 825 | |
1e600395 | 826 | #ifdef HAVE_G_TYPE_INIT |
637fa988 | 827 | g_type_init (); |
1e600395 | 828 | #endif |
637fa988 JD |
829 | gconf_client = gconf_client_get_default (); |
830 | s = gconf_client_get_string (gconf_client, SYSTEM_MONO_FONT, NULL); | |
831 | if (s) | |
832 | { | |
833 | current_mono_font = xstrdup (s); | |
834 | g_free (s); | |
835 | } | |
99852628 JD |
836 | s = gconf_client_get_string (gconf_client, SYSTEM_FONT, NULL); |
837 | if (s) | |
838 | { | |
839 | current_font = xstrdup (s); | |
840 | g_free (s); | |
841 | } | |
637fa988 JD |
842 | gconf_client_set_error_handling (gconf_client, GCONF_CLIENT_HANDLE_NONE); |
843 | gconf_client_add_dir (gconf_client, | |
844 | SYSTEM_MONO_FONT, | |
845 | GCONF_CLIENT_PRELOAD_ONELEVEL, | |
846 | NULL); | |
847 | gconf_client_notify_add (gconf_client, | |
848 | SYSTEM_MONO_FONT, | |
849 | something_changedCB, | |
850 | NULL, NULL, NULL); | |
9851bfc5 | 851 | #endif /* HAVE_GCONF && HAVE_XFT && ! HAVE_GSETTINGS */ |
637fa988 JD |
852 | } |
853 | ||
854 | static void | |
971de7fb | 855 | init_xsettings (struct x_display_info *dpyinfo) |
637fa988 | 856 | { |
637fa988 JD |
857 | Display *dpy = dpyinfo->display; |
858 | ||
859 | BLOCK_INPUT; | |
860 | ||
637fa988 JD |
861 | /* Select events so we can detect client messages sent when selection |
862 | owner changes. */ | |
863 | XSelectInput (dpy, dpyinfo->root_window, StructureNotifyMask); | |
864 | ||
865 | get_prop_window (dpyinfo); | |
866 | if (dpyinfo->xsettings_window != None) | |
f904c0f9 | 867 | read_and_apply_settings (dpyinfo, False); |
637fa988 JD |
868 | |
869 | UNBLOCK_INPUT; | |
637fa988 JD |
870 | } |
871 | ||
872 | void | |
971de7fb | 873 | xsettings_initialize (struct x_display_info *dpyinfo) |
637fa988 JD |
874 | { |
875 | if (first_dpyinfo == NULL) first_dpyinfo = dpyinfo; | |
9851bfc5 | 876 | init_gsettings (); |
637fa988 | 877 | init_gconf (); |
f904c0f9 | 878 | init_xsettings (dpyinfo); |
637fa988 JD |
879 | } |
880 | ||
0d1d0d26 | 881 | const char * |
971de7fb | 882 | xsettings_get_system_font (void) |
0d1d0d26 JD |
883 | { |
884 | return current_mono_font; | |
885 | } | |
637fa988 | 886 | |
e87b6180 | 887 | #ifdef USE_LUCID |
99852628 | 888 | const char * |
971de7fb | 889 | xsettings_get_system_normal_font (void) |
99852628 JD |
890 | { |
891 | return current_font; | |
892 | } | |
e87b6180 | 893 | #endif |
99852628 JD |
894 | |
895 | DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font, | |
896 | Sfont_get_system_normal_font, | |
897 | 0, 0, 0, | |
bf935339 | 898 | doc: /* Get the system default application font. */) |
5842a27b | 899 | (void) |
99852628 | 900 | { |
2674ddc8 | 901 | return current_font ? build_string (current_font) : Qnil; |
99852628 JD |
902 | } |
903 | ||
637fa988 JD |
904 | DEFUN ("font-get-system-font", Ffont_get_system_font, Sfont_get_system_font, |
905 | 0, 0, 0, | |
bf935339 | 906 | doc: /* Get the system default fixed width font. */) |
5842a27b | 907 | (void) |
637fa988 | 908 | { |
2674ddc8 | 909 | return current_mono_font ? build_string (current_mono_font) : Qnil; |
637fa988 JD |
910 | } |
911 | ||
a7ca3326 | 912 | DEFUN ("tool-bar-get-system-style", Ftool_bar_get_system_style, |
16a97296 | 913 | Stool_bar_get_system_style, 0, 0, 0, |
f904c0f9 | 914 | doc: /* Get the system tool bar style. |
4721152c | 915 | If no system tool bar style is known, return `tool-bar-style' if set to a |
f904c0f9 | 916 | known style. Otherwise return image. */) |
5842a27b | 917 | (void) |
f904c0f9 JD |
918 | { |
919 | if (EQ (Vtool_bar_style, Qimage) | |
920 | || EQ (Vtool_bar_style, Qtext) | |
921 | || EQ (Vtool_bar_style, Qboth) | |
8a52f00a JD |
922 | || EQ (Vtool_bar_style, Qboth_horiz) |
923 | || EQ (Vtool_bar_style, Qtext_image_horiz)) | |
f904c0f9 JD |
924 | return Vtool_bar_style; |
925 | if (!NILP (current_tool_bar_style)) | |
926 | return current_tool_bar_style; | |
927 | return Qimage; | |
928 | } | |
929 | ||
637fa988 | 930 | void |
971de7fb | 931 | syms_of_xsettings (void) |
637fa988 JD |
932 | { |
933 | current_mono_font = NULL; | |
99852628 | 934 | current_font = NULL; |
637fa988 | 935 | first_dpyinfo = NULL; |
9851bfc5 JD |
936 | #ifdef HAVE_GSETTINGS |
937 | gsettings_client = NULL; | |
938 | gsettings_obj = NULL; | |
939 | #else | |
637fa988 JD |
940 | #ifdef HAVE_GCONF |
941 | gconf_client = NULL; | |
9851bfc5 | 942 | #endif |
637fa988 JD |
943 | #endif |
944 | ||
cd3520a4 JB |
945 | DEFSYM (Qmonospace_font_name, "monospace-font-name"); |
946 | DEFSYM (Qfont_name, "font-name"); | |
947 | DEFSYM (Qfont_render, "font-render"); | |
637fa988 | 948 | defsubr (&Sfont_get_system_font); |
99852628 | 949 | defsubr (&Sfont_get_system_normal_font); |
637fa988 | 950 | |
29208e82 | 951 | DEFVAR_BOOL ("font-use-system-font", use_system_font, |
bf935339 J |
952 | doc: /* *Non-nil means to apply the system defined font dynamically. |
953 | When this is non-nil and the system defined fixed width font changes, we | |
954 | update frames dynamically. | |
955 | If this variable is nil, Emacs ignores system font changes. */); | |
dfb3c4c6 JD |
956 | use_system_font = 0; |
957 | ||
29208e82 | 958 | DEFVAR_LISP ("xft-settings", Vxft_settings, |
67477f30 JD |
959 | doc: /* Font settings applied to Xft. */); |
960 | Vxft_settings = make_string ("", 0); | |
961 | ||
0d1d0d26 JD |
962 | #ifdef HAVE_XFT |
963 | Fprovide (intern_c_string ("font-render-setting"), Qnil); | |
9851bfc5 | 964 | #if defined (HAVE_GCONF) || defined (HAVE_GSETTINGS) |
637fa988 JD |
965 | Fprovide (intern_c_string ("system-font-setting"), Qnil); |
966 | #endif | |
637fa988 | 967 | #endif |
f904c0f9 JD |
968 | |
969 | current_tool_bar_style = Qnil; | |
cd3520a4 | 970 | DEFSYM (Qtool_bar_style, "tool-bar-style"); |
f904c0f9 JD |
971 | defsubr (&Stool_bar_get_system_style); |
972 | ||
973 | Fprovide (intern_c_string ("dynamic-setting"), Qnil); | |
637fa988 | 974 | } |