From 16137a4d4c51fae1d577ea4c534af4a89f5c1558 Mon Sep 17 00:00:00 2001 From: Victor Kareh Date: Thu, 1 Feb 2018 00:14:38 -0500 Subject: Add HiDPI configuration in XSettings This change allows for Xft/Gdk settings to be changed dynamically for the scaling factor and font DPI. It also has an optional auto-detection algorithm for the most appropriate settings to use for different screen sizes and resolutions. The auto-detect is re-triggered whenever the screen size changes. --- plugins/xsettings/msd-xsettings-manager.c | 135 +++++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 19 deletions(-) (limited to 'plugins') diff --git a/plugins/xsettings/msd-xsettings-manager.c b/plugins/xsettings/msd-xsettings-manager.c index 9105088..d12b23e 100644 --- a/plugins/xsettings/msd-xsettings-manager.c +++ b/plugins/xsettings/msd-xsettings-manager.c @@ -52,6 +52,7 @@ #define CURSOR_THEME_KEY "cursor-theme" #define CURSOR_SIZE_KEY "cursor-size" +#define SCALING_FACTOR_KEY "window-scaling-factor" #define FONT_RENDER_SCHEMA "org.mate.font-rendering" #define FONT_ANTIALIASING_KEY "antialiasing" @@ -73,6 +74,14 @@ #define DPI_LOW_REASONABLE_VALUE 50 #define DPI_HIGH_REASONABLE_VALUE 500 +/* The minimum resolution at which we turn on a window-scale of 2 */ +#define HIDPI_LIMIT (DPI_FALLBACK * 2) + +/* The minimum screen height at which we turn on a window-scale of 2; + * below this there just isn't enough vertical real estate for GNOME + * apps to work, and it's better to just be tiny */ +#define HIDPI_MIN_HEIGHT 1500 + typedef struct _TranslationEntry TranslationEntry; typedef void (* TranslationFunc) (MateXSettingsManager *manager, TranslationEntry *trans, @@ -92,6 +101,7 @@ struct MateXSettingsManagerPrivate GHashTable *gsettings; GSettings *gsettings_font; fontconfig_monitor_handle_t *fontconfig_handle; + gint window_scale; }; #define MSD_XSETTINGS_ERROR msd_xsettings_error_quark () @@ -213,6 +223,70 @@ static TranslationEntry translations [] = { { SOUND_SCHEMA, "input-feedback-sounds", "Net/EnableInputFeedbackSounds", translate_bool_int } }; +/* Auto-detect the most appropriate scale factor for the primary monitor. + * A lot of this code is shamelessly copied and adapted from Linux Mint/Cinnamon. + */ +static int +get_window_scale_auto () +{ + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle rect; + int width_mm, height_mm; + int monitor_scale, window_scale; + + display = gdk_display_get_default (); + monitor = gdk_display_get_primary_monitor (display); + + /* Use current value as the default */ + window_scale = 1; + + gdk_monitor_get_geometry (monitor, &rect); + width_mm = gdk_monitor_get_width_mm (monitor); + height_mm = gdk_monitor_get_height_mm (monitor); + monitor_scale = gdk_monitor_get_scale_factor (monitor); + + if (rect.height * monitor_scale < HIDPI_MIN_HEIGHT) + return 1; + + /* Some monitors/TV encode the aspect ratio (16/9 or 16/10) instead of the physical size */ + if ((width_mm == 160 && height_mm == 90) || + (width_mm == 160 && height_mm == 100) || + (width_mm == 16 && height_mm == 9) || + (width_mm == 16 && height_mm == 10)) + return 1; + + if (width_mm > 0 && height_mm > 0) { + double dpi_x, dpi_y; + + dpi_x = (double)rect.width * monitor_scale / (width_mm / 25.4); + dpi_y = (double)rect.height * monitor_scale / (height_mm / 25.4); + /* We don't completely trust these values so both must be high, and never pick + * higher ratio than 2 automatically */ + if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT) + window_scale = 2; + } + + return window_scale; +} + +static int +get_window_scale (MateXSettingsManager *manager) +{ + GSettings *gsettings; + gint scale; + + /* Get scale factor from gsettings */ + gsettings = g_hash_table_lookup (manager->priv->gsettings, INTERFACE_SCHEMA); + scale = g_settings_get_int (gsettings, SCALING_FACTOR_KEY); + + /* Auto-detect */ + if (scale == 0) + scale = get_window_scale_auto (); + + return scale; +} + static double dpi_from_pixels_and_mm (int pixels, int mm) @@ -232,7 +306,6 @@ get_dpi_from_x_server (void) { GdkScreen *screen; double dpi; - gint scale; screen = gdk_screen_get_default (); if (screen != NULL) { @@ -255,21 +328,15 @@ get_dpi_from_x_server (void) dpi = DPI_FALLBACK; } - - scale = gdk_window_get_scale_factor (gdk_screen_get_root_window (screen)); - if (scale) - dpi = dpi * scale; - return dpi; } static double -get_dpi_from_gsettings_or_x_server (GSettings *gsettings) +get_dpi_from_gsettings_or_x_server (GSettings *gsettings, gint scale) { - double value; double dpi; - value = g_settings_get_double (gsettings, FONT_DPI_KEY); + dpi = g_settings_get_double (gsettings, FONT_DPI_KEY); /* If the user has ever set the DPI preference in GSettings, we use that. * Otherwise, we see if the X server reports a reasonable DPI value: some X @@ -277,11 +344,11 @@ get_dpi_from_gsettings_or_x_server (GSettings *gsettings) * fonts which are unusable. */ - if (value != 0) { - dpi = value; - } else { + if (dpi == 0) dpi = get_dpi_from_x_server (); - } + + dpi *= (double)scale; + dpi = CLAMP(dpi, DPI_LOW_REASONABLE_VALUE, DPI_HIGH_REASONABLE_VALUE); return dpi; } @@ -290,7 +357,9 @@ typedef struct { gboolean antialias; gboolean hinting; + int window_scale; int dpi; + int scaled_dpi; char *cursor_theme; int cursor_size; const char *rgba; @@ -307,7 +376,6 @@ xft_settings_get (MateXSettingsManager *manager, MateXftSettings *settings) { GSettings *mouse_gsettings; - GdkScreen *screen; char *antialiasing; char *hinting; char *rgba_order; @@ -315,22 +383,25 @@ xft_settings_get (MateXSettingsManager *manager, gint scale; mouse_gsettings = g_hash_table_lookup (manager->priv->gsettings, MOUSE_SCHEMA); - screen = gdk_screen_get_default(); antialiasing = g_settings_get_string (manager->priv->gsettings_font, FONT_ANTIALIASING_KEY); hinting = g_settings_get_string (manager->priv->gsettings_font, FONT_HINTING_KEY); rgba_order = g_settings_get_string (manager->priv->gsettings_font, FONT_RGBA_ORDER_KEY); - dpi = get_dpi_from_gsettings_or_x_server (manager->priv->gsettings_font); - scale = gdk_window_get_scale_factor (gdk_screen_get_root_window (screen)); + scale = get_window_scale (manager); + dpi = get_dpi_from_gsettings_or_x_server (manager->priv->gsettings_font, scale); settings->antialias = TRUE; settings->hinting = TRUE; settings->hintstyle = "hintslight"; - settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */ + settings->window_scale = scale; + settings->dpi = dpi / scale * 1024; /* Xft wants 1/1024ths of an inch */ + settings->scaled_dpi = dpi * 1024; settings->cursor_theme = g_settings_get_string (mouse_gsettings, CURSOR_THEME_KEY); settings->cursor_size = scale * g_settings_get_int (mouse_gsettings, CURSOR_SIZE_KEY); settings->rgba = "rgb"; + manager->priv->window_scale = scale; + if (rgba_order) { int i; gboolean found = FALSE; @@ -404,7 +475,9 @@ xft_settings_set_xsettings (MateXSettingsManager *manager, xsettings_manager_set_int (manager->priv->managers [i], "Xft/Antialias", settings->antialias); xsettings_manager_set_int (manager->priv->managers [i], "Xft/Hinting", settings->hinting); xsettings_manager_set_string (manager->priv->managers [i], "Xft/HintStyle", settings->hintstyle); - xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->dpi); + xsettings_manager_set_int (manager->priv->managers [i], "Gdk/WindowScalingFactor", settings->window_scale); + xsettings_manager_set_int (manager->priv->managers [i], "Gdk/UnscaledDPI", settings->dpi); + xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->scaled_dpi); xsettings_manager_set_string (manager->priv->managers [i], "Xft/RGBA", settings->rgba); xsettings_manager_set_string (manager->priv->managers [i], "Xft/lcdfilter", g_str_equal (settings->rgba, "rgb") ? "lcddefault" : "none"); @@ -507,6 +580,23 @@ update_xft_settings (MateXSettingsManager *manager) mate_settings_profile_end (NULL); } +static void +screen_callback (GdkScreen *screen, + MateXSettingsManager *manager) +{ + int i; + int new_scale = get_window_scale (manager); + + if (manager->priv->window_scale == new_scale) + return; + + update_xft_settings (manager); + + for (i = 0; manager->priv->managers [i]; i++) { + xsettings_manager_notify (manager->priv->managers [i]); + } +} + static void xft_callback (GSettings *gsettings, const gchar *key, @@ -609,6 +699,7 @@ xsettings_callback (GSettings *gsettings, GVariant *value; if (g_str_equal (key, CURSOR_THEME_KEY) || + g_str_equal (key, SCALING_FACTOR_KEY) || g_str_equal (key, CURSOR_SIZE_KEY)) { xft_callback (NULL, key, manager); return; @@ -692,6 +783,7 @@ mate_xsettings_manager_start (MateXSettingsManager *manager, { guint i; GList *list, *l; + GdkScreen *screen; g_debug ("Starting xsettings manager"); mate_settings_profile_start (NULL); @@ -718,6 +810,7 @@ mate_xsettings_manager_start (MateXSettingsManager *manager, g_signal_connect_object (G_OBJECT (l->data), "changed", G_CALLBACK (xsettings_callback), manager, 0); } + g_list_free (list); for (i = 0; i < G_N_ELEMENTS (translations); i++) { @@ -738,6 +831,10 @@ mate_xsettings_manager_start (MateXSettingsManager *manager, g_variant_unref (val); } + /* Detect changes in screen resolution */ + screen = gdk_screen_get_default(); + g_signal_connect(screen, "size-changed", G_CALLBACK (screen_callback), manager); + manager->priv->gsettings_font = g_settings_new (FONT_RENDER_SCHEMA); g_signal_connect (manager->priv->gsettings_font, "changed", G_CALLBACK (xft_callback), manager); update_xft_settings (manager); -- cgit v1.2.1