diff --git a/src/backends/meta-monitor-manager-dummy.c b/src/backends/meta-monitor-manager-dummy.c index b0fbddca0..f5b993f24 100644 --- a/src/backends/meta-monitor-manager-dummy.c +++ b/src/backends/meta-monitor-manager-dummy.c @@ -499,9 +499,10 @@ update_screen_size (MetaMonitorManager *manager, } static gboolean -meta_monitor_manager_dummy_apply_monitors_config (MetaMonitorManager *manager, - MetaMonitorsConfig *config, - GError **error) +meta_monitor_manager_dummy_apply_monitors_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; @@ -519,6 +520,13 @@ meta_monitor_manager_dummy_apply_monitors_config (MetaMonitorManager *manager, error)) return FALSE; + if (method == META_MONITORS_CONFIG_METHOD_VERIFY) + { + g_ptr_array_free (crtc_infos, TRUE); + g_ptr_array_free (output_infos, TRUE); + return TRUE; + } + apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h index d5a98c20a..ade2c6049 100644 --- a/src/backends/meta-monitor-manager-private.h +++ b/src/backends/meta-monitor-manager-private.h @@ -74,6 +74,14 @@ typedef enum _MetaMonitorManagerCapability META_MONITOR_MANAGER_CAPABILITY_MIRRORING = (1 << 0) } MetaMonitorManagerCapability; +/* Equivalent to the 'method' enum in org.gnome.Mutter.DisplayConfig */ +typedef enum _MetaMonitorsConfigMethod +{ + META_MONITORS_CONFIG_METHOD_VERIFY = 0, + META_MONITORS_CONFIG_METHOD_TEMPORARY = 1, + META_MONITORS_CONFIG_METHOD_PERSISTENT = 2 +} MetaMonitorsConfigMethod; + typedef enum { META_MONITOR_TRANSFORM_NORMAL, @@ -316,9 +324,10 @@ struct _MetaMonitorManagerClass void (*ensure_initial_config) (MetaMonitorManager *); - gboolean (*apply_monitors_config) (MetaMonitorManager *, - MetaMonitorsConfig *, - GError **); + gboolean (*apply_monitors_config) (MetaMonitorManager *, + MetaMonitorsConfig *, + MetaMonitorsConfigMethod , + GError **); void (*apply_configuration) (MetaMonitorManager *, MetaCrtcInfo **, diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c index e60f93ae1..64a45b6e3 100644 --- a/src/backends/meta-monitor-manager.c +++ b/src/backends/meta-monitor-manager.c @@ -315,14 +315,15 @@ meta_monitor_manager_ensure_initial_config (MetaMonitorManager *manager) } static gboolean -meta_monitor_manager_apply_monitors_config (MetaMonitorManager *manager, - MetaMonitorsConfig *config, - GError **error) +meta_monitor_manager_apply_monitors_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); - return manager_class->apply_monitors_config (manager, config, error); + return manager_class->apply_monitors_config (manager, config, method, error); } gboolean @@ -359,6 +360,10 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) { MetaMonitorsConfig *config = NULL; GError *error = NULL; + gboolean use_stored_config; + MetaMonitorsConfigMethod method; + MetaMonitorsConfigMethod fallback_method = + META_MONITORS_CONFIG_METHOD_TEMPORARY; if (!manager->config_manager) { @@ -366,12 +371,20 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) return NULL; } - if (should_use_stored_config (manager)) + use_stored_config = should_use_stored_config (manager); + if (use_stored_config) + method = META_MONITORS_CONFIG_METHOD_PERSISTENT; + else + method = META_MONITORS_CONFIG_METHOD_TEMPORARY; + + if (use_stored_config) { config = meta_monitor_config_manager_get_stored (manager->config_manager); if (config) { - if (!meta_monitor_manager_apply_monitors_config (manager, config, + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + method, &error)) { config = NULL; @@ -390,7 +403,10 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) config = meta_monitor_config_manager_create_suggested (manager->config_manager); if (config) { - if (!meta_monitor_manager_apply_monitors_config (manager, config, &error)) + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + method, + &error)) { g_clear_object (&config); g_warning ("Failed to use suggested monitor configuration: %s", @@ -406,7 +422,10 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) config = meta_monitor_config_manager_create_linear (manager->config_manager); if (config) { - if (!meta_monitor_manager_apply_monitors_config (manager, config, &error)) + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + method, + &error)) { g_clear_object (&config); g_warning ("Failed to use linear monitor configuration: %s", @@ -422,7 +441,10 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) config = meta_monitor_config_manager_create_fallback (manager->config_manager); if (config) { - if (!meta_monitor_manager_apply_monitors_config (manager, config, &error)) + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + fallback_method, + &error)) { g_clear_object (&config); g_warning ("Failed to use fallback monitor configuration: %s", @@ -440,7 +462,10 @@ done: if (!config) { - meta_monitor_manager_apply_monitors_config (manager, NULL, &error); + meta_monitor_manager_apply_monitors_config (manager, + NULL, + fallback_method, + &error); return NULL; } @@ -1364,6 +1389,361 @@ meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, #undef LOGICAL_MONITOR_FORMAT #undef LOGICAL_MONITORS_FORMAT +static gboolean +meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, + float scale) +{ + float *supported_scales; + int n_supported_scales; + int i; + + meta_monitor_manager_get_supported_scales (manager, + &supported_scales, + &n_supported_scales); + for (i = 0; i < n_supported_scales; i++) + { + if (supported_scales[i] == scale) + return TRUE; + } + + return FALSE; +} + +static gboolean +meta_monitor_manager_is_config_applicable (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + GError **error) +{ + GList *l; + + for (l = config->logical_monitor_configs; l; l = l->next) + { + MetaLogicalMonitorConfig *logical_monitor_config = l->data; + float scale = logical_monitor_config->scale; + GList *k; + + if (!meta_monitor_manager_is_scale_supported (manager, scale)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Scale not supported by backend"); + return FALSE; + } + + for (k = logical_monitor_config->monitor_configs; k; k = k->next) + { + MetaMonitorConfig *monitor_config = k->data; + MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec; + MetaMonitorModeSpec *mode_spec = monitor_config->mode_spec; + MetaMonitor *monitor; + MetaMonitorMode *monitor_mode; + + monitor = meta_monitor_manager_get_monitor_from_spec (manager, + monitor_spec); + if (!monitor) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Specified monitor not found"); + return FALSE; + } + + monitor_mode = meta_monitor_get_mode_from_spec (monitor, mode_spec); + if (!monitor_mode) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Specified monitor mode not available"); + return FALSE; + } + } + } + + return TRUE; +} + +static MetaMonitorSpec * +find_monitor_spec (MetaMonitorManager *manager, + char *connector) +{ + GList *monitors; + GList *l; + + monitors = meta_monitor_manager_get_monitors (manager); + for (l = monitors; l; l = l->next) + { + MetaMonitor *monitor = l->data; + MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor); + + if (g_str_equal (connector, monitor_spec->connector)) + return meta_monitor_spec_clone (monitor_spec); + } + + return NULL; +} + +#define MONITOR_MODE_SPEC_FORMAT "(iid)" +#define MONITOR_CONFIG_FORMAT "(s" MONITOR_MODE_SPEC_FORMAT "a{sv})" +#define MONITOR_CONFIGS_FORMAT "a" MONITOR_CONFIG_FORMAT + +#define LOGICAL_MONITOR_CONFIG_FORMAT "(iidb" MONITOR_CONFIGS_FORMAT ")" + +static MetaMonitorConfig * +create_monitor_config_from_variant (MetaMonitorManager *manager, + GVariant *monitor_config_variant, + GError **error) +{ + + MetaMonitorConfig *monitor_config = NULL; + char *connector; + MetaMonitorSpec *monitor_spec; + MetaMonitorModeSpec *monitor_mode_spec; + GVariant *properties_variant = NULL; + gboolean enable_underscanning = FALSE; + int32_t mode_width; + int32_t mode_height; + double mode_refresh_rate; + + monitor_spec = g_new0 (MetaMonitorSpec, 1); + monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1); + + g_variant_get (monitor_config_variant, "(s" MONITOR_MODE_SPEC_FORMAT "@a{sv})", + &connector, + &mode_width, + &mode_height, + &mode_refresh_rate, + &properties_variant); + + *monitor_mode_spec = (MetaMonitorModeSpec) { + .width = mode_width, + .height = mode_height, + .refresh_rate = mode_refresh_rate + }; + + monitor_spec = find_monitor_spec (manager, connector); + if (!monitor_spec) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid connector '%s' specified", connector); + g_free (monitor_mode_spec); + return NULL; + } + + if (!meta_verify_monitor_mode_spec (monitor_mode_spec, error)) + { + g_free (monitor_mode_spec); + meta_monitor_spec_free (monitor_spec); + return NULL; + } + + g_variant_lookup (properties_variant, "underscanning", "b", &enable_underscanning); + + monitor_config = g_new0 (MetaMonitorConfig, 1); + *monitor_config = (MetaMonitorConfig) { + .monitor_spec = monitor_spec, + .mode_spec = monitor_mode_spec, + .enable_underscanning = enable_underscanning + }; + + return monitor_config; +} + +static gboolean +derive_logical_monitor_size (GList *monitor_configs, + int *width, + int *height, + GError **error) +{ + MetaMonitorConfig *monitor_config; + + if (!monitor_configs) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Empty logical monitor"); + return FALSE; + } + + monitor_config = monitor_configs->data; + *width = monitor_config->mode_spec->width; + *height = monitor_config->mode_spec->height; + + return TRUE; +} + +static MetaLogicalMonitorConfig * +create_logical_monitor_config_from_variant (MetaMonitorManager *manager, + GVariant *logical_monitor_config_variant, + GError **error) +{ + MetaLogicalMonitorConfig *logical_monitor_config; + int x, y, width, height; + double scale; + gboolean is_primary; + GVariantIter *monitor_configs_iter; + GList *monitor_configs = NULL; + + g_variant_get (logical_monitor_config_variant, LOGICAL_MONITOR_CONFIG_FORMAT, + &x, + &y, + &scale, + &is_primary, + &monitor_configs_iter); + + while (TRUE) + { + GVariant *monitor_config_variant = + g_variant_iter_next_value (monitor_configs_iter); + MetaMonitorConfig *monitor_config; + + if (!monitor_config_variant) + break; + + monitor_config = + create_monitor_config_from_variant (manager, + monitor_config_variant, error); + if (!monitor_config) + goto err; + + if (!meta_verify_monitor_config (monitor_config, error)) + { + meta_monitor_config_free (monitor_config); + goto err; + } + + monitor_configs = g_list_append (monitor_configs, monitor_config); + } + g_variant_iter_free (monitor_configs_iter); + + if (!derive_logical_monitor_size (monitor_configs, &width, &height, error)) + goto err; + + logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); + *logical_monitor_config = (MetaLogicalMonitorConfig) { + .layout = { + .x = x, + .y = y, + .width = width, + .height = height + }, + .scale = (int) scale, + .is_primary = is_primary, + .monitor_configs = monitor_configs + }; + + if (!meta_verify_logical_monitor_config (logical_monitor_config, error)) + { + meta_logical_monitor_config_free (logical_monitor_config); + return NULL; + } + + return logical_monitor_config; + +err: + g_list_free_full (monitor_configs, (GDestroyNotify) meta_monitor_config_free); + return NULL; +} + +static gboolean +meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skeleton, + GDBusMethodInvocation *invocation, + guint serial, + guint method, + GVariant *logical_monitor_configs_variant, + GVariant *properties_variant) +{ + MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton); + GVariantIter logical_monitor_configs_iter; + MetaMonitorsConfig *config; + GList *logical_monitor_configs = NULL; + GError *error = NULL; + + if (!manager->config_manager) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Used new configuration API with old configuration system"); + return TRUE; + } + + if (serial != manager->serial) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "The requested configuration is based on stale information"); + return TRUE; + } + + g_variant_iter_init (&logical_monitor_configs_iter, + logical_monitor_configs_variant); + while (TRUE) + { + GVariant *logical_monitor_config_variant = + g_variant_iter_next_value (&logical_monitor_configs_iter); + MetaLogicalMonitorConfig *logical_monitor_config; + + if (!logical_monitor_config_variant) + break; + + logical_monitor_config = + create_logical_monitor_config_from_variant (manager, + logical_monitor_config_variant, + &error); + if (!logical_monitor_config) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + error->message); + g_error_free (error); + g_list_free_full (logical_monitor_configs, + (GDestroyNotify) meta_logical_monitor_config_free); + return TRUE; + } + + logical_monitor_configs = g_list_append (logical_monitor_configs, + logical_monitor_config); + } + + config = meta_monitors_config_new (logical_monitor_configs); + if (!meta_verify_monitors_config (config, &error)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + error->message); + g_error_free (error); + g_object_unref (config); + return TRUE; + } + + if (!meta_monitor_manager_is_config_applicable (manager, config, &error)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + error->message); + g_error_free (error); + g_object_unref (config); + return TRUE; + } + + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + method, + &error)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + error->message); + g_error_free (error); + g_object_unref (config); + return TRUE; + } + + meta_dbus_display_config_complete_apply_monitors_config (skeleton, invocation); + + return TRUE; +} + +#undef MONITOR_MODE_SPEC_FORMAT +#undef MONITOR_CONFIG_FORMAT +#undef MONITOR_CONFIGS_FORMAT +#undef LOGICAL_MONITOR_CONFIG_FORMAT + static void legacy_confirm_configuration (MetaMonitorManager *manager, gboolean confirmed) @@ -1569,6 +1949,7 @@ meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface) iface->handle_get_crtc_gamma = meta_monitor_manager_handle_get_crtc_gamma; iface->handle_set_crtc_gamma = meta_monitor_manager_handle_set_crtc_gamma; iface->handle_get_current_state = meta_monitor_manager_handle_get_current_state; + iface->handle_apply_monitors_config = meta_monitor_manager_handle_apply_monitors_config; } static void diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c index 1866c3801..92be434e8 100644 --- a/src/backends/native/meta-monitor-manager-kms.c +++ b/src/backends/native/meta-monitor-manager-kms.c @@ -1424,9 +1424,10 @@ update_screen_size (MetaMonitorManager *manager, } static gboolean -meta_monitor_manager_kms_apply_monitors_config (MetaMonitorManager *manager, - MetaMonitorsConfig *config, - GError **error) +meta_monitor_manager_kms_apply_monitors_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; @@ -1444,6 +1445,13 @@ meta_monitor_manager_kms_apply_monitors_config (MetaMonitorManager *manager, error)) return FALSE; + if (method == META_MONITORS_CONFIG_METHOD_VERIFY) + { + g_ptr_array_free (crtc_infos, TRUE); + g_ptr_array_free (output_infos, TRUE); + return TRUE; + } + apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c index 06d38cfa3..d0e520986 100644 --- a/src/backends/x11/meta-monitor-manager-xrandr.c +++ b/src/backends/x11/meta-monitor-manager-xrandr.c @@ -1300,9 +1300,10 @@ meta_monitor_manager_xrandr_ensure_initial_config (MetaMonitorManager *manager) } static gboolean -meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *manager, - MetaMonitorsConfig *config, - GError **error) +meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; @@ -1318,11 +1319,14 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *manager, error)) return FALSE; - apply_crtc_assignments (manager, - (MetaCrtcInfo **) crtc_infos->pdata, - crtc_infos->len, - (MetaOutputInfo **) output_infos->pdata, - output_infos->len); + if (method != META_MONITORS_CONFIG_METHOD_VERIFY) + { + apply_crtc_assignments (manager, + (MetaCrtcInfo **) crtc_infos->pdata, + crtc_infos->len, + (MetaOutputInfo **) output_infos->pdata, + output_infos->len); + } g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); diff --git a/src/org.gnome.Mutter.DisplayConfig.xml b/src/org.gnome.Mutter.DisplayConfig.xml index 60923a84c..43ffb28a7 100644 --- a/src/org.gnome.Mutter.DisplayConfig.xml +++ b/src/org.gnome.Mutter.DisplayConfig.xml @@ -359,5 +359,42 @@ + + + + + + + + diff --git a/src/tests/meta-monitor-manager-test.c b/src/tests/meta-monitor-manager-test.c index 14afcd91a..292abe7c5 100644 --- a/src/tests/meta-monitor-manager-test.c +++ b/src/tests/meta-monitor-manager-test.c @@ -256,9 +256,10 @@ update_screen_size (MetaMonitorManager *manager, } static gboolean -meta_monitor_manager_test_apply_monitors_config (MetaMonitorManager *manager, - MetaMonitorsConfig *config, - GError **error) +meta_monitor_manager_test_apply_monitors_config (MetaMonitorManager *manager, + MetaMonitorsConfig *config, + MetaMonitorsConfigMethod method, + GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; @@ -280,6 +281,13 @@ meta_monitor_manager_test_apply_monitors_config (MetaMonitorManager *manager, error)) return FALSE; + if (method == META_MONITORS_CONFIG_METHOD_VERIFY) + { + g_ptr_array_free (crtc_infos, TRUE); + g_ptr_array_free (output_infos, TRUE); + return TRUE; + } + apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len,