From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= Date: Thu, 4 Apr 2019 01:18:03 +0200 Subject: x11-Add-support-for-fractional-scaling-using-Randr Add scaling support using randr under x11. Origin: https://gitlab.gnome.org/3v1n0/mutter/commits/xrandr-scaling Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/1820850 Forwarded: No, forwarding is in progress and planned though --- data/meson.build | 7 + data/org.gnome.mutter.gschema.xml.in | 5 +- data/org.gnome.mutter.x11.gschema.xml.in | 30 ++ src/backends/meta-crtc.c | 21 + src/backends/meta-crtc.h | 6 + src/backends/meta-monitor-config-manager.c | 180 ++++++- src/backends/meta-monitor-config-manager.h | 5 + src/backends/meta-monitor-config-migration.c | 15 +- src/backends/meta-monitor-config-store.c | 17 + src/backends/meta-monitor-manager-dummy.c | 24 +- src/backends/meta-monitor-manager-private.h | 34 +- src/backends/meta-monitor-manager.c | 511 +++++++++++++++++-- src/backends/meta-monitor.c | 60 ++- src/backends/meta-monitor.h | 6 +- src/backends/meta-settings-private.h | 13 + src/backends/meta-settings.c | 148 +++++- src/backends/native/meta-monitor-manager-native.c | 41 +- src/backends/x11/meta-crtc-xrandr.c | 100 +++- src/backends/x11/meta-crtc-xrandr.h | 14 +- src/backends/x11/meta-gpu-xrandr.c | 112 ++++- src/backends/x11/meta-gpu-xrandr.h | 4 + src/backends/x11/meta-monitor-manager-xrandr.c | 588 +++++++++++++++++----- src/backends/x11/meta-monitor-manager-xrandr.h | 4 +- src/backends/x11/meta-output-xrandr.c | 3 +- src/compositor/meta-compositor-x11.c | 99 +++- src/core/boxes-private.h | 4 + src/core/boxes.c | 21 + src/core/window.c | 19 + src/org.gnome.Mutter.DisplayConfig.xml | 5 + src/tests/meta-monitor-manager-test.c | 13 +- 30 files changed, 1839 insertions(+), 270 deletions(-) create mode 100644 data/org.gnome.mutter.x11.gschema.xml.in diff --git a/data/meson.build b/data/meson.build index b1e81d1..977b340 100644 --- a/data/meson.build +++ b/data/meson.build @@ -55,6 +55,13 @@ configure_file( install_dir: schemadir ) +configure_file( + input: 'org.gnome.mutter.x11.gschema.xml.in', + output: 'org.gnome.mutter.x11.gschema.xml', + configuration: gschema_config, + install_dir: schemadir +) + install_data(['mutter-schemas.convert'], install_dir: join_paths(datadir, 'GConf/gsettings'), ) diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in index 23fa9f3..e64691d 100644 --- a/data/org.gnome.mutter.gschema.xml.in +++ b/data/org.gnome.mutter.gschema.xml.in @@ -134,7 +134,10 @@ • “autoclose-xwayland” — automatically terminates Xwayland if all relevant X11 clients are gone. Does not require a restart. - + • “x11-randr-fractional-scaling” — enable fractional scaling under X11 + using xrandr scaling. It might reduce + performances. + Does not require a restart. diff --git a/data/org.gnome.mutter.x11.gschema.xml.in b/data/org.gnome.mutter.x11.gschema.xml.in new file mode 100644 index 0000000..3696659 --- /dev/null +++ b/data/org.gnome.mutter.x11.gschema.xml.in @@ -0,0 +1,30 @@ + + + + + + + + + + + "scale-ui-down" + + Choose the scaling mode to be used under X11 via Randr extension. + + Supported methods are: + + • “scale-up” — Scale everything up to the requested scale, shrinking + the UI. The applications will look blurry when scaling + at higher values and the resolution will be lowered. + • “scale-ui-down — Scale up the UI toolkits to the closest integer + scaling value upwards, while scale down the display + to match the requested scaling level. + It increases the resolution of the logical display. + + + + + + diff --git a/src/backends/meta-crtc.c b/src/backends/meta-crtc.c index 09f9199..df4033d 100644 --- a/src/backends/meta-crtc.c +++ b/src/backends/meta-crtc.c @@ -117,6 +117,7 @@ meta_crtc_set_config (MetaCrtc *crtc, config->layout = *layout; config->mode = mode; config->transform = transform; + config->scale = 1.0f; priv->config = config; } @@ -137,6 +138,26 @@ meta_crtc_get_config (MetaCrtc *crtc) return priv->config; } +void +meta_crtc_set_config_scale (MetaCrtc *crtc, + float scale) +{ + MetaCrtcPrivate *priv = meta_crtc_get_instance_private (crtc); + + g_return_if_fail (scale > 0); + + if (priv->config) + priv->config->scale = scale; +} + +float +meta_crtc_get_config_scale (MetaCrtc *crtc) +{ + MetaCrtcPrivate *priv = meta_crtc_get_instance_private (crtc); + + return priv->config ? priv->config->scale : 1.0f; +} + static void meta_crtc_set_property (GObject *object, guint prop_id, diff --git a/src/backends/meta-crtc.h b/src/backends/meta-crtc.h index f6a3bc1..acdc2b7 100644 --- a/src/backends/meta-crtc.h +++ b/src/backends/meta-crtc.h @@ -33,6 +33,7 @@ typedef struct _MetaCrtcConfig graphene_rect_t layout; MetaMonitorTransform transform; MetaCrtcMode *mode; + float scale; } MetaCrtcConfig; #define META_TYPE_CRTC (meta_crtc_get_type ()) @@ -68,6 +69,11 @@ void meta_crtc_set_config (MetaCrtc *crtc, MetaCrtcMode *mode, MetaMonitorTransform transform); +void meta_crtc_set_config_scale (MetaCrtc *crtc, + float scale); + +float meta_crtc_get_config_scale (MetaCrtc *crtc); + META_EXPORT_TEST void meta_crtc_unset_config (MetaCrtc *crtc); diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c index 0253e07..406e247 100644 --- a/src/backends/meta-monitor-config-manager.c +++ b/src/backends/meta-monitor-config-manager.c @@ -217,6 +217,18 @@ assign_monitor_crtc (MetaMonitor *monitor, else crtc_hw_transform = META_MONITOR_TRANSFORM_NORMAL; + scale = data->logical_monitor_config->scale; + if (!meta_monitor_manager_is_scale_supported (data->monitor_manager, + data->config->layout_mode, + monitor, mode, scale)) + { + scale = roundf (scale); + if (!meta_monitor_manager_is_scale_supported (data->monitor_manager, + data->config->layout_mode, + monitor, mode, scale)) + scale = 1.0f; + } + meta_monitor_calculate_crtc_pos (monitor, mode, output, crtc_transform, &crtc_x, &crtc_y); @@ -231,6 +243,8 @@ assign_monitor_crtc (MetaMonitor *monitor, case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: scale = 1.0; break; + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: + break; } crtc_mode = monitor_crtc_mode->crtc_mode; @@ -258,6 +272,7 @@ assign_monitor_crtc (MetaMonitor *monitor, .mode = crtc_mode, .layout = crtc_layout, .transform = crtc_hw_transform, + .scale = scale, .outputs = g_ptr_array_new () }; g_ptr_array_add (crtc_assignment->outputs, output); @@ -542,7 +557,11 @@ meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager typedef enum _MonitorMatchRule { MONITOR_MATCH_ALL = 0, - MONITOR_MATCH_EXTERNAL = (1 << 0) + MONITOR_MATCH_EXTERNAL = (1 << 0), + MONITOR_MATCH_BUILTIN = (1 << 1), + MONITOR_MATCH_PRIMARY = (1 << 2), + MONITOR_MATCH_VISIBLE = (1 << 3), + MONITOR_MATCH_WITH_POSITION = (1 << 4), } MonitorMatchRule; static MetaMonitor * @@ -696,12 +696,69 @@ get_monitor_transform (MetaMonitorManager *monitor_manager, } } +static float +get_preferred_preferred_max_scale (MetaMonitorManager *monitor_manager, + MetaLogicalMonitorLayoutMode layout_mode, + MonitorMatchRule match_rule) +{ + float scale = 1.0f; + GList *monitors, *l; + + monitors = meta_monitor_manager_get_monitors (monitor_manager); + + for (l = monitors; l; l = l->next) + { + float s; + MetaMonitor *monitor = l->data; + MetaMonitorMode *mode = meta_monitor_get_preferred_mode (monitor); + + if (match_rule & MONITOR_MATCH_PRIMARY) + { + if (!meta_monitor_is_primary (monitor)) + continue; + } + + if (match_rule & MONITOR_MATCH_BUILTIN) + { + if (!meta_monitor_is_laptop_panel (monitor)) + continue; + } + else if (match_rule & MONITOR_MATCH_EXTERNAL) + { + if (meta_monitor_is_laptop_panel (monitor)) + continue; + } + + if (match_rule & MONITOR_MATCH_VISIBLE) + { + if (meta_monitor_is_laptop_panel (monitor) && + is_lid_closed (monitor_manager)) + continue; + } + + if (match_rule & MONITOR_MATCH_WITH_POSITION) + { + if (!meta_monitor_get_suggested_position (monitor, NULL, NULL)) + continue; + } + + s = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, + layout_mode, + monitor, + mode); + scale = MAX (scale, s); + } + + return scale; +} + static MetaLogicalMonitorConfig * create_logical_monitor_config (MetaMonitorManager *monitor_manager, MetaMonitor *monitor, MetaMonitorMode *mode, int x, int y, + float max_scale, MetaLogicalMonitorConfig *primary_logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode) { @@ -700,6 +776,7 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_ma scale = primary_logical_monitor_config->scale; else scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, + monitor_manager->layout_mode, monitor, mode); @@ -709,6 +786,13 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_ma width = (int) roundf (width / scale); height = (int) roundf (height / scale); break; + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: + { + float ui_scale = scale / ceilf (max_scale); + width = (int) roundf (width / ui_scale); + height = (int) roundf (height / ui_scale); + } + break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } @@ -747,6 +831,7 @@ meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_mana MetaMonitor *primary_monitor; MetaLogicalMonitorLayoutMode layout_mode; MetaLogicalMonitorConfig *primary_logical_monitor_config; + float max_scale = 1.0f; int x; GList *monitors; GList *l; @@ -758,10 +843,16 @@ meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_mana layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + max_scale = get_preferred_preferred_max_scale (monitor_manager, + layout_mode, + MONITOR_MATCH_VISIBLE); + primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, + max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; @@ -786,6 +877,7 @@ meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_mana create_preferred_logical_monitor_config (monitor_manager, monitor, x, 0, + max_scale, primary_logical_monitor_config, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, @@ -813,6 +905,7 @@ meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_ma GList *logical_monitor_configs; MetaLogicalMonitorLayoutMode layout_mode; MetaLogicalMonitorConfig *primary_logical_monitor_config; + float max_scale = 1.0f; primary_monitor = find_primary_monitor (monitor_manager); if (!primary_monitor) @@ -820,10 +913,16 @@ meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_ma layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + max_scale = get_preferred_preferred_max_scale (monitor_manager, + layout_mode, + MONITOR_MATCH_PRIMARY); + primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, + max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; @@ -828,6 +828,7 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_m MetaMonitor *monitor, int x, int y, + float max_scale, MetaLogicalMonitorConfig *primary_logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode) { @@ -835,6 +836,7 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_m monitor, meta_monitor_get_preferred_mode (monitor), x, y, + max_scale, primary_logical_monitor_config, layout_mode); } @@ -846,6 +945,7 @@ meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_m GList *logical_monitor_configs; GList *region; int x, y; + float max_scale = 1; GList *monitors; GList *l; @@ -849,6 +849,7 @@ create_logical_monitor_config_from_monitor (MetaMonitorManager *monito { MetaRectangle monitor_layout; MetaMonitorMode *mode; + float max_scale = 1.0F; meta_monitor_derive_layout (monitor, &monitor_layout); mode = meta_monitor_get_current_mode (monitor); @@ -858,6 +859,7 @@ create_logical_monitor_config_from_monitor (MetaMonitorManager *monito mode, monitor_layout.x, monitor_layout.y, + max_scale, primary_logical_monitor_config, layout_mode); } @@ -858,10 +958,16 @@ meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_m layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + max_scale = get_preferred_preferred_max_scale (monitor_manager, + layout_mode, + MONITOR_MATCH_WITH_POSITION); + primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, x, y, + max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; @@ -885,6 +991,7 @@ meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_m create_preferred_logical_monitor_config (monitor_manager, monitor, x, y, + max_scale, primary_logical_monitor_config, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, @@ -903,6 +1010,21 @@ meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_m region = g_list_prepend (region, &logical_monitor_config->layout); } + for (l = region; region->next && l; l = l->next) + { + MetaRectangle *rect = l->data; + + if (!meta_rectangle_has_adjacent_in_region (region, rect)) + { + g_warning ("Suggested monitor config has monitors with no neighbors, " + "rejecting"); + g_list_free (region); + g_list_free_full (logical_monitor_configs, + (GDestroyNotify) meta_logical_monitor_config_free); + return NULL; + } + } + g_list_free (region); if (!logical_monitor_configs) @@ -1071,6 +1193,39 @@ meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager return create_for_builtin_display_rotation (config_manager, TRUE, META_MONITOR_TRANSFORM_NORMAL); } +MetaMonitorsConfig * +meta_monitor_config_manager_create_for_layout (MetaMonitorConfigManager *config_manager, + MetaMonitorsConfig *config, + MetaLogicalMonitorLayoutMode layout_mode) +{ + MetaMonitorManager *monitor_manager = config_manager->monitor_manager; + GList *logical_monitor_configs; + GList *l; + + if (!config) + return NULL; + + if (config->layout_mode == layout_mode) + return g_object_ref (config); + + logical_monitor_configs = + clone_logical_monitor_config_list (config->logical_monitor_configs); + + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL) + { + for (l = logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *monitor_config = l->data; + monitor_config->scale = roundf (monitor_config->scale); + } + } + + return meta_monitors_config_new (monitor_manager, + logical_monitor_configs, + layout_mode, + META_MONITORS_CONFIG_FLAG_NONE); +} + static MetaMonitorsConfig * create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager) { @@ -1159,7 +1314,9 @@ create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager) if (!mode) continue; - scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, monitor, mode); + scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, + monitor_manager->layout_mode, + monitor, mode); best_scale = MAX (best_scale, scale); monitor_configs = g_list_prepend (monitor_configs, create_monitor_config (monitor, mode)); } @@ -1195,6 +1352,7 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager) MetaMonitorManager *monitor_manager = config_manager->monitor_manager; GList *logical_monitor_configs = NULL; int x = 0; + float max_scale = 1.0f; MetaLogicalMonitorLayoutMode layout_mode; GList *monitors; GList *l; @@ -1202,6 +1360,11 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager) layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + max_scale = get_preferred_preferred_max_scale (monitor_manager, + layout_mode, + MONITOR_MATCH_EXTERNAL); + monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { @@ -1215,6 +1378,7 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager) create_preferred_logical_monitor_config (monitor_manager, monitor, x, 0, + max_scale, NULL, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, @@ -1249,6 +1413,7 @@ create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager) MetaLogicalMonitorConfig *primary_logical_monitor_config; MetaMonitor *monitor; MetaMonitorsConfig *monitors_config; + float max_scale = 1.0f; monitor = meta_monitor_manager_get_laptop_panel (monitor_manager); if (!monitor) @@ -1256,10 +1421,16 @@ create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager) layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + max_scale = get_preferred_preferred_max_scale (monitor_manager, + layout_mode, + MONITOR_MATCH_BUILTIN); + primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, 0, 0, + max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; @@ -1666,6 +1837,7 @@ gboolean meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorManager *monitor_manager, + float max_scale, GError **error) { GList *l; @@ -1702,6 +1874,10 @@ meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor switch (layout_mode) { + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: + expected_mode_width /= ceilf (max_scale); + expected_mode_height /= ceilf (max_scale); + /* fall through! */ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: expected_mode_width = roundf (expected_mode_width * logical_monitor_config->scale); diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h index 86756a7..8d9fc6c 100644 --- a/src/backends/meta-monitor-config-manager.h +++ b/src/backends/meta-monitor-config-manager.h @@ -110,6 +110,10 @@ MetaMonitorsConfig * meta_monitor_config_manager_create_for_orientation (MetaMon META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager *config_manager); +MetaMonitorsConfig * meta_monitor_config_manager_create_for_layout (MetaMonitorConfigManager *config_manager, + MetaMonitorsConfig *config, + MetaLogicalMonitorLayoutMode layout_mode); + META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager *config_manager, MetaMonitorSwitchConfigType config_type); @@ -191,6 +195,7 @@ META_EXPORT_TEST gboolean meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorManager *monitor_manager, + float max_scale, GError **error); META_EXPORT_TEST diff --git a/src/backends/meta-monitor-config-migration.c b/src/backends/meta-monitor-config-migration.c index d619dc4..69c426c 100644 --- a/src/backends/meta-monitor-config-migration.c +++ b/src/backends/meta-monitor-config-migration.c @@ -1190,6 +1190,9 @@ meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); GList *l; + MetaLogicalMonitorLayoutMode layout_mode; + + layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); for (l = config->logical_monitor_configs; l; l = l->next) { @@ -1199,7 +1202,6 @@ meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, MetaMonitor *monitor; MetaMonitorModeSpec *monitor_mode_spec; MetaMonitorMode *monitor_mode; - float scale; monitor_config = logical_monitor_config->monitor_configs->data; monitor_spec = monitor_config->monitor_spec; @@ -1215,13 +1217,14 @@ meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, return FALSE; } - scale = meta_monitor_calculate_mode_scale (monitor, monitor_mode); - - logical_monitor_config->scale = scale; + logical_monitor_config->scale = + meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, + layout_mode, + monitor, + monitor_mode); } - config->layout_mode = - meta_monitor_manager_get_default_layout_mode (monitor_manager); + config->layout_mode = layout_mode; config->flags &= ~META_MONITORS_CONFIG_FLAG_MIGRATED; if (!meta_verify_monitors_config (config, monitor_manager, error)) diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c index 4dd357a..326723e 100644 --- a/src/backends/meta-monitor-config-store.c +++ b/src/backends/meta-monitor-config-store.c @@ -496,6 +496,7 @@ handle_start_element (GMarkupParseContext *context, static gboolean derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, + float max_scale, GError **error) { MetaMonitorConfig *monitor_config; @@ -533,6 +534,10 @@ derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_conf switch (layout_mode) { + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: + width *= ceilf (max_scale); + height *= ceilf (max_scale); + /* fall through! */ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: width = roundf (width / logical_monitor_config->scale); height = roundf (height / logical_monitor_config->scale); @@ -740,6 +745,7 @@ handle_end_element (GMarkupParseContext *context, GList *l; MetaLogicalMonitorLayoutMode layout_mode; MetaMonitorsConfigFlag config_flags = META_MONITORS_CONFIG_FLAG_NONE; + float max_scale = 1.0f; g_assert (g_str_equal (element_name, "configuration")); @@ -749,18 +755,29 @@ handle_end_element (GMarkupParseContext *context, layout_mode = meta_monitor_manager_get_default_layout_mode (store->monitor_manager); + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + for (l = parser->current_logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *logical_monitor_config = l->data; + max_scale = MAX (max_scale, logical_monitor_config->scale); + } + } + for (l = parser->current_logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (!derive_logical_monitor_layout (logical_monitor_config, layout_mode, + max_scale, error)) return; if (!meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, store->monitor_manager, + max_scale, error)) return; } diff --git a/src/backends/meta-monitor-manager-dummy.c b/src/backends/meta-monitor-manager-dummy.c index d08fb02..32f3b92 100644 --- a/src/backends/meta-monitor-manager-dummy.c +++ b/src/backends/meta-monitor-manager-dummy.c @@ -372,6 +372,15 @@ append_tiled_monitor (MetaMonitorManager *manager, } } +static gboolean +has_tiled_monitors (void) +{ + const char *tiled_monitors_str; + + tiled_monitors_str = g_getenv ("MUTTER_DEBUG_TILED_DUMMY_MONITORS"); + return g_strcmp0 (tiled_monitors_str, "1") == 0; +} + static void meta_monitor_manager_dummy_read_current (MetaMonitorManager *manager) { @@ -380,7 +389,6 @@ meta_monitor_manager_dummy_read_current (MetaMonitorManager *manager) float *monitor_scales = NULL; const char *num_monitors_str; const char *monitor_scales_str; - const char *tiled_monitors_str; gboolean tiled_monitors; unsigned int i; GList *outputs; @@ -458,8 +466,7 @@ meta_monitor_manager_dummy_read_current (MetaMonitorManager *manager) g_strfreev (scales_str_list); } - tiled_monitors_str = g_getenv ("MUTTER_DEBUG_TILED_DUMMY_MONITORS"); - tiled_monitors = g_strcmp0 (tiled_monitors_str, "1") == 0; + tiled_monitors = has_tiled_monitors (); modes = NULL; crtcs = NULL; @@ -638,9 +645,10 @@ meta_monitor_manager_dummy_is_transform_handled (MetaMonitorManager *manager, } static float -meta_monitor_manager_dummy_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) +meta_monitor_manager_dummy_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode) { MetaOutput *output; MetaOutputDummy *output_dummy; @@ -664,6 +672,7 @@ meta_monitor_manager_dummy_calculate_supported_scales (MetaMonitorManager switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; @@ -694,6 +703,9 @@ meta_monitor_manager_dummy_get_capabilities (MetaMonitorManager *manager) MetaMonitorManagerCapability capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; + if (has_tiled_monitors ()) + capabilities |= META_MONITOR_MANAGER_CAPABILITY_TILING; + if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h index 60c1e90..4cc666d 100644 --- a/src/backends/meta-monitor-manager-private.h +++ b/src/backends/meta-monitor-manager-private.h @@ -45,7 +45,8 @@ typedef enum _MetaMonitorManagerCapability META_MONITOR_MANAGER_CAPABILITY_NONE = 0, META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE = (1 << 0), META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 1), - META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT = (1 << 2), + META_MONITOR_MANAGER_CAPABILITY_TILING = (1 << 2), + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING = (1 << 3), } MetaMonitorManagerCapability; /* Equivalent to the 'method' enum in org.gnome.Mutter.DisplayConfig */ @@ -59,7 +61,8 @@ typedef enum _MetaMonitorsConfigMethod typedef enum _MetaLogicalMonitorLayoutMode { META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL = 1, - META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL = 2 + META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL = 2, + META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL = 3 } MetaLogicalMonitorLayoutMode; /* @@ -73,6 +76,7 @@ struct _MetaCrtcAssignment MetaCrtc *crtc; MetaCrtcMode *mode; graphene_rect_t layout; + float scale; MetaMonitorTransform transform; GPtrArray *outputs; }; @@ -134,6 +138,7 @@ struct _MetaMonitorManager int screen_height; GList *monitors; + GList *scale_override_monitors; GList *logical_monitors; MetaLogicalMonitor *primary_logical_monitor; @@ -165,6 +170,9 @@ struct _MetaMonitorManager * @apply_monitors_config: Tries to apply the given config using the given * method. Throws an error if something went wrong. * + * @update_screen_size_derived: Computes the screen size for derived + * configuration. + * * @set_power_save_mode: Sets the #MetaPowerSave mode (for all displays). * * @change_backlight: Changes the backlight intensity to the given value (in @@ -231,6 +239,9 @@ struct _MetaMonitorManagerClass unsigned short *green, unsigned short *blue); + void (*update_screen_size_derived) (MetaMonitorManager *, + MetaMonitorsConfig *); + void (* tiled_monitor_added) (MetaMonitorManager *manager, MetaMonitor *monitor); @@ -241,9 +252,10 @@ struct _MetaMonitorManagerClass MetaCrtc *crtc, MetaMonitorTransform transform); - float (* calculate_monitor_mode_scale) (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode); + float (* calculate_monitor_mode_scale) (MetaMonitorManager *, + MetaLogicalMonitorLayoutMode , + MetaMonitor *, + MetaMonitorMode *); float * (* calculate_supported_scales) (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, @@ -366,9 +378,10 @@ void meta_monitor_manager_lid_is_closed_changed (MetaMonitorManage gboolean meta_monitor_manager_is_headless (MetaMonitorManager *manager); -float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode); +float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode); float * meta_monitor_manager_calculate_supported_scales (MetaMonitorManager *, MetaLogicalMonitorLayoutMode , @@ -382,6 +395,11 @@ gboolean meta_monitor_manager_is_scale_supported (MetaMonitorManager MetaMonitorMode *monitor_mode, float scale); +float meta_monitor_manager_get_maximum_crtc_scale (MetaMonitorManager *manager); + +gboolean meta_monitor_manager_disable_scale_for_monitor (MetaMonitorManager *manager, + MetaLogicalMonitor *monitor); + MetaMonitorManagerCapability meta_monitor_manager_get_capabilities (MetaMonitorManager *manager); diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c index a75da93..6cf4aad 100644 --- a/src/backends/meta-monitor-manager.c +++ b/src/backends/meta-monitor-manager.c @@ -120,8 +120,18 @@ static gboolean meta_monitor_manager_is_config_complete (MetaMonitorManager *manager, MetaMonitorsConfig *config); -static MetaMonitor * -meta_monitor_manager_get_active_monitor (MetaMonitorManager *manager); +static gboolean +is_global_scale_matching_in_config (MetaMonitorsConfig *config, + float scale); + +static gboolean +meta_monitor_manager_is_scale_supported_with_threshold (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + float scale, + float threshold, + float *out_scale); static void meta_monitor_manager_real_read_current_state (MetaMonitorManager *manager); @@ -212,15 +222,45 @@ meta_monitor_manager_rebuild_logical_monitors (MetaMonitorManager *manager, primary_logical_monitor); } +float +meta_monitor_manager_get_maximum_crtc_scale (MetaMonitorManager *manager) +{ + GList *l; + float scale; + + scale = 1.0f; + for (l = manager->monitors; l != NULL; l = l->next) + { + MetaMonitor *monitor = l->data; + MetaOutput *output = meta_monitor_get_main_output (monitor); + MetaCrtc *crtc = meta_output_get_assigned_crtc (output); + + if (crtc) + { + const MetaCrtcConfig *crtc_config = meta_crtc_get_config (crtc); + + scale = MAX (scale, crtc_config ? crtc_config->scale : 1.0f); + } + } + + return scale; +} + static float derive_configured_global_scale (MetaMonitorManager *manager, MetaMonitorsConfig *config) { - MetaLogicalMonitorConfig *logical_monitor_config; + GList *l; + + for (l = config->logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *monitor_config = l->data; - logical_monitor_config = config->logical_monitor_configs->data; + if (is_global_scale_matching_in_config (config, monitor_config->scale)) + return monitor_config->scale; + } - return logical_monitor_config->scale; + return 1.0f; } static float @@ -231,24 +271,70 @@ calculate_monitor_scale (MetaMonitorManager *manager, monitor_mode = meta_monitor_get_current_mode (monitor); return meta_monitor_manager_calculate_monitor_mode_scale (manager, + manager->layout_mode, monitor, monitor_mode); } +static gboolean +meta_monitor_manager_is_scale_supported_by_other_monitors (MetaMonitorManager *manager, + MetaMonitor *not_this_one, + float scale) +{ + GList *l; + + for (l = manager->monitors; l; l = l->next) + { + MetaMonitor *monitor = l->data; + MetaMonitorMode *mode; + + if (monitor == not_this_one || !meta_monitor_is_active (monitor)) + continue; + + mode = meta_monitor_get_current_mode (monitor); + if (!meta_monitor_manager_is_scale_supported (manager, manager->layout_mode, + monitor, mode, scale)) + return FALSE; + } + + return TRUE; +} + static float derive_calculated_global_scale (MetaMonitorManager *manager) { MetaMonitor *monitor = NULL; + float scale; + GList *l; + scale = 1.0f; monitor = meta_monitor_manager_get_primary_monitor (manager); - if (!monitor || !meta_monitor_is_active (monitor)) - monitor = meta_monitor_manager_get_active_monitor (manager); + if (monitor && meta_monitor_is_active (monitor)) + { + scale = calculate_monitor_scale (manager, monitor); + if (meta_monitor_manager_is_scale_supported_by_other_monitors (manager, + monitor, + scale)) + return scale; + } - if (!monitor) - return 1.0; + for (l = manager->monitors; l; l = l->next) + { + MetaMonitor *other_monitor = l->data; + float monitor_scale; + + if (other_monitor == monitor || !meta_monitor_is_active (other_monitor)) + continue; - return calculate_monitor_scale (manager, monitor); + monitor_scale = calculate_monitor_scale (manager, other_monitor); + if (meta_monitor_manager_is_scale_supported_by_other_monitors (manager, + other_monitor, + monitor_scale)) + scale = MAX (scale, monitor_scale); + } + + return scale; } static float @@ -270,6 +356,51 @@ derive_scale_from_config (MetaMonitorManager *manager, return 1.0; } +static gboolean +derive_scale_from_crtc (MetaMonitorManager *manager, + MetaMonitor *monitor, + float *out_scale) +{ + MetaMonitorManagerCapability capabilities; + MetaMonitorMode *monitor_mode; + float threshold; + MetaOutput *output; + MetaCrtc *crtc; + float scale; + + capabilities = meta_monitor_manager_get_capabilities (manager); + + if (!(capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) + return FALSE; + + if (!(capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) + return FALSE; + + output = meta_monitor_get_main_output (monitor); + crtc = meta_output_get_assigned_crtc (output); + + if (!crtc) + return FALSE; + + /* Due to integer and possibly inverse scaling applied to the output the + * result could not match exactly, so we apply a more relaxed threshold + * in this case. */ + threshold = 0.001f; + + scale = meta_crtc_get_config_scale (crtc); + monitor_mode = meta_monitor_get_current_mode (monitor); + if (meta_monitor_manager_is_scale_supported_with_threshold (manager, + manager->layout_mode, + monitor, + monitor_mode, + scale, + threshold, + out_scale)) + return TRUE; + + return FALSE; +} + static void meta_monitor_manager_rebuild_logical_monitors_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) @@ -317,11 +448,17 @@ meta_monitor_manager_rebuild_logical_monitors_derived (MetaMonitorManager *manag float scale; if (use_global_scale) - scale = global_scale; - else if (config) - scale = derive_scale_from_config (manager, config, &layout); + scale = roundf (global_scale); else - scale = calculate_monitor_scale (manager, monitor); + { + if (!derive_scale_from_crtc (manager, monitor, &scale)) + { + if (config) + scale = derive_scale_from_config (manager, config, &layout); + else + scale = calculate_monitor_scale (manager, monitor); + } + } g_assert (scale > 0); @@ -433,16 +570,24 @@ meta_monitor_manager_is_headless (MetaMonitorManager *manager) } float -meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) +meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode) { + float scale; MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); - return manager_class->calculate_monitor_mode_scale (manager, - monitor, - monitor_mode); + scale = manager_class->calculate_monitor_mode_scale (manager, + layout_mode, + monitor, + monitor_mode); + + if (g_list_find (manager->scale_override_monitors, monitor)) + return ceilf (scale); + + return scale; } float * @@ -622,6 +768,8 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) MetaMonitorsConfigMethod method; MetaMonitorsConfigMethod fallback_method = META_MONITORS_CONFIG_METHOD_TEMPORARY; + MetaLogicalMonitorLayoutMode layout_mode = + meta_monitor_manager_get_default_layout_mode (manager); use_stored_config = should_use_stored_config (manager); if (use_stored_config) @@ -631,7 +779,18 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) if (use_stored_config) { + g_autoptr(MetaMonitorsConfig) new_config = NULL; + config = meta_monitor_config_manager_get_stored (manager->config_manager); + if (config && config->layout_mode != layout_mode) + { + new_config = + meta_monitor_config_manager_create_for_layout (manager->config_manager, + config, + layout_mode); + config = new_config; + } + if (config) { if (!meta_monitor_manager_apply_monitors_config (manager, @@ -676,6 +835,16 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) { config = g_object_ref (config); + if (config && config->layout_mode != layout_mode) + { + MetaMonitorsConfig *new_config = + meta_monitor_config_manager_create_for_layout (manager->config_manager, + config, + layout_mode); + g_object_unref (config); + config = new_config; + } + if (meta_monitor_manager_is_config_complete (manager, config)) { if (!meta_monitor_manager_apply_monitors_config (manager, @@ -755,7 +755,9 @@ meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager) static gboolean should_use_stored_config (MetaMonitorManager *manager) { - return !meta_monitor_manager_has_hotplug_mode_update (manager); + return (manager->in_init || + (!manager->scale_override_monitors && + !meta_monitor_manager_has_hotplug_mode_update (manager))); } static gboolean @@ -764,7 +766,7 @@ can_derive_current_config (MetaMonitorManager *manager) MetaMonitorManagerCapability capabilities; capabilities = meta_monitor_manager_get_capabilities (manager); - return !!(capabilities & META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT); + return !!(capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE); } MetaMonitorsConfig * @@ -859,6 +1028,66 @@ orientation_changed (MetaOrientationManager *orientation_manager, handle_orientation_change (orientation_manager, manager); } +static gboolean +apply_x11_fractional_scaling_config (MetaMonitorManager *manager) +{ + g_autoptr(GError) error = NULL; + g_autoptr(MetaMonitorsConfig) config = NULL; + MetaMonitorsConfig *applied_config; + MetaLogicalMonitorLayoutMode layout_mode = + meta_monitor_manager_get_default_layout_mode (manager); + + if (!META_IS_MONITOR_MANAGER_XRANDR (manager)) + return TRUE; + + applied_config = + meta_monitor_config_manager_get_current (manager->config_manager); + config = + meta_monitor_config_manager_create_for_layout (manager->config_manager, + applied_config, + layout_mode); + if (!config) + return FALSE; + + if (meta_monitor_manager_apply_monitors_config (manager, + config, + META_MONITORS_CONFIG_METHOD_PERSISTENT, + &error)) + { + if (config != applied_config && manager->persistent_timeout_id) + { + if (G_UNLIKELY (applied_config != + meta_monitor_config_manager_get_previous (manager->config_manager))) + { + g_warning ("The removed configuration doesn't match the " + "previously applied one, reverting may not work"); + } + else + { + g_autoptr(MetaMonitorsConfig) previous_config = NULL; + + /* The previous config we applied was just a temporary one that + * GNOME control center passed us while toggling the fractional + * scaling. So, in such case, once the configuration with the + * correct layout has been applied, we need to ignore the + * temporary one. */ + previous_config = + meta_monitor_config_manager_pop_previous (manager->config_manager); + + g_assert_true (applied_config == previous_config); + } + } + } + else + { + g_warning ("Impossible to apply the layout config %s\n", + error->message); + return FALSE; + } + + return TRUE; +} + static void experimental_features_changed (MetaSettings *settings, MetaExperimentalFeature old_experimental_features, @@ -866,6 +1095,8 @@ experimental_features_changed (MetaSettings *settings, { gboolean was_stage_views_scaled; gboolean is_stage_views_scaled; + gboolean was_x11_scaling; + gboolean x11_scaling; gboolean should_reconfigure = FALSE; was_stage_views_scaled = @@ -875,10 +1106,23 @@ experimental_features_changed (MetaSettings *settings, meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); + was_x11_scaling = + !!(old_experimental_features & + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING); + x11_scaling = + meta_settings_is_experimental_feature_enabled ( + settings, + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING); if (is_stage_views_scaled != was_stage_views_scaled) should_reconfigure = TRUE; + if (was_x11_scaling != x11_scaling) + { + if (!apply_x11_fractional_scaling_config (manager)) + should_reconfigure = TRUE; + } + if (should_reconfigure) meta_monitor_manager_reconfigure (manager); @@ -1421,6 +1665,33 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton, return TRUE; } +static void +restore_previous_experimental_config (MetaMonitorManager *manager, + MetaMonitorsConfig *previous_config) +{ + MetaBackend *backend = manager->backend; + MetaSettings *settings = meta_backend_get_settings (backend); + gboolean was_fractional; + + if (!META_IS_MONITOR_MANAGER_XRANDR (manager)) + return; + + was_fractional = + previous_config->layout_mode != META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; + + if (meta_settings_is_experimental_feature_enabled (settings, + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING) == was_fractional) + return; + + g_signal_handler_block (settings, + manager->experimental_features_changed_handler_id); + + meta_settings_enable_x11_fractional_scaling (settings, was_fractional); + + g_signal_handler_unblock (settings, + manager->experimental_features_changed_handler_id); +} + static void restore_previous_config (MetaMonitorManager *manager) { @@ -1434,6 +1705,8 @@ restore_previous_config (MetaMonitorManager *manager) { MetaMonitorsConfigMethod method; + restore_previous_experimental_config (manager, previous_config); + method = META_MONITORS_CONFIG_METHOD_TEMPORARY; if (meta_monitor_manager_apply_monitors_config (manager, previous_config, @@ -1490,6 +1763,41 @@ request_persistent_confirmation (MetaMonitorManager *manager) g_signal_emit (manager, signals[CONFIRM_DISPLAY_CHANGE], 0); } +gboolean +meta_monitor_manager_disable_scale_for_monitor (MetaMonitorManager *manager, + MetaLogicalMonitor *monitor) +{ + switch (manager->layout_mode) + { + case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: + break; + default: + return FALSE; + } + + if (monitor && fmodf (monitor->scale, 1.0) != 0.0f) + { + if (manager->scale_override_monitors) + { + g_clear_pointer (&manager->scale_override_monitors, g_list_free); + g_object_unref (meta_monitor_config_manager_pop_previous (manager->config_manager)); + } + + manager->scale_override_monitors = g_list_copy (monitor->monitors); + meta_monitor_manager_ensure_configured (manager); + return TRUE; + } + + if (manager->scale_override_monitors) + { + g_clear_pointer (&manager->scale_override_monitors, g_list_free); + restore_previous_config (manager); + } + + return FALSE; +} + #define META_DISPLAY_CONFIG_MODE_FLAGS_PREFERRED (1 << 0) #define META_DISPLAY_CONFIG_MODE_FLAGS_CURRENT (1 << 1) @@ -1517,6 +1825,7 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, MetaMonitorManagerCapability capabilities; int ui_scaling_factor; int max_screen_width, max_screen_height; + char *renderer; g_variant_builder_init (&monitors_builder, G_VARIANT_TYPE (MONITORS_FORMAT)); @@ -1563,6 +1872,7 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, preferred_scale = meta_monitor_manager_calculate_monitor_mode_scale (manager, + manager->layout_mode, monitor, monitor_mode); @@ -1670,6 +1980,14 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, } g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}")); + + renderer = g_ascii_strdown (G_OBJECT_TYPE_NAME (manager) + + strlen (g_type_name (g_type_parent (G_OBJECT_TYPE (manager)))), + -1); + g_variant_builder_add (&properties_builder, "{sv}", + "renderer", + g_variant_new_take_string (renderer)); + capabilities = meta_monitor_manager_get_capabilities (manager); g_variant_builder_add (&properties_builder, "{sv}", @@ -1688,6 +2006,14 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, "global-scale-required", g_variant_new_boolean (TRUE)); } + else if (META_IS_MONITOR_MANAGER_XRANDR (manager) && + (capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING) && + (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) + { + g_variant_builder_add (&properties_builder, "{sv}", + "x11-fractional-scaling", + g_variant_new_boolean (TRUE)); + } ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); g_variant_builder_add (&properties_builder, "{sv}", @@ -1732,12 +2058,14 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, #undef LOGICAL_MONITOR_FORMAT #undef LOGICAL_MONITORS_FORMAT -gboolean -meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, - MetaLogicalMonitorLayoutMode layout_mode, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode, - float scale) +static gboolean +meta_monitor_manager_is_scale_supported_with_threshold (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + float scale, + float threshold, + float *out_scale) { g_autofree float *supported_scales = NULL; int n_supported_scales; @@ -1751,8 +2079,66 @@ meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, &n_supported_scales); for (i = 0; i < n_supported_scales; i++) { - if (supported_scales[i] == scale) - return TRUE; + if (fabs (supported_scales[i] - scale) < threshold) + { + if (out_scale) + *out_scale = supported_scales[i]; + + return TRUE; + } + } + + return FALSE; +} + +gboolean +meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + float scale) +{ + return meta_monitor_manager_is_scale_supported_with_threshold (manager, + layout_mode, + monitor, + monitor_mode, + scale, + FLT_EPSILON, + NULL); +} + +static gboolean +is_global_scale_matching_in_config (MetaMonitorsConfig *config, + float scale) +{ + GList *l; + + for (l = config->logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *logical_monitor_config = l->data; + + if (fabs (logical_monitor_config->scale - scale) > FLT_EPSILON) + return FALSE; + } + + return TRUE; +} + +static gboolean +meta_monitor_manager_is_scale_supported_for_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + float scale) +{ + if (meta_monitor_manager_is_scale_supported (manager, config->layout_mode, + monitor, monitor_mode, scale)) + { + if (meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) + return is_global_scale_matching_in_config (config, scale); + + return TRUE; } return FALSE; @@ -1796,11 +2182,11 @@ meta_monitor_manager_is_config_applicable (MetaMonitorManager *manager, return FALSE; } - if (!meta_monitor_manager_is_scale_supported (manager, - config->layout_mode, - monitor, - monitor_mode, - scale)) + if (!meta_monitor_manager_is_scale_supported_for_config (manager, + config, + monitor, + monitor_mode, + scale)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Scale not supported by backend"); @@ -2021,6 +2407,7 @@ derive_logical_monitor_size (MetaMonitorConfig *monitor_config, switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: width = roundf (width / scale); height = roundf (height / scale); break; @@ -2120,9 +2507,11 @@ create_logical_monitor_config_from_variant (MetaMonitorManager *manager .monitor_configs = monitor_configs }; - if (!meta_verify_logical_monitor_config (logical_monitor_config, + if (layout_mode != META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL && + !meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, manager, + 1.0f, error)) { meta_logical_monitor_config_free (logical_monitor_config); @@ -2143,6 +2532,7 @@ is_valid_layout_mode (MetaLogicalMonitorLayoutMode layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: return TRUE; } @@ -2165,6 +2555,7 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet MetaMonitorsConfig *config; GList *logical_monitor_configs = NULL; GError *error = NULL; + float max_scale = 1.0f; if (serial != manager->serial) { @@ -2236,10 +2627,42 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet return TRUE; } + max_scale = MAX (max_scale, logical_monitor_config->scale); logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); } + if (manager->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + GList *l; + int ui_scale = ceilf (max_scale); + + for (l = logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *logical_monitor_config = l->data; + + logical_monitor_config->layout.width = + roundf (logical_monitor_config->layout.width * ui_scale); + logical_monitor_config->layout.height = + roundf (logical_monitor_config->layout.height * ui_scale); + + if (!meta_verify_logical_monitor_config (logical_monitor_config, + manager->layout_mode, + manager, + ui_scale, + &error)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "%s", error->message); + g_error_free (error); + g_list_free_full (logical_monitor_configs, + (GDestroyNotify) meta_logical_monitor_config_free); + return TRUE; + } + } + } + config = meta_monitors_config_new (manager, logical_monitor_configs, layout_mode, @@ -2746,12 +3169,6 @@ meta_monitor_manager_get_laptop_panel (MetaMonitorManager *manager) return find_monitor (manager, meta_monitor_is_laptop_panel); } -static MetaMonitor * -meta_monitor_manager_get_active_monitor (MetaMonitorManager *manager) -{ - return find_monitor (manager, meta_monitor_is_active); -} - MetaMonitor * meta_monitor_manager_get_monitor_from_connector (MetaMonitorManager *manager, const char *connector) @@ -2934,6 +3351,10 @@ rebuild_monitors (MetaMonitorManager *manager) { GList *gpus; GList *l; + gboolean has_tiling; + + has_tiling = meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_TILING; if (manager->monitors) { @@ -2952,7 +3373,7 @@ rebuild_monitors (MetaMonitorManager *manager) MetaOutput *output = k->data; const MetaOutputInfo *output_info = meta_output_get_info (output); - if (output_info->tile_info.group_id) + if (has_tiling && output_info->tile_info.group_id) { if (is_main_tiled_monitor_output (output)) { @@ -3170,7 +3591,7 @@ meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager, else manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; - manager->layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; + manager->layout_mode = meta_monitor_manager_get_default_layout_mode (manager); meta_monitor_manager_rebuild_logical_monitors_derived (manager, config); } @@ -3179,10 +3600,14 @@ void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { + MetaMonitorManagerClass *klass = META_MONITOR_MANAGER_GET_CLASS (manager); GList *old_logical_monitors; meta_monitor_manager_update_monitor_modes_derived (manager); + if (klass->update_screen_size_derived) + klass->update_screen_size_derived (manager, config); + if (manager->in_init) return; diff --git a/src/backends/meta-monitor.c b/src/backends/meta-monitor.c index e02f8ed..d3ad030 100644 --- a/src/backends/meta-monitor.c +++ b/src/backends/meta-monitor.c @@ -730,8 +730,11 @@ meta_monitor_normal_get_suggested_position (MetaMonitor *monitor, if (output_info->suggested_x < 0 && output_info->suggested_y < 0) return FALSE; - *x = output_info->suggested_x; - *y = output_info->suggested_y; + if (x) + *x = output_info->suggested_x; + + if (y) + *y = output_info->suggested_y; return TRUE; } @@ -1657,8 +1660,9 @@ meta_monitor_calculate_crtc_pos (MetaMonitor *monitor, #define SMALLEST_4K_WIDTH 3656 static float -calculate_scale (MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) +calculate_scale (MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + MetaMonitorScalesConstraint constraints) { int resolution_width, resolution_height; int width_mm, height_mm; @@ -1714,8 +1718,9 @@ out: } float -meta_monitor_calculate_mode_scale (MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) +meta_monitor_calculate_mode_scale (MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + MetaMonitorScalesConstraint constraints) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); @@ -1725,7 +1730,7 @@ meta_monitor_calculate_mode_scale (MetaMonitor *monitor, &global_scaling_factor)) return global_scaling_factor; - return calculate_scale (monitor, monitor_mode); + return calculate_scale (monitor, monitor_mode, constraints); } static gboolean @@ -1735,6 +1740,16 @@ is_logical_size_large_enough (int width, return width * height >= MINIMUM_LOGICAL_AREA; } +static gboolean +is_scale_valid_for_size (float width, + float height, + float scale) +{ + return scale >= MINIMUM_SCALE_FACTOR && + scale <= MAXIMUM_SCALE_FACTOR && + is_logical_size_large_enough (floorf (width/scale), floorf (width/scale)); +} + gboolean meta_monitor_mode_should_be_advertised (MetaMonitorMode *monitor_mode) { @@ -1764,21 +1779,16 @@ get_closest_scale_factor_for_resolution (float width, gboolean found_one; best_scale = 0; - scaled_w = width / scale; - scaled_h = height / scale; - if (scale < MINIMUM_SCALE_FACTOR || - scale > MAXIMUM_SCALE_FACTOR || - !is_logical_size_large_enough (floorf (scaled_w), floorf (scaled_h))) + if (!is_scale_valid_for_size (width, height, scale)) goto out; - if (floorf (scaled_w) == scaled_w && floorf (scaled_h) == scaled_h) + if (fmodf (width, scale) == 0.0 && fmodf (height, scale) == 0.0) return scale; i = 0; found_one = FALSE; - base_scaled_w = floorf (scaled_w); - + base_scaled_w = floorf (width / scale); do { for (j = 0; j < 2; j++) @@ -1838,16 +1848,24 @@ meta_monitor_calculate_supported_scales (MetaMonitor *monitor, float scale; float scale_value = i + j * SCALE_FACTORS_STEPS; - if ((constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) && - fmodf (scale_value, 1.0) != 0.0) + if (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) { - continue; + if (fmodf (scale_value, 1.0) != 0.0) + continue; } - scale = get_closest_scale_factor_for_resolution (width, - height, - scale_value); + if ((constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) || + (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_LOGICAL)) + { + if (!is_scale_valid_for_size (width, height, scale_value)) + continue; + scale = scale_value; + } + else + scale = get_closest_scale_factor_for_resolution (width, + height, + scale_value); if (scale > 0.0f) g_array_append_val (supported_scales, scale); } diff --git a/src/backends/meta-monitor.h b/src/backends/meta-monitor.h index 341657a..234a6f9 100644 --- a/src/backends/meta-monitor.h +++ b/src/backends/meta-monitor.h @@ -62,6 +62,7 @@ typedef enum _MetaMonitorScalesConstraint { META_MONITOR_SCALES_CONSTRAINT_NONE = 0, META_MONITOR_SCALES_CONSTRAINT_NO_FRAC = (1 << 0), + META_MONITOR_SCALES_CONSTRAINT_NO_LOGICAL = (1 << 1), } MetaMonitorScalesConstraint; #define META_TYPE_MONITOR (meta_monitor_get_type ()) @@ -211,8 +212,9 @@ void meta_monitor_calculate_crtc_pos (MetaMonitor *monitor, int *out_y); META_EXPORT_TEST -float meta_monitor_calculate_mode_scale (MetaMonitor *monitor, - MetaMonitorMode *monitor_mode); +float meta_monitor_calculate_mode_scale (MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + MetaMonitorScalesConstraint constraints); META_EXPORT_TEST float * meta_monitor_calculate_supported_scales (MetaMonitor *monitor, diff --git a/src/backends/meta-settings-private.h b/src/backends/meta-settings-private.h index cde7e04..4b8b4c5 100644 --- a/src/backends/meta-settings-private.h +++ b/src/backends/meta-settings-private.h @@ -36,6 +36,7 @@ typedef enum _MetaExperimentalFeature META_EXPERIMENTAL_FEATURE_RT_SCHEDULER = (1 << 2), META_EXPERIMENTAL_FEATURE_DMA_BUF_SCREEN_SHARING = (1 << 3), META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND = (1 << 4), + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING = (1 << 5), } MetaExperimentalFeature; typedef enum _MetaXwaylandExtension @@ -44,6 +45,13 @@ typedef enum _MetaXwaylandExtension META_XWAYLAND_EXTENSION_XTEST = (1 << 1), } MetaXwaylandExtension; +typedef enum _MetaX11ScaleMode +{ + META_X11_SCALE_MODE_NONE = 0, + META_X11_SCALE_MODE_UP = 1, + META_X11_SCALE_MODE_UI_DOWN = 2, +} MetaX11ScaleMode; + #define META_TYPE_SETTINGS (meta_settings_get_type ()) G_DECLARE_FINAL_TYPE (MetaSettings, meta_settings, META, SETTINGS, GObject) @@ -78,4 +86,9 @@ gboolean meta_settings_are_xwayland_grabs_allowed (MetaSettings *settings); int meta_settings_get_xwayland_disable_extensions (MetaSettings *settings); +MetaX11ScaleMode meta_settings_get_x11_scale_mode (MetaSettings *settings); + +void meta_settings_enable_x11_fractional_scaling (MetaSettings *settings, + gboolean enabled); + #endif /* META_SETTINGS_PRIVATE_H */ diff --git a/src/backends/meta-settings.c b/src/backends/meta-settings.c index 6a754d4..b2a6408 100644 --- a/src/backends/meta-settings.c +++ b/src/backends/meta-settings.c @@ -40,6 +40,7 @@ enum UI_SCALING_FACTOR_CHANGED, GLOBAL_SCALING_FACTOR_CHANGED, FONT_DPI_CHANGED, + X11_SCALE_MODE_CHANGED, EXPERIMENTAL_FEATURES_CHANGED, N_SIGNALS @@ -56,6 +57,7 @@ struct _MetaSettings GSettings *interface_settings; GSettings *mutter_settings; GSettings *wayland_settings; + GSettings *x11_settings; int ui_scaling_factor; int global_scaling_factor; @@ -71,6 +73,8 @@ struct _MetaSettings /* A bitmask of MetaXwaylandExtension enum */ int xwayland_disable_extensions; + + MetaX11ScaleMode x11_scale_mode; }; G_DEFINE_TYPE (MetaSettings, meta_settings, G_TYPE_OBJECT) @@ -80,14 +84,39 @@ calculate_ui_scaling_factor (MetaSettings *settings) { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (settings->backend); - MetaLogicalMonitor *primary_logical_monitor; - primary_logical_monitor = - meta_monitor_manager_get_primary_logical_monitor (monitor_manager); - if (!primary_logical_monitor) - return 1; + if (!meta_is_wayland_compositor () && + monitor_manager && + (meta_monitor_manager_get_capabilities (monitor_manager) & + META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) + { + MetaLogicalMonitorLayoutMode layout_mode = + meta_monitor_manager_get_default_layout_mode (monitor_manager); + + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + return + ceilf (meta_monitor_manager_get_maximum_crtc_scale (monitor_manager)); + } + else if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL) + { + return 1.0f; + } + } + + if (monitor_manager) + { + MetaLogicalMonitor *primary_logical_monitor; + + primary_logical_monitor = + meta_monitor_manager_get_primary_logical_monitor (monitor_manager); + if (!primary_logical_monitor) + return 1; + + return (int) meta_logical_monitor_get_scale (primary_logical_monitor); + } - return (int) meta_logical_monitor_get_scale (primary_logical_monitor); + return 1; } static gboolean @@ -235,6 +264,76 @@ meta_settings_override_experimental_features (MetaSettings *settings) settings->experimental_features_overridden = TRUE; } +static gboolean +update_x11_scale_mode (MetaSettings *settings) +{ + MetaX11ScaleMode scale_mode; + + if (!(settings->experimental_features & + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) + { + scale_mode = META_X11_SCALE_MODE_NONE; + } + else + { + scale_mode = + g_settings_get_enum (settings->x11_settings, "fractional-scale-mode"); + } + + if (settings->x11_scale_mode != scale_mode) + { + settings->x11_scale_mode = scale_mode; + return TRUE; + } + + return FALSE; +} + +void meta_settings_enable_x11_fractional_scaling (MetaSettings *settings, + gboolean enable) +{ + g_auto(GStrv) existing_features = NULL; + gboolean have_fractional_scaling = FALSE; + g_autoptr(GVariantBuilder) builder = NULL; + MetaExperimentalFeature old_experimental_features; + + if (enable == meta_settings_is_experimental_feature_enabled (settings, + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) + return; + + /* Change the internal value now, as we don't want to wait for gsettings */ + old_experimental_features = settings->experimental_features; + settings->experimental_features |= + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING; + + update_x11_scale_mode (settings); + + g_signal_emit (settings, signals[EXPERIMENTAL_FEATURES_CHANGED], 0, + (unsigned int) old_experimental_features); + + /* Add or remove the fractional scaling feature from mutter */ + existing_features = g_settings_get_strv (settings->mutter_settings, + "experimental-features"); + builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); + for (int i = 0; existing_features[i] != NULL; i++) + { + if (g_strcmp0 (existing_features[i], "x11-randr-fractional-scaling") == 0) + { + if (enable) + have_fractional_scaling = TRUE; + else + continue; + } + + g_variant_builder_add (builder, "s", existing_features[i]); + } + if (enable && !have_fractional_scaling) + g_variant_builder_add (builder, "s", "x11-randr-fractional-scaling"); + + g_settings_set_value (settings->mutter_settings, "experimental-features", + g_variant_builder_end (builder)); +} + void meta_settings_enable_experimental_feature (MetaSettings *settings, MetaExperimentalFeature feature) @@ -242,6 +341,9 @@ meta_settings_enable_experimental_feature (MetaSettings *settings, g_assert (settings->experimental_features_overridden); settings->experimental_features |= feature; + + if (update_x11_scale_mode (settings)) + g_signal_emit (settings, signals[X11_SCALE_MODE_CHANGED], 0, NULL); } static gboolean @@ -275,6 +377,8 @@ experimental_features_handler (GVariant *features_variant, feature = META_EXPERIMENTAL_FEATURE_DMA_BUF_SCREEN_SHARING; else if (g_str_equal (feature_str, "autoclose-xwayland")) feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND; + else if (g_str_equal (feature_str, "x11-randr-fractional-scaling")) + feature = META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING; if (feature) g_message ("Enabling experimental feature '%s'", feature_str); @@ -287,6 +391,7 @@ experimental_features_handler (GVariant *features_variant, if (features != settings->experimental_features) { settings->experimental_features = features; + update_x11_scale_mode (settings); *result = GINT_TO_POINTER (TRUE); } else @@ -420,6 +525,18 @@ wayland_settings_changed (GSettings *wayland_settings, } } +static void +x11_settings_changed (GSettings *wayland_settings, + gchar *key, + MetaSettings *settings) +{ + if (g_str_equal (key, "fractional-scale-mode")) + { + if (update_x11_scale_mode (settings)) + g_signal_emit (settings, signals[X11_SCALE_MODE_CHANGED], 0, NULL); + } +} + void meta_settings_get_xwayland_grab_patterns (MetaSettings *settings, GPtrArray **allow_list_patterns, @@ -441,6 +558,12 @@ meta_settings_get_xwayland_disable_extensions (MetaSettings *settings) return (settings->xwayland_disable_extensions); } +MetaX11ScaleMode +meta_settings_get_x11_scale_mode (MetaSettings *settings) +{ + return settings->x11_scale_mode; +} + MetaSettings * meta_settings_new (MetaBackend *backend) { @@ -460,6 +583,7 @@ meta_settings_dispose (GObject *object) g_clear_object (&settings->mutter_settings); g_clear_object (&settings->interface_settings); g_clear_object (&settings->wayland_settings); + g_clear_object (&settings->x11_settings); g_clear_pointer (&settings->xwayland_grab_allow_list_patterns, g_ptr_array_unref); g_clear_pointer (&settings->xwayland_grab_deny_list_patterns, @@ -483,6 +607,10 @@ meta_settings_init (MetaSettings *settings) g_signal_connect (settings->wayland_settings, "changed", G_CALLBACK (wayland_settings_changed), settings); + settings->x11_settings = g_settings_new ("org.gnome.mutter.x11"); + g_signal_connect (settings->x11_settings, "changed", + G_CALLBACK (x11_settings_changed), + settings); /* Chain up inter-dependent settings. */ g_signal_connect (settings, "global-scaling-factor-changed", @@ -549,6 +677,14 @@ meta_settings_class_init (MetaSettingsClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + signals[X11_SCALE_MODE_CHANGED] = + g_signal_new ("x11-scale-mode-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[EXPERIMENTAL_FEATURES_CHANGED] = g_signal_new ("experimental-features-changed", G_TYPE_FROM_CLASS (object_class), diff --git a/src/backends/native/meta-monitor-manager-native.c b/src/backends/native/meta-monitor-manager-native.c index fd5e778..3864474 100644 --- a/src/backends/native/meta-monitor-manager-native.c +++ b/src/backends/native/meta-monitor-manager-native.c @@ -571,20 +571,9 @@ meta_monitor_manager_native_is_transform_handled (MetaMonitorManager *manager, transform); } -static float -meta_monitor_manager_native_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) -{ - return meta_monitor_calculate_mode_scale (monitor, monitor_mode); -} +static MetaMonitorScalesConstraint +get_monitor_scale_constraints_per_layout_mode (MetaLogicalMonitorLayoutMode layout_mode) -static float * -meta_monitor_manager_native_calculate_supported_scales (MetaMonitorManager *manager, - MetaLogicalMonitorLayoutMode layout_mode, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode, - int *n_supported_scales) { MetaMonitorScalesConstraint constraints = META_MONITOR_SCALES_CONSTRAINT_NONE; @@ -598,6 +587,32 @@ meta_monitor_manager_native_calculate_supported_scales (MetaMonitorManager break; } + return constraints; +} + +static float +meta_monitor_manager_native_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode) +{ + MetaMonitorScalesConstraint constraints = + get_monitor_scale_constraints_per_layout_mode (layout_mode); + + return meta_monitor_calculate_mode_scale (monitor, monitor_mode, constraints); +} + +static float * +meta_monitor_manager_native_calculate_supported_scales (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode, + int *n_supported_scales) +{ + MetaMonitorScalesConstraint constraints = + get_monitor_scale_constraints_per_layout_mode (layout_mode); + + return meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, n_supported_scales); diff --git a/src/backends/x11/meta-crtc-xrandr.c b/src/backends/x11/meta-crtc-xrandr.c index e06448b6..e17d3ea 100644 --- a/src/backends/x11/meta-crtc-xrandr.c +++ b/src/backends/x11/meta-crtc-xrandr.c @@ -36,6 +36,7 @@ #include "backends/x11/meta-crtc-xrandr.h" #include +#include #include #include @@ -46,6 +47,9 @@ #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-monitor-manager-xrandr.h" +#define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) +#define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) + struct _MetaCrtcXrandr { MetaCrtc parent; @@ -110,6 +114,63 @@ meta_crtc_xrandr_set_config (MetaCrtcXrandr *crtc_xrandr, *out_timestamp = reply->timestamp; free (reply); + + return TRUE; +} + +gboolean +meta_crtc_xrandr_set_scale (MetaCrtc *crtc, + xcb_randr_crtc_t xrandr_crtc, + float scale) +{ + MetaGpu *gpu = meta_crtc_get_gpu (crtc); + MetaBackend *backend = meta_gpu_get_backend (gpu); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + MetaMonitorManagerXrandr *monitor_manager_xrandr = + META_MONITOR_MANAGER_XRANDR (monitor_manager); + Display *xdisplay; + const char *scale_filter; + xcb_connection_t *xcb_conn; + xcb_void_cookie_t transform_cookie; + xcb_generic_error_t *xcb_error = NULL; + xcb_render_transform_t transformation = { + DOUBLE_TO_FIXED (1), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), + DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (1), DOUBLE_TO_FIXED (0), + DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (1) + }; + + if (!(meta_monitor_manager_get_capabilities (monitor_manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) + return FALSE; + + xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); + xcb_conn = XGetXCBConnection (xdisplay); + + if (fabsf (scale - 1.0f) > 0.001) + { + scale_filter = FilterGood; + transformation.matrix11 = DOUBLE_TO_FIXED (1.0 / scale); + transformation.matrix22 = DOUBLE_TO_FIXED (1.0 / scale); + } + else + scale_filter = FilterFast; + + transform_cookie = + xcb_randr_set_crtc_transform_checked (xcb_conn, xrandr_crtc, transformation, + strlen (scale_filter), scale_filter, + 0, NULL); + + xcb_error = xcb_request_check (xcb_conn, transform_cookie); + if (xcb_error) + { + g_warning ("Impossible to set scaling on crtc %u to %f, error id %u", + xrandr_crtc, scale, xcb_error->error_code); + g_clear_pointer (&xcb_error, free); + + return FALSE; + } + return TRUE; } @@ -221,11 +282,34 @@ meta_crtc_xrandr_get_current_mode (MetaCrtcXrandr *crtc_xrandr) return crtc_xrandr->current_mode; } +static float +meta_monitor_scale_from_transformation (XRRCrtcTransformAttributes *transformation) +{ + XTransform *xt; + float scale; + + if (!transformation) + return 1.0f; + + xt = &transformation->currentTransform; + + if (xt->matrix[0][0] == xt->matrix[1][1]) + scale = XFixedToDouble (xt->matrix[0][0]); + else + scale = XFixedToDouble (xt->matrix[0][0] + xt->matrix[1][1]) / 2.0; + + g_return_val_if_fail (scale > 0.0f, 1.0f); + + return 1.0f / scale; +} + MetaCrtcXrandr * -meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, - XRRCrtcInfo *xrandr_crtc, - RRCrtc crtc_id, - XRRScreenResources *resources) +meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, + XRRCrtcInfo *xrandr_crtc, + RRCrtc crtc_id, + XRRScreenResources *resources, + XRRCrtcTransformAttributes *transform_attributes, + float scale_multiplier) { MetaGpu *gpu = META_GPU (gpu_xrandr); MetaBackend *backend = meta_gpu_get_backend (gpu); @@ -285,6 +369,9 @@ meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, if (crtc_xrandr->current_mode) { + float crtc_scale = + meta_monitor_scale_from_transformation (transform_attributes); + meta_crtc_set_config (META_CRTC (crtc_xrandr), &GRAPHENE_RECT_INIT (crtc_xrandr->rect.x, crtc_xrandr->rect.y, @@ -292,6 +379,11 @@ meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, crtc_xrandr->rect.height), crtc_xrandr->current_mode, crtc_xrandr->transform); + + if (scale_multiplier > 0.0f) + crtc_scale *= scale_multiplier; + + meta_crtc_set_config_scale (META_CRTC (crtc_xrandr), crtc_scale); } return crtc_xrandr; diff --git a/src/backends/x11/meta-crtc-xrandr.h b/src/backends/x11/meta-crtc-xrandr.h index dbf5da0..dbaeb26 100644 --- a/src/backends/x11/meta-crtc-xrandr.h +++ b/src/backends/x11/meta-crtc-xrandr.h @@ -44,14 +44,20 @@ gboolean meta_crtc_xrandr_set_config (MetaCrtcXrandr *crtc_xrandr, int n_outputs, xcb_timestamp_t *out_timestamp); +gboolean meta_crtc_xrandr_set_scale (MetaCrtc *crtc, + xcb_randr_crtc_t xrandr_crtc, + float scale); + gboolean meta_crtc_xrandr_is_assignment_changed (MetaCrtcXrandr *crtc_xrandr, MetaCrtcAssignment *crtc_assignment); MetaCrtcMode * meta_crtc_xrandr_get_current_mode (MetaCrtcXrandr *crtc_xrandr); -MetaCrtcXrandr * meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, - XRRCrtcInfo *xrandr_crtc, - RRCrtc crtc_id, - XRRScreenResources *resources); +MetaCrtcXrandr * meta_crtc_xrandr_new (MetaGpuXrandr *gpu_xrandr, + XRRCrtcInfo *xrandr_crtc, + RRCrtc crtc_id, + XRRScreenResources *resources, + XRRCrtcTransformAttributes *transform_attributes, + float scale_multiplier); #endif /* META_CRTC_XRANDR_H */ diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c index bc3292d..0e9d55a 100644 --- a/src/backends/x11/meta-gpu-xrandr.c +++ b/src/backends/x11/meta-gpu-xrandr.c @@ -23,6 +23,7 @@ * along with this program; if not, see . */ +#include "backends/meta-crtc.h" #include "config.h" #include "backends/x11/meta-gpu-xrandr.h" @@ -45,6 +45,8 @@ struct _MetaGpuXrandr XRRScreenResources *resources; + int min_screen_width; + int min_screen_height; int max_screen_width; int max_screen_height; @@ -56,6 +59,15 @@ meta_gpu_xrandr_get_resources (MetaGpuXrandr *gpu_xrandr) return gpu_xrandr->resources; } +void +meta_gpu_xrandr_get_min_screen_size (MetaGpuXrandr *gpu_xrandr, + int *min_width, + int *min_height) +{ + *min_width = gpu_xrandr->min_screen_width; + *min_height = gpu_xrandr->min_screen_height; +} + void meta_gpu_xrandr_get_max_screen_size (MetaGpuXrandr *gpu_xrandr, int *max_width, @@ -98,6 +100,60 @@ get_xmode_name (XRRModeInfo *xmode) return g_strdup_printf ("%dx%d", width, height); } +static int +get_current_dpi_scale (MetaMonitorManagerXrandr *manager_xrandr, + MetaGpuXrandr *gpu_xrandr) +{ + Atom actual; + int result, format; + unsigned long n, left; + g_autofree unsigned char *data = NULL; + g_auto(GStrv) resources = NULL; + Display *dpy; + int i; + + if (gpu_xrandr->resources->timestamp == + meta_monitor_manager_xrandr_get_config_timestamp (manager_xrandr)) + { + MetaMonitorManager *monitor_manager = META_MONITOR_MANAGER (manager_xrandr); + MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager); + MetaSettings *settings = meta_backend_get_settings (backend); + + return meta_settings_get_ui_scaling_factor (settings); + } + + dpy = meta_monitor_manager_xrandr_get_xdisplay (manager_xrandr); + result = XGetWindowProperty (dpy, DefaultRootWindow (dpy), + XA_RESOURCE_MANAGER, 0L, 65536, False, + XA_STRING, &actual, &format, + &n, &left, &data); + + if (result != Success || !data || actual != XA_STRING) + return 1; + + resources = g_strsplit ((char *) data, "\n", -1); + + for (i = 0; resources && resources[i]; ++i) + { + if (g_str_has_prefix (resources[i], "Xft.dpi:")) + { + g_auto(GStrv) res = g_strsplit (resources[i], "\t", 2); + + if (res && res[0] && res[1]) + { + guint64 dpi; + dpi = g_ascii_strtoull (res[1], NULL, 10); + + if (dpi > 0 && dpi < 96 * 10) + return MAX (1, roundf ((float) dpi / 96.0f)); + } + } + } + + return 1; +} + + static float calculate_xrandr_refresh_rate (XRRModeInfo *xmode) { @@ -135,12 +191,13 @@ update_screen_size (MetaGpuXrandr *gpu_xrandr) META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); - int min_width, min_height; + gboolean has_transform; + int dpi_scale = 1; Screen *screen; XRRGetScreenSizeRange (xdisplay, DefaultRootWindow (xdisplay), - &min_width, - &min_height, + &gpu_xrandr->min_screen_width, + &gpu_xrandr->min_screen_height, &gpu_xrandr->max_screen_width, &gpu_xrandr->max_screen_height); @@ -162,22 +228,60 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, } meta_gpu_take_modes (gpu, modes); + has_transform = !!(meta_monitor_manager_get_capabilities (monitor_manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING); + + if (has_transform && + meta_monitor_manager_get_default_layout_mode (monitor_manager) == + META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + dpi_scale = get_current_dpi_scale (monitor_manager_xrandr, gpu_xrandr); + for (i = 0; i < (unsigned)resources->ncrtc; i++) { XRRCrtcInfo *xrandr_crtc; + XRRCrtcTransformAttributes *transform_attributes; RRCrtc crtc_id; MetaCrtcXrandr *crtc_xrandr; crtc_id = resources->crtcs[i]; xrandr_crtc = XRRGetCrtcInfo (xdisplay, resources, crtc_id); + + if (!has_transform || + !XRRGetCrtcTransform (xdisplay, crtc_id, &transform_attributes)) + transform_attributes = NULL; + crtc_xrandr = meta_crtc_xrandr_new (gpu_xrandr, - xrandr_crtc, crtc_id, resources); + xrandr_crtc, crtc_id, resources, + transform_attributes, dpi_scale); + XFree (transform_attributes); XRRFreeCrtcInfo (xrandr_crtc); crtcs = g_list_append (crtcs, crtc_xrandr); } + if (has_transform && dpi_scale == 1 && + meta_monitor_manager_get_default_layout_mode (monitor_manager) == + META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + dpi_scale = + ceilf (meta_monitor_manager_get_maximum_crtc_scale (monitor_manager)); + + if (dpi_scale > 1) + { + for (l = crtcs; l; l = l->next) + { + MetaCrtc *crtc = l->data; + const MetaCrtcConfig *crtc_config = meta_crtc_get_config (crtc); + + if (!crtc_config) + continue; + + meta_crtc_set_config_scale (crtc, crtc_config->scale * dpi_scale); + } + } + } + meta_gpu_take_crtcs (gpu, crtcs); primary_output = XRRGetOutputPrimary (xdisplay, @@ -252,6 +309,8 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, GList *outputs = NULL; GList *modes = NULL; GList *crtcs = NULL; + gboolean has_transform; + int dpi_scale = 1; if (!meta_monitor_manager_xrandr_has_randr (monitor_manager_xrandr)) return read_current_fallback (gpu_xrandr, monitor_manager_xrandr); diff --git a/src/backends/x11/meta-gpu-xrandr.h b/src/backends/x11/meta-gpu-xrandr.h index 2086f86..a1f3b48 100644 --- a/src/backends/x11/meta-gpu-xrandr.h +++ b/src/backends/x11/meta-gpu-xrandr.h @@ -33,6 +33,10 @@ G_DECLARE_FINAL_TYPE (MetaGpuXrandr, meta_gpu_xrandr, META, GPU_XRANDR, MetaGpu) XRRScreenResources * meta_gpu_xrandr_get_resources (MetaGpuXrandr *gpu_xrandr); +void meta_gpu_xrandr_get_min_screen_size (MetaGpuXrandr *gpu_xrandr, + int *min_width, + int *min_height); + void meta_gpu_xrandr_get_max_screen_size (MetaGpuXrandr *gpu_xrandr, int *max_width, int *max_height); diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c index 489a9b4..f2ddbfe 100644 --- a/src/backends/x11/meta-monitor-manager-xrandr.c +++ b/src/backends/x11/meta-monitor-manager-xrandr.c @@ -36,6 +36,7 @@ * and udev. */ +#include "backends/meta-backend-types.h" #include "config.h" #include "backends/x11/meta-monitor-manager-xrandr.h" @@ -64,6 +65,9 @@ * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c * for the reasoning */ #define DPI_FALLBACK 96.0 +#define RANDR_VERSION_FORMAT(major, minor) ((major * 100) + minor) +#define RANDR_TILING_MIN_VERSION RANDR_VERSION_FORMAT (1, 5) +#define RANDR_TRANSFORM_MIN_VERSION RANDR_VERSION_FORMAT (1, 3) struct _MetaMonitorManagerXrandr { @@ -81,16 +81,16 @@ struct _MetaMonitorManagerXrandr guint logind_signal_sub_id; gboolean has_randr; - gboolean has_randr15; + int randr_version; xcb_timestamp_t last_xrandr_set_timestamp; GHashTable *tiled_monitor_atoms; - float *supported_scales; - int n_supported_scales; }; +static MetaGpu * meta_monitor_manager_xrandr_get_gpu (MetaMonitorManagerXrandr *manager_xrandr); + struct _MetaMonitorManagerXrandrClass { MetaMonitorManagerClass parent_class; @@ -119,10 +119,10 @@ meta_monitor_manager_xrandr_has_randr (MetaMonitorManagerXrandr *manager_xrandr) return manager_xrandr->has_randr; } -gboolean -meta_monitor_manager_xrandr_has_randr15 (MetaMonitorManagerXrandr *manager_xrandr) +uint32_t +meta_monitor_manager_xrandr_get_config_timestamp (MetaMonitorManagerXrandr *manager_xrandr) { - return manager_xrandr->has_randr15; + return manager_xrandr->last_xrandr_set_timestamp; } static GBytes * @@ -187,6 +191,81 @@ meta_monitor_manager_xrandr_set_power_save_mode (MetaMonitorManager *manager, DPMSSetTimeouts (manager_xrandr->xdisplay, 0, 0, 0); } +static void +meta_monitor_manager_xrandr_update_screen_size (MetaMonitorManagerXrandr *manager_xrandr, + int width, + int height, + float scale) +{ + MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); + MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); + xcb_connection_t *xcb_conn; + xcb_generic_error_t *xcb_error; + xcb_void_cookie_t xcb_cookie; + Screen *screen; + int min_width; + int min_height; + int max_width; + int max_height; + int width_mm; + int height_mm; + + g_assert (width > 0 && height > 0 && scale > 0); + + if (manager->screen_width == width && manager->screen_height == height) + return; + + screen = ScreenOfDisplay (manager_xrandr->xdisplay, + DefaultScreen (manager_xrandr->xdisplay)); + meta_gpu_xrandr_get_min_screen_size (META_GPU_XRANDR (gpu), + &min_width, &min_height); + meta_gpu_xrandr_get_max_screen_size (META_GPU_XRANDR (gpu), + &max_width, &max_height); + width = MIN (MAX (min_width, width), max_width); + height = MIN (MAX (min_height, height), max_height); + + /* The 'physical size' of an X screen is meaningless if that screen can + * consist of many monitors. So just pick a size that make the dpi 96. + * + * Firefox and Evince apparently believe what X tells them. + */ + width_mm = (width / (DPI_FALLBACK * scale)) * 25.4 + 0.5; + height_mm = (height / (DPI_FALLBACK * scale)) * 25.4 + 0.5; + + if (width == WidthOfScreen (screen) && height == HeightOfScreen (screen) && + width_mm == WidthMMOfScreen (screen) && height_mm == HeightMMOfScreen (screen)) + return; + + xcb_conn = XGetXCBConnection (manager_xrandr->xdisplay); + + xcb_grab_server (xcb_conn); + + /* Some drivers (nvidia I look at you!) might no advertise some CRTCs, so in + * such case, we may ignore X errors here */ + xcb_cookie = xcb_randr_set_screen_size_checked (xcb_conn, + DefaultRootWindow (manager_xrandr->xdisplay), + width, height, + width_mm, height_mm); + xcb_error = xcb_request_check (xcb_conn, xcb_cookie); + if (!xcb_error) + { + manager->screen_width = width; + manager->screen_height = height; + } + else + { + gchar buf[64]; + + XGetErrorText (manager_xrandr->xdisplay, xcb_error->error_code, buf, + sizeof (buf) - 1); + g_warning ("Impossible to resize screen at size %dx%d, error id %u: %s", + width, height, xcb_error->error_code, buf); + g_clear_pointer (&xcb_error, free); + } + + xcb_ungrab_server (xcb_conn); +} + static xcb_randr_rotation_t meta_monitor_transform_to_xrandr (MetaMonitorTransform transform) { @@ -242,13 +321,50 @@ xrandr_set_crtc_config (MetaMonitorManagerXrandr *manager_xrandr, return TRUE; } +static float +get_maximum_crtc_assignments_scale (MetaCrtcAssignment **crtc_assignments, + unsigned int n_crtc_assignments) +{ + float max_scale = 1.0f; + unsigned int i; + + for (i = 0; i < n_crtc_assignments; i++) + { + MetaCrtcAssignment *crtc_assignment = crtc_assignments[i]; + + if (crtc_assignment->mode) + max_scale = MAX (max_scale, crtc_assignment->scale); + } + + return max_scale; +} + static gboolean -is_crtc_assignment_changed (MetaCrtc *crtc, +is_crtc_assignment_changed (MetaMonitorManager *monitor_manager, + MetaCrtc *crtc, MetaCrtcAssignment **crtc_assignments, - unsigned int n_crtc_assignments) + unsigned int n_crtc_assignments, + gboolean *weak_change) { + MetaLogicalMonitorLayoutMode layout_mode; + gboolean have_scaling; + float max_crtc_scale = 1.0f; + float max_req_scale = 1.0f; unsigned int i; + layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + have_scaling = meta_monitor_manager_get_capabilities (monitor_manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; + + if (have_scaling && + layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + max_crtc_scale = + meta_monitor_manager_get_maximum_crtc_scale (monitor_manager); + max_req_scale = + get_maximum_crtc_assignments_scale (crtc_assignments, n_crtc_assignments); + } + for (i = 0; i < n_crtc_assignments; i++) { MetaCrtcAssignment *crtc_assignment = crtc_assignments[i]; @@ -256,8 +372,44 @@ is_crtc_assignment_changed (MetaCrtc *crtc, if (crtc_assignment->crtc != crtc) continue; - return meta_crtc_xrandr_is_assignment_changed (META_CRTC_XRANDR (crtc), - crtc_assignment); + if (meta_crtc_xrandr_is_assignment_changed (META_CRTC_XRANDR (crtc), + crtc_assignment)) + return TRUE; + + if (have_scaling) + { + const MetaCrtcConfig *crtc_config = meta_crtc_get_config (crtc); + float crtc_scale = crtc_config ? crtc_config->scale : 1.0f; + float req_output_scale = crtc_assignment->scale; + + if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL) + { + if (fmodf (crtc_scale, 1.0) == 0.0f) + { + *weak_change = fabsf (crtc_scale - req_output_scale) > 0.001; + return FALSE; + } + } + else if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + /* In scale ui-down mode we need to check if the actual output + * scale that will be applied to the crtc has actually changed + * from the current value, so we need to compare the current crtc + * scale with the scale that will be applied taking care of the + * UI scale (max crtc scale) and of the requested maximum scale. + * If we don't do this, we'd try to call randr calls which won't + * ever trigger a RRScreenChangeNotify, as no actual change is + * needed, and thus we won't ever emit a monitors-changed signal. + */ + crtc_scale /= ceilf (max_crtc_scale); + req_output_scale /= ceilf (max_req_scale); + } + + if (fabsf (crtc_scale - req_output_scale) > 0.001) + return TRUE; + } + + return FALSE; } return !!meta_crtc_xrandr_get_current_mode (META_CRTC_XRANDR (crtc)); @@ -333,7 +485,8 @@ is_assignments_changed (MetaMonitorManager *manager, MetaCrtcAssignment **crtc_assignments, unsigned int n_crtc_assignments, MetaOutputAssignment **output_assignments, - unsigned int n_output_assignments) + unsigned int n_output_assignments, + gboolean *weak_change) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); @@ -344,7 +497,9 @@ is_assignments_changed (MetaMonitorManager *manager, { MetaCrtc *crtc = l->data; - if (is_crtc_assignment_changed (crtc, crtc_assignments, n_crtc_assignments)) + if (is_crtc_assignment_changed (manager, crtc, + crtc_assignments, n_crtc_assignments, + weak_change)) return TRUE; } @@ -360,6 +515,32 @@ is_assignments_changed (MetaMonitorManager *manager, return TRUE; } + if (meta_monitor_manager_get_default_layout_mode (manager) == + META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + /* If nothing has changed, ensure that the crtc logical scaling matches + * with the requested one, as in case of global UI logical layout we might + * assume that it is in fact equal, while it's techincally different. + * Not doing this would then cause a wrong computation of the max crtc + * scale and thus of the UI scaling. */ + for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) + { + MetaCrtc *crtc = l->data; + unsigned int i; + + for (i = 0; i < n_crtc_assignments; i++) + { + MetaCrtcAssignment *crtc_assignment = crtc_assignments[i]; + + if (crtc_assignment->crtc == crtc) + { + meta_crtc_set_config_scale (crtc, crtc_assignment->scale); + break; + } + } + } + } + return FALSE; } @@ -375,31 +556,55 @@ apply_crtc_assignments (MetaMonitorManager *manager, MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); g_autoptr (GList) to_configure_outputs = NULL; g_autoptr (GList) to_disable_crtcs = NULL; - unsigned i; + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaSettings *settings = meta_backend_get_settings (backend); + MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); + unsigned i, valid_crtcs; GList *l; - int width, height, width_mm, height_mm; + int width, height; + float max_scale; + float avg_screen_scale; + gboolean have_scaling; to_configure_outputs = g_list_copy (meta_gpu_get_outputs (gpu)); to_disable_crtcs = g_list_copy (meta_gpu_get_crtcs (gpu)); XGrabServer (manager_xrandr->xdisplay); - /* First compute the new size of the screen (framebuffer) */ + have_scaling = meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; + + /* Compute the new size of the screen (framebuffer) */ + max_scale = get_maximum_crtc_assignments_scale (crtcs, n_crtcs); width = 0; height = 0; + avg_screen_scale = 0; + valid_crtcs = 0; for (i = 0; i < n_crtcs; i++) { MetaCrtcAssignment *crtc_assignment = crtcs[i]; MetaCrtc *crtc = crtc_assignment->crtc; + float scale = 1.0f; if (crtc_assignment->mode == NULL) continue; to_disable_crtcs = g_list_remove (to_disable_crtcs, crtc); - width = MAX (width, (int) roundf (crtc_assignment->layout.origin.x + - crtc_assignment->layout.size.width)); - height = MAX (height, (int) roundf (crtc_assignment->layout.origin.y + - crtc_assignment->layout.size.height)); + if (have_scaling && scale_mode == META_X11_SCALE_MODE_UI_DOWN) + { + scale = (ceilf (max_scale) / crtc_assignment->scale) * + crtc_assignment->scale; + } + + width = MAX (width, + (int) roundf (crtc_assignment->layout.origin.x + + crtc_assignment->layout.size.width * scale)); + height = MAX (height, + (int) roundf (crtc_assignment->layout.origin.y + + crtc_assignment->layout.size.height * scale)); + + avg_screen_scale += (crtc_assignment->scale - avg_screen_scale) / + (float) (++valid_crtcs); } /* Second disable all newly disabled CRTCs, or CRTCs that in the previous @@ -433,6 +638,10 @@ apply_crtc_assignments (MetaMonitorManager *manager, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); + if (have_scaling) + meta_crtc_xrandr_set_scale (crtc, + (xcb_randr_crtc_t) meta_crtc_get_id (crtc), + 1.0f); meta_crtc_unset_config (crtc); } @@ -453,6 +662,10 @@ apply_crtc_assignments (MetaMonitorManager *manager, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); + if (have_scaling) + meta_crtc_xrandr_set_scale (crtc, + (xcb_randr_crtc_t) meta_crtc_get_id (crtc), + 1.0f); meta_crtc_unset_config (crtc); } @@ -460,17 +673,12 @@ apply_crtc_assignments (MetaMonitorManager *manager, if (!n_crtcs) goto out; - g_assert (width > 0 && height > 0); - /* The 'physical size' of an X screen is meaningless if that screen - * can consist of many monitors. So just pick a size that make the - * dpi 96. - * - * Firefox and Evince apparently believe what X tells them. - */ - width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5; - height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5; - XRRSetScreenSize (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), - width, height, width_mm, height_mm); + if (width > manager->screen_width || height > manager->screen_height) + { + meta_monitor_manager_xrandr_update_screen_size (manager_xrandr, + width, height, + avg_screen_scale); + } for (i = 0; i < n_crtcs; i++) { @@ -486,12 +694,21 @@ apply_crtc_assignments (MetaMonitorManager *manager, int x, y; xcb_randr_rotation_t rotation; xcb_randr_mode_t mode; + float scale = 1.0f; crtc_mode = crtc_assignment->mode; n_output_ids = crtc_assignment->outputs->len; output_ids = g_new (xcb_randr_output_t, n_output_ids); + if (have_scaling && scale_mode != META_X11_SCALE_MODE_NONE) + { + scale = crtc_assignment->scale; + + if (scale_mode == META_X11_SCALE_MODE_UI_DOWN) + scale /= ceilf (max_scale); + } + for (j = 0; j < n_output_ids; j++) { MetaOutput *output; @@ -516,6 +733,14 @@ apply_crtc_assignments (MetaMonitorManager *manager, rotation = meta_monitor_transform_to_xrandr (crtc_assignment->transform); mode = meta_crtc_mode_get_id (crtc_mode); + + if (have_scaling && + !meta_crtc_xrandr_set_scale (crtc, crtc_id, scale)) + { + meta_warning ("Scalig CRTC %d at %f failed\n", + (unsigned) crtc_id, scale); + } + if (!xrandr_set_crtc_config (manager_xrandr, crtc, save_timestamp, @@ -544,6 +769,20 @@ apply_crtc_assignments (MetaMonitorManager *manager, &crtc_assignment->layout, crtc_mode, crtc_assignment->transform); + meta_crtc_set_config_scale (crtc, crtc_assignment->scale); + + if (have_scaling && scale_mode == META_X11_SCALE_MODE_UI_DOWN) + { + const MetaCrtcConfig *crtc_config = meta_crtc_get_config (crtc); + graphene_size_t *crtc_size = + (graphene_size_t *) &crtc_config->layout.size; + + scale = (ceilf (max_scale) / crtc_assignment->scale) * + crtc_assignment->scale; + + crtc_size->width = roundf (crtc_size->width * scale); + crtc_size->height = roundf (crtc_size->height * scale); + } } } @@ -559,6 +798,13 @@ apply_crtc_assignments (MetaMonitorManager *manager, (GFunc) meta_output_unassign_crtc, NULL); + if (width > 0 && height > 0) + { + meta_monitor_manager_xrandr_update_screen_size (manager_xrandr, + width, height, + avg_screen_scale); + } + out: XUngrabServer (manager_xrandr->xdisplay); XFlush (manager_xrandr->xdisplay); @@ -585,14 +831,88 @@ meta_monitor_manager_xrandr_ensure_initial_config (MetaMonitorManager *manager) } static void -meta_monitor_manager_xrandr_rebuild_derived (MetaMonitorManager *manager, - MetaMonitorsConfig *config) +meta_monitor_manager_xrandr_update_screen_size_derived (MetaMonitorManager *manager, + MetaMonitorsConfig *config) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaSettings *settings = meta_backend_get_settings (backend); + MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); + int screen_width = 0; + int screen_height = 0; + unsigned n_crtcs = 0; + float average_scale = 0; + gboolean have_scaling; + GList *l; + + have_scaling = meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; + + /* Compute the new size of the screen (framebuffer) */ + for (l = manager->monitors; l != NULL; l = l->next) + { + MetaMonitor *monitor = l->data; + MetaOutput *output = meta_monitor_get_main_output (monitor); + MetaCrtc *crtc = meta_output_get_assigned_crtc (output); + const MetaCrtcConfig *crtc_config; + const graphene_rect_t *crtc_layout; + float scale = 1.0f; + + if (!crtc) + continue; - g_clear_pointer (&manager_xrandr->supported_scales, g_free); - meta_monitor_manager_rebuild_derived (manager, config); + crtc_config = meta_crtc_get_config (crtc); + + if (!crtc_config) + continue; + + if (!have_scaling || scale_mode != META_X11_SCALE_MODE_UI_DOWN) + { + /* When scaling up we should not reduce the screen size, or X will + * fail miserably, while we must do it when scaling down, in order to + * increase the available screen area we can use. */ + scale = crtc_config->scale > 1.0f ? crtc_config->scale : 1.0f; + } + + /* When computing the screen size from the crtc rects we don't have to + * use inverted values when monitors are rotated, because this is already + * taken in account in the crtc rectangles */ + crtc_layout = &crtc_config->layout; + screen_width = MAX (screen_width, crtc_layout->origin.x + + roundf (crtc_layout->size.width * scale)); + screen_height = MAX (screen_height, crtc_layout->origin.y + + roundf (crtc_layout->size.height * scale)); + ++n_crtcs; + + /* This value isn't completely exact, since it doesn't take care of the + * actual crtc sizes, however, since w're going to use this only to set + * the MM size of the screen, and given that this value is just an + * estimation, we don't need to be super precise. */ + average_scale += (crtc_config->scale - average_scale) / (float) n_crtcs; + } + + if (screen_width > 0 && screen_height > 0) + { + meta_monitor_manager_xrandr_update_screen_size (manager_xrandr, + screen_width, + screen_height, + average_scale); + } +} + +static void +maybe_update_ui_scaling_factor (MetaMonitorManager *manager, + MetaMonitorsConfig *config) +{ + if (config->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL || + manager->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) + { + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaSettings *settings = meta_backend_get_settings (backend); + + meta_settings_update_ui_scaling_factor (settings); + } } static gboolean @@ -609,7 +929,7 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *mana if (!manager->in_init) apply_crtc_assignments (manager, TRUE, NULL, 0, NULL, 0); - meta_monitor_manager_xrandr_rebuild_derived (manager, NULL); + meta_monitor_manager_rebuild_derived (manager, NULL); return TRUE; } @@ -621,6 +941,8 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *mana if (method != META_MONITORS_CONFIG_METHOD_VERIFY) { + gboolean weak_change = FALSE; + /* * If the assignment has not changed, we won't get any notification about * any new configuration from the X server; but we still need to update @@ -628,12 +950,16 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *mana * have changed locally, such as the logical monitors scale. This means we * must check that our new assignment actually changes anything, otherwise * just update the logical state. + * If we record a weak change it means that only UI scaling needs to be + * updated and so that we don't have to reconfigure the CRTCs, but still + * need to update the logical state. */ if (is_assignments_changed (manager, (MetaCrtcAssignment **) crtc_assignments->pdata, crtc_assignments->len, (MetaOutputAssignment **) output_assignments->pdata, - output_assignments->len)) + output_assignments->len, + &weak_change)) { apply_crtc_assignments (manager, TRUE, @@ -641,10 +967,14 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *mana crtc_assignments->len, (MetaOutputAssignment **) output_assignments->pdata, output_assignments->len); + maybe_update_ui_scaling_factor (manager, config); } else { - meta_monitor_manager_xrandr_rebuild_derived (manager, config); + if (weak_change) + maybe_update_ui_scaling_factor (manager, config); + + meta_monitor_manager_rebuild_derived (manager, config); } } @@ -777,7 +1107,8 @@ meta_monitor_manager_xrandr_tiled_monitor_added (MetaMonitorManager *manager, GList *l; int i; - if (manager_xrandr->has_randr15 == FALSE) + if (!(meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_TILING)) return; product = meta_monitor_get_product (monitor); @@ -826,7 +1157,8 @@ meta_monitor_manager_xrandr_tiled_monitor_removed (MetaMonitorManager *manager, int monitor_count; - if (manager_xrandr->has_randr15 == FALSE) + if (!(meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_TILING)) return; monitor_xrandr_data = meta_monitor_xrandr_data_from_monitor (monitor); @@ -844,10 +1176,12 @@ meta_monitor_manager_xrandr_tiled_monitor_removed (MetaMonitorManager *manager, static void meta_monitor_manager_xrandr_init_monitors (MetaMonitorManagerXrandr *manager_xrandr) { + MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); XRRMonitorInfo *m; int n, i; - if (manager_xrandr->has_randr15 == FALSE) + if (!(meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_TILING)) return; /* delete any tiled monitors setup, as mutter will want to recreate @@ -879,83 +1213,26 @@ meta_monitor_manager_xrandr_is_transform_handled (MetaMonitorManager *manager, return TRUE; } -static float -meta_monitor_manager_xrandr_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) -{ - return meta_monitor_calculate_mode_scale (monitor, monitor_mode); -} - -static void -add_supported_scale (GArray *supported_scales, - float scale) +static MetaMonitorScalesConstraint +get_scale_constraints (MetaMonitorManager *manager) { - unsigned int i; + MetaMonitorScalesConstraint constraints = 0; - for (i = 0; i < supported_scales->len; i++) - { - float supported_scale = g_array_index (supported_scales, float, i); - - if (scale == supported_scale) - return; - } - - g_array_append_val (supported_scales, scale); -} - -static int -compare_scales (gconstpointer a, - gconstpointer b) -{ - float f = *(float *) a - *(float *) b; + if (meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) + constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; - if (f < 0) - return -1; - if (f > 0) - return 1; - return 0; + return constraints; } -static void -ensure_supported_monitor_scales (MetaMonitorManager *manager) +static float +meta_monitor_manager_xrandr_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode) { - MetaMonitorManagerXrandr *manager_xrandr = - META_MONITOR_MANAGER_XRANDR (manager); - MetaMonitorScalesConstraint constraints; - GList *l; - GArray *supported_scales; - - if (manager_xrandr->supported_scales) - return; - - constraints = META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; - supported_scales = g_array_new (FALSE, FALSE, sizeof (float)); - - for (l = manager->monitors; l; l = l->next) - { - MetaMonitor *monitor = l->data; - MetaMonitorMode *monitor_mode; - float *monitor_scales; - int n_monitor_scales; - int i; - - monitor_mode = meta_monitor_get_preferred_mode (monitor); - monitor_scales = - meta_monitor_calculate_supported_scales (monitor, - monitor_mode, - constraints, - &n_monitor_scales); - - for (i = 0; i < n_monitor_scales; i++) - add_supported_scale (supported_scales, monitor_scales[i]); - g_array_sort (supported_scales, compare_scales); - g_free (monitor_scales); - } - - manager_xrandr->supported_scales = (float *) supported_scales->data; - manager_xrandr->n_supported_scales = supported_scales->len; - g_array_free (supported_scales, FALSE); + return meta_monitor_calculate_mode_scale (monitor, monitor_mode, + get_scale_constraints (manager)); } static float * @@ -996,9 +1291,41 @@ meta_monitor_manager_xrandr_get_max_screen_size (MetaMonitorManager *manager, return TRUE; } +static void +scale_mode_changed (MetaSettings *settings, + MetaMonitorManager *manager) +{ + if (!(meta_monitor_manager_get_capabilities (manager) & + META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) + return; + + if (!meta_settings_is_experimental_feature_enabled (settings, + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) + return; + + meta_monitor_manager_reconfigure (manager); + meta_settings_update_ui_scaling_factor (settings); +} + static MetaLogicalMonitorLayoutMode meta_monitor_manager_xrandr_get_default_layout_mode (MetaMonitorManager *manager) { + MetaMonitorManagerCapability capabilities = + meta_monitor_manager_get_capabilities (manager); + + if ((capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING) && + (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) + { + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaSettings *settings = meta_backend_get_settings (backend); + MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); + + if (scale_mode == META_X11_SCALE_MODE_UI_DOWN) + return META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL; + else if (scale_mode == META_X11_SCALE_MODE_UP) + return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; + } + return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } @@ -1017,6 +1344,7 @@ meta_monitor_manager_xrandr_constructed (GObject *object) MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + MetaSettings *settings = meta_backend_get_settings (backend); manager_xrandr->xdisplay = meta_backend_x11_get_xdisplay (backend_x11); @@ -1037,19 +1365,19 @@ meta_monitor_manager_xrandr_constructed (GObject *object) | RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask); - manager_xrandr->has_randr15 = FALSE; XRRQueryVersion (manager_xrandr->xdisplay, &major_version, &minor_version); - if (major_version > 1 || - (major_version == 1 && - minor_version >= 5)) - { - manager_xrandr->has_randr15 = TRUE; - manager_xrandr->tiled_monitor_atoms = g_hash_table_new (NULL, NULL); - } + manager_xrandr->randr_version = RANDR_VERSION_FORMAT (major_version, + minor_version); + if (manager_xrandr->randr_version >= RANDR_TILING_MIN_VERSION) + manager_xrandr->tiled_monitor_atoms = g_hash_table_new (NULL, NULL); + meta_monitor_manager_xrandr_init_monitors (manager_xrandr); } + g_signal_connect_object (settings, "x11-scale-mode-changed", + G_CALLBACK (scale_mode_changed), manager_xrandr, 0); + G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->constructed (object); } @@ -1082,6 +1409,7 @@ meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass) manager_class->read_current_state = meta_monitor_manager_xrandr_read_current_state; manager_class->ensure_initial_config = meta_monitor_manager_xrandr_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_xrandr_apply_monitors_config; + manager_class->update_screen_size_derived = meta_monitor_manager_xrandr_update_screen_size_derived; manager_class->set_power_save_mode = meta_monitor_manager_xrandr_set_power_save_mode; manager_class->change_backlight = meta_monitor_manager_xrandr_change_backlight; manager_class->get_crtc_gamma = meta_monitor_manager_xrandr_get_crtc_gamma; @@ -1275,21 +1275,38 @@ meta_monitor_manager_xrandr_calculate_supported_scales (MetaMonitorManager MetaMonitorMode *monitor_mode, int *n_supported_scales) { - MetaMonitorManagerXrandr *manager_xrandr = - META_MONITOR_MANAGER_XRANDR (manager); - - ensure_supported_monitor_scales (manager); - - *n_supported_scales = manager_xrandr->n_supported_scales; - return g_memdup2 (manager_xrandr->supported_scales, - manager_xrandr->n_supported_scales * sizeof (float)); + return meta_monitor_calculate_supported_scales (monitor, monitor_mode, + get_scale_constraints (manager), + n_supported_scales); } static MetaMonitorManagerCapability meta_monitor_manager_xrandr_get_capabilities (MetaMonitorManager *manager) { - return (META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED | - META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT); + MetaMonitorManagerCapability capabilities; + MetaMonitorManagerXrandr *xrandr_manager = META_MONITOR_MANAGER_XRANDR (manager); + MetaBackend *backend = meta_monitor_manager_get_backend (manager); + MetaSettings *settings = meta_backend_get_settings (backend); + + capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; + + if (xrandr_manager->randr_version >= RANDR_TILING_MIN_VERSION) + capabilities |= META_MONITOR_MANAGER_CAPABILITY_TILING; + + if (xrandr_manager->randr_version >= RANDR_TRANSFORM_MIN_VERSION) + capabilities |= META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; + + if (meta_settings_is_experimental_feature_enabled (settings, + META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) + { + capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; + } + else + { + capabilities |= META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED; + } + + return capabilities; } static gboolean @@ -1465,7 +1482,7 @@ meta_monitor_manager_xrandr_finalize (GObject *object) MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object); g_hash_table_destroy (manager_xrandr->tiled_monitor_atoms); - g_free (manager_xrandr->supported_scales); + if (manager_xrandr->logind_watch_id > 0) g_bus_unwatch_name (manager_xrandr->logind_watch_id); @@ -1575,7 +1592,7 @@ meta_monitor_manager_xrandr_update (MetaMonitorManagerXrandr *manager_xrandr) config = NULL; } - meta_monitor_manager_xrandr_rebuild_derived (manager, config); + meta_monitor_manager_rebuild_derived (manager, config); } } diff --git a/src/backends/x11/meta-monitor-manager-xrandr.h b/src/backends/x11/meta-monitor-manager-xrandr.h index dc75134..aa385f9 100644 --- a/src/backends/x11/meta-monitor-manager-xrandr.h +++ b/src/backends/x11/meta-monitor-manager-xrandr.h @@ -35,11 +35,13 @@ Display * meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *ma gboolean meta_monitor_manager_xrandr_has_randr (MetaMonitorManagerXrandr *manager_xrandr); -gboolean meta_monitor_manager_xrandr_has_randr15 (MetaMonitorManagerXrandr *manager_xrandr); - gboolean meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager, XEvent *event); + +uint32_t meta_monitor_manager_xrandr_get_config_timestamp (MetaMonitorManagerXrandr *manager); void meta_monitor_manager_xrandr_update_dpms_state (MetaMonitorManagerXrandr *manager_xrandr); + + #endif /* META_MONITOR_MANAGER_XRANDR_H */ diff --git a/src/backends/x11/meta-output-xrandr.c b/src/backends/x11/meta-output-xrandr.c index 7265624..1d3da34 100644 --- a/src/backends/x11/meta-output-xrandr.c +++ b/src/backends/x11/meta-output-xrandr.c @@ -915,7 +915,8 @@ meta_output_xrandr_new (MetaGpuXrandr *gpu_xrandr, output_info->height_mm = xrandr_output->mm_height; } - if (meta_monitor_manager_xrandr_has_randr15 (monitor_manager_xrandr)) + if ((meta_monitor_manager_get_capabilities (monitor_manager) & + META_MONITOR_MANAGER_CAPABILITY_TILING)) output_info_init_tile_info (output_info, xdisplay, output_id); output_info_init_modes (output_info, gpu, xrandr_output); output_info_init_crtcs (output_info, gpu, xrandr_output); diff --git a/src/compositor/meta-compositor-x11.c b/src/compositor/meta-compositor-x11.c index 1d0ba4c..70017ca 100644 --- a/src/compositor/meta-compositor-x11.c +++ b/src/compositor/meta-compositor-x11.c @@ -31,6 +31,8 @@ #include "compositor/meta-sync-ring.h" #include "compositor/meta-window-actor-x11.h" #include "core/display-private.h" +#include "core/window-private.h" +#include "core/window-private.h" #include "core/stack-tracker.h" #include "core/stereo.h" #include "x11/meta-x11-display-private.h" @@ -52,6 +54,8 @@ struct _MetaCompositorX11 gboolean xserver_uses_monotonic_clock; int64_t xserver_time_query_time_us; int64_t xserver_time_offset_us; + + gboolean randr_scale_disabled; int glx_opcode; gboolean stereo_tree_ext; @@ -267,20 +270,91 @@ shape_cow_for_window (MetaCompositorX11 *compositor_x11, } } +static void +on_redirected_monitor_changed (MetaWindow *window, + int old_monitor, + MetaCompositorX11 *compositor_x11) +{ + MetaBackend *backend = meta_get_backend (); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + + if (old_monitor >= 0 && window->monitor && + window->monitor->number != old_monitor) + { + g_signal_handlers_block_by_func (window, + on_redirected_monitor_changed, + compositor_x11); + + if (!compositor_x11->randr_scale_disabled) + { + compositor_x11->randr_scale_disabled = + meta_monitor_manager_disable_scale_for_monitor (monitor_manager, + window->monitor); + } + + g_signal_handlers_unblock_by_func (window, + on_redirected_monitor_changed, + compositor_x11); + } + else + shape_cow_for_window (META_COMPOSITOR_X11 (compositor_x11), window); +} + +static MetaWindow * +get_unredirectable_window (MetaCompositorX11 *compositor_x11) +{ + MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); + MetaWindowActor *window_actor; + MetaWindowActorX11 *window_actor_x11; + + window_actor = meta_compositor_get_top_window_actor (compositor); + if (!window_actor) + return NULL; + + window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); + if (!meta_window_actor_x11_should_unredirect (window_actor_x11)) + return NULL; + + return meta_window_actor_get_meta_window (window_actor); +} + static void set_unredirected_window (MetaCompositorX11 *compositor_x11, MetaWindow *window) { + MetaBackend *backend; + MetaMonitorManager *monitor_manager; MetaWindow *prev_unredirected_window = compositor_x11->unredirected_window; if (prev_unredirected_window == window) - return; + { + if (!window && compositor_x11->randr_scale_disabled && + !get_unredirectable_window (compositor_x11)) + { + backend = meta_get_backend (); + monitor_manager = meta_backend_get_monitor_manager (backend); + + compositor_x11->randr_scale_disabled = + meta_monitor_manager_disable_scale_for_monitor (monitor_manager, + NULL); + } + + return; + } + + backend = meta_get_backend (); + monitor_manager = meta_backend_get_monitor_manager (backend); if (prev_unredirected_window) { MetaWindowActor *window_actor; MetaWindowActorX11 *window_actor_x11; + g_signal_handlers_disconnect_by_func (prev_unredirected_window, + on_redirected_monitor_changed, + compositor_x11); + window_actor = meta_window_actor_from_window (prev_unredirected_window); window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); meta_window_actor_x11_set_unredirected (window_actor_x11, FALSE); @@ -294,6 +368,17 @@ set_unredirected_window (MetaCompositorX11 *compositor_x11, MetaWindowActor *window_actor; MetaWindowActorX11 *window_actor_x11; + if (!compositor_x11->randr_scale_disabled) + { + compositor_x11->randr_scale_disabled = + meta_monitor_manager_disable_scale_for_monitor (monitor_manager, + window->monitor); + } + + g_signal_connect_object (window, "monitor-changed", + G_CALLBACK (on_redirected_monitor_changed), + compositor_x11, 0); + window_actor = meta_window_actor_from_window (window); window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); meta_window_actor_x11_set_unredirected (window_actor_x11, TRUE); @@ -305,21 +390,11 @@ maybe_unredirect_top_window (MetaCompositorX11 *compositor_x11) { MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); MetaWindow *window_to_unredirect = NULL; - MetaWindowActor *window_actor; - MetaWindowActorX11 *window_actor_x11; if (meta_compositor_is_unredirect_inhibited (compositor)) goto out; - window_actor = meta_compositor_get_top_window_actor (compositor); - if (!window_actor) - goto out; - - window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); - if (!meta_window_actor_x11_should_unredirect (window_actor_x11)) - goto out; - - window_to_unredirect = meta_window_actor_get_meta_window (window_actor); + window_to_unredirect = get_unredirectable_window (compositor_x11); out: set_unredirected_window (compositor_x11, window_to_unredirect); diff --git a/src/core/boxes-private.h b/src/core/boxes-private.h index d398164..482b0e5 100644 --- a/src/core/boxes-private.h +++ b/src/core/boxes-private.h @@ -158,6 +158,10 @@ gboolean meta_rectangle_overlaps_with_region ( const GList *spanning_rects, const MetaRectangle *rect); +gboolean meta_rectangle_has_adjacent_in_region ( + const GList *spanning_rects, + const MetaRectangle *rect); + /* Make the rectangle small enough to fit into one of the spanning_rects, * but make it no smaller than min_size. */ diff --git a/src/core/boxes.c b/src/core/boxes.c index 9a9633e..afefba7 100644 --- a/src/core/boxes.c +++ b/src/core/boxes.c @@ -899,6 +899,27 @@ meta_rectangle_overlaps_with_region (const GList *spanning_rects, return overlaps; } +gboolean +meta_rectangle_has_adjacent_in_region (const GList *spanning_rects, + const MetaRectangle *rect) +{ + const GList *l; + + for (l = spanning_rects; l; l = l->next) + { + MetaRectangle *other = (MetaRectangle *) l->data; + + if (rect == other || meta_rectangle_equal (rect, other)) + continue; + + if (meta_rectangle_is_adjacent_to ((MetaRectangle *) rect, other)) + { + return TRUE; + } + } + + return FALSE; +} void meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects, diff --git a/src/core/window.c b/src/core/window.c index ea56f33..d2e7698 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -227,6 +227,7 @@ enum UNMANAGED, SIZE_CHANGED, POSITION_CHANGED, + MONITOR_CHANGED, SHOWN, LAST_SIGNAL @@ -683,6 +684,21 @@ meta_window_class_init (MetaWindowClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + /** + * MetaWindow::monitor-changed: + * @window: a #MetaWindow + * @old_monitor: the old monitor index or -1 if not known + * + * This is emitted when the window has changed monitor + */ + window_signals[MONITOR_CHANGED] = + g_signal_new ("monitor-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_INT); + /** * MetaWindow::shown: * @window: a #MetaWindow @@ -936,6 +952,9 @@ meta_window_main_monitor_changed (MetaWindow *window, { META_WINDOW_GET_CLASS (window)->main_monitor_changed (window, old); + g_signal_emit (window, window_signals[MONITOR_CHANGED], 0, + old ? old->number : -1); + if (old) g_signal_emit_by_name (window->display, "window-left-monitor", old->number, window); diff --git a/src/org.gnome.Mutter.DisplayConfig.xml b/src/org.gnome.Mutter.DisplayConfig.xml index 7522652..0843e3a 100644 --- a/src/org.gnome.Mutter.DisplayConfig.xml +++ b/src/org.gnome.Mutter.DisplayConfig.xml @@ -393,6 +393,11 @@ using the logical monitor scale. * 2 : physical - the dimension of a logical monitor is derived from the monitor modes associated with it. + * 3 : logical with ui scaling - the dimension of a logical monitor + is derived from the monitor modes associated with it, + then scaled using the logical monitor scale that is also + scaled by the global UI scaling (computed using the maximum + ceiled scaling value across the displays). * "supports-changing-layout-mode" (b): True if the layout mode can be changed. Absence of this means the layout mode cannot be changed. diff --git a/src/tests/meta-monitor-manager-test.c b/src/tests/meta-monitor-manager-test.c index 5a672c5..31e112a 100644 --- a/src/tests/meta-monitor-manager-test.c +++ b/src/tests/meta-monitor-manager-test.c @@ -290,9 +290,10 @@ meta_monitor_manager_test_is_transform_handled (MetaMonitorManager *manager, } static float -meta_monitor_manager_test_calculate_monitor_mode_scale (MetaMonitorManager *manager, - MetaMonitor *monitor, - MetaMonitorMode *monitor_mode) +meta_monitor_manager_test_calculate_monitor_mode_scale (MetaMonitorManager *manager, + MetaLogicalMonitorLayoutMode layout_mode, + MetaMonitor *monitor, + MetaMonitorMode *monitor_mode) { MetaOutput *output; MetaOutputTest *output_test; @@ -319,6 +320,7 @@ meta_monitor_manager_test_calculate_supported_scales (MetaMonitorManager switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: + case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; @@ -344,8 +346,9 @@ is_monitor_framebuffer_scaled (void) static MetaMonitorManagerCapability meta_monitor_manager_test_get_capabilities (MetaMonitorManager *manager) { - MetaMonitorManagerCapability capabilities = - META_MONITOR_MANAGER_CAPABILITY_NONE; + MetaMonitorManagerCapability capabilities; + + capabilities = META_MONITOR_MANAGER_CAPABILITY_TILING; if (is_monitor_framebuffer_scaled ()) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE;