1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 Rodrigo Moya
4 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5 * Copyright (C) 2012-2021 MATE Developers
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <time.h>
33
34 #include <X11/Xatom.h>
35
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <gdk/gdk.h>
39 #include <gdk/gdkx.h>
40 #include <gtk/gtk.h>
41 #include <gio/gio.h>
42
43 #include "mate-settings-profile.h"
44 #include "msd-xsettings-manager.h"
45 #include "xsettings-manager.h"
46 #include "fontconfig-monitor.h"
47 #include "wm-common.h"
48
49 #define MOUSE_SCHEMA "org.mate.peripherals-mouse"
50 #define INTERFACE_SCHEMA "org.mate.interface"
51 #define SOUND_SCHEMA "org.mate.sound"
52
53 #define CURSOR_THEME_KEY "cursor-theme"
54 #define CURSOR_SIZE_KEY "cursor-size"
55 #define SCALING_FACTOR_KEY "window-scaling-factor"
56 #define SCALING_FACTOR_QT_KEY "window-scaling-factor-qt-sync"
57
58 #define FONT_RENDER_SCHEMA "org.mate.font-rendering"
59 #define FONT_ANTIALIASING_KEY "antialiasing"
60 #define FONT_HINTING_KEY "hinting"
61 #define FONT_RGBA_ORDER_KEY "rgba-order"
62 #define FONT_DPI_KEY "dpi"
63
64 /* X servers sometimes lie about the screen's physical dimensions, so we cannot
65 * compute an accurate DPI value. When this happens, the user gets fonts that
66 * are too huge or too tiny. So, we see what the server returns: if it reports
67 * something outside of the range [DPI_LOW_REASONABLE_VALUE,
68 * DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use
69 * DPI_FALLBACK instead.
70 *
71 * See get_dpi_from_gsettings_or_server() below, and also
72 * https://bugzilla.novell.com/show_bug.cgi?id=217790
73 */
74 #define DPI_FALLBACK 96
75 #define DPI_LOW_REASONABLE_VALUE 50
76 #define DPI_HIGH_REASONABLE_VALUE 500
77
78 /* The minimum resolution at which we turn on a window-scale of 2 */
79 #define HIDPI_LIMIT (DPI_FALLBACK * 2)
80
81 /* The minimum screen height at which we turn on a window-scale of 2;
82 * below this there just isn't enough vertical real estate for GNOME
83 * apps to work, and it's better to just be tiny */
84 #define HIDPI_MIN_HEIGHT 1500
85
86 #define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((GPOINTER_TO_INT(i) == 2) ? TRUE : FALSE))
87 #define GBOOLEAN_TO_POINTER(i) (GINT_TO_POINTER ((i) ? 2 : 1))
88
89 typedef struct _TranslationEntry TranslationEntry;
90 typedef void (* TranslationFunc) (MateXSettingsManager *manager,
91 TranslationEntry *trans,
92 GVariant *value);
93
94 struct _TranslationEntry {
95 const char *gsettings_schema;
96 const char *gsettings_key;
97 const char *xsetting_name;
98
99 TranslationFunc translate;
100 };
101
102 struct MateXSettingsManagerPrivate
103 {
104 XSettingsManager **managers;
105 GHashTable *gsettings;
106 GSettings *gsettings_font;
107 fontconfig_monitor_handle_t *fontconfig_handle;
108 gint window_scale;
109 };
110
111 #define MSD_XSETTINGS_ERROR msd_xsettings_error_quark ()
112
113 enum {
114 MSD_XSETTINGS_ERROR_INIT
115 };
116
117 static void mate_xsettings_manager_finalize (GObject *object);
118
119 G_DEFINE_TYPE_WITH_PRIVATE (MateXSettingsManager, mate_xsettings_manager, G_TYPE_OBJECT)
120
121 static gpointer manager_object = NULL;
122
123 static GQuark
msd_xsettings_error_quark(void)124 msd_xsettings_error_quark (void)
125 {
126 return g_quark_from_static_string ("msd-xsettings-error-quark");
127 }
128
129 static void
translate_bool_int(MateXSettingsManager * manager,TranslationEntry * trans,GVariant * value)130 translate_bool_int (MateXSettingsManager *manager,
131 TranslationEntry *trans,
132 GVariant *value)
133 {
134 int i;
135
136 for (i = 0; manager->priv->managers [i]; i++) {
137 xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
138 g_variant_get_boolean (value));
139 }
140 }
141
142 static void
translate_int_int(MateXSettingsManager * manager,TranslationEntry * trans,GVariant * value)143 translate_int_int (MateXSettingsManager *manager,
144 TranslationEntry *trans,
145 GVariant *value)
146 {
147 int i;
148
149 for (i = 0; manager->priv->managers [i]; i++) {
150 xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name,
151 g_variant_get_int32 (value));
152 }
153 }
154
155 static void
translate_string_string(MateXSettingsManager * manager,TranslationEntry * trans,GVariant * value)156 translate_string_string (MateXSettingsManager *manager,
157 TranslationEntry *trans,
158 GVariant *value)
159 {
160 int i;
161
162 for (i = 0; manager->priv->managers [i]; i++) {
163 xsettings_manager_set_string (manager->priv->managers [i],
164 trans->xsetting_name,
165 g_variant_get_string (value, NULL));
166 }
167 }
168
169 static void
translate_string_string_toolbar(MateXSettingsManager * manager,TranslationEntry * trans,GVariant * value)170 translate_string_string_toolbar (MateXSettingsManager *manager,
171 TranslationEntry *trans,
172 GVariant *value)
173 {
174 int i;
175 const char *tmp;
176
177 /* This is kind of a workaround since GNOME expects the key value to be
178 * "both_horiz" and gtk+ wants the XSetting to be "both-horiz".
179 */
180 tmp = g_variant_get_string (value, NULL);
181 if (tmp && strcmp (tmp, "both_horiz") == 0) {
182 tmp = "both-horiz";
183 }
184
185 for (i = 0; manager->priv->managers [i]; i++) {
186 xsettings_manager_set_string (manager->priv->managers [i],
187 trans->xsetting_name,
188 tmp);
189 }
190 }
191
192 static TranslationEntry translations [] = {
193 { MOUSE_SCHEMA, "double-click", "Net/DoubleClickTime", translate_int_int },
194 { MOUSE_SCHEMA, "drag-threshold", "Net/DndDragThreshold", translate_int_int },
195 { MOUSE_SCHEMA, "cursor-theme", "Gtk/CursorThemeName", translate_string_string },
196 { MOUSE_SCHEMA, "cursor-size", "Gtk/CursorThemeSize", translate_int_int },
197
198 { INTERFACE_SCHEMA, "font-name", "Gtk/FontName", translate_string_string },
199 { INTERFACE_SCHEMA, "gtk-key-theme", "Gtk/KeyThemeName", translate_string_string },
200 { INTERFACE_SCHEMA, "toolbar-style", "Gtk/ToolbarStyle", translate_string_string_toolbar },
201 { INTERFACE_SCHEMA, "toolbar-icons-size", "Gtk/ToolbarIconSize", translate_string_string },
202 { INTERFACE_SCHEMA, "cursor-blink", "Net/CursorBlink", translate_bool_int },
203 { INTERFACE_SCHEMA, "cursor-blink-time", "Net/CursorBlinkTime", translate_int_int },
204 { INTERFACE_SCHEMA, "gtk-theme", "Net/ThemeName", translate_string_string },
205 { INTERFACE_SCHEMA, "gtk-color-scheme", "Gtk/ColorScheme", translate_string_string },
206 { INTERFACE_SCHEMA, "gtk-im-preedit-style", "Gtk/IMPreeditStyle", translate_string_string },
207 { INTERFACE_SCHEMA, "gtk-im-status-style", "Gtk/IMStatusStyle", translate_string_string },
208 { INTERFACE_SCHEMA, "gtk-im-module", "Gtk/IMModule", translate_string_string },
209 { INTERFACE_SCHEMA, "icon-theme", "Net/IconThemeName", translate_string_string },
210 { INTERFACE_SCHEMA, "file-chooser-backend", "Gtk/FileChooserBackend", translate_string_string },
211 { INTERFACE_SCHEMA, "gtk-decoration-layout", "Gtk/DecorationLayout", translate_string_string },
212 { INTERFACE_SCHEMA, "gtk-shell-shows-app-menu","Gtk/ShellShowsAppMenu", translate_bool_int },
213 { INTERFACE_SCHEMA, "gtk-shell-shows-menubar","Gtk/ShellShowsMenubar", translate_bool_int },
214 { INTERFACE_SCHEMA, "menus-have-icons", "Gtk/MenuImages", translate_bool_int },
215 { INTERFACE_SCHEMA, "buttons-have-icons", "Gtk/ButtonImages", translate_bool_int },
216 { INTERFACE_SCHEMA, "menubar-accel", "Gtk/MenuBarAccel", translate_string_string },
217 { INTERFACE_SCHEMA, "show-input-method-menu", "Gtk/ShowInputMethodMenu", translate_bool_int },
218 { INTERFACE_SCHEMA, "show-unicode-menu", "Gtk/ShowUnicodeMenu", translate_bool_int },
219 { INTERFACE_SCHEMA, "automatic-mnemonics", "Gtk/AutoMnemonics", translate_bool_int },
220 {INTERFACE_SCHEMA, "gtk-enable-primary-paste", "Gtk/EnablePrimaryPaste",
221 translate_bool_int },
222 { INTERFACE_SCHEMA, "gtk-enable-animations", "Gtk/EnableAnimations", translate_bool_int },
223 { INTERFACE_SCHEMA, "gtk-dialogs-use-header", "Gtk/DialogsUseHeader", translate_bool_int },
224
225 { SOUND_SCHEMA, "theme-name", "Net/SoundThemeName", translate_string_string },
226 { SOUND_SCHEMA, "event-sounds", "Net/EnableEventSounds" , translate_bool_int },
227 { SOUND_SCHEMA, "input-feedback-sounds", "Net/EnableInputFeedbackSounds", translate_bool_int }
228 };
229
230 /* Auto-detect the most appropriate scale factor for the primary monitor.
231 * A lot of this code is shamelessly copied and adapted from Linux Mint/Cinnamon.
232 */
233 static int
get_window_scale_auto(void)234 get_window_scale_auto (void)
235 {
236 GdkDisplay *display;
237 GdkMonitor *monitor;
238 GdkRectangle rect;
239 int width_mm, height_mm;
240 int monitor_scale, window_scale;
241
242 display = gdk_display_get_default ();
243 monitor = gdk_display_get_primary_monitor (display);
244
245 /* Use current value as the default */
246 window_scale = 1;
247
248 gdk_monitor_get_geometry (monitor, &rect);
249 width_mm = gdk_monitor_get_width_mm (monitor);
250 height_mm = gdk_monitor_get_height_mm (monitor);
251 monitor_scale = gdk_monitor_get_scale_factor (monitor);
252
253 if (rect.height * monitor_scale < HIDPI_MIN_HEIGHT)
254 return 1;
255
256 /* Some monitors/TV encode the aspect ratio (16/9 or 16/10) instead of the physical size */
257 if ((width_mm == 160 && height_mm == 90) ||
258 (width_mm == 160 && height_mm == 100) ||
259 (width_mm == 16 && height_mm == 9) ||
260 (width_mm == 16 && height_mm == 10))
261 return 1;
262
263 if (width_mm > 0 && height_mm > 0) {
264 double dpi_x, dpi_y;
265
266 dpi_x = (double)rect.width * monitor_scale / (width_mm / 25.4);
267 dpi_y = (double)rect.height * monitor_scale / (height_mm / 25.4);
268 /* We don't completely trust these values so both must be high, and never pick
269 * higher ratio than 2 automatically */
270 if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT)
271 window_scale = 2;
272 }
273
274 return window_scale;
275 }
276
277 static int
get_window_scale(MateXSettingsManager * manager)278 get_window_scale (MateXSettingsManager *manager)
279 {
280 GSettings *gsettings;
281 gint scale;
282
283 /* Get scale factor from gsettings */
284 gsettings = g_hash_table_lookup (manager->priv->gsettings, INTERFACE_SCHEMA);
285 scale = g_settings_get_int (gsettings, SCALING_FACTOR_KEY);
286
287 /* Auto-detect */
288 if (scale == 0)
289 scale = get_window_scale_auto ();
290
291 return scale;
292 }
293
294 static double
dpi_from_pixels_and_mm(int pixels,int mm)295 dpi_from_pixels_and_mm (int pixels,
296 int mm)
297 {
298 double dpi;
299
300 if (mm >= 1)
301 dpi = pixels / (mm / 25.4);
302 else
303 dpi = 0;
304
305 return dpi;
306 }
307
308 static double
get_dpi_from_x_server(void)309 get_dpi_from_x_server (void)
310 {
311 GdkScreen *screen;
312 double dpi;
313
314 screen = gdk_screen_get_default ();
315 if (screen != NULL) {
316 double width_dpi, height_dpi;
317
318 Screen *xscreen = gdk_x11_screen_get_xscreen (screen);
319
320 width_dpi = dpi_from_pixels_and_mm (WidthOfScreen (xscreen), WidthMMOfScreen (xscreen));
321 height_dpi = dpi_from_pixels_and_mm (HeightOfScreen (xscreen), HeightMMOfScreen (xscreen));
322
323 if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE
324 || height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE) {
325 dpi = DPI_FALLBACK;
326 } else {
327 dpi = (width_dpi + height_dpi) / 2.0;
328 }
329 } else {
330 /* Huh!? No screen? */
331
332 dpi = DPI_FALLBACK;
333 }
334
335 return dpi;
336 }
337
338 static double
get_dpi_from_gsettings_or_x_server(GSettings * gsettings,gint scale)339 get_dpi_from_gsettings_or_x_server (GSettings *gsettings, gint scale)
340 {
341 double dpi;
342
343 dpi = g_settings_get_double (gsettings, FONT_DPI_KEY);
344
345 /* If the user has ever set the DPI preference in GSettings, we use that.
346 * Otherwise, we see if the X server reports a reasonable DPI value: some X
347 * servers report completely bogus values, and the user gets huge or tiny
348 * fonts which are unusable.
349 */
350
351 if (dpi == 0)
352 dpi = get_dpi_from_x_server ();
353
354 dpi *= (double)scale;
355 dpi = CLAMP(dpi, DPI_LOW_REASONABLE_VALUE, DPI_HIGH_REASONABLE_VALUE);
356
357 return dpi;
358 }
359
360 typedef struct
361 {
362 gboolean antialias;
363 gboolean hinting;
364 int window_scale;
365 int dpi;
366 int scaled_dpi;
367 char *cursor_theme;
368 int cursor_size;
369 const char *rgba;
370 const char *hintstyle;
371 } MateXftSettings;
372
373 static const char *rgba_types[] = { "rgb", "bgr", "vbgr", "vrgb" };
374
375 /* Read GSettings values and determine the appropriate Xft settings based on them
376 * This probably could be done a bit more cleanly with g_settings_get_enum
377 */
378 static void
xft_settings_get(MateXSettingsManager * manager,MateXftSettings * settings)379 xft_settings_get (MateXSettingsManager *manager,
380 MateXftSettings *settings)
381 {
382 GSettings *mouse_gsettings;
383 char *antialiasing;
384 char *hinting;
385 char *rgba_order;
386 double dpi;
387 gint scale;
388
389 mouse_gsettings = g_hash_table_lookup (manager->priv->gsettings, MOUSE_SCHEMA);
390
391 antialiasing = g_settings_get_string (manager->priv->gsettings_font, FONT_ANTIALIASING_KEY);
392 hinting = g_settings_get_string (manager->priv->gsettings_font, FONT_HINTING_KEY);
393 rgba_order = g_settings_get_string (manager->priv->gsettings_font, FONT_RGBA_ORDER_KEY);
394 scale = get_window_scale (manager);
395 dpi = get_dpi_from_gsettings_or_x_server (manager->priv->gsettings_font, scale);
396
397 settings->antialias = TRUE;
398 settings->hinting = TRUE;
399 settings->hintstyle = "hintslight";
400 settings->window_scale = scale;
401 settings->dpi = dpi / scale * 1024; /* Xft wants 1/1024ths of an inch */
402 settings->scaled_dpi = dpi * 1024;
403 settings->cursor_theme = g_settings_get_string (mouse_gsettings, CURSOR_THEME_KEY);
404 settings->cursor_size = scale * g_settings_get_int (mouse_gsettings, CURSOR_SIZE_KEY);
405 settings->rgba = "rgb";
406
407 if (rgba_order) {
408 int i;
409 gboolean found = FALSE;
410
411 for (i = 0; i < G_N_ELEMENTS (rgba_types) && !found; i++) {
412 if (strcmp (rgba_order, rgba_types[i]) == 0) {
413 settings->rgba = rgba_types[i];
414 found = TRUE;
415 }
416 }
417
418 if (!found) {
419 g_warning ("Invalid value for " FONT_RGBA_ORDER_KEY ": '%s'",
420 rgba_order);
421 }
422 }
423
424 if (hinting) {
425 if (strcmp (hinting, "none") == 0) {
426 settings->hinting = 0;
427 settings->hintstyle = "hintnone";
428 } else if (strcmp (hinting, "slight") == 0) {
429 settings->hinting = 1;
430 settings->hintstyle = "hintslight";
431 } else if (strcmp (hinting, "medium") == 0) {
432 settings->hinting = 1;
433 settings->hintstyle = "hintmedium";
434 } else if (strcmp (hinting, "full") == 0) {
435 settings->hinting = 1;
436 settings->hintstyle = "hintfull";
437 } else {
438 g_warning ("Invalid value for " FONT_HINTING_KEY ": '%s'",
439 hinting);
440 }
441 }
442
443 if (antialiasing) {
444 gboolean use_rgba = FALSE;
445
446 if (strcmp (antialiasing, "none") == 0) {
447 settings->antialias = 0;
448 } else if (strcmp (antialiasing, "grayscale") == 0) {
449 settings->antialias = 1;
450 } else if (strcmp (antialiasing, "rgba") == 0) {
451 settings->antialias = 1;
452 use_rgba = TRUE;
453 } else {
454 g_warning ("Invalid value for " FONT_ANTIALIASING_KEY " : '%s'",
455 antialiasing);
456 }
457
458 if (!use_rgba) {
459 settings->rgba = "none";
460 }
461 }
462
463 g_free (rgba_order);
464 g_free (hinting);
465 g_free (antialiasing);
466 }
467
468 /* Set environment variable.
469 * This should only work during the initialization phase. */
470 static gboolean
update_user_env_variable(const gchar * variable,const gchar * value,GError ** error)471 update_user_env_variable (const gchar *variable,
472 const gchar *value,
473 GError **error)
474 {
475 GDBusConnection *connection;
476 gboolean environment_updated;
477 GVariant *reply;
478 GError *bus_error = NULL;
479
480 g_setenv (variable, value, TRUE);
481
482 environment_updated = FALSE;
483 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
484
485 if (connection == NULL) {
486 return FALSE;
487 }
488
489 reply = g_dbus_connection_call_sync (connection,
490 "org.gnome.SessionManager",
491 "/org/gnome/SessionManager",
492 "org.gnome.SessionManager",
493 "Setenv",
494 g_variant_new ("(ss)", variable, value),
495 NULL,
496 G_DBUS_CALL_FLAGS_NONE,
497 -1, NULL, &bus_error);
498
499 if (bus_error != NULL) {
500 g_propagate_error (error, bus_error);
501 } else {
502 environment_updated = TRUE;
503 g_variant_unref (reply);
504 }
505
506 g_clear_object (&connection);
507
508 return environment_updated;
509 }
510
511 static gboolean
delayed_toggle_bg_draw(gpointer value)512 delayed_toggle_bg_draw (gpointer value)
513 {
514 GSettings *settings;
515
516 settings = g_settings_new ("org.mate.background");
517 g_settings_set_boolean (settings, "show-desktop-icons", GPOINTER_TO_BOOLEAN (value));
518 g_object_unref (settings);
519
520 return G_SOURCE_REMOVE;
521 }
522
523 static void
scale_change_workarounds(MateXSettingsManager * manager,int new_scale,int unscaled_dpi)524 scale_change_workarounds (MateXSettingsManager *manager, int new_scale, int unscaled_dpi)
525 {
526 if (manager->priv->window_scale == new_scale)
527 return;
528
529 GError *error = NULL;
530
531 /* This is only useful during the Initialization phase, so we guard against
532 * unnecessarily attempting to set it later. */
533 if (!manager->priv->window_scale) {
534 GSettings *gsettings;
535 gsettings = g_hash_table_lookup (manager->priv->gsettings, INTERFACE_SCHEMA);
536 /* If enabled, set env variables to properly scale QT applications */
537 if (g_settings_get_boolean (gsettings, SCALING_FACTOR_QT_KEY)) {
538 char dpibuf[G_ASCII_DTOSTR_BUF_SIZE];
539 g_snprintf (dpibuf, sizeof (dpibuf), "%d", (int) (unscaled_dpi / 1024.0 + 0.5));
540
541 if (!update_user_env_variable ("QT_FONT_DPI", dpibuf, &error)) {
542 g_warning ("There was a problem when setting QT_FONT_DPI=%s: %s", dpibuf, error->message);
543 g_clear_error (&error);
544 }
545 if (!update_user_env_variable ("QT_SCALE_FACTOR", new_scale == 2 ? "2" : "1", &error)) {
546 g_warning ("There was a problem when setting QT_SCALE_FACTOR=%d: %s", new_scale, error->message);
547 g_clear_error (&error);
548 }
549 }
550 } else {
551 /* Restart marco */
552 /* FIXME: The ideal scenario would be for marco to respect window scaling and thus
553 * resize itself. Currently this is not happening, so msd restarts it when the window
554 * scaling factor changes so that it's visually correct. */
555 wm_common_update_window();
556 gchar *wm = wm_common_get_current_window_manager ();
557 if (g_strcmp0 (wm, WM_COMMON_MARCO) == 0) {
558 gchar *marco[3] = {"marco", "--replace", NULL};
559 if (!g_spawn_async (NULL, marco, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) {
560 g_warning ("There was a problem restarting marco: %s", error->message);
561 g_clear_error (&error);
562 }
563 }
564 g_free (wm);
565
566 /* Restart mate-panel */
567 /* FIXME: The ideal scenario would be for mate-panel to respect window scaling and thus
568 * resize itself. Currently this is not happening, so msd restarts it when the window
569 * scaling factor changes so that it's visually correct. */
570 gchar *mate_panel[3] = {"killall", "mate-panel", NULL};
571 if (!g_spawn_async (NULL, mate_panel, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) {
572 g_warning ("There was a problem restarting mate-panel: %s", error->message);
573 g_clear_error (&error);
574 }
575
576 /* Toggle icons on desktop to fix size */
577 /* FIXME: The ideal scenario would be for caja to respect window scaling and thus
578 * resize itself. Currently this is not happening, so msd restarts it when the window
579 * scaling factor changes so that it's visually correct. */
580 GSettings *desktop_settings;
581 desktop_settings = g_settings_new ("org.mate.background");
582 if (g_settings_get_boolean (desktop_settings, "show-desktop-icons")) {
583 /* Delay the toggle to allow enough time for the desktop to redraw */
584 g_timeout_add_seconds (1, delayed_toggle_bg_draw, GBOOLEAN_TO_POINTER (FALSE));
585 g_timeout_add_seconds (2, delayed_toggle_bg_draw, GBOOLEAN_TO_POINTER (TRUE));
586 }
587 g_object_unref (desktop_settings);
588 }
589
590 /* Store new scale value */
591 manager->priv->window_scale = new_scale;
592 }
593
594 static void
xft_settings_set_xsettings(MateXSettingsManager * manager,MateXftSettings * settings)595 xft_settings_set_xsettings (MateXSettingsManager *manager,
596 MateXftSettings *settings)
597 {
598 int i;
599
600 mate_settings_profile_start (NULL);
601
602 for (i = 0; manager->priv->managers [i]; i++) {
603 xsettings_manager_set_int (manager->priv->managers [i], "Xft/Antialias", settings->antialias);
604 xsettings_manager_set_int (manager->priv->managers [i], "Xft/Hinting", settings->hinting);
605 xsettings_manager_set_string (manager->priv->managers [i], "Xft/HintStyle", settings->hintstyle);
606 xsettings_manager_set_int (manager->priv->managers [i], "Gdk/WindowScalingFactor", settings->window_scale);
607 xsettings_manager_set_int (manager->priv->managers [i], "Gdk/UnscaledDPI", settings->dpi);
608 xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->scaled_dpi);
609 xsettings_manager_set_string (manager->priv->managers [i], "Xft/RGBA", settings->rgba);
610 xsettings_manager_set_string (manager->priv->managers [i], "Xft/lcdfilter",
611 g_str_equal (settings->rgba, "rgb") ? "lcddefault" : "none");
612 xsettings_manager_set_int (manager->priv->managers [i], "Gtk/CursorThemeSize", settings->cursor_size);
613 xsettings_manager_set_string (manager->priv->managers [i], "Gtk/CursorThemeName", settings->cursor_theme);
614 }
615 mate_settings_profile_end (NULL);
616
617 scale_change_workarounds (manager, settings->window_scale, settings->dpi);
618 }
619
620 static void
update_property(GString * props,const gchar * key,const gchar * value)621 update_property (GString *props, const gchar* key, const gchar* value)
622 {
623 gchar* needle;
624 size_t needle_len;
625 gchar* found = NULL;
626
627 /* update an existing property */
628 needle = g_strconcat (key, ":", NULL);
629 needle_len = strlen (needle);
630 if (g_str_has_prefix (props->str, needle))
631 found = props->str;
632 else
633 found = strstr (props->str, needle);
634
635 if (found) {
636 size_t value_index;
637 gchar* end;
638
639 end = strchr (found, '\n');
640 value_index = (found - props->str) + needle_len + 1;
641 g_string_erase (props, value_index, end ? (end - found - needle_len) : -1);
642 g_string_insert (props, value_index, "\n");
643 g_string_insert (props, value_index, value);
644 } else {
645 g_string_append_printf (props, "%s:\t%s\n", key, value);
646 }
647
648 g_free (needle);
649 }
650
651 static void
xft_settings_set_xresources(MateXftSettings * settings)652 xft_settings_set_xresources (MateXftSettings *settings)
653 {
654 GString *add_string;
655 char dpibuf[G_ASCII_DTOSTR_BUF_SIZE];
656 Display *dpy;
657
658 mate_settings_profile_start (NULL);
659
660 /* get existing properties */
661 dpy = XOpenDisplay (NULL);
662 g_return_if_fail (dpy != NULL);
663 add_string = g_string_new (XResourceManagerString (dpy));
664
665 g_debug("xft_settings_set_xresources: orig res '%s'", add_string->str);
666
667 g_snprintf (dpibuf, sizeof (dpibuf), "%d", (int) (settings->scaled_dpi / 1024.0 + 0.5));
668 update_property (add_string, "Xft.dpi", dpibuf);
669 update_property (add_string, "Xft.antialias",
670 settings->antialias ? "1" : "0");
671 update_property (add_string, "Xft.hinting",
672 settings->hinting ? "1" : "0");
673 update_property (add_string, "Xft.hintstyle",
674 settings->hintstyle);
675 update_property (add_string, "Xft.rgba",
676 settings->rgba);
677 update_property (add_string, "Xft.lcdfilter",
678 g_str_equal (settings->rgba, "rgb") ? "lcddefault" : "none");
679 update_property (add_string, "Xcursor.theme",
680 settings->cursor_theme);
681 update_property (add_string, "Xcursor.size",
682 g_ascii_dtostr (dpibuf, sizeof (dpibuf), (double) settings->cursor_size));
683
684 g_debug("xft_settings_set_xresources: new res '%s'", add_string->str);
685
686 /* Set the new X property */
687 XChangeProperty(dpy, RootWindow (dpy, 0),
688 XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, (unsigned char *) add_string->str, add_string->len);
689 XCloseDisplay (dpy);
690
691 g_string_free (add_string, TRUE);
692
693 mate_settings_profile_end (NULL);
694 }
695
696 /* We mirror the Xft properties both through XSETTINGS and through
697 * X resources
698 */
699 static void
update_xft_settings(MateXSettingsManager * manager)700 update_xft_settings (MateXSettingsManager *manager)
701 {
702 MateXftSettings settings;
703
704 mate_settings_profile_start (NULL);
705
706 xft_settings_get (manager, &settings);
707 xft_settings_set_xsettings (manager, &settings);
708 xft_settings_set_xresources (&settings);
709
710 mate_settings_profile_end (NULL);
711 }
712
713 static void
recalculate_scale_callback(GdkScreen * screen G_GNUC_UNUSED,MateXSettingsManager * manager)714 recalculate_scale_callback (GdkScreen *screen G_GNUC_UNUSED,
715 MateXSettingsManager *manager)
716 {
717 int i;
718 int new_scale = get_window_scale (manager);
719
720 if (manager->priv->window_scale == new_scale)
721 return;
722
723 update_xft_settings (manager);
724
725 for (i = 0; manager->priv->managers [i]; i++) {
726 xsettings_manager_notify (manager->priv->managers [i]);
727 }
728 }
729
730 static void
xft_callback(GSettings * gsettings G_GNUC_UNUSED,const gchar * key G_GNUC_UNUSED,MateXSettingsManager * manager)731 xft_callback (GSettings *gsettings G_GNUC_UNUSED,
732 const gchar *key G_GNUC_UNUSED,
733 MateXSettingsManager *manager)
734 {
735 int i;
736
737 update_xft_settings (manager);
738
739 for (i = 0; manager->priv->managers [i]; i++) {
740 xsettings_manager_notify (manager->priv->managers [i]);
741 }
742 }
743
744 static void
fontconfig_callback(fontconfig_monitor_handle_t * handle,MateXSettingsManager * manager)745 fontconfig_callback (fontconfig_monitor_handle_t *handle,
746 MateXSettingsManager *manager)
747 {
748 int i;
749 int timestamp = time (NULL);
750
751 mate_settings_profile_start (NULL);
752
753 for (i = 0; manager->priv->managers [i]; i++) {
754 xsettings_manager_set_int (manager->priv->managers [i], "Fontconfig/Timestamp", timestamp);
755 xsettings_manager_notify (manager->priv->managers [i]);
756 }
757 mate_settings_profile_end (NULL);
758 }
759
760 static gboolean
start_fontconfig_monitor_idle_cb(MateXSettingsManager * manager)761 start_fontconfig_monitor_idle_cb (MateXSettingsManager *manager)
762 {
763 mate_settings_profile_start (NULL);
764
765 manager->priv->fontconfig_handle = fontconfig_monitor_start ((GFunc) fontconfig_callback, manager);
766
767 mate_settings_profile_end (NULL);
768
769 return FALSE;
770 }
771
772 static void
start_fontconfig_monitor(MateXSettingsManager * manager)773 start_fontconfig_monitor (MateXSettingsManager *manager)
774 {
775 mate_settings_profile_start (NULL);
776
777 fontconfig_cache_init ();
778
779 g_idle_add ((GSourceFunc) start_fontconfig_monitor_idle_cb, manager);
780
781 mate_settings_profile_end (NULL);
782 }
783
784 static void
stop_fontconfig_monitor(MateXSettingsManager * manager)785 stop_fontconfig_monitor (MateXSettingsManager *manager)
786 {
787 if (manager->priv->fontconfig_handle) {
788 fontconfig_monitor_stop (manager->priv->fontconfig_handle);
789 manager->priv->fontconfig_handle = NULL;
790 }
791 }
792
793 static void
process_value(MateXSettingsManager * manager,TranslationEntry * trans,GVariant * value)794 process_value (MateXSettingsManager *manager,
795 TranslationEntry *trans,
796 GVariant *value)
797 {
798 (* trans->translate) (manager, trans, value);
799 }
800
801 static TranslationEntry *
find_translation_entry(GSettings * gsettings,const char * key)802 find_translation_entry (GSettings *gsettings, const char *key)
803 {
804 guint i;
805 char *schema;
806
807 g_object_get (gsettings, "schema", &schema, NULL);
808
809 for (i = 0; i < G_N_ELEMENTS (translations); i++) {
810 if (g_str_equal (schema, translations[i].gsettings_schema) &&
811 g_str_equal (key, translations[i].gsettings_key)) {
812 g_free (schema);
813 return &translations[i];
814 }
815 }
816
817 g_free (schema);
818
819 return NULL;
820 }
821
822 static void
xsettings_callback(GSettings * gsettings,const char * key,MateXSettingsManager * manager)823 xsettings_callback (GSettings *gsettings,
824 const char *key,
825 MateXSettingsManager *manager)
826 {
827 TranslationEntry *trans;
828 int i;
829 GVariant *value;
830
831 if (g_str_equal (key, CURSOR_THEME_KEY) ||
832 g_str_equal (key, SCALING_FACTOR_KEY) ||
833 g_str_equal (key, CURSOR_SIZE_KEY)) {
834 xft_callback (NULL, key, manager);
835 return;
836 }
837
838 trans = find_translation_entry (gsettings, key);
839 if (trans == NULL) {
840 return;
841 }
842
843 value = g_settings_get_value (gsettings, key);
844
845 process_value (manager, trans, value);
846
847 g_variant_unref (value);
848
849 for (i = 0; manager->priv->managers [i]; i++) {
850 xsettings_manager_set_string (manager->priv->managers [i],
851 "Net/FallbackIconTheme",
852 "mate");
853 }
854
855 for (i = 0; manager->priv->managers [i]; i++) {
856 xsettings_manager_notify (manager->priv->managers [i]);
857 }
858 }
859
860 static void
terminate_cb(void * data)861 terminate_cb (void *data)
862 {
863 gboolean *terminated = data;
864
865 if (*terminated) {
866 return;
867 }
868
869 *terminated = TRUE;
870
871 gtk_main_quit ();
872 }
873
874 static gboolean
setup_xsettings_managers(MateXSettingsManager * manager)875 setup_xsettings_managers (MateXSettingsManager *manager)
876 {
877 GdkDisplay *display;
878 gboolean res;
879 gboolean terminated;
880
881 display = gdk_display_get_default ();
882
883 res = xsettings_manager_check_running (gdk_x11_display_get_xdisplay (display),
884 gdk_x11_screen_get_screen_number (gdk_screen_get_default ()));
885 if (res) {
886 g_warning ("You can only run one xsettings manager at a time; exiting");
887 return FALSE;
888 }
889
890 manager->priv->managers = g_new0 (XSettingsManager *, 2);
891
892 terminated = FALSE;
893
894 GdkScreen *screen;
895
896 screen = gdk_display_get_default_screen (display);
897
898 manager->priv->managers [0] = xsettings_manager_new (gdk_x11_display_get_xdisplay (display),
899 gdk_x11_screen_get_screen_number (screen),
900 terminate_cb,
901 &terminated);
902 if (! manager->priv->managers [0]) {
903 g_warning ("Could not create xsettings manager for screen!");
904 return FALSE;
905 }
906
907 return TRUE;
908 }
909
910 gboolean
mate_xsettings_manager_start(MateXSettingsManager * manager,GError ** error)911 mate_xsettings_manager_start (MateXSettingsManager *manager,
912 GError **error)
913 {
914 guint i;
915 GList *list, *l;
916 GdkScreen *screen;
917
918 g_debug ("Starting xsettings manager");
919 mate_settings_profile_start (NULL);
920
921 if (!setup_xsettings_managers (manager)) {
922 g_set_error (error, MSD_XSETTINGS_ERROR,
923 MSD_XSETTINGS_ERROR_INIT,
924 "Could not initialize xsettings manager.");
925 return FALSE;
926 }
927
928 manager->priv->gsettings = g_hash_table_new_full (g_str_hash, g_str_equal,
929 NULL, (GDestroyNotify) g_object_unref);
930
931 g_hash_table_insert (manager->priv->gsettings,
932 MOUSE_SCHEMA, g_settings_new (MOUSE_SCHEMA));
933 g_hash_table_insert (manager->priv->gsettings,
934 INTERFACE_SCHEMA, g_settings_new (INTERFACE_SCHEMA));
935 g_hash_table_insert (manager->priv->gsettings,
936 SOUND_SCHEMA, g_settings_new (SOUND_SCHEMA));
937
938 list = g_hash_table_get_values (manager->priv->gsettings);
939 for (l = list; l != NULL; l = l->next) {
940 g_signal_connect_object (G_OBJECT (l->data), "changed",
941 G_CALLBACK (xsettings_callback), manager, 0);
942 }
943
944 g_list_free (list);
945
946 for (i = 0; i < G_N_ELEMENTS (translations); i++) {
947 GVariant *val;
948 GSettings *gsettings;
949
950 gsettings = g_hash_table_lookup (manager->priv->gsettings,
951 translations[i].gsettings_schema);
952
953 if (gsettings == NULL) {
954 g_warning ("Schemas '%s' has not been setup", translations[i].gsettings_schema);
955 continue;
956 }
957
958 val = g_settings_get_value (gsettings, translations[i].gsettings_key);
959
960 process_value (manager, &translations[i], val);
961 g_variant_unref (val);
962 }
963
964 /* Detect changes in screen resolution */
965 screen = gdk_screen_get_default();
966 g_signal_connect(screen, "size-changed", G_CALLBACK (recalculate_scale_callback), manager);
967 g_signal_connect(screen, "monitors-changed", G_CALLBACK (recalculate_scale_callback), manager);
968
969 manager->priv->gsettings_font = g_settings_new (FONT_RENDER_SCHEMA);
970 g_signal_connect (manager->priv->gsettings_font, "changed", G_CALLBACK (xft_callback), manager);
971 update_xft_settings (manager);
972
973 start_fontconfig_monitor (manager);
974
975 for (i = 0; manager->priv->managers [i]; i++)
976 xsettings_manager_set_string (manager->priv->managers [i],
977 "Net/FallbackIconTheme",
978 "mate");
979
980 for (i = 0; manager->priv->managers [i]; i++) {
981 xsettings_manager_notify (manager->priv->managers [i]);
982 }
983
984 mate_settings_profile_end (NULL);
985
986 return TRUE;
987 }
988
989 void
mate_xsettings_manager_stop(MateXSettingsManager * manager)990 mate_xsettings_manager_stop (MateXSettingsManager *manager)
991 {
992 MateXSettingsManagerPrivate *p = manager->priv;
993 int i;
994
995 g_debug ("Stopping xsettings manager");
996
997 if (p->managers != NULL) {
998 for (i = 0; p->managers [i]; ++i)
999 xsettings_manager_destroy (p->managers [i]);
1000
1001 g_free (p->managers);
1002 p->managers = NULL;
1003 }
1004
1005 if (p->gsettings != NULL) {
1006 g_hash_table_destroy (p->gsettings);
1007 p->gsettings = NULL;
1008 }
1009
1010 if (p->gsettings_font != NULL) {
1011 g_object_unref (p->gsettings_font);
1012 p->gsettings_font = NULL;
1013 }
1014
1015 stop_fontconfig_monitor (manager);
1016
1017 }
1018
1019 static void
mate_xsettings_manager_class_init(MateXSettingsManagerClass * klass)1020 mate_xsettings_manager_class_init (MateXSettingsManagerClass *klass)
1021 {
1022 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1023
1024 object_class->finalize = mate_xsettings_manager_finalize;
1025 }
1026
1027 static void
mate_xsettings_manager_init(MateXSettingsManager * manager)1028 mate_xsettings_manager_init (MateXSettingsManager *manager)
1029 {
1030 manager->priv = mate_xsettings_manager_get_instance_private (manager);
1031 }
1032
1033 static void
mate_xsettings_manager_finalize(GObject * object)1034 mate_xsettings_manager_finalize (GObject *object)
1035 {
1036 MateXSettingsManager *xsettings_manager;
1037
1038 g_return_if_fail (object != NULL);
1039 g_return_if_fail (MATE_IS_XSETTINGS_MANAGER (object));
1040
1041 xsettings_manager = MATE_XSETTINGS_MANAGER (object);
1042
1043 g_return_if_fail (xsettings_manager->priv != NULL);
1044
1045 G_OBJECT_CLASS (mate_xsettings_manager_parent_class)->finalize (object);
1046 }
1047
1048 MateXSettingsManager *
mate_xsettings_manager_new(void)1049 mate_xsettings_manager_new (void)
1050 {
1051 if (manager_object != NULL) {
1052 g_object_ref (manager_object);
1053 } else {
1054 manager_object = g_object_new (MATE_TYPE_XSETTINGS_MANAGER, NULL);
1055 g_object_add_weak_pointer (manager_object,
1056 (gpointer *) &manager_object);
1057 }
1058
1059 return MATE_XSETTINGS_MANAGER (manager_object);
1060 }
1061