From 6646211d0fcbf97688516bddaeaa1caa7252f2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 5 Jun 2023 17:45:15 +0200 Subject: [PATCH 1/7] display: Remove unnecessary config abstraction Long long ago during a transition period we had two sources of configuration - one using libgnome-rr, and one using D-Bus directly using a more high level API where mutter abstracted away a lot of low level things. After some time, the old libgnome-rr implementation was torched to allow moving forward, but the abstraction was left. Now is the time to remove it. Or, the time was long ago, but it's never too late. --- panels/display/cc-display-config-dbus.c | 2119 ----------------- panels/display/cc-display-config-dbus.h | 40 - .../display/cc-display-config-manager-dbus.c | 241 -- .../display/cc-display-config-manager-dbus.h | 34 - panels/display/cc-display-config-manager.c | 191 +- panels/display/cc-display-config-manager.h | 15 +- panels/display/cc-display-config.c | 1859 +++++++++++++-- panels/display/cc-display-config.h | 103 +- panels/display/cc-display-panel.c | 4 +- panels/display/cc-night-light-page.c | 4 +- panels/display/meson.build | 2 - panels/screen/cc-screen-panel.c | 4 +- subprojects/gvc | 2 +- 13 files changed, 1924 insertions(+), 2694 deletions(-) delete mode 100644 panels/display/cc-display-config-dbus.c delete mode 100644 panels/display/cc-display-config-dbus.h delete mode 100644 panels/display/cc-display-config-manager-dbus.c delete mode 100644 panels/display/cc-display-config-manager-dbus.h diff --git a/panels/display/cc-display-config-dbus.c b/panels/display/cc-display-config-dbus.c deleted file mode 100644 index d94f5417bf..0000000000 --- a/panels/display/cc-display-config-dbus.c +++ /dev/null @@ -1,2119 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include - -#include "cc-display-config-dbus.h" - -#define MODE_BASE_FORMAT "siiddad" -#define MODE_FORMAT "(" MODE_BASE_FORMAT "a{sv})" -#define MODES_FORMAT "a" MODE_FORMAT -#define MONITOR_SPEC_FORMAT "(ssss)" -#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})" -#define MONITORS_FORMAT "a" MONITOR_FORMAT - -#define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT -#define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})" -#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT - -#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})" - -typedef enum _CcDisplayModeFlags -{ - MODE_PREFERRED = 1 << 0, - MODE_CURRENT = 1 << 1, - MODE_INTERLACED = 1 << 2, -} CcDisplayModeFlags; - -struct _CcDisplayModeDBus -{ - CcDisplayMode parent_instance; - CcDisplayMonitorDBus *monitor; - - char *id; - int width; - int height; - double refresh_rate; - double preferred_scale; - GArray *supported_scales; - guint32 flags; -}; - -G_DEFINE_TYPE (CcDisplayModeDBus, - cc_display_mode_dbus, - CC_TYPE_DISPLAY_MODE) - -static gboolean -cc_display_mode_dbus_equal (const CcDisplayModeDBus *m1, - const CcDisplayModeDBus *m2) -{ - if (!m1 && !m2) - return TRUE; - else if (!m1 || !m2) - return FALSE; - - return m1->width == m2->width && - m1->height == m2->height && - m1->refresh_rate == m2->refresh_rate && - (m1->flags & MODE_INTERLACED) == (m2->flags & MODE_INTERLACED); -} - -static gboolean -cc_display_mode_dbus_is_clone_mode (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return !self->id; -} - -static void -cc_display_mode_dbus_get_resolution (CcDisplayMode *pself, - int *w, int *h) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - if (w) - *w = self->width; - if (h) - *h = self->height; -} - -static GArray * cc_display_mode_dbus_get_supported_scales (CcDisplayMode *pself); - -static double -cc_display_mode_dbus_get_preferred_scale (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return self->preferred_scale; -} - -static gboolean -cc_display_mode_dbus_is_supported_scale (CcDisplayMode *pself, - double scale) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - guint i; - for (i = 0; i < self->supported_scales->len; i++) - { - double v = g_array_index (self->supported_scales, double, i); - - if (G_APPROX_VALUE (v, scale, DBL_EPSILON)) - return TRUE; - } - return FALSE; -} - - -static gboolean -cc_display_mode_dbus_is_interlaced (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return !!(self->flags & MODE_INTERLACED); -} - -static gboolean -cc_display_mode_dbus_is_preferred (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return !!(self->flags & MODE_PREFERRED); -} - -static int -cc_display_mode_dbus_get_freq (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return self->refresh_rate; -} - -static double -cc_display_mode_dbus_get_freq_f (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - - return self->refresh_rate; -} - -static void -cc_display_mode_dbus_init (CcDisplayModeDBus *self) -{ - self->supported_scales = g_array_new (FALSE, FALSE, sizeof (double)); -} - -static void -cc_display_mode_dbus_finalize (GObject *object) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (object); - - g_free (self->id); - g_array_free (self->supported_scales, TRUE); - - G_OBJECT_CLASS (cc_display_mode_dbus_parent_class)->finalize (object); -} - -static void -cc_display_mode_dbus_class_init (CcDisplayModeDBusClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - CcDisplayModeClass *parent_class = CC_DISPLAY_MODE_CLASS (klass); - - gobject_class->finalize = cc_display_mode_dbus_finalize; - - parent_class->is_clone_mode = cc_display_mode_dbus_is_clone_mode; - parent_class->get_resolution = cc_display_mode_dbus_get_resolution; - parent_class->get_supported_scales = cc_display_mode_dbus_get_supported_scales; - parent_class->get_preferred_scale = cc_display_mode_dbus_get_preferred_scale; - parent_class->is_interlaced = cc_display_mode_dbus_is_interlaced; - parent_class->is_preferred = cc_display_mode_dbus_is_preferred; - parent_class->get_freq = cc_display_mode_dbus_get_freq; - parent_class->get_freq_f = cc_display_mode_dbus_get_freq_f; -} - -static CcDisplayModeDBus * -cc_display_mode_dbus_new_virtual (int width, - int height, - double preferred_scale, - GArray *supported_scales) -{ - g_autoptr(GVariant) properties_variant = NULL; - CcDisplayModeDBus *self; - - self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL); - - self->width = width; - self->height = height; - self->preferred_scale = preferred_scale; - self->supported_scales = g_array_ref (supported_scales); - - return self; -} - -static CcDisplayModeDBus * -cc_display_mode_dbus_new (CcDisplayMonitorDBus *monitor, - GVariant *variant) -{ - double d; - g_autoptr(GVariantIter) scales_iter = NULL; - g_autoptr(GVariant) properties_variant = NULL; - gboolean is_current; - gboolean is_preferred; - gboolean is_interlaced; - CcDisplayModeDBus *self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL); - - self->monitor = monitor; - - g_variant_get (variant, "(" MODE_BASE_FORMAT "@a{sv})", - &self->id, - &self->width, - &self->height, - &self->refresh_rate, - &self->preferred_scale, - &scales_iter, - &properties_variant); - - while (g_variant_iter_next (scales_iter, "d", &d)) - g_array_append_val (self->supported_scales, d); - - if (!g_variant_lookup (properties_variant, "is-current", "b", &is_current)) - is_current = FALSE; - if (!g_variant_lookup (properties_variant, "is-preferred", "b", &is_preferred)) - is_preferred = FALSE; - if (!g_variant_lookup (properties_variant, "is-interlaced", "b", &is_interlaced)) - is_interlaced = FALSE; - - if (is_current) - self->flags |= MODE_CURRENT; - if (is_preferred) - self->flags |= MODE_PREFERRED; - if (is_interlaced) - self->flags |= MODE_INTERLACED; - - return self; -} - - -#define CC_TYPE_DISPLAY_LOGICAL_MONITOR (cc_display_logical_monitor_get_type ()) -G_DECLARE_FINAL_TYPE (CcDisplayLogicalMonitor, cc_display_logical_monitor, - CC, DISPLAY_LOGICAL_MONITOR, GObject) - -struct _CcDisplayLogicalMonitor -{ - GObject parent_instance; - - int x; - int y; - double scale; - CcDisplayRotation rotation; - gboolean primary; - - GHashTable *monitors; -}; - -G_DEFINE_TYPE (CcDisplayLogicalMonitor, - cc_display_logical_monitor, - G_TYPE_OBJECT) - -static gboolean -cc_display_logical_monitor_equal (const CcDisplayLogicalMonitor *m1, - const CcDisplayLogicalMonitor *m2) -{ - if (!m1 && !m2) - return TRUE; - else if (!m1 || !m2) - return FALSE; - - return m1->x == m2->x && - m1->y == m2->y && - G_APPROX_VALUE (m1->scale, m2->scale, DBL_EPSILON) && - m1->rotation == m2->rotation && - m1->primary == m2->primary; -} - -static void -cc_display_logical_monitor_init (CcDisplayLogicalMonitor *self) -{ - self->scale = 1.0; - self->monitors = g_hash_table_new (NULL, NULL); -} - -static void -cc_display_logical_monitor_finalize (GObject *object) -{ - CcDisplayLogicalMonitor *self = CC_DISPLAY_LOGICAL_MONITOR (object); - - g_warn_if_fail (g_hash_table_size (self->monitors) == 0); - g_clear_pointer (&self->monitors, g_hash_table_destroy); - - G_OBJECT_CLASS (cc_display_logical_monitor_parent_class)->finalize (object); -} - -static void -cc_display_logical_monitor_class_init (CcDisplayLogicalMonitorClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = cc_display_logical_monitor_finalize; -} - - -typedef enum _CcDisplayMonitorUnderscanning -{ - UNDERSCANNING_UNSUPPORTED = 0, - UNDERSCANNING_DISABLED, - UNDERSCANNING_ENABLED -} CcDisplayMonitorUnderscanning; - -struct _CcDisplayMonitorDBus -{ - CcDisplayMonitor parent_instance; - CcDisplayConfigDBus *config; - - gchar *connector_name; - gchar *vendor_name; - gchar *product_name; - gchar *product_serial; - gchar *display_name; - - int width_mm; - int height_mm; - gboolean builtin; - CcDisplayMonitorUnderscanning underscanning; - CcDisplayMonitorPrivacy privacy_screen; - int max_width; - int max_height; - - GList *modes; - CcDisplayMode *current_mode; - CcDisplayMode *preferred_mode; - - CcDisplayLogicalMonitor *logical_monitor; -}; - -G_DEFINE_TYPE (CcDisplayMonitorDBus, - cc_display_monitor_dbus, - CC_TYPE_DISPLAY_MONITOR) - -static void -register_logical_monitor (CcDisplayConfigDBus *self, - CcDisplayLogicalMonitor *logical_monitor); -static void -cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self, - CcDisplayMonitorDBus *new_primary); -static void -cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self, - CcDisplayMonitorDBus *old_primary); -static void -cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self); -static void -cc_display_config_dbus_append_right (CcDisplayConfigDBus *self, - CcDisplayLogicalMonitor *monitor); -static void -cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self); - - -static const char * -cc_display_monitor_dbus_get_display_name (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->display_name) - return self->display_name; - - return self->connector_name; -} - -static const char * -cc_display_monitor_dbus_get_connector_name (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->connector_name; -} - -static gboolean -cc_display_monitor_dbus_is_builtin (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->builtin; -} - -static gboolean -cc_display_monitor_dbus_is_primary (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->logical_monitor) - return self->logical_monitor->primary; - - return FALSE; -} - -static void -cc_display_monitor_dbus_set_primary (CcDisplayMonitor *pself, - gboolean primary) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (primary) - cc_display_config_dbus_set_primary (self->config, self); - else - cc_display_config_dbus_unset_primary (self->config, self); -} - -static gboolean -cc_display_monitor_dbus_is_active (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->logical_monitor != NULL; -} - -static void -cc_display_monitor_dbus_set_logical_monitor (CcDisplayMonitorDBus *self, - CcDisplayLogicalMonitor *logical_monitor) -{ - gboolean was_primary = FALSE; - - if (self->logical_monitor) - { - was_primary = self->logical_monitor->primary; - if (was_primary) - cc_display_config_dbus_unset_primary (self->config, self); - g_hash_table_remove (self->logical_monitor->monitors, self); - g_object_unref (self->logical_monitor); - } - - self->logical_monitor = logical_monitor; - - if (self->logical_monitor) - { - g_hash_table_add (self->logical_monitor->monitors, self); - g_object_ref (self->logical_monitor); - /* unset primary with NULL will select this monitor if it is the only one.*/ - if (was_primary) - cc_display_config_dbus_set_primary (self->config, self); - else - cc_display_config_dbus_unset_primary (self->config, NULL); - } -} - -static void -cc_display_monitor_dbus_set_active (CcDisplayMonitor *pself, - gboolean active) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (!self->current_mode && active) - { - if (self->preferred_mode) - self->current_mode = self->preferred_mode; - else if (self->modes) - self->current_mode = (CcDisplayMode *) self->modes->data; - else - g_warning ("Couldn't find a mode to activate monitor at %s", self->connector_name); - } - - if (!self->logical_monitor && active) - { - CcDisplayLogicalMonitor *logical_monitor; - logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); - cc_display_monitor_dbus_set_logical_monitor (self, logical_monitor); - cc_display_config_dbus_append_right (self->config, logical_monitor); - register_logical_monitor (self->config, logical_monitor); - } - else if (self->logical_monitor && !active) - { - cc_display_monitor_dbus_set_logical_monitor (self, NULL); - } - - g_signal_emit_by_name (self, "active"); -} - -static CcDisplayRotation -cc_display_monitor_dbus_get_rotation (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->logical_monitor) - return self->logical_monitor->rotation; - - return CC_DISPLAY_ROTATION_NONE; -} - -static void -cc_display_monitor_dbus_set_rotation (CcDisplayMonitor *pself, - CcDisplayRotation rotation) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (!self->logical_monitor) - return; - - if (self->logical_monitor->rotation != rotation) - { - self->logical_monitor->rotation = rotation; - - g_signal_emit_by_name (self, "rotation"); - } -} - -static gboolean -cc_display_monitor_dbus_supports_rotation (CcDisplayMonitor *pself, - CcDisplayRotation rotation) -{ - return TRUE; -} - -static void -cc_display_monitor_dbus_get_physical_size (CcDisplayMonitor *pself, - int *w, int *h) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (w) - *w = self->width_mm; - if (h) - *h = self->height_mm; -} - -static void -cc_display_monitor_dbus_get_geometry (CcDisplayMonitor *pself, - int *x, int *y, int *w, int *h) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - CcDisplayMode *mode = NULL; - - if (self->logical_monitor) - { - if (x) - *x = self->logical_monitor->x; - if (y) - *y = self->logical_monitor->y; - } - else - { - if (x) - *x = -1; - if (y) - *y = -1; - } - - if (self->current_mode) - mode = self->current_mode; - else if (self->preferred_mode) - mode = self->preferred_mode; - else if (self->modes) - mode = CC_DISPLAY_MODE (self->modes->data); - - if (mode) - cc_display_mode_get_resolution (mode, w, h); - else - { - g_warning ("Monitor at %s has no modes?", self->connector_name); - if (w) - *w = -1; - if (h) - *h = -1; - } -} - -static CcDisplayMode * -cc_display_monitor_dbus_get_mode (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->current_mode; -} - -static CcDisplayMode * -cc_display_monitor_dbus_get_preferred_mode (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->preferred_mode; -} - -static guint32 -cc_display_monitor_dbus_get_id (CcDisplayMonitor *pself) -{ - return 0; -} - -static GList * -cc_display_monitor_dbus_get_modes (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->modes; -} - -static gboolean -cc_display_monitor_dbus_supports_underscanning (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->underscanning != UNDERSCANNING_UNSUPPORTED; -} - -static gboolean -cc_display_monitor_dbus_get_underscanning (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->underscanning == UNDERSCANNING_ENABLED; -} - -static CcDisplayMonitorPrivacy -cc_display_monitor_dbus_get_privacy (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - return self->privacy_screen; -} - -static void -cc_display_monitor_dbus_set_underscanning (CcDisplayMonitor *pself, - gboolean underscanning) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->underscanning == UNDERSCANNING_UNSUPPORTED) - return; - - if (underscanning) - self->underscanning = UNDERSCANNING_ENABLED; - else - self->underscanning = UNDERSCANNING_DISABLED; -} - -static CcDisplayMode * -cc_display_monitor_dbus_get_closest_mode (CcDisplayMonitorDBus *self, - CcDisplayModeDBus *mode) -{ - CcDisplayModeDBus *best = NULL; - GList *l; - - for (l = self->modes; l != NULL; l = l->next) - { - CcDisplayModeDBus *similar = l->data; - - if (similar->width != mode->width || - similar->height != mode->height) - continue; - - if (similar->refresh_rate == mode->refresh_rate && - (similar->flags & MODE_INTERLACED) == (mode->flags & MODE_INTERLACED)) - { - best = similar; - break; - } - - /* There might be a better heuristic. */ - if (!best || best->refresh_rate < similar->refresh_rate) - { - best = similar; - continue; - } - } - - return CC_DISPLAY_MODE (best); -} - -static void -cc_display_monitor_dbus_set_mode (CcDisplayMonitor *pself, - CcDisplayMode *new_mode) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - CcDisplayMode *mode; - - g_return_if_fail (new_mode != NULL); - - mode = cc_display_monitor_dbus_get_closest_mode (self, CC_DISPLAY_MODE_DBUS (new_mode)); - - self->current_mode = mode; - - if (!cc_display_mode_dbus_is_supported_scale (mode, cc_display_monitor_get_scale (pself))) - cc_display_monitor_set_scale (pself, cc_display_mode_get_preferred_scale (mode)); - - g_signal_emit_by_name (self, "mode"); -} - -static void -cc_display_monitor_dbus_set_compatible_clone_mode (CcDisplayMonitor *pself, - CcDisplayMode *clone_mode) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - GList *l; - CcDisplayMode *best_mode = NULL; - int clone_width, clone_height; - - g_return_if_fail (cc_display_mode_is_clone_mode (clone_mode)); - - cc_display_mode_get_resolution (clone_mode, &clone_width, &clone_height); - - for (l = self->modes; l; l = l->next) - { - CcDisplayMode *mode = l->data; - int width, height; - - cc_display_mode_get_resolution (mode, &width, &height); - if (width != clone_width || height != clone_height) - continue; - - if (!best_mode) - { - best_mode = mode; - continue; - } - - if (cc_display_mode_get_freq_f (mode) > - cc_display_mode_get_freq_f (best_mode)) - best_mode = mode; - } - - g_return_if_fail (best_mode); - - cc_display_monitor_set_mode (CC_DISPLAY_MONITOR (self), best_mode); -} - -static void -cc_display_monitor_dbus_set_position (CcDisplayMonitor *pself, - int x, int y) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->logical_monitor) - { - gboolean notify = FALSE; - - if (self->logical_monitor->x != x || self->logical_monitor->y != y) - notify = TRUE; - - self->logical_monitor->x = x; - self->logical_monitor->y = y; - - if (notify) - g_signal_emit_by_name (self, "position-changed"); - } - -} - -static double -cc_display_monitor_dbus_get_scale (CcDisplayMonitor *pself) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (self->logical_monitor) - return self->logical_monitor->scale; - - return 1.0; -} - -static void -cc_display_monitor_dbus_set_scale (CcDisplayMonitor *pself, - double scale) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself); - - if (!self->current_mode) - return; - - if (!cc_display_mode_dbus_is_supported_scale (self->current_mode, scale)) - return; - - if (!self->logical_monitor) - return; - - if (!G_APPROX_VALUE (self->logical_monitor->scale, scale, DBL_EPSILON)) - { - self->logical_monitor->scale = scale; - - g_signal_emit_by_name (self, "scale"); - } -} - -static void -cc_display_monitor_dbus_init (CcDisplayMonitorDBus *self) -{ - self->underscanning = UNDERSCANNING_UNSUPPORTED; - self->max_width = G_MAXINT; - self->max_height = G_MAXINT; -} - -static void -cc_display_monitor_dbus_finalize (GObject *object) -{ - CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (object); - - g_free (self->connector_name); - g_free (self->vendor_name); - g_free (self->product_name); - g_free (self->product_serial); - g_free (self->display_name); - - g_list_free_full (self->modes, g_object_unref); - - if (self->logical_monitor) - { - g_hash_table_remove (self->logical_monitor->monitors, self); - g_object_unref (self->logical_monitor); - } - - G_OBJECT_CLASS (cc_display_monitor_dbus_parent_class)->finalize (object); -} - -static void -cc_display_monitor_dbus_class_init (CcDisplayMonitorDBusClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - CcDisplayMonitorClass *parent_class = CC_DISPLAY_MONITOR_CLASS (klass); - - gobject_class->finalize = cc_display_monitor_dbus_finalize; - - parent_class->get_display_name = cc_display_monitor_dbus_get_display_name; - parent_class->get_connector_name = cc_display_monitor_dbus_get_connector_name; - parent_class->is_builtin = cc_display_monitor_dbus_is_builtin; - parent_class->is_primary = cc_display_monitor_dbus_is_primary; - parent_class->set_primary = cc_display_monitor_dbus_set_primary; - parent_class->is_active = cc_display_monitor_dbus_is_active; - parent_class->set_active = cc_display_monitor_dbus_set_active; - parent_class->get_rotation = cc_display_monitor_dbus_get_rotation; - parent_class->set_rotation = cc_display_monitor_dbus_set_rotation; - parent_class->supports_rotation = cc_display_monitor_dbus_supports_rotation; - parent_class->get_physical_size = cc_display_monitor_dbus_get_physical_size; - parent_class->get_geometry = cc_display_monitor_dbus_get_geometry; - parent_class->get_mode = cc_display_monitor_dbus_get_mode; - parent_class->get_preferred_mode = cc_display_monitor_dbus_get_preferred_mode; - parent_class->get_id = cc_display_monitor_dbus_get_id; - parent_class->get_modes = cc_display_monitor_dbus_get_modes; - parent_class->supports_underscanning = cc_display_monitor_dbus_supports_underscanning; - parent_class->get_underscanning = cc_display_monitor_dbus_get_underscanning; - parent_class->set_underscanning = cc_display_monitor_dbus_set_underscanning; - parent_class->get_privacy = cc_display_monitor_dbus_get_privacy; - parent_class->set_mode = cc_display_monitor_dbus_set_mode; - parent_class->set_compatible_clone_mode = cc_display_monitor_dbus_set_compatible_clone_mode; - parent_class->set_position = cc_display_monitor_dbus_set_position; - parent_class->get_scale = cc_display_monitor_dbus_get_scale; - parent_class->set_scale = cc_display_monitor_dbus_set_scale; -} - -static void -construct_modes (CcDisplayMonitorDBus *self, - GVariantIter *modes) -{ - CcDisplayModeDBus *mode; - - while (TRUE) - { - g_autoptr(GVariant) variant = NULL; - - if (!g_variant_iter_next (modes, "@"MODE_FORMAT, &variant)) - break; - - mode = cc_display_mode_dbus_new (self, variant); - self->modes = g_list_prepend (self->modes, mode); - - if (mode->flags & MODE_PREFERRED) - self->preferred_mode = CC_DISPLAY_MODE (mode); - if (mode->flags & MODE_CURRENT) - self->current_mode = CC_DISPLAY_MODE (mode); - } - - self->modes = g_list_reverse (self->modes); -} - -static CcDisplayMonitorDBus * -cc_display_monitor_dbus_new (GVariant *variant, - CcDisplayConfigDBus *config) -{ - CcDisplayMonitorDBus *self = g_object_new (CC_TYPE_DISPLAY_MONITOR_DBUS, NULL); - gchar *s1, *s2, *s3, *s4; - g_autoptr(GVariantIter) modes = NULL; - g_autoptr(GVariantIter) props = NULL; - - self->config = config; - - g_variant_get (variant, MONITOR_FORMAT, - &s1, &s2, &s3, &s4, &modes, &props); - self->connector_name = s1; - self->vendor_name = s2; - self->product_name = s3; - self->product_serial = s4; - - construct_modes (self, modes); - - while (TRUE) - { - const char *s; - g_autoptr(GVariant) v = NULL; - - if (!g_variant_iter_next (props, "{&sv}", &s, &v)) - break; - - if (g_str_equal (s, "width-mm")) - { - g_variant_get (v, "i", &self->width_mm); - } - else if (g_str_equal (s, "height-mm")) - { - g_variant_get (v, "i", &self->height_mm); - } - else if (g_str_equal (s, "is-underscanning")) - { - gboolean underscanning = FALSE; - g_variant_get (v, "b", &underscanning); - if (underscanning) - self->underscanning = UNDERSCANNING_ENABLED; - else - self->underscanning = UNDERSCANNING_DISABLED; - } - else if (g_str_equal (s, "max-screen-size")) - { - g_variant_get (v, "ii", &self->max_width, &self->max_height); - } - else if (g_str_equal (s, "is-builtin")) - { - g_variant_get (v, "b", &self->builtin); - } - else if (g_str_equal (s, "display-name")) - { - g_variant_get (v, "s", &self->display_name); - } - else if (g_str_equal (s, "privacy-screen-state")) - { - gboolean enabled; - gboolean locked; - g_variant_get (v, "(bb)", &enabled, &locked); - - if (enabled) - self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_ENABLED; - else - self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_DISABLED; - - if (locked) - self->privacy_screen |= CC_DISPLAY_MONITOR_PRIVACY_LOCKED; - } - } - - return self; -} - - -typedef enum _CcDisplayLayoutMode -{ - CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1, - CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2 -} CcDisplayLayoutMode; - -typedef enum _CcDisplayConfigMethod -{ - CC_DISPLAY_CONFIG_METHOD_VERIFY = 0, - CC_DISPLAY_CONFIG_METHOD_TEMPORARY = 1, - CC_DISPLAY_CONFIG_METHOD_PERSISTENT = 2 -} CcDisplayConfigMethod; - -struct _CcDisplayConfigDBus -{ - CcDisplayConfig parent_instance; - - GVariant *state; - GDBusConnection *connection; - GDBusProxy *proxy; - - int min_width; - int min_height; - - guint panel_orientation_managed; - - guint32 serial; - gboolean supports_mirroring; - gboolean supports_changing_layout_mode; - gboolean global_scale_required; - CcDisplayLayoutMode layout_mode; - - GList *monitors; - CcDisplayMonitorDBus *primary; - - GHashTable *logical_monitors; -}; - -G_DEFINE_TYPE (CcDisplayConfigDBus, - cc_display_config_dbus, - CC_TYPE_DISPLAY_CONFIG) - -enum -{ - PROP_0, - PROP_STATE, - PROP_CONNECTION, -}; - -static GList * -cc_display_config_dbus_get_monitors (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - return self->monitors; -} - -static GVariant * -build_monitors_variant (GHashTable *monitors) -{ - GVariantBuilder builder; - GHashTableIter iter; - CcDisplayMonitorDBus *monitor; - - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - g_hash_table_iter_init (&iter, monitors); - - while (g_hash_table_iter_next (&iter, (void **) &monitor, NULL)) - { - GVariantBuilder props_builder; - CcDisplayModeDBus *mode_dbus; - - if (!monitor->current_mode) - continue; - - g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&props_builder, "{sv}", - "underscanning", - g_variant_new_boolean (monitor->underscanning == UNDERSCANNING_ENABLED)); - - mode_dbus = CC_DISPLAY_MODE_DBUS (monitor->current_mode); - g_variant_builder_add (&builder, "(ss@*)", - monitor->connector_name, - mode_dbus->id, - g_variant_builder_end (&props_builder)); - } - - return g_variant_builder_end (&builder); -} - -static GVariant * -build_logical_monitors_parameter (CcDisplayConfigDBus *self) -{ - GVariantBuilder builder; - GHashTableIter iter; - CcDisplayLogicalMonitor *logical_monitor; - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(iiduba(ssa{sv}))")); - g_hash_table_iter_init (&iter, self->logical_monitors); - - while (g_hash_table_iter_next (&iter, (void **) &logical_monitor, NULL)) - g_variant_builder_add (&builder, "(iidub@*)", - logical_monitor->x, - logical_monitor->y, - logical_monitor->scale, - logical_monitor->rotation, - logical_monitor->primary, - build_monitors_variant (logical_monitor->monitors)); - - return g_variant_builder_end (&builder); -} - -static GVariant * -build_apply_parameters (CcDisplayConfigDBus *self, - CcDisplayConfigMethod method) -{ - GVariantBuilder props_builder; - g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); - - if (self->supports_changing_layout_mode) - g_variant_builder_add (&props_builder, "{sv}", - "layout-mode", g_variant_new_uint32 (self->layout_mode)); - - return g_variant_new ("(uu@*@*)", - self->serial, - method, - build_logical_monitors_parameter (self), - g_variant_builder_end (&props_builder)); -} - -static gboolean -config_apply (CcDisplayConfigDBus *self, - CcDisplayConfigMethod method, - GError **error) -{ - g_autoptr(GVariant) retval = NULL; - - cc_display_config_dbus_ensure_non_offset_coords (self); - - retval = g_dbus_proxy_call_sync (self->proxy, - "ApplyMonitorsConfig", - build_apply_parameters (self, method), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, - error); - return retval != NULL; -} - -static gboolean -cc_display_config_dbus_is_applicable (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - g_autoptr(GError) error = NULL; - - if (!config_apply (self, CC_DISPLAY_CONFIG_METHOD_VERIFY, &error)) - { - g_warning ("Config not applicable: %s", error->message); - return FALSE; - } - else - { - return TRUE; - } -} - -static CcDisplayMonitorDBus * -monitor_from_spec (CcDisplayConfigDBus *self, - const gchar *connector, - const gchar *vendor, - const gchar *product, - const gchar *serial) -{ - GList *l; - for (l = self->monitors; l != NULL; l = l->next) - { - CcDisplayMonitorDBus *m = l->data; - if (g_str_equal (m->connector_name, connector) && - g_str_equal (m->vendor_name, vendor) && - g_str_equal (m->product_name, product) && - g_str_equal (m->product_serial, serial)) - return m; - } - return NULL; -} - -static gboolean -cc_display_config_dbus_equal (CcDisplayConfig *pself, - CcDisplayConfig *pother) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - CcDisplayConfigDBus *other = CC_DISPLAY_CONFIG_DBUS (pother); - GList *l; - - g_return_val_if_fail (pself, FALSE); - g_return_val_if_fail (pother, FALSE); - - cc_display_config_dbus_ensure_non_offset_coords (self); - cc_display_config_dbus_ensure_non_offset_coords (other); - - for (l = self->monitors; l != NULL; l = l->next) - { - CcDisplayMonitorDBus *m1 = l->data; - CcDisplayMonitorDBus *m2 = monitor_from_spec (other, - m1->connector_name, - m1->vendor_name, - m1->product_name, - m1->product_serial); - if (!m2) - return FALSE; - - if (m1->underscanning != m2->underscanning) - return FALSE; - - if (!cc_display_logical_monitor_equal (m1->logical_monitor, m2->logical_monitor)) - return FALSE; - - /* Modes should not be compared if both monitors have no logical monitor. */ - if (m1->logical_monitor == NULL && m2->logical_monitor == NULL) - continue; - - if (!cc_display_mode_dbus_equal (CC_DISPLAY_MODE_DBUS (m1->current_mode), - CC_DISPLAY_MODE_DBUS (m2->current_mode))) - return FALSE; - } - - return TRUE; -} - -static void -cc_display_config_dbus_set_primary (CcDisplayConfigDBus *self, - CcDisplayMonitorDBus *new_primary) -{ - if (self->primary == new_primary) - return; - - if (!new_primary->logical_monitor) - return; - - if (self->primary && self->primary->logical_monitor) - { - self->primary->logical_monitor->primary = FALSE; - g_signal_emit_by_name (self->primary, "primary"); - } - - self->primary = new_primary; - self->primary->logical_monitor->primary = TRUE; - - g_signal_emit_by_name (self->primary, "primary"); - g_signal_emit_by_name (self, "primary"); -} - -static void -cc_display_config_dbus_unset_primary (CcDisplayConfigDBus *self, - CcDisplayMonitorDBus *old_primary) -{ - GList *l; - - if (self->primary != old_primary) - return; - - for (l = self->monitors; l != NULL; l = l->next) - { - CcDisplayMonitorDBus *monitor = l->data; - if (monitor->logical_monitor && - monitor != old_primary) - { - cc_display_config_dbus_set_primary (self, monitor); - break; - } - } - - if (self->primary == old_primary) - self->primary = NULL; -} - -static gboolean -cc_display_config_dbus_is_cloning (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - guint n_active_monitors = 0; - GList *l; - - for (l = self->monitors; l != NULL; l = l->next) - if (cc_display_monitor_is_active (CC_DISPLAY_MONITOR (l->data))) - n_active_monitors += 1; - - return n_active_monitors > 1 && g_hash_table_size (self->logical_monitors) == 1; -} - -static void -cc_display_config_dbus_set_cloning (CcDisplayConfig *pself, - gboolean clone) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - gboolean is_cloning = cc_display_config_is_cloning (pself); - CcDisplayLogicalMonitor *logical_monitor; - GList *l; - - if (clone && !is_cloning) - { - logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); - for (l = self->monitors; l != NULL; l = l->next) - { - if (!cc_display_monitor_is_usable (l->data)) - continue; - cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data), - logical_monitor); - } - register_logical_monitor (self, logical_monitor); - } - else if (!clone && is_cloning) - { - for (l = self->monitors; l != NULL; l = l->next) - { - logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); - cc_display_monitor_dbus_set_logical_monitor (CC_DISPLAY_MONITOR_DBUS (l->data), - logical_monitor); - register_logical_monitor (self, logical_monitor); - } - cc_display_config_dbus_make_linear (self); - } -} - -static gboolean -mode_supports_scale (CcDisplayMode *mode, - double scale) -{ - g_autoptr(GArray) scales = NULL; - int i; - - scales = cc_display_mode_get_supported_scales (mode); - for (i = 0; i < scales->len; i++) - { - if (G_APPROX_VALUE (scale, g_array_index (scales, double, i), - DBL_EPSILON)) - return TRUE; - } - - return FALSE; -} - -static void -remove_unsupported_scales (CcDisplayMode *mode, - GArray *supported_scales) -{ - g_autoptr(GArray) mode_scales = NULL; - int i; - - mode_scales = cc_display_mode_get_supported_scales (mode); - i = 0; - while (i < supported_scales->len) - { - double scale; - - if (i == supported_scales->len) - break; - - scale = g_array_index (supported_scales, double, i); - - if (mode_supports_scale (mode, scale)) - { - i++; - continue; - } - - g_array_remove_range (supported_scales, i, 1); - } -} - -static gboolean -monitor_has_compatible_clone_mode (CcDisplayMonitorDBus *monitor, - CcDisplayModeDBus *mode, - GArray *supported_scales) -{ - GList *l; - - for (l = monitor->modes; l; l = l->next) - { - CcDisplayModeDBus *other_mode = l->data; - - if (other_mode->width != mode->width || - other_mode->height != mode->height) - continue; - - if ((other_mode->flags & MODE_INTERLACED) != - (mode->flags & MODE_INTERLACED)) - continue; - - remove_unsupported_scales (CC_DISPLAY_MODE (other_mode), supported_scales); - - return TRUE; - } - - return FALSE; -} - -static gboolean -monitors_has_compatible_clone_mode (CcDisplayConfigDBus *self, - CcDisplayModeDBus *mode, - GArray *supported_scales) -{ - GList *l; - - for (l = self->monitors; l; l = l->next) - { - CcDisplayMonitorDBus *monitor = l->data; - - if (!monitor_has_compatible_clone_mode (monitor, mode, supported_scales)) - return FALSE; - } - - return TRUE; -} - -static gboolean -is_mode_better (CcDisplayModeDBus *mode, - CcDisplayModeDBus *other_mode) -{ - if (mode->width * mode->height > other_mode->width * other_mode->height) - return TRUE; - else if (mode->width * mode->height < other_mode->width * other_mode->height) - return FALSE; - - if (!(mode->flags & MODE_INTERLACED) && - (other_mode->flags & MODE_INTERLACED)) - return TRUE; - - return FALSE; -} - -static GList * -cc_display_config_dbus_generate_cloning_modes (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - CcDisplayMonitorDBus *base_monitor = NULL; - GList *l; - GList *clone_modes = NULL; - CcDisplayModeDBus *best_mode = NULL; - - for (l = self->monitors; l; l = l->next) - { - CcDisplayMonitor *monitor = l->data; - - if (cc_display_monitor_is_active (monitor)) - { - base_monitor = CC_DISPLAY_MONITOR_DBUS (monitor); - break; - } - } - - if (!base_monitor) - return NULL; - - for (l = base_monitor->modes; l; l = l->next) - { - CcDisplayModeDBus *mode = l->data; - CcDisplayModeDBus *virtual_mode; - g_autoptr (GArray) supported_scales = NULL; - - supported_scales = - cc_display_mode_get_supported_scales (CC_DISPLAY_MODE (mode)); - - if (!monitors_has_compatible_clone_mode (self, mode, supported_scales)) - continue; - - virtual_mode = cc_display_mode_dbus_new_virtual (mode->width, - mode->height, - mode->preferred_scale, - supported_scales); - clone_modes = g_list_append (clone_modes, virtual_mode); - - if (!best_mode || is_mode_better (virtual_mode, best_mode)) - best_mode = virtual_mode; - } - - best_mode->flags |= MODE_PREFERRED; - - return clone_modes; -} - -static gboolean -cc_display_config_dbus_apply (CcDisplayConfig *pself, - GError **error) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - return config_apply (self, CC_DISPLAY_CONFIG_METHOD_PERSISTENT, error); -} - -static gboolean -cc_display_config_dbus_is_layout_logical (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - return self->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL; -} - -static gboolean -is_scale_allowed_by_active_monitors (CcDisplayConfigDBus *self, - CcDisplayMode *mode, - double scale); - -static gboolean -is_scaled_mode_allowed (CcDisplayConfigDBus *self, - CcDisplayModeDBus *mode, - double scale) -{ - gint width, height; - - /* Do the math as if the monitor is always in landscape mode. */ - width = round (mode->width / scale); - height = round (mode->height / scale); - - if (MAX (width, height) < self->min_width || - MIN (width, height) < self->min_height) - return FALSE; - - if (!self->global_scale_required) - return TRUE; - - return is_scale_allowed_by_active_monitors (self, CC_DISPLAY_MODE (mode), scale); -} - -static gboolean -is_scale_allowed_by_active_monitors (CcDisplayConfigDBus *self, - CcDisplayMode *mode, - double scale) -{ - GList *l; - - for (l = self->monitors; l != NULL; l = l->next) - { - CcDisplayMonitorDBus *m = CC_DISPLAY_MONITOR_DBUS (l->data); - - if (!cc_display_monitor_is_active (CC_DISPLAY_MONITOR (m))) - continue; - - if (!cc_display_mode_dbus_is_supported_scale (mode, scale)) - return FALSE; - } - - return TRUE; -} - -static GArray * -cc_display_mode_dbus_get_supported_scales (CcDisplayMode *pself) -{ - CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself); - CcDisplayConfig *config = CC_DISPLAY_CONFIG (self->monitor->config); - - if (cc_display_config_is_cloning (config)) - { - GArray *scales = g_array_copy (self->supported_scales); - int i; - - for (i = scales->len - 1; i >= 0; i--) - { - double scale = g_array_index (scales, double, i); - - if (!is_scale_allowed_by_active_monitors (self->monitor->config, - pself, scale)) - g_array_remove_index (scales, i); - } - - return g_steal_pointer (&scales); - } - - return g_array_ref (self->supported_scales); -} - -static void -filter_out_invalid_scaled_modes (CcDisplayConfigDBus *self) -{ - GList *l; - - for (l = self->monitors; l; l = l->next) - { - CcDisplayMonitorDBus *monitor = l->data; - GList *ll = monitor->modes; - - while (ll != NULL) - { - CcDisplayModeDBus *mode = ll->data; - GList *current = ll; - double current_scale = -1; - int i; - - ll = ll->next; - - if (monitor->current_mode != CC_DISPLAY_MODE (mode) && - monitor->preferred_mode != CC_DISPLAY_MODE (mode) && - !is_scaled_mode_allowed (self, mode, 1.0)) - { - g_clear_object (&mode); - monitor->modes = g_list_delete_link (monitor->modes, current); - continue; - } - - if (monitor->current_mode == CC_DISPLAY_MODE (mode)) - current_scale = cc_display_monitor_dbus_get_scale (CC_DISPLAY_MONITOR (monitor)); - - for (i = mode->supported_scales->len - 1; i >= 0; i--) - { - float scale = g_array_index (mode->supported_scales, double, i); - - if (!G_APPROX_VALUE (scale, current_scale, DBL_EPSILON) && - !G_APPROX_VALUE (scale, mode->preferred_scale, DBL_EPSILON) && - !is_scaled_mode_allowed (self, mode, scale)) - { - g_array_remove_index (mode->supported_scales, i); - } - } - } - } -} - -static void -cc_display_config_dbus_set_minimum_size (CcDisplayConfig *pself, - int width, - int height) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - g_assert (width >= 0 && height >= 0); - g_assert (((self->min_width == 0 && self->min_height == 0) || - (self->min_width >= width && self->min_height >= height)) && - "Minimum size can't be set again to higher values"); - - self->min_width = width; - self->min_height = height; - - filter_out_invalid_scaled_modes (self); -} - -static gboolean -cc_display_config_dbus_is_scaled_mode_valid (CcDisplayConfig *pself, - CcDisplayMode *mode, - double scale) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - if (cc_display_config_is_cloning (pself)) - return is_scale_allowed_by_active_monitors (self, mode, scale); - - return cc_display_mode_dbus_is_supported_scale (mode, scale); -} - -static gboolean -cc_display_config_dbus_get_panel_orientation_managed (CcDisplayConfig *pself) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself); - - return self->panel_orientation_managed; -} - -static void -cc_display_config_dbus_init (CcDisplayConfigDBus *self) -{ - self->serial = 0; - self->supports_mirroring = TRUE; - self->supports_changing_layout_mode = FALSE; - self->global_scale_required = FALSE; - self->layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL; - self->logical_monitors = g_hash_table_new (NULL, NULL); -} - -static void -remove_logical_monitor (gpointer data, - GObject *object) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (data); - - g_hash_table_remove (self->logical_monitors, object); -} - -static void -register_logical_monitor (CcDisplayConfigDBus *self, - CcDisplayLogicalMonitor *logical_monitor) -{ - g_hash_table_add (self->logical_monitors, logical_monitor); - g_object_weak_ref (G_OBJECT (logical_monitor), remove_logical_monitor, self); - g_object_unref (logical_monitor); -} - -static void -apply_global_scale_requirement (CcDisplayConfigDBus *self, - CcDisplayMonitor *monitor) -{ - GList *l; - double scale = cc_display_monitor_get_scale (monitor); - - for (l = self->monitors; l != NULL; l = l->next) - { - CcDisplayMonitor *m = l->data; - if (m != monitor) - cc_display_monitor_set_scale (m, scale); - } -} - -static void -construct_monitors (CcDisplayConfigDBus *self, - GVariantIter *monitors, - GVariantIter *logical_monitors) -{ - while (TRUE) - { - CcDisplayMonitorDBus *monitor; - g_autoptr(GVariant) variant = NULL; - - if (!g_variant_iter_next (monitors, "@"MONITOR_FORMAT, &variant)) - break; - - monitor = cc_display_monitor_dbus_new (variant, self); - self->monitors = g_list_prepend (self->monitors, monitor); - - if (self->global_scale_required) - g_signal_connect_object (monitor, "scale", - G_CALLBACK (apply_global_scale_requirement), - self, G_CONNECT_SWAPPED); - } - - self->monitors = g_list_reverse (self->monitors); - - while (TRUE) - { - g_autoptr(GVariant) variant = NULL; - CcDisplayLogicalMonitor *logical_monitor; - g_autoptr(GVariantIter) monitor_specs = NULL; - const gchar *s1, *s2, *s3, *s4; - gboolean primary; - - if (!g_variant_iter_next (logical_monitors, "@"LOGICAL_MONITOR_FORMAT, &variant)) - break; - - logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); - g_variant_get (variant, LOGICAL_MONITOR_FORMAT, - &logical_monitor->x, - &logical_monitor->y, - &logical_monitor->scale, - &logical_monitor->rotation, - &primary, - &monitor_specs, - NULL); - - while (g_variant_iter_next (monitor_specs, "(&s&s&s&s)", &s1, &s2, &s3, &s4)) - { - CcDisplayMonitorDBus *m = monitor_from_spec (self, s1, s2, s3, s4); - if (!m) - { - g_warning ("Couldn't find monitor given spec: %s, %s, %s, %s", - s1, s2, s3, s4); - continue; - } - - cc_display_monitor_dbus_set_logical_monitor (m, logical_monitor); - } - - if (g_hash_table_size (logical_monitor->monitors) > 0) - { - if (primary) - { - CcDisplayMonitorDBus *m = NULL; - GHashTableIter iter; - g_hash_table_iter_init (&iter, logical_monitor->monitors); - g_hash_table_iter_next (&iter, (void **) &m, NULL); - - cc_display_config_dbus_set_primary (self, m); - } - } - else - { - g_warning ("Got an empty logical monitor, ignoring"); - } - - register_logical_monitor (self, logical_monitor); - } -} - -static void -update_panel_orientation_managed (CcDisplayConfigDBus *self) -{ - g_autoptr(GVariant) v = NULL; - gboolean panel_orientation_managed = FALSE; - - if (self->proxy != NULL) - { - v = g_dbus_proxy_get_cached_property (self->proxy, "PanelOrientationManaged"); - if (v) - { - panel_orientation_managed = g_variant_get_boolean (v); - } - } - - if (panel_orientation_managed == self->panel_orientation_managed) - return; - - self->panel_orientation_managed = panel_orientation_managed; - g_signal_emit_by_name (self, "panel-orientation-managed", self->panel_orientation_managed); -} - -static void -proxy_properties_changed_cb (CcDisplayConfigDBus *self, - GVariant *changed_properties, - GStrv invalidated_properties) -{ - GVariantDict dict; - - g_variant_dict_init (&dict, changed_properties); - - if (g_variant_dict_contains (&dict, "PanelOrientationManaged")) - update_panel_orientation_managed (self); -} - -static void -cc_display_config_dbus_constructed (GObject *object) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object); - g_autoptr(GVariantIter) monitors = NULL; - g_autoptr(GVariantIter) logical_monitors = NULL; - g_autoptr(GVariantIter) props = NULL; - g_autoptr(GError) error = NULL; - - g_variant_get (self->state, - CURRENT_STATE_FORMAT, - &self->serial, - &monitors, - &logical_monitors, - &props); - - while (TRUE) - { - const char *s; - g_autoptr(GVariant) v = NULL; - - if (!g_variant_iter_next (props, "{&sv}", &s, &v)) - break; - - if (g_str_equal (s, "supports-mirroring")) - { - g_variant_get (v, "b", &self->supports_mirroring); - } - else if (g_str_equal (s, "supports-changing-layout-mode")) - { - g_variant_get (v, "b", &self->supports_changing_layout_mode); - } - else if (g_str_equal (s, "global-scale-required")) - { - g_variant_get (v, "b", &self->global_scale_required); - } - else if (g_str_equal (s, "layout-mode")) - { - guint32 u = 0; - g_variant_get (v, "u", &u); - if (u >= CC_DISPLAY_LAYOUT_MODE_LOGICAL && - u <= CC_DISPLAY_LAYOUT_MODE_PHYSICAL) - self->layout_mode = u; - } - } - - construct_monitors (self, monitors, logical_monitors); - filter_out_invalid_scaled_modes (self); - - self->proxy = g_dbus_proxy_new_sync (self->connection, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - NULL, - &error); - if (error) - g_warning ("Could not create DisplayConfig proxy: %s", error->message); - - g_signal_connect_swapped (self->proxy, "g-properties-changed", - G_CALLBACK (proxy_properties_changed_cb), self); - update_panel_orientation_managed (self); - - G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->constructed (object); -} - -static void -cc_display_config_dbus_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object); - - switch (prop_id) - { - case PROP_STATE: - self->state = g_value_dup_variant (value); - break; - case PROP_CONNECTION: - self->connection = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -cc_display_config_dbus_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object); - - switch (prop_id) - { - case PROP_STATE: - g_value_set_variant (value, self->state); - break; - case PROP_CONNECTION: - g_value_set_object (value, self->connection); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -cc_display_config_dbus_dispose (GObject *object) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object); - - if (self->logical_monitors) - { - GHashTableIter iter; - gpointer monitor; - - g_hash_table_iter_init (&iter, self->logical_monitors); - - while (g_hash_table_iter_next (&iter, &monitor, NULL)) - g_object_weak_unref (G_OBJECT (monitor), remove_logical_monitor, self); - } - - G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->dispose (object); -} - -static void -cc_display_config_dbus_finalize (GObject *object) -{ - CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object); - - g_clear_pointer (&self->state, g_variant_unref); - g_clear_object (&self->connection); - g_clear_object (&self->proxy); - - g_clear_list (&self->monitors, g_object_unref); - g_clear_pointer (&self->logical_monitors, g_hash_table_destroy); - - G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->finalize (object); -} - -static void -cc_display_config_dbus_class_init (CcDisplayConfigDBusClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - CcDisplayConfigClass *parent_class = CC_DISPLAY_CONFIG_CLASS (klass); - GParamSpec *pspec; - - gobject_class->constructed = cc_display_config_dbus_constructed; - gobject_class->set_property = cc_display_config_dbus_set_property; - gobject_class->get_property = cc_display_config_dbus_get_property; - gobject_class->dispose = cc_display_config_dbus_dispose; - gobject_class->finalize = cc_display_config_dbus_finalize; - - parent_class->get_monitors = cc_display_config_dbus_get_monitors; - parent_class->is_applicable = cc_display_config_dbus_is_applicable; - parent_class->equal = cc_display_config_dbus_equal; - parent_class->apply = cc_display_config_dbus_apply; - parent_class->is_cloning = cc_display_config_dbus_is_cloning; - parent_class->set_cloning = cc_display_config_dbus_set_cloning; - parent_class->generate_cloning_modes = cc_display_config_dbus_generate_cloning_modes; - parent_class->is_layout_logical = cc_display_config_dbus_is_layout_logical; - parent_class->is_scaled_mode_valid = cc_display_config_dbus_is_scaled_mode_valid; - parent_class->set_minimum_size = cc_display_config_dbus_set_minimum_size; - parent_class->get_panel_orientation_managed = - cc_display_config_dbus_get_panel_orientation_managed; - - pspec = g_param_spec_variant ("state", - "GVariant", - "GVariant", - G_VARIANT_TYPE (CURRENT_STATE_FORMAT), - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (gobject_class, PROP_STATE, pspec); - - pspec = g_param_spec_object ("connection", - "GDBusConnection", - "GDBusConnection", - G_TYPE_DBUS_CONNECTION, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (gobject_class, PROP_CONNECTION, pspec); -} - -static gint -sort_x_axis (gconstpointer a, gconstpointer b) -{ - const CcDisplayLogicalMonitor *ma = a; - const CcDisplayLogicalMonitor *mb = b; - return ma->x - mb->x; -} - -static gint -sort_y_axis (gconstpointer a, gconstpointer b) -{ - const CcDisplayLogicalMonitor *ma = a; - const CcDisplayLogicalMonitor *mb = b; - return ma->y - mb->y; -} - -static void -add_x_delta (gpointer d1, gpointer d2) -{ - CcDisplayLogicalMonitor *m = d1; - int delta = GPOINTER_TO_INT (d2); - m->x += delta; -} - -static gboolean -logical_monitor_is_rotated (CcDisplayLogicalMonitor *lm) -{ - switch (lm->rotation) - { - case CC_DISPLAY_ROTATION_90: - case CC_DISPLAY_ROTATION_270: - case CC_DISPLAY_ROTATION_90_FLIPPED: - case CC_DISPLAY_ROTATION_270_FLIPPED: - return TRUE; - default: - return FALSE; - } -} - -static int -logical_monitor_width (CcDisplayLogicalMonitor *lm) -{ - CcDisplayMonitorDBus *monitor; - CcDisplayModeDBus *mode; - GHashTableIter iter; - int width; - - g_hash_table_iter_init (&iter, lm->monitors); - g_hash_table_iter_next (&iter, (void **) &monitor, NULL); - mode = CC_DISPLAY_MODE_DBUS (monitor->current_mode); - if (logical_monitor_is_rotated (lm)) - width = mode ? mode->height : 0; - else - width = mode ? mode->width : 0; - - if (monitor->config->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL) - return round (width / lm->scale); - else - return width; -} - -static void -add_y_delta (gpointer d1, gpointer d2) -{ - CcDisplayLogicalMonitor *m = d1; - int delta = GPOINTER_TO_INT (d2); - m->y += delta; -} - -static void -cc_display_config_dbus_ensure_non_offset_coords (CcDisplayConfigDBus *self) -{ - GList *x_axis, *y_axis; - CcDisplayLogicalMonitor *m; - - if (g_hash_table_size (self->logical_monitors) == 0) - return; - - x_axis = g_hash_table_get_keys (self->logical_monitors); - x_axis = g_list_sort (x_axis, sort_x_axis); - y_axis = g_hash_table_get_keys (self->logical_monitors); - y_axis = g_list_sort (y_axis, sort_y_axis); - - m = x_axis->data; - if (m->x != 0) - g_list_foreach (x_axis, add_x_delta, GINT_TO_POINTER (- m->x)); - - m = y_axis->data; - if (m->y != 0) - g_list_foreach (y_axis, add_y_delta, GINT_TO_POINTER (- m->y)); - - g_list_free (x_axis); - g_list_free (y_axis); -} - -static void -cc_display_config_dbus_append_right (CcDisplayConfigDBus *self, - CcDisplayLogicalMonitor *monitor) -{ - GList *x_axis; - CcDisplayLogicalMonitor *last; - - if (g_hash_table_size (self->logical_monitors) == 0) - { - monitor->x = 0; - monitor->y = 0; - return; - } - - x_axis = g_hash_table_get_keys (self->logical_monitors); - x_axis = g_list_sort (x_axis, sort_x_axis); - last = g_list_last (x_axis)->data; - monitor->x = last->x + logical_monitor_width (last); - monitor->y = last->y; - - g_list_free (x_axis); -} - -static void -cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self) -{ - CcDisplayLogicalMonitor *primary; - GList *logical_monitors, *l; - int x; - - if (self->primary && self->primary->logical_monitor) - { - primary = self->primary->logical_monitor; - primary->x = primary->y = 0; - x = logical_monitor_width (primary); - } - else - { - primary = NULL; - x = 0; - } - - logical_monitors = g_hash_table_get_keys (self->logical_monitors); - for (l = logical_monitors; l != NULL; l = l->next) - { - CcDisplayLogicalMonitor *m = l->data; - - if (m == primary) - continue; - - m->x = x; - m->y = 0; - x += logical_monitor_width (m); - } - - g_list_free (logical_monitors); -} diff --git a/panels/display/cc-display-config-dbus.h b/panels/display/cc-display-config-dbus.h deleted file mode 100644 index 8609087c75..0000000000 --- a/panels/display/cc-display-config-dbus.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#pragma once - -#include - -#include "cc-display-config.h" - -G_BEGIN_DECLS - -#define CC_TYPE_DISPLAY_MODE_DBUS (cc_display_mode_dbus_get_type ()) -G_DECLARE_FINAL_TYPE (CcDisplayModeDBus, cc_display_mode_dbus, - CC, DISPLAY_MODE_DBUS, CcDisplayMode) - -#define CC_TYPE_DISPLAY_MONITOR_DBUS (cc_display_monitor_dbus_get_type ()) -G_DECLARE_FINAL_TYPE (CcDisplayMonitorDBus, cc_display_monitor_dbus, - CC, DISPLAY_MONITOR_DBUS, CcDisplayMonitor) - -#define CC_TYPE_DISPLAY_CONFIG_DBUS (cc_display_config_dbus_get_type ()) -G_DECLARE_FINAL_TYPE (CcDisplayConfigDBus, cc_display_config_dbus, - CC, DISPLAY_CONFIG_DBUS, CcDisplayConfig) - -G_END_DECLS diff --git a/panels/display/cc-display-config-manager-dbus.c b/panels/display/cc-display-config-manager-dbus.c deleted file mode 100644 index 678b696dbf..0000000000 --- a/panels/display/cc-display-config-manager-dbus.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include "cc-display-config-dbus.h" -#include "cc-display-config-manager-dbus.h" - -#include - -struct _CcDisplayConfigManagerDBus -{ - CcDisplayConfigManager parent_instance; - - GCancellable *cancellable; - GDBusConnection *connection; - guint monitors_changed_id; - - GVariant *current_state; - - gboolean apply_allowed; - gboolean night_light_supported; -}; - -G_DEFINE_TYPE (CcDisplayConfigManagerDBus, - cc_display_config_manager_dbus, - CC_TYPE_DISPLAY_CONFIG_MANAGER) - -static CcDisplayConfig * -cc_display_config_manager_dbus_get_current (CcDisplayConfigManager *pself) -{ - CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (pself); - - if (!self->current_state) - return NULL; - - return g_object_new (CC_TYPE_DISPLAY_CONFIG_DBUS, - "state", self->current_state, - "connection", self->connection, NULL); -} - -static void -got_current_state (GObject *object, - GAsyncResult *result, - gpointer data) -{ - CcDisplayConfigManagerDBus *self; - GVariant *variant; - g_autoptr(GError) error = NULL; - - variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), - result, &error); - if (!variant) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data); - g_clear_pointer (&self->current_state, g_variant_unref); - _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (data)); - g_warning ("Error calling GetCurrentState: %s", error->message); - } - return; - } - - self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data); - g_clear_pointer (&self->current_state, g_variant_unref); - self->current_state = variant; - - _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (self)); -} - -static void -get_current_state (CcDisplayConfigManagerDBus *self) -{ - g_dbus_connection_call (self->connection, - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - "GetCurrentState", - NULL, - NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - self->cancellable, - got_current_state, - self); -} - -static void -monitors_changed (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer data) -{ - CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data); - get_current_state (self); -} - -static void -bus_gotten (GObject *object, - GAsyncResult *result, - gpointer data) -{ - CcDisplayConfigManagerDBus *self; - GDBusConnection *connection; - g_autoptr(GError) error = NULL; - g_autoptr(GDBusProxy) proxy = NULL; - g_autoptr(GVariant) variant = NULL; - - connection = g_bus_get_finish (result, &error); - if (!connection) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - _cc_display_config_manager_emit_changed (CC_DISPLAY_CONFIG_MANAGER (data)); - g_warning ("Error obtaining DBus connection: %s", error->message); - } - return; - } - - self = CC_DISPLAY_CONFIG_MANAGER_DBUS (data); - self->connection = connection; - self->monitors_changed_id = - g_dbus_connection_signal_subscribe (self->connection, - "org.gnome.Mutter.DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - "MonitorsChanged", - "/org/gnome/Mutter/DisplayConfig", - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - monitors_changed, - self, - NULL); - - proxy = g_dbus_proxy_new_sync (self->connection, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - NULL, - &error); - if (!proxy) - { - g_warning ("Failed to create D-Bus proxy to \"org.gnome.Mutter.DisplayConfig\": %s", - error->message); - return; - } - - variant = g_dbus_proxy_get_cached_property (proxy, "ApplyMonitorsConfigAllowed"); - if (variant) - self->apply_allowed = g_variant_get_boolean (variant); - else - g_warning ("Missing property 'ApplyMonitorsConfigAllowed' on DisplayConfig API"); - - variant = g_dbus_proxy_get_cached_property (proxy, "NightLightSupported"); - if (variant) - self->night_light_supported = g_variant_get_boolean (variant); - else - g_warning ("Missing property 'NightLightSupported' on DisplayConfig API"); - - get_current_state (self); -} - -static void -cc_display_config_manager_dbus_init (CcDisplayConfigManagerDBus *self) -{ - self->apply_allowed = TRUE; - self->night_light_supported = TRUE; - self->cancellable = g_cancellable_new (); - g_bus_get (G_BUS_TYPE_SESSION, self->cancellable, bus_gotten, self); -} - -static void -cc_display_config_manager_dbus_finalize (GObject *object) -{ - CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (object); - - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - - if (self->monitors_changed_id && self->connection) - g_dbus_connection_signal_unsubscribe (self->connection, - self->monitors_changed_id); - g_clear_object (&self->connection); - g_clear_pointer (&self->current_state, g_variant_unref); - - G_OBJECT_CLASS (cc_display_config_manager_dbus_parent_class)->finalize (object); -} - -static gboolean -cc_display_config_manager_dbus_get_apply_allowed (CcDisplayConfigManager *pself) -{ - CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (pself); - - return self->apply_allowed; -} - -static gboolean -cc_display_config_manager_dbus_get_night_light_supported (CcDisplayConfigManager *pself) -{ - CcDisplayConfigManagerDBus *self = CC_DISPLAY_CONFIG_MANAGER_DBUS (pself); - - return self->night_light_supported; -} - -static void -cc_display_config_manager_dbus_class_init (CcDisplayConfigManagerDBusClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - CcDisplayConfigManagerClass *parent_class = CC_DISPLAY_CONFIG_MANAGER_CLASS (klass); - - gobject_class->finalize = cc_display_config_manager_dbus_finalize; - - parent_class->get_current = cc_display_config_manager_dbus_get_current; - parent_class->get_apply_allowed = cc_display_config_manager_dbus_get_apply_allowed; - parent_class->get_night_light_supported = cc_display_config_manager_dbus_get_night_light_supported; -} - -CcDisplayConfigManager * -cc_display_config_manager_dbus_new (void) -{ - return g_object_new (CC_TYPE_DISPLAY_CONFIG_MANAGER_DBUS, NULL); -} diff --git a/panels/display/cc-display-config-manager-dbus.h b/panels/display/cc-display-config-manager-dbus.h deleted file mode 100644 index a099598133..0000000000 --- a/panels/display/cc-display-config-manager-dbus.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#pragma once - -#include - -#include "cc-display-config-manager.h" - -G_BEGIN_DECLS - -#define CC_TYPE_DISPLAY_CONFIG_MANAGER_DBUS (cc_display_config_manager_dbus_get_type ()) -G_DECLARE_FINAL_TYPE (CcDisplayConfigManagerDBus, cc_display_config_manager_dbus, - CC, DISPLAY_CONFIG_MANAGER_DBUS, CcDisplayConfigManager) - -CcDisplayConfigManager * cc_display_config_manager_dbus_new (void); - -G_END_DECLS diff --git a/panels/display/cc-display-config-manager.c b/panels/display/cc-display-config-manager.c index f231edd691..5de6f77d4b 100644 --- a/panels/display/cc-display-config-manager.c +++ b/panels/display/cc-display-config-manager.c @@ -19,6 +19,22 @@ #include "cc-display-config-manager.h" +#include + +struct _CcDisplayConfigManager +{ + GObject parent_instance; + + GCancellable *cancellable; + GDBusConnection *connection; + guint monitors_changed_id; + + GVariant *current_state; + + gboolean apply_allowed; + gboolean night_light_supported; +}; + G_DEFINE_TYPE (CcDisplayConfigManager, cc_display_config_manager, G_TYPE_OBJECT) @@ -31,14 +47,166 @@ enum static guint config_manager_signals[N_CONFIG_MANAGER_SIGNALS] = { 0 }; +static void +got_current_state (GObject *object, + GAsyncResult *result, + gpointer data) +{ + CcDisplayConfigManager *self; + GVariant *variant; + g_autoptr(GError) error = NULL; + + variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), + result, &error); + if (!variant) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + self = CC_DISPLAY_CONFIG_MANAGER (data); + g_clear_pointer (&self->current_state, g_variant_unref); + g_signal_emit (self, config_manager_signals[CONFIG_MANAGER_CHANGED], 0); + g_warning ("Error calling GetCurrentState: %s", error->message); + } + return; + } + + self = CC_DISPLAY_CONFIG_MANAGER (data); + g_clear_pointer (&self->current_state, g_variant_unref); + self->current_state = variant; + + g_signal_emit (self, config_manager_signals[CONFIG_MANAGER_CHANGED], 0); +} + +static void +get_current_state (CcDisplayConfigManager *self) +{ + g_dbus_connection_call (self->connection, + "org.gnome.Mutter.DisplayConfig", + "/org/gnome/Mutter/DisplayConfig", + "org.gnome.Mutter.DisplayConfig", + "GetCurrentState", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, + self->cancellable, + got_current_state, + self); +} + +static void +monitors_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer data) +{ + CcDisplayConfigManager *self = CC_DISPLAY_CONFIG_MANAGER (data); + get_current_state (self); +} + +static void +bus_gotten (GObject *object, + GAsyncResult *result, + gpointer data) +{ + CcDisplayConfigManager *self; + GDBusConnection *connection; + g_autoptr(GError) error = NULL; + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GVariant) variant = NULL; + + connection = g_bus_get_finish (result, &error); + if (!connection) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + self = CC_DISPLAY_CONFIG_MANAGER (data); + g_signal_emit (self, config_manager_signals[CONFIG_MANAGER_CHANGED], 0); + g_warning ("Error obtaining DBus connection: %s", error->message); + } + return; + } + + self = CC_DISPLAY_CONFIG_MANAGER (data); + self->connection = connection; + self->monitors_changed_id = + g_dbus_connection_signal_subscribe (self->connection, + "org.gnome.Mutter.DisplayConfig", + "org.gnome.Mutter.DisplayConfig", + "MonitorsChanged", + "/org/gnome/Mutter/DisplayConfig", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + monitors_changed, + self, + NULL); + + proxy = g_dbus_proxy_new_sync (self->connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.Mutter.DisplayConfig", + "/org/gnome/Mutter/DisplayConfig", + "org.gnome.Mutter.DisplayConfig", + NULL, + &error); + if (!proxy) + { + g_warning ("Failed to create D-Bus proxy to \"org.gnome.Mutter.DisplayConfig\": %s", + error->message); + return; + } + + variant = g_dbus_proxy_get_cached_property (proxy, "ApplyMonitorsConfigAllowed"); + if (variant) + self->apply_allowed = g_variant_get_boolean (variant); + else + g_warning ("Missing property 'ApplyMonitorsConfigAllowed' on DisplayConfig API"); + + variant = g_dbus_proxy_get_cached_property (proxy, "NightLightSupported"); + if (variant) + self->night_light_supported = g_variant_get_boolean (variant); + else + g_warning ("Missing property 'NightLightSupported' on DisplayConfig API"); + + get_current_state (self); +} + +static void +cc_display_config_manager_finalize (GObject *object) +{ + CcDisplayConfigManager *self = CC_DISPLAY_CONFIG_MANAGER (object); + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + if (self->monitors_changed_id && self->connection) + g_dbus_connection_signal_unsubscribe (self->connection, + self->monitors_changed_id); + g_clear_object (&self->connection); + g_clear_pointer (&self->current_state, g_variant_unref); + + G_OBJECT_CLASS (cc_display_config_manager_parent_class)->finalize (object); +} + static void cc_display_config_manager_init (CcDisplayConfigManager *self) { + self->apply_allowed = TRUE; + self->night_light_supported = TRUE; + self->cancellable = g_cancellable_new (); + g_bus_get (G_BUS_TYPE_SESSION, self->cancellable, bus_gotten, self); } static void cc_display_config_manager_class_init (CcDisplayConfigManagerClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = cc_display_config_manager_finalize; + config_manager_signals[CONFIG_MANAGER_CHANGED] = g_signal_new ("changed", CC_TYPE_DISPLAY_CONFIG_MANAGER, @@ -48,26 +216,31 @@ cc_display_config_manager_class_init (CcDisplayConfigManagerClass *klass) G_TYPE_NONE, 0); } -void -_cc_display_config_manager_emit_changed (CcDisplayConfigManager *self) -{ - g_signal_emit (self, config_manager_signals[CONFIG_MANAGER_CHANGED], 0); -} - CcDisplayConfig * cc_display_config_manager_get_current (CcDisplayConfigManager *self) { - return CC_DISPLAY_CONFIG_MANAGER_GET_CLASS (self)->get_current (self); + if (!self->current_state) + return NULL; + + return g_object_new (CC_TYPE_DISPLAY_CONFIG, + "state", self->current_state, + "connection", self->connection, NULL); } gboolean cc_display_config_manager_get_apply_allowed (CcDisplayConfigManager *self) { - return CC_DISPLAY_CONFIG_MANAGER_GET_CLASS (self)->get_apply_allowed (self); + return self->apply_allowed; } gboolean cc_display_config_manager_get_night_light_supported (CcDisplayConfigManager *self) { - return CC_DISPLAY_CONFIG_MANAGER_GET_CLASS (self)->get_night_light_supported (self); + return self->night_light_supported; +} + +CcDisplayConfigManager * +cc_display_config_manager_new (void) +{ + return g_object_new (CC_TYPE_DISPLAY_CONFIG_MANAGER, NULL); } diff --git a/panels/display/cc-display-config-manager.h b/panels/display/cc-display-config-manager.h index ab1e84f853..3c8995801a 100644 --- a/panels/display/cc-display-config-manager.h +++ b/panels/display/cc-display-config-manager.h @@ -26,17 +26,8 @@ G_BEGIN_DECLS #define CC_TYPE_DISPLAY_CONFIG_MANAGER (cc_display_config_manager_get_type ()) -G_DECLARE_DERIVABLE_TYPE (CcDisplayConfigManager, cc_display_config_manager, - CC, DISPLAY_CONFIG_MANAGER, GObject) - -struct _CcDisplayConfigManagerClass -{ - GObjectClass parent_class; - - CcDisplayConfig * (*get_current) (CcDisplayConfigManager *self); - gboolean (* get_apply_allowed) (CcDisplayConfigManager *self); - gboolean (* get_night_light_supported) (CcDisplayConfigManager *self); -}; +G_DECLARE_FINAL_TYPE (CcDisplayConfigManager, cc_display_config_manager, + CC, DISPLAY_CONFIG_MANAGER, GObject) CcDisplayConfig * cc_display_config_manager_get_current (CcDisplayConfigManager *self); @@ -44,6 +35,6 @@ gboolean cc_display_config_manager_get_apply_allowed (CcDisplayConfigManager *se gboolean cc_display_config_manager_get_night_light_supported (CcDisplayConfigManager *self); -void _cc_display_config_manager_emit_changed (CcDisplayConfigManager *self); +CcDisplayConfigManager * cc_display_config_manager_new (void); G_END_DECLS diff --git a/panels/display/cc-display-config.c b/panels/display/cc-display-config.c index f2ccda5221..7b0759298c 100644 --- a/panels/display/cc-display-config.c +++ b/panels/display/cc-display-config.c @@ -21,12 +21,202 @@ #include #include "cc-display-config.h" +#define MODE_BASE_FORMAT "siiddad" +#define MODE_FORMAT "(" MODE_BASE_FORMAT "a{sv})" +#define MODES_FORMAT "a" MODE_FORMAT +#define MONITOR_SPEC_FORMAT "(ssss)" +#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})" +#define MONITORS_FORMAT "a" MONITOR_FORMAT + +#define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT +#define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})" +#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT + +#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})" + +enum +{ + PROP_0, + PROP_STATE, + PROP_CONNECTION, +}; + +typedef enum _CcDisplayModeFlags +{ + MODE_PREFERRED = 1 << 0, + MODE_CURRENT = 1 << 1, + MODE_INTERLACED = 1 << 2, +} CcDisplayModeFlags; + +typedef enum _CcDisplayLayoutMode +{ + CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1, + CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2 +} CcDisplayLayoutMode; + +typedef enum _CcDisplayConfigMethod +{ + CC_DISPLAY_CONFIG_METHOD_VERIFY = 0, + CC_DISPLAY_CONFIG_METHOD_TEMPORARY = 1, + CC_DISPLAY_CONFIG_METHOD_PERSISTENT = 2 +} CcDisplayConfigMethod; + +typedef enum _CcDisplayMonitorUnderscanning +{ + UNDERSCANNING_UNSUPPORTED = 0, + UNDERSCANNING_DISABLED, + UNDERSCANNING_ENABLED +} CcDisplayMonitorUnderscanning; + +struct _CcDisplayMode +{ + GObject parent_instance; + + CcDisplayMonitor *monitor; + + char *id; + int width; + int height; + double refresh_rate; + double preferred_scale; + GArray *supported_scales; + guint32 flags; +}; + +struct _CcDisplayLogicalMonitor +{ + GObject parent_instance; + + int x; + int y; + double scale; + CcDisplayRotation rotation; + gboolean primary; + + GHashTable *monitors; +}; + +struct _CcDisplayMonitor +{ + GObject parent_instance; + + int ui_number; + char *ui_name; + char *ui_number_name; + gboolean is_usable; + + CcDisplayConfig *config; + + char *connector_name; + char *vendor_name; + char *product_name; + char *product_serial; + char *display_name; + + int width_mm; + int height_mm; + gboolean builtin; + CcDisplayMonitorUnderscanning underscanning; + CcDisplayMonitorPrivacy privacy_screen; + int max_width; + int max_height; + + GList *modes; + CcDisplayMode *current_mode; + CcDisplayMode *preferred_mode; + + CcDisplayLogicalMonitor *logical_monitor; +}; + +struct _CcDisplayConfig +{ + GObject parent_instance; + + GList *ui_sorted_monitors; + + GVariant *state; + GDBusConnection *connection; + GDBusProxy *proxy; + + int min_width; + int min_height; + + guint panel_orientation_managed; + + guint32 serial; + gboolean supports_mirroring; + gboolean supports_changing_layout_mode; + gboolean global_scale_required; + CcDisplayLayoutMode layout_mode; + + GList *monitors; + CcDisplayMonitor *primary; + + GHashTable *logical_monitors; +}; + +G_DEFINE_TYPE (CcDisplayMode, + cc_display_mode, + G_TYPE_OBJECT) + +G_DEFINE_TYPE (CcDisplayMonitor, + cc_display_monitor, + G_TYPE_OBJECT) + +G_DEFINE_TYPE (CcDisplayLogicalMonitor, + cc_display_logical_monitor, + G_TYPE_OBJECT) + +G_DEFINE_TYPE (CcDisplayConfig, + cc_display_config, + G_TYPE_OBJECT) + static const double known_diagonals[] = { 12.1, 13.3, 15.6 }; +static gint +logical_monitor_sort_x_axis (gconstpointer a, + gconstpointer b) +{ + const CcDisplayLogicalMonitor *ma = a; + const CcDisplayLogicalMonitor *mb = b; + + return ma->x - mb->x; +} + +static gint +logical_monitor_sort_y_axis (gconstpointer a, + gconstpointer b) +{ + const CcDisplayLogicalMonitor *ma = a; + const CcDisplayLogicalMonitor *mb = b; + + return ma->y - mb->y; +} + +static void +logical_monitor_add_x_delta (gpointer d1, + gpointer d2) +{ + CcDisplayLogicalMonitor *m = d1; + int delta = GPOINTER_TO_INT (d2); + + m->x += delta; +} + +static void +logical_monitor_add_y_delta (gpointer d1, + gpointer d2) +{ + CcDisplayLogicalMonitor *m = d1; + int delta = GPOINTER_TO_INT (d2); + + m->y += delta; +} + static char * diagonal_to_str (double d) { @@ -74,102 +264,252 @@ make_output_ui_name (CcDisplayMonitor *output) return g_strdup_printf ("%s", cc_display_monitor_get_display_name (output)); } - - -G_DEFINE_TYPE (CcDisplayMode, - cc_display_mode, - G_TYPE_OBJECT) - static void cc_display_mode_init (CcDisplayMode *self) { + self->supported_scales = g_array_new (FALSE, FALSE, sizeof (double)); +} + +static void +cc_display_mode_finalize (GObject *object) +{ + CcDisplayMode *self = CC_DISPLAY_MODE (object); + + g_free (self->id); + g_array_free (self->supported_scales, TRUE); + + G_OBJECT_CLASS (cc_display_mode_parent_class)->finalize (object); } static void cc_display_mode_class_init (CcDisplayModeClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = cc_display_mode_finalize; } gboolean cc_display_mode_is_clone_mode (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->is_clone_mode (self); + return !self->id; } void cc_display_mode_get_resolution (CcDisplayMode *self, int *w, int *h) { - return CC_DISPLAY_MODE_GET_CLASS (self)->get_resolution (self, w, h); + if (w) + *w = self->width; + if (h) + *h = self->height; +} + +static gboolean +cc_display_mode_is_supported_scale (CcDisplayMode *self, + double scale) +{ + unsigned int i; + + for (i = 0; i < self->supported_scales->len; i++) + { + double v = g_array_index (self->supported_scales, double, i); + + if (G_APPROX_VALUE (v, scale, DBL_EPSILON)) + return TRUE; + } + return FALSE; +} + +static gboolean +is_scale_allowed_by_active_monitors (CcDisplayConfig *self, + CcDisplayMode *mode, + double scale) +{ + GList *l; + + for (l = self->monitors; l != NULL; l = l->next) + { + CcDisplayMonitor *m = CC_DISPLAY_MONITOR (l->data); + + if (!cc_display_monitor_is_active (CC_DISPLAY_MONITOR (m))) + continue; + + if (!cc_display_mode_is_supported_scale (mode, scale)) + return FALSE; + } + + return TRUE; +} + +static gboolean +is_scaled_mode_allowed (CcDisplayConfig *self, + CcDisplayMode *mode, + double scale) +{ + gint width, height; + + /* Do the math as if the monitor is always in landscape mode. */ + width = round (mode->width / scale); + height = round (mode->height / scale); + + if (MAX (width, height) < self->min_width || + MIN (width, height) < self->min_height) + return FALSE; + + if (!self->global_scale_required) + return TRUE; + + return is_scale_allowed_by_active_monitors (self, CC_DISPLAY_MODE (mode), scale); } GArray * cc_display_mode_get_supported_scales (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->get_supported_scales (self); + CcDisplayConfig *config = CC_DISPLAY_CONFIG (self->monitor->config); + + if (cc_display_config_is_cloning (config)) + { + GArray *scales = g_array_copy (self->supported_scales); + int i; + + for (i = scales->len - 1; i >= 0; i--) + { + double scale = g_array_index (scales, double, i); + + if (!is_scale_allowed_by_active_monitors (self->monitor->config, + self, scale)) + g_array_remove_index (scales, i); + } + + return g_steal_pointer (&scales); + } + + return g_array_ref (self->supported_scales); } double cc_display_mode_get_preferred_scale (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->get_preferred_scale (self); + return self->preferred_scale; } gboolean cc_display_mode_is_interlaced (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->is_interlaced (self); + return !!(self->flags & MODE_INTERLACED); } gboolean cc_display_mode_is_preferred (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->is_preferred (self); + return !!(self->flags & MODE_PREFERRED); } int cc_display_mode_get_freq (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->get_freq (self); + return self->refresh_rate; } double cc_display_mode_get_freq_f (CcDisplayMode *self) { - return CC_DISPLAY_MODE_GET_CLASS (self)->get_freq_f (self); + return self->refresh_rate; } +static CcDisplayMode * +cc_display_mode_new_virtual (int width, + int height, + double preferred_scale, + GArray *supported_scales) +{ + g_autoptr(GVariant) properties_variant = NULL; + CcDisplayMode *self; -struct _CcDisplayMonitorPrivate { - int ui_number; - gchar *ui_name; - gchar *ui_number_name; - gboolean is_usable; -}; -typedef struct _CcDisplayMonitorPrivate CcDisplayMonitorPrivate; + self = g_object_new (CC_TYPE_DISPLAY_MODE, NULL); + + self->width = width; + self->height = height; + self->preferred_scale = preferred_scale; + self->supported_scales = g_array_ref (supported_scales); + + return self; +} -G_DEFINE_TYPE_WITH_PRIVATE (CcDisplayMonitor, - cc_display_monitor, - G_TYPE_OBJECT) +static CcDisplayMode * +cc_display_mode_new (CcDisplayMonitor *monitor, + GVariant *variant) +{ + double d; + g_autoptr(GVariantIter) scales_iter = NULL; + g_autoptr(GVariant) properties_variant = NULL; + gboolean is_current; + gboolean is_preferred; + gboolean is_interlaced; + CcDisplayMode *self; + + self = g_object_new (CC_TYPE_DISPLAY_MODE, NULL); + self->monitor = monitor; + + g_variant_get (variant, "(" MODE_BASE_FORMAT "@a{sv})", + &self->id, + &self->width, + &self->height, + &self->refresh_rate, + &self->preferred_scale, + &scales_iter, + &properties_variant); + + while (g_variant_iter_next (scales_iter, "d", &d)) + g_array_append_val (self->supported_scales, d); + + if (!g_variant_lookup (properties_variant, "is-current", "b", &is_current)) + is_current = FALSE; + if (!g_variant_lookup (properties_variant, "is-preferred", "b", &is_preferred)) + is_preferred = FALSE; + if (!g_variant_lookup (properties_variant, "is-interlaced", "b", &is_interlaced)) + is_interlaced = FALSE; + + if (is_current) + self->flags |= MODE_CURRENT; + if (is_preferred) + self->flags |= MODE_PREFERRED; + if (is_interlaced) + self->flags |= MODE_INTERLACED; + + return self; +} static void cc_display_monitor_init (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - priv->ui_number = 0; - priv->ui_name = NULL; - priv->ui_number_name = NULL; - priv->is_usable = TRUE; + self->ui_number = 0; + self->is_usable = TRUE; + self->underscanning = UNDERSCANNING_UNSUPPORTED; + self->max_width = G_MAXINT; + self->max_height = G_MAXINT; } static void cc_display_monitor_finalize (GObject *object) { CcDisplayMonitor *self = CC_DISPLAY_MONITOR (object); - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - g_clear_pointer (&priv->ui_name, g_free); - g_clear_pointer (&priv->ui_number_name, g_free); + g_free (self->ui_name); + g_free (self->ui_number_name); + g_free (self->connector_name); + g_free (self->vendor_name); + g_free (self->product_name); + g_free (self->product_serial); + g_free (self->display_name); + + g_list_free_full (self->modes, g_object_unref); + + if (self->logical_monitor) + { + g_hash_table_remove (self->logical_monitor->monitors, self); + g_object_unref (self->logical_monitor); + } G_OBJECT_CLASS (cc_display_monitor_parent_class)->finalize (object); } @@ -221,178 +561,526 @@ cc_display_monitor_class_init (CcDisplayMonitorClass *klass) const char * cc_display_monitor_get_display_name (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_display_name (self); + if (self->display_name) + return self->display_name; + + return self->connector_name; } const char * cc_display_monitor_get_connector_name (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_connector_name (self); + return self->connector_name; } gboolean cc_display_monitor_is_builtin (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->is_builtin (self); + return self->builtin; } gboolean cc_display_monitor_is_primary (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->is_primary (self); + if (self->logical_monitor) + return self->logical_monitor->primary; + + return FALSE; +} + +static void +cc_display_config_set_primary (CcDisplayConfig *self, + CcDisplayMonitor *new_primary) +{ + if (self->primary == new_primary) + return; + + if (!new_primary->logical_monitor) + return; + + if (self->primary && self->primary->logical_monitor) + { + self->primary->logical_monitor->primary = FALSE; + g_signal_emit_by_name (self->primary, "primary"); + } + + self->primary = new_primary; + self->primary->logical_monitor->primary = TRUE; + + g_signal_emit_by_name (self->primary, "primary"); + g_signal_emit_by_name (self, "primary"); +} + +static void +cc_display_config_unset_primary (CcDisplayConfig *self, + CcDisplayMonitor *old_primary) +{ + GList *l; + + if (self->primary != old_primary) + return; + + for (l = self->monitors; l; l = l->next) + { + CcDisplayMonitor *monitor = l->data; + if (monitor->logical_monitor && + monitor != old_primary) + { + cc_display_config_set_primary (self, monitor); + break; + } + } + + if (self->primary == old_primary) + self->primary = NULL; } void -cc_display_monitor_set_primary (CcDisplayMonitor *self, gboolean primary) +cc_display_monitor_set_primary (CcDisplayMonitor *self, + gboolean primary) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_primary (self, primary); + if (primary) + cc_display_config_set_primary (self->config, self); + else + cc_display_config_unset_primary (self->config, self); } gboolean cc_display_monitor_is_active (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->is_active (self); + return self->logical_monitor != NULL; +} + +static void +cc_display_monitor_set_logical_monitor (CcDisplayMonitor *self, + CcDisplayLogicalMonitor *logical_monitor) +{ + gboolean was_primary = FALSE; + + if (self->logical_monitor) + { + was_primary = self->logical_monitor->primary; + if (was_primary) + cc_display_config_unset_primary (self->config, self); + g_hash_table_remove (self->logical_monitor->monitors, self); + g_object_unref (self->logical_monitor); + } + + self->logical_monitor = logical_monitor; + + if (self->logical_monitor) + { + g_hash_table_add (self->logical_monitor->monitors, self); + g_object_ref (self->logical_monitor); + /* unset primary with NULL will select this monitor if it is the only one.*/ + if (was_primary) + cc_display_config_set_primary (self->config, self); + else + cc_display_config_unset_primary (self->config, NULL); + } +} + +static void +remove_logical_monitor (gpointer data, + GObject *object) +{ + CcDisplayConfig *self = CC_DISPLAY_CONFIG (data); + + g_hash_table_remove (self->logical_monitors, object); +} + +static void +register_logical_monitor (CcDisplayConfig *self, + CcDisplayLogicalMonitor *logical_monitor) +{ + g_hash_table_add (self->logical_monitors, logical_monitor); + g_object_weak_ref (G_OBJECT (logical_monitor), remove_logical_monitor, self); + g_object_unref (logical_monitor); +} + +static gboolean +logical_monitor_is_rotated (CcDisplayLogicalMonitor *lm) +{ + switch (lm->rotation) + { + case CC_DISPLAY_ROTATION_90: + case CC_DISPLAY_ROTATION_270: + case CC_DISPLAY_ROTATION_90_FLIPPED: + case CC_DISPLAY_ROTATION_270_FLIPPED: + return TRUE; + default: + return FALSE; + } +} + +static int +logical_monitor_width (CcDisplayLogicalMonitor *lm) +{ + CcDisplayMonitor *monitor; + CcDisplayMode *mode; + GHashTableIter iter; + int width; + + g_hash_table_iter_init (&iter, lm->monitors); + g_hash_table_iter_next (&iter, (void **) &monitor, NULL); + mode = CC_DISPLAY_MODE (monitor->current_mode); + if (logical_monitor_is_rotated (lm)) + width = mode ? mode->height : 0; + else + width = mode ? mode->width : 0; + + if (monitor->config->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL) + return round (width / lm->scale); + else + return width; +} + + +static void +cc_display_config_append_right (CcDisplayConfig *self, + CcDisplayLogicalMonitor *monitor) +{ + GList *x_axis; + CcDisplayLogicalMonitor *last; + + if (g_hash_table_size (self->logical_monitors) == 0) + { + monitor->x = 0; + monitor->y = 0; + return; + } + + x_axis = g_hash_table_get_keys (self->logical_monitors); + x_axis = g_list_sort (x_axis, logical_monitor_sort_x_axis); + last = g_list_last (x_axis)->data; + monitor->x = last->x + logical_monitor_width (last); + monitor->y = last->y; + + g_list_free (x_axis); } void cc_display_monitor_set_active (CcDisplayMonitor *self, gboolean active) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_active (self, active); + if (!self->current_mode && active) + { + if (self->preferred_mode) + self->current_mode = self->preferred_mode; + else if (self->modes) + self->current_mode = (CcDisplayMode *) self->modes->data; + else + g_warning ("Couldn't find a mode to activate monitor at %s", self->connector_name); + } + + if (!self->logical_monitor && active) + { + CcDisplayLogicalMonitor *logical_monitor; + + logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); + cc_display_monitor_set_logical_monitor (self, logical_monitor); + cc_display_config_append_right (self->config, logical_monitor); + register_logical_monitor (self->config, logical_monitor); + } + else if (self->logical_monitor && !active) + { + cc_display_monitor_set_logical_monitor (self, NULL); + } + + g_signal_emit_by_name (self, "active"); } CcDisplayRotation cc_display_monitor_get_rotation (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_rotation (self); + if (self->logical_monitor) + return self->logical_monitor->rotation; + + return CC_DISPLAY_ROTATION_NONE; } void cc_display_monitor_set_rotation (CcDisplayMonitor *self, CcDisplayRotation rotation) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_rotation (self, rotation); + if (!self->logical_monitor) + return; + + if (self->logical_monitor->rotation != rotation) + { + self->logical_monitor->rotation = rotation; + + g_signal_emit_by_name (self, "rotation"); + } } gboolean cc_display_monitor_supports_rotation (CcDisplayMonitor *self, CcDisplayRotation r) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->supports_rotation (self, r); + return TRUE; } void cc_display_monitor_get_physical_size (CcDisplayMonitor *self, int *w, int *h) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_physical_size (self, w, h); + if (w) + *w = self->width_mm; + if (h) + *h = self->height_mm; } void cc_display_monitor_get_geometry (CcDisplayMonitor *self, int *x, int *y, int *w, int *h) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_geometry (self, x, y, w, h); + CcDisplayMode *mode = NULL; + + if (self->logical_monitor) + { + if (x) + *x = self->logical_monitor->x; + if (y) + *y = self->logical_monitor->y; + } + else + { + if (x) + *x = -1; + if (y) + *y = -1; + } + + if (self->current_mode) + mode = self->current_mode; + else if (self->preferred_mode) + mode = self->preferred_mode; + else if (self->modes) + mode = CC_DISPLAY_MODE (self->modes->data); + + if (mode) + cc_display_mode_get_resolution (mode, w, h); + else + { + g_warning ("Monitor at %s has no modes?", self->connector_name); + if (w) + *w = -1; + if (h) + *h = -1; + } } CcDisplayMode * cc_display_monitor_get_mode (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_mode (self); + return self->current_mode; } CcDisplayMode * cc_display_monitor_get_preferred_mode (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_preferred_mode (self); + return self->preferred_mode; } guint32 cc_display_monitor_get_id (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_id (self); + return 0; } GList * cc_display_monitor_get_modes (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_modes (self); + return self->modes; } gboolean cc_display_monitor_supports_underscanning (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->supports_underscanning (self); + return self->underscanning != UNDERSCANNING_UNSUPPORTED; } gboolean cc_display_monitor_get_underscanning (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_underscanning (self); + return self->underscanning == UNDERSCANNING_ENABLED; } void cc_display_monitor_set_underscanning (CcDisplayMonitor *self, gboolean underscanning) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_underscanning (self, underscanning); + if (self->underscanning == UNDERSCANNING_UNSUPPORTED) + return; + + if (underscanning) + self->underscanning = UNDERSCANNING_ENABLED; + else + self->underscanning = UNDERSCANNING_DISABLED; } CcDisplayMonitorPrivacy cc_display_monitor_get_privacy (CcDisplayMonitor *self) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_privacy (self); + return self->privacy_screen; } -void -cc_display_monitor_set_mode (CcDisplayMonitor *self, CcDisplayMode *m) +static CcDisplayMode * +cc_display_monitor_get_closest_mode (CcDisplayMonitor *self, + CcDisplayMode *mode) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_mode (self, m); -} + CcDisplayMode *best = NULL; + GList *l; -void -cc_display_monitor_set_compatible_clone_mode (CcDisplayMonitor *self, CcDisplayMode *m) -{ - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_mode (self, m); + for (l = self->modes; l != NULL; l = l->next) + { + CcDisplayMode *similar = l->data; + + if (similar->width != mode->width || + similar->height != mode->height) + continue; + + if (similar->refresh_rate == mode->refresh_rate && + (similar->flags & MODE_INTERLACED) == (mode->flags & MODE_INTERLACED)) + { + best = similar; + break; + } + + /* There might be a better heuristic. */ + if (!best || best->refresh_rate < similar->refresh_rate) + { + best = similar; + continue; + } + } + + return CC_DISPLAY_MODE (best); } void -cc_display_monitor_set_position (CcDisplayMonitor *self, int x, int y) +cc_display_monitor_set_mode (CcDisplayMonitor *self, + CcDisplayMode *new_mode) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_position (self, x, y); -} + CcDisplayMode *mode; -double -cc_display_monitor_get_scale (CcDisplayMonitor *self) + g_return_if_fail (new_mode != NULL); + + mode = cc_display_monitor_get_closest_mode (self, + CC_DISPLAY_MODE (new_mode)); + + self->current_mode = mode; + + if (!cc_display_mode_is_supported_scale (mode, + cc_display_monitor_get_scale (self))) + { + cc_display_monitor_set_scale (self, + cc_display_mode_get_preferred_scale (mode)); + } + + g_signal_emit_by_name (self, "mode"); +} + +void +cc_display_monitor_set_compatible_clone_mode (CcDisplayMonitor *self, + CcDisplayMode *clone_mode) +{ + GList *l; + CcDisplayMode *best_mode = NULL; + int clone_width, clone_height; + + g_return_if_fail (cc_display_mode_is_clone_mode (clone_mode)); + + cc_display_mode_get_resolution (clone_mode, &clone_width, &clone_height); + + for (l = self->modes; l; l = l->next) + { + CcDisplayMode *mode = l->data; + int width, height; + + cc_display_mode_get_resolution (mode, &width, &height); + if (width != clone_width || height != clone_height) + continue; + + if (!best_mode) + { + best_mode = mode; + continue; + } + + if (cc_display_mode_get_freq_f (mode) > + cc_display_mode_get_freq_f (best_mode)) + best_mode = mode; + } + + g_return_if_fail (best_mode); + + cc_display_monitor_set_mode (CC_DISPLAY_MONITOR (self), best_mode); +} + +void +cc_display_monitor_set_position (CcDisplayMonitor *self, int x, int y) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->get_scale (self); + if (self->logical_monitor) + { + gboolean notify = FALSE; + + if (self->logical_monitor->x != x || self->logical_monitor->y != y) + notify = TRUE; + + self->logical_monitor->x = x; + self->logical_monitor->y = y; + + if (notify) + g_signal_emit_by_name (self, "position-changed"); + } +} + +double +cc_display_monitor_get_scale (CcDisplayMonitor *self) +{ + if (self->logical_monitor) + return self->logical_monitor->scale; + + return 1.0; } void -cc_display_monitor_set_scale (CcDisplayMonitor *self, double s) +cc_display_monitor_set_scale (CcDisplayMonitor *self, + double scale) { - return CC_DISPLAY_MONITOR_GET_CLASS (self)->set_scale (self, s); + if (!self->current_mode) + return; + + if (!cc_display_mode_is_supported_scale (self->current_mode, scale)) + return; + + if (!self->logical_monitor) + return; + + if (!G_APPROX_VALUE (self->logical_monitor->scale, scale, DBL_EPSILON)) + { + self->logical_monitor->scale = scale; + + g_signal_emit_by_name (self, "scale"); + } } gboolean cc_display_monitor_is_useful (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return priv->is_usable && + return self->is_usable && cc_display_monitor_is_active (self); } gboolean cc_display_monitor_is_usable (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return priv->is_usable; + return self->is_usable; } void -cc_display_monitor_set_usable (CcDisplayMonitor *self, gboolean is_usable) +cc_display_monitor_set_usable (CcDisplayMonitor *self, + gboolean is_usable) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - priv->is_usable = is_usable; + self->is_usable = is_usable; g_signal_emit_by_name (self, "is-usable"); } @@ -400,92 +1088,540 @@ cc_display_monitor_set_usable (CcDisplayMonitor *self, gboolean is_usable) gint cc_display_monitor_get_ui_number (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return priv->ui_number; + return self->ui_number; } const char * cc_display_monitor_get_ui_name (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return priv->ui_name; + return self->ui_name; } const char * cc_display_monitor_get_ui_number_name (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return priv->ui_number_name; + return self->ui_number_name; } char * cc_display_monitor_dup_ui_number_name (CcDisplayMonitor *self) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); - - return g_strdup (priv->ui_number_name); + return g_strdup (self->ui_number_name); } static void cc_display_monitor_set_ui_info (CcDisplayMonitor *self, gint ui_number, gchar *ui_name) { - CcDisplayMonitorPrivate *priv = cc_display_monitor_get_instance_private (self); + self->ui_number = ui_number; + g_free (self->ui_name); + self->ui_name = ui_name; + self->ui_number_name = g_strdup_printf ("%d\u2003%s", ui_number, ui_name); +} - priv->ui_number = ui_number; - g_free (priv->ui_name); - priv->ui_name = ui_name; - priv->ui_number_name = g_strdup_printf ("%d\u2003%s", ui_number, ui_name); +static void +update_panel_orientation_managed (CcDisplayConfig *self) +{ + g_autoptr(GVariant) v = NULL; + gboolean panel_orientation_managed = FALSE; + + if (self->proxy != NULL) + { + v = g_dbus_proxy_get_cached_property (self->proxy, "PanelOrientationManaged"); + if (v) + { + panel_orientation_managed = g_variant_get_boolean (v); + } + } + + if (panel_orientation_managed == self->panel_orientation_managed) + return; + + self->panel_orientation_managed = panel_orientation_managed; + g_signal_emit_by_name (self, "panel-orientation-managed", self->panel_orientation_managed); } -struct _CcDisplayConfigPrivate { - GList *ui_sorted_monitors; -}; -typedef struct _CcDisplayConfigPrivate CcDisplayConfigPrivate; +static void +proxy_properties_changed_cb (CcDisplayConfig *self, + GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties) +{ + GVariantDict dict; -G_DEFINE_TYPE_WITH_PRIVATE (CcDisplayConfig, - cc_display_config, - G_TYPE_OBJECT) + g_variant_dict_init (&dict, changed_properties); + + if (g_variant_dict_contains (&dict, "PanelOrientationManaged")) + update_panel_orientation_managed (self); +} static void -cc_display_config_init (CcDisplayConfig *self) +apply_global_scale_requirement (CcDisplayConfig *self, + CcDisplayMonitor *monitor) { - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); + GList *l; + double scale = cc_display_monitor_get_scale (monitor); - priv->ui_sorted_monitors = NULL; + for (l = self->monitors; l != NULL; l = l->next) + { + CcDisplayMonitor *m = l->data; + if (m != monitor) + cc_display_monitor_set_scale (m, scale); + } +} + +static void +construct_modes (CcDisplayMonitor *self, + GVariantIter *modes) +{ + CcDisplayMode *mode; + + while (TRUE) + { + g_autoptr(GVariant) variant = NULL; + + if (!g_variant_iter_next (modes, "@"MODE_FORMAT, &variant)) + break; + + mode = cc_display_mode_new (self, variant); + self->modes = g_list_prepend (self->modes, mode); + + if (mode->flags & MODE_PREFERRED) + self->preferred_mode = CC_DISPLAY_MODE (mode); + if (mode->flags & MODE_CURRENT) + self->current_mode = CC_DISPLAY_MODE (mode); + } + + self->modes = g_list_reverse (self->modes); +} + +static CcDisplayMonitor * +cc_display_monitor_new (GVariant *variant, + CcDisplayConfig *config) +{ + CcDisplayMonitor *self; + gchar *s1, *s2, *s3, *s4; + g_autoptr(GVariantIter) modes = NULL; + g_autoptr(GVariantIter) props = NULL; + + self = g_object_new (CC_TYPE_DISPLAY_MONITOR, NULL); + self->config = config; + + g_variant_get (variant, MONITOR_FORMAT, + &s1, &s2, &s3, &s4, &modes, &props); + self->connector_name = s1; + self->vendor_name = s2; + self->product_name = s3; + self->product_serial = s4; + + construct_modes (self, modes); + + while (TRUE) + { + const char *s; + g_autoptr(GVariant) v = NULL; + + if (!g_variant_iter_next (props, "{&sv}", &s, &v)) + break; + + if (g_str_equal (s, "width-mm")) + { + g_variant_get (v, "i", &self->width_mm); + } + else if (g_str_equal (s, "height-mm")) + { + g_variant_get (v, "i", &self->height_mm); + } + else if (g_str_equal (s, "is-underscanning")) + { + gboolean underscanning = FALSE; + g_variant_get (v, "b", &underscanning); + if (underscanning) + self->underscanning = UNDERSCANNING_ENABLED; + else + self->underscanning = UNDERSCANNING_DISABLED; + } + else if (g_str_equal (s, "max-screen-size")) + { + g_variant_get (v, "ii", &self->max_width, &self->max_height); + } + else if (g_str_equal (s, "is-builtin")) + { + g_variant_get (v, "b", &self->builtin); + } + else if (g_str_equal (s, "display-name")) + { + g_variant_get (v, "s", &self->display_name); + } + else if (g_str_equal (s, "privacy-screen-state")) + { + gboolean enabled; + gboolean locked; + g_variant_get (v, "(bb)", &enabled, &locked); + + if (enabled) + self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_ENABLED; + else + self->privacy_screen = CC_DISPLAY_MONITOR_PRIVACY_DISABLED; + + if (locked) + self->privacy_screen |= CC_DISPLAY_MONITOR_PRIVACY_LOCKED; + } + } + + return self; +} + +static gboolean +cc_display_logical_monitor_equal (const CcDisplayLogicalMonitor *m1, + const CcDisplayLogicalMonitor *m2) +{ + if (!m1 && !m2) + return TRUE; + else if (!m1 || !m2) + return FALSE; + + return m1->x == m2->x && + m1->y == m2->y && + G_APPROX_VALUE (m1->scale, m2->scale, DBL_EPSILON) && + m1->rotation == m2->rotation && + m1->primary == m2->primary; +} + +static void +cc_display_logical_monitor_finalize (GObject *object) +{ + CcDisplayLogicalMonitor *self = CC_DISPLAY_LOGICAL_MONITOR (object); + + g_warn_if_fail (g_hash_table_size (self->monitors) == 0); + g_clear_pointer (&self->monitors, g_hash_table_destroy); + + G_OBJECT_CLASS (cc_display_logical_monitor_parent_class)->finalize (object); +} + +static void +cc_display_logical_monitor_class_init (CcDisplayLogicalMonitorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = cc_display_logical_monitor_finalize; +} + +static void +cc_display_logical_monitor_init (CcDisplayLogicalMonitor *self) +{ + self->scale = 1.0; + self->monitors = g_hash_table_new (NULL, NULL); +} + +static CcDisplayMonitor * +monitor_from_spec (CcDisplayConfig *self, + const char *connector, + const char *vendor, + const char *product, + const char *serial) +{ + GList *l; + + for (l = self->monitors; l; l = l->next) + { + CcDisplayMonitor *m = l->data; + + if (g_str_equal (m->connector_name, connector) && + g_str_equal (m->vendor_name, vendor) && + g_str_equal (m->product_name, product) && + g_str_equal (m->product_serial, serial)) + return m; + } + + return NULL; +} + +static void +construct_monitors (CcDisplayConfig *self, + GVariantIter *monitors, + GVariantIter *logical_monitors) +{ + while (TRUE) + { + CcDisplayMonitor *monitor; + g_autoptr(GVariant) variant = NULL; + + if (!g_variant_iter_next (monitors, "@"MONITOR_FORMAT, &variant)) + break; + + monitor = cc_display_monitor_new (variant, self); + self->monitors = g_list_prepend (self->monitors, monitor); + + if (self->global_scale_required) + g_signal_connect_object (monitor, "scale", + G_CALLBACK (apply_global_scale_requirement), + self, G_CONNECT_SWAPPED); + } + + self->monitors = g_list_reverse (self->monitors); + + while (TRUE) + { + g_autoptr(GVariant) variant = NULL; + CcDisplayLogicalMonitor *logical_monitor; + g_autoptr(GVariantIter) monitor_specs = NULL; + const gchar *s1, *s2, *s3, *s4; + gboolean primary; + + if (!g_variant_iter_next (logical_monitors, "@"LOGICAL_MONITOR_FORMAT, &variant)) + break; + + logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); + g_variant_get (variant, LOGICAL_MONITOR_FORMAT, + &logical_monitor->x, + &logical_monitor->y, + &logical_monitor->scale, + &logical_monitor->rotation, + &primary, + &monitor_specs, + NULL); + + while (g_variant_iter_next (monitor_specs, "(&s&s&s&s)", &s1, &s2, &s3, &s4)) + { + CcDisplayMonitor *m = monitor_from_spec (self, s1, s2, s3, s4); + if (!m) + { + g_warning ("Couldn't find monitor given spec: %s, %s, %s, %s", + s1, s2, s3, s4); + continue; + } + + cc_display_monitor_set_logical_monitor (m, logical_monitor); + } + + if (g_hash_table_size (logical_monitor->monitors) > 0) + { + if (primary) + { + CcDisplayMonitor *m = NULL; + GHashTableIter iter; + g_hash_table_iter_init (&iter, logical_monitor->monitors); + g_hash_table_iter_next (&iter, (void **) &m, NULL); + + cc_display_config_set_primary (self, m); + } + } + else + { + g_warning ("Got an empty logical monitor, ignoring"); + } + + register_logical_monitor (self, logical_monitor); + } +} + +static void +filter_out_invalid_scaled_modes (CcDisplayConfig *self) +{ + GList *l; + + for (l = self->monitors; l; l = l->next) + { + CcDisplayMonitor *monitor = l->data; + GList *ll = monitor->modes; + + while (ll != NULL) + { + CcDisplayMode *mode = ll->data; + GList *current = ll; + double current_scale = -1; + int i; + + ll = ll->next; + + if (monitor->current_mode != CC_DISPLAY_MODE (mode) && + monitor->preferred_mode != CC_DISPLAY_MODE (mode) && + !is_scaled_mode_allowed (self, mode, 1.0)) + { + g_clear_object (&mode); + monitor->modes = g_list_delete_link (monitor->modes, current); + continue; + } + + if (monitor->current_mode == CC_DISPLAY_MODE (mode)) + current_scale = cc_display_monitor_get_scale (CC_DISPLAY_MONITOR (monitor)); + + for (i = mode->supported_scales->len - 1; i >= 0; i--) + { + float scale = g_array_index (mode->supported_scales, double, i); + + if (!G_APPROX_VALUE (scale, current_scale, DBL_EPSILON) && + !G_APPROX_VALUE (scale, mode->preferred_scale, DBL_EPSILON) && + !is_scaled_mode_allowed (self, mode, scale)) + { + g_array_remove_index (mode->supported_scales, i); + } + } + } + } } static void cc_display_config_constructed (GObject *object) { CcDisplayConfig *self = CC_DISPLAY_CONFIG (object); - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); - GList *monitors = cc_display_config_get_monitors (self); - GList *item; + GList *monitors; + GList *l; + g_autoptr(GVariantIter) monitors_iter = NULL; + g_autoptr(GVariantIter) logical_monitors_iter = NULL; + g_autoptr(GVariantIter) props_iter = NULL; + g_autoptr(GError) error = NULL; + + g_variant_get (self->state, + CURRENT_STATE_FORMAT, + &self->serial, + &monitors_iter, + &logical_monitors_iter, + &props_iter); + + while (TRUE) + { + const char *s; + g_autoptr(GVariant) v = NULL; + + if (!g_variant_iter_next (props_iter, "{&sv}", &s, &v)) + break; - for (item = monitors; item != NULL; item = item->next) + if (g_str_equal (s, "supports-mirroring")) + { + g_variant_get (v, "b", &self->supports_mirroring); + } + else if (g_str_equal (s, "supports-changing-layout-mode")) + { + g_variant_get (v, "b", &self->supports_changing_layout_mode); + } + else if (g_str_equal (s, "global-scale-required")) + { + g_variant_get (v, "b", &self->global_scale_required); + } + else if (g_str_equal (s, "layout-mode")) + { + guint32 u = 0; + g_variant_get (v, "u", &u); + if (u >= CC_DISPLAY_LAYOUT_MODE_LOGICAL && + u <= CC_DISPLAY_LAYOUT_MODE_PHYSICAL) + self->layout_mode = u; + } + } + + construct_monitors (self, monitors_iter, logical_monitors_iter); + filter_out_invalid_scaled_modes (self); + + self->proxy = g_dbus_proxy_new_sync (self->connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.Mutter.DisplayConfig", + "/org/gnome/Mutter/DisplayConfig", + "org.gnome.Mutter.DisplayConfig", + NULL, + &error); + if (error) + g_warning ("Could not create DisplayConfig proxy: %s", error->message); + + g_signal_connect_swapped (self->proxy, "g-properties-changed", + G_CALLBACK (proxy_properties_changed_cb), self); + update_panel_orientation_managed (self); + + monitors = cc_display_config_get_monitors (self); + for (l = monitors; l; l = l->next) { - CcDisplayMonitor *monitor = item->data; + CcDisplayMonitor *monitor = l->data; if (cc_display_monitor_is_builtin (monitor)) - priv->ui_sorted_monitors = g_list_prepend (priv->ui_sorted_monitors, monitor); + { + self->ui_sorted_monitors = g_list_prepend (self->ui_sorted_monitors, + monitor); + } else - priv->ui_sorted_monitors = g_list_append (priv->ui_sorted_monitors, monitor); + { + self->ui_sorted_monitors = g_list_append (self->ui_sorted_monitors, + monitor); + } } cc_display_config_update_ui_numbers_names(self); } +static void +cc_display_config_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcDisplayConfig *self = CC_DISPLAY_CONFIG (object); + + switch (prop_id) + { + case PROP_STATE: + self->state = g_value_dup_variant (value); + break; + case PROP_CONNECTION: + self->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_display_config_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcDisplayConfig *self = CC_DISPLAY_CONFIG (object); + + switch (prop_id) + { + case PROP_STATE: + g_value_set_variant (value, self->state); + break; + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_display_config_dispose (GObject *object) +{ + CcDisplayConfig *self = CC_DISPLAY_CONFIG (object); + + if (self->logical_monitors) + { + GHashTableIter iter; + gpointer monitor; + + g_hash_table_iter_init (&iter, self->logical_monitors); + + while (g_hash_table_iter_next (&iter, &monitor, NULL)) + g_object_weak_unref (G_OBJECT (monitor), remove_logical_monitor, self); + } + + G_OBJECT_CLASS (cc_display_config_parent_class)->dispose (object); +} + static void cc_display_config_finalize (GObject *object) { CcDisplayConfig *self = CC_DISPLAY_CONFIG (object); - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); - g_list_free (priv->ui_sorted_monitors); + g_list_free (self->ui_sorted_monitors); + + g_clear_pointer (&self->state, g_variant_unref); + g_clear_object (&self->connection); + g_clear_object (&self->proxy); + + g_clear_list (&self->monitors, g_object_unref); + g_clear_pointer (&self->logical_monitors, g_hash_table_destroy); G_OBJECT_CLASS (cc_display_config_parent_class)->finalize (object); } @@ -494,6 +1630,32 @@ static void cc_display_config_class_init (CcDisplayConfigClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->set_property = cc_display_config_set_property; + gobject_class->get_property = cc_display_config_get_property; + gobject_class->finalize = cc_display_config_finalize; + gobject_class->constructed = cc_display_config_constructed; + gobject_class->dispose = cc_display_config_dispose; + + pspec = g_param_spec_variant ("state", + "GVariant", + "GVariant", + G_VARIANT_TYPE (CURRENT_STATE_FORMAT), + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, PROP_STATE, pspec); + + pspec = g_param_spec_object ("connection", + "GDBusConnection", + "GDBusConnection", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, PROP_CONNECTION, pspec); g_signal_new ("primary", CC_TYPE_DISPLAY_CONFIG, @@ -505,37 +1667,44 @@ cc_display_config_class_init (CcDisplayConfigClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} - gobject_class->constructed = cc_display_config_constructed; - gobject_class->finalize = cc_display_config_finalize; +static void +cc_display_config_init (CcDisplayConfig *self) +{ + self->serial = 0; + self->supports_mirroring = TRUE; + self->supports_changing_layout_mode = FALSE; + self->global_scale_required = FALSE; + self->layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL; + self->logical_monitors = g_hash_table_new (NULL, NULL); } GList * cc_display_config_get_monitors (CcDisplayConfig *self) { g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), NULL); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->get_monitors (self); + + return self->monitors; } GList * cc_display_config_get_ui_sorted_monitors (CcDisplayConfig *self) { - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); - g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), NULL); - return priv->ui_sorted_monitors; + + return self->ui_sorted_monitors; } int cc_display_config_count_useful_monitors (CcDisplayConfig *self) { - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); GList *outputs, *l; guint count = 0; g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), 0); - outputs = priv->ui_sorted_monitors; + outputs = self->ui_sorted_monitors; for (l = outputs; l != NULL; l = l->next) { CcDisplayMonitor *output = l->data; @@ -548,11 +1717,147 @@ cc_display_config_count_useful_monitors (CcDisplayConfig *self) } +static void +cc_display_config_ensure_non_offset_coords (CcDisplayConfig *self) +{ + GList *x_axis, *y_axis; + CcDisplayLogicalMonitor *m; + + if (g_hash_table_size (self->logical_monitors) == 0) + return; + + x_axis = g_hash_table_get_keys (self->logical_monitors); + x_axis = g_list_sort (x_axis, logical_monitor_sort_x_axis); + y_axis = g_hash_table_get_keys (self->logical_monitors); + y_axis = g_list_sort (y_axis, logical_monitor_sort_y_axis); + + m = x_axis->data; + if (m->x != 0) + { + g_list_foreach (x_axis, logical_monitor_add_x_delta, + GINT_TO_POINTER (- m->x)); + } + + m = y_axis->data; + if (m->y != 0) + { + g_list_foreach (y_axis, logical_monitor_add_y_delta, + GINT_TO_POINTER (- m->y)); + } + + g_list_free (x_axis); + g_list_free (y_axis); +} + +static GVariant * +build_monitors_variant (GHashTable *monitors) +{ + GVariantBuilder builder; + GHashTableIter iter; + CcDisplayMonitor *monitor; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_hash_table_iter_init (&iter, monitors); + + while (g_hash_table_iter_next (&iter, (void **) &monitor, NULL)) + { + GVariantBuilder props_builder; + CcDisplayMode *mode; + + if (!monitor->current_mode) + continue; + + g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&props_builder, "{sv}", + "underscanning", + g_variant_new_boolean (monitor->underscanning == + UNDERSCANNING_ENABLED)); + + mode = CC_DISPLAY_MODE (monitor->current_mode); + g_variant_builder_add (&builder, "(ss@*)", + monitor->connector_name, + mode->id, + g_variant_builder_end (&props_builder)); + } + + return g_variant_builder_end (&builder); +} + +static GVariant * +build_logical_monitors_parameter (CcDisplayConfig *self) +{ + GVariantBuilder builder; + GHashTableIter iter; + CcDisplayLogicalMonitor *logical_monitor; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(iiduba(ssa{sv}))")); + g_hash_table_iter_init (&iter, self->logical_monitors); + + while (g_hash_table_iter_next (&iter, (void **) &logical_monitor, NULL)) + g_variant_builder_add (&builder, "(iidub@*)", + logical_monitor->x, + logical_monitor->y, + logical_monitor->scale, + logical_monitor->rotation, + logical_monitor->primary, + build_monitors_variant (logical_monitor->monitors)); + + return g_variant_builder_end (&builder); +} + +static GVariant * +build_apply_parameters (CcDisplayConfig *self, + CcDisplayConfigMethod method) +{ + GVariantBuilder props_builder; + g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); + + if (self->supports_changing_layout_mode) + g_variant_builder_add (&props_builder, "{sv}", + "layout-mode", g_variant_new_uint32 (self->layout_mode)); + + return g_variant_new ("(uu@*@*)", + self->serial, + method, + build_logical_monitors_parameter (self), + g_variant_builder_end (&props_builder)); +} + +static gboolean +config_apply (CcDisplayConfig *self, + CcDisplayConfigMethod method, + GError **error) +{ + g_autoptr(GVariant) retval = NULL; + + cc_display_config_ensure_non_offset_coords (self); + + retval = g_dbus_proxy_call_sync (self->proxy, + "ApplyMonitorsConfig", + build_apply_parameters (self, method), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, + NULL, + error); + return retval != NULL; +} + gboolean cc_display_config_is_applicable (CcDisplayConfig *self) { + g_autoptr(GError) error = NULL; + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->is_applicable (self); + + if (!config_apply (self, CC_DISPLAY_CONFIG_METHOD_VERIFY, &error)) + { + g_warning ("Config not applicable: %s", error->message); + return FALSE; + } + else + { + return TRUE; + } } void @@ -573,14 +1878,60 @@ cc_display_config_set_mode_on_all_outputs (CcDisplayConfig *config, } } +static gboolean +cc_display_mode_equal (const CcDisplayMode *m1, + const CcDisplayMode *m2) +{ + if (!m1 && !m2) + return TRUE; + else if (!m1 || !m2) + return FALSE; + + return m1->width == m2->width && + m1->height == m2->height && + m1->refresh_rate == m2->refresh_rate && + (m1->flags & MODE_INTERLACED) == (m2->flags & MODE_INTERLACED); +} + gboolean cc_display_config_equal (CcDisplayConfig *self, CcDisplayConfig *other) { + GList *l; + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (other), FALSE); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->equal (self, other); + cc_display_config_ensure_non_offset_coords (self); + cc_display_config_ensure_non_offset_coords (other); + + for (l = self->monitors; l != NULL; l = l->next) + { + CcDisplayMonitor *m1 = l->data; + CcDisplayMonitor *m2 = monitor_from_spec (other, + m1->connector_name, + m1->vendor_name, + m1->product_name, + m1->product_serial); + if (!m2) + return FALSE; + + if (m1->underscanning != m2->underscanning) + return FALSE; + + if (!cc_display_logical_monitor_equal (m1->logical_monitor, m2->logical_monitor)) + return FALSE; + + /* Modes should not be compared if both monitors have no logical monitor. */ + if (m1->logical_monitor == NULL && m2->logical_monitor == NULL) + continue; + + if (!cc_display_mode_equal (CC_DISPLAY_MODE (m1->current_mode), + CC_DISPLAY_MODE (m2->current_mode))) + return FALSE; + } + + return TRUE; } gboolean @@ -597,36 +1948,259 @@ cc_display_config_apply (CcDisplayConfig *self, return FALSE; } - return CC_DISPLAY_CONFIG_GET_CLASS (self)->apply (self, error); + return config_apply (self, CC_DISPLAY_CONFIG_METHOD_PERSISTENT, error); } gboolean cc_display_config_is_cloning (CcDisplayConfig *self) { + unsigned int n_active_monitors = 0; + GList *l; + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->is_cloning (self); + + for (l = self->monitors; l != NULL; l = l->next) + { + if (cc_display_monitor_is_active (CC_DISPLAY_MONITOR (l->data))) + n_active_monitors += 1; + } + + return n_active_monitors > 1 && g_hash_table_size (self->logical_monitors) == 1; +} + +static void +cc_display_config_make_linear (CcDisplayConfig *self) +{ + CcDisplayLogicalMonitor *primary; + GList *logical_monitors, *l; + int x; + + if (self->primary && self->primary->logical_monitor) + { + primary = self->primary->logical_monitor; + primary->x = primary->y = 0; + x = logical_monitor_width (primary); + } + else + { + primary = NULL; + x = 0; + } + + logical_monitors = g_hash_table_get_keys (self->logical_monitors); + for (l = logical_monitors; l != NULL; l = l->next) + { + CcDisplayLogicalMonitor *m = l->data; + + if (m == primary) + continue; + + m->x = x; + m->y = 0; + x += logical_monitor_width (m); + } + + g_list_free (logical_monitors); } void cc_display_config_set_cloning (CcDisplayConfig *self, gboolean clone) { + gboolean is_cloning; + CcDisplayLogicalMonitor *logical_monitor; + GList *l; + g_return_if_fail (CC_IS_DISPLAY_CONFIG (self)); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->set_cloning (self, clone); + + is_cloning = cc_display_config_is_cloning (self); + if (clone && !is_cloning) + { + logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); + for (l = self->monitors; l != NULL; l = l->next) + { + cc_display_monitor_set_logical_monitor (CC_DISPLAY_MONITOR (l->data), + logical_monitor); + } + register_logical_monitor (self, logical_monitor); + } + else if (!clone && is_cloning) + { + for (l = self->monitors; l != NULL; l = l->next) + { + logical_monitor = g_object_new (CC_TYPE_DISPLAY_LOGICAL_MONITOR, NULL); + cc_display_monitor_set_logical_monitor (CC_DISPLAY_MONITOR (l->data), + logical_monitor); + register_logical_monitor (self, logical_monitor); + } + cc_display_config_make_linear (self); + } +} + +static gboolean +is_mode_better (CcDisplayMode *mode, + CcDisplayMode *other_mode) +{ + if (mode->width * mode->height > other_mode->width * other_mode->height) + return TRUE; + else if (mode->width * mode->height < other_mode->width * other_mode->height) + return FALSE; + + if (!(mode->flags & MODE_INTERLACED) && + (other_mode->flags & MODE_INTERLACED)) + return TRUE; + + return FALSE; +} + +static gboolean +mode_supports_scale (CcDisplayMode *mode, + double scale) +{ + g_autoptr(GArray) scales = NULL; + int i; + + scales = cc_display_mode_get_supported_scales (mode); + for (i = 0; i < scales->len; i++) + { + if (G_APPROX_VALUE (scale, g_array_index (scales, double, i), + DBL_EPSILON)) + return TRUE; + } + + return FALSE; +} + +static void +remove_unsupported_scales (CcDisplayMode *mode, + GArray *supported_scales) +{ + g_autoptr(GArray) mode_scales = NULL; + int i; + + mode_scales = cc_display_mode_get_supported_scales (mode); + i = 0; + while (i < supported_scales->len) + { + double scale; + + if (i == supported_scales->len) + break; + + scale = g_array_index (supported_scales, double, i); + + if (mode_supports_scale (mode, scale)) + { + i++; + continue; + } + + g_array_remove_range (supported_scales, i, 1); + } +} + +static gboolean +monitor_has_compatible_clone_mode (CcDisplayMonitor *monitor, + CcDisplayMode *mode, + GArray *supported_scales) +{ + GList *l; + + for (l = monitor->modes; l; l = l->next) + { + CcDisplayMode *other_mode = l->data; + + if (other_mode->width != mode->width || + other_mode->height != mode->height) + continue; + + if ((other_mode->flags & MODE_INTERLACED) != + (mode->flags & MODE_INTERLACED)) + continue; + + remove_unsupported_scales (CC_DISPLAY_MODE (other_mode), supported_scales); + + return TRUE; + } + + return FALSE; +} + +static gboolean +monitors_has_compatible_clone_mode (CcDisplayConfig *self, + CcDisplayMode *mode, + GArray *supported_scales) +{ + GList *l; + + for (l = self->monitors; l; l = l->next) + { + CcDisplayMonitor *monitor = l->data; + + if (!monitor_has_compatible_clone_mode (monitor, mode, supported_scales)) + return FALSE; + } + + return TRUE; } GList * cc_display_config_generate_cloning_modes (CcDisplayConfig *self) { + CcDisplayMonitor *base_monitor = NULL; + GList *l; + GList *clone_modes = NULL; + CcDisplayMode *best_mode = NULL; + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), NULL); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->generate_cloning_modes (self); + + for (l = self->monitors; l; l = l->next) + { + CcDisplayMonitor *monitor = l->data; + + if (cc_display_monitor_is_active (monitor)) + { + base_monitor = CC_DISPLAY_MONITOR (monitor); + break; + } + } + + if (!base_monitor) + return NULL; + + for (l = base_monitor->modes; l; l = l->next) + { + CcDisplayMode *mode = l->data; + CcDisplayMode *virtual_mode; + g_autoptr (GArray) supported_scales = NULL; + + supported_scales = + cc_display_mode_get_supported_scales (CC_DISPLAY_MODE (mode)); + + if (!monitors_has_compatible_clone_mode (self, mode, supported_scales)) + continue; + + virtual_mode = cc_display_mode_new_virtual (mode->width, + mode->height, + mode->preferred_scale, + supported_scales); + clone_modes = g_list_append (clone_modes, virtual_mode); + + if (!best_mode || is_mode_better (virtual_mode, best_mode)) + best_mode = virtual_mode; + } + + best_mode->flags |= MODE_PREFERRED; + + return clone_modes; } gboolean cc_display_config_is_layout_logical (CcDisplayConfig *self) { g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->is_layout_logical (self); + + return self->layout_mode == CC_DISPLAY_LAYOUT_MODE_LOGICAL; } void @@ -635,7 +2209,16 @@ cc_display_config_set_minimum_size (CcDisplayConfig *self, int height) { g_return_if_fail (CC_IS_DISPLAY_CONFIG (self)); - CC_DISPLAY_CONFIG_GET_CLASS (self)->set_minimum_size (self, width, height); + + g_assert (width >= 0 && height >= 0); + g_assert (((self->min_width == 0 && self->min_height == 0) || + (self->min_width >= width && self->min_height >= height)) && + "Minimum size can't be set again to higher values"); + + self->min_width = width; + self->min_height = height; + + filter_out_invalid_scaled_modes (self); } gboolean @@ -645,22 +2228,26 @@ cc_display_config_is_scaled_mode_valid (CcDisplayConfig *self, { g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); g_return_val_if_fail (CC_IS_DISPLAY_MODE (mode), FALSE); - return CC_DISPLAY_CONFIG_GET_CLASS (self)->is_scaled_mode_valid (self, mode, scale); + + if (cc_display_config_is_cloning (self)) + return is_scale_allowed_by_active_monitors (self, mode, scale); + + return cc_display_mode_is_supported_scale (mode, scale); } gboolean cc_display_config_get_panel_orientation_managed (CcDisplayConfig *self) { - return CC_DISPLAY_CONFIG_GET_CLASS (self)->get_panel_orientation_managed (self); + return self->panel_orientation_managed; } void cc_display_config_update_ui_numbers_names (CcDisplayConfig *self) { - CcDisplayConfigPrivate *priv = cc_display_config_get_instance_private (self); GList *item; gint ui_number = 1; - for (item = priv->ui_sorted_monitors; item != NULL; item = item->next) + + for (item = self->ui_sorted_monitors; item != NULL; item = item->next) { CcDisplayMonitor *monitor = item->data; char *ui_name; diff --git a/panels/display/cc-display-config.h b/panels/display/cc-display-config.h index ea52c2d599..e8ae6278ea 100644 --- a/panels/display/cc-display-config.h +++ b/panels/display/cc-display-config.h @@ -76,106 +76,21 @@ typedef enum _CcDisplayMonitorPrivacy CC_DISPLAY_MONITOR_PRIVACY_LOCKED = 1 << 2, } CcDisplayMonitorPrivacy; - #define CC_TYPE_DISPLAY_MODE (cc_display_mode_get_type ()) -G_DECLARE_DERIVABLE_TYPE (CcDisplayMode, cc_display_mode, - CC, DISPLAY_MODE, GObject) - -struct _CcDisplayModeClass -{ - GObjectClass parent_class; - - gboolean (*is_clone_mode) (CcDisplayMode *self); - void (*get_resolution) (CcDisplayMode *self, int *w, int *h); - GArray* (*get_supported_scales) (CcDisplayMode *self); - double (*get_preferred_scale) (CcDisplayMode *self); - gboolean (*is_interlaced) (CcDisplayMode *self); - gboolean (*is_preferred) (CcDisplayMode *self); - int (*get_freq) (CcDisplayMode *self); - double (*get_freq_f) (CcDisplayMode *self); -}; - +G_DECLARE_FINAL_TYPE (CcDisplayMode, cc_display_mode, + CC, DISPLAY_MODE, GObject) #define CC_TYPE_DISPLAY_MONITOR (cc_display_monitor_get_type ()) -G_DECLARE_DERIVABLE_TYPE (CcDisplayMonitor, cc_display_monitor, - CC, DISPLAY_MONITOR, GObject) - -struct _CcDisplayMonitorClass -{ - GObjectClass parent_class; - - guint32 (*get_id) (CcDisplayMonitor *self); - const char* (*get_display_name) (CcDisplayMonitor *self); - const char* (*get_connector_name) (CcDisplayMonitor *self); - gboolean (*is_builtin) (CcDisplayMonitor *self); - gboolean (*is_primary) (CcDisplayMonitor *self); - void (*set_primary) (CcDisplayMonitor *self, - gboolean primary); - gboolean (*is_active) (CcDisplayMonitor *self); - void (*set_active) (CcDisplayMonitor *self, - gboolean a); - CcDisplayRotation (*get_rotation) (CcDisplayMonitor *self); - void (*set_rotation) (CcDisplayMonitor *self, - CcDisplayRotation r); - gboolean (*supports_rotation) (CcDisplayMonitor *self, - CcDisplayRotation r); - void (*get_physical_size) (CcDisplayMonitor *self, - int *w, - int *h); - void (*get_geometry) (CcDisplayMonitor *self, - int *x, - int *y, - int *w, - int *h); - gboolean (*supports_underscanning) (CcDisplayMonitor *self); - gboolean (*get_underscanning) (CcDisplayMonitor *self); - void (*set_underscanning) (CcDisplayMonitor *self, - gboolean u); - CcDisplayMonitorPrivacy (*get_privacy) (CcDisplayMonitor *self); - CcDisplayMode* (*get_mode) (CcDisplayMonitor *self); - CcDisplayMode* (*get_preferred_mode) (CcDisplayMonitor *self); - GList* (*get_modes) (CcDisplayMonitor *self); - void (*set_compatible_clone_mode) (CcDisplayMonitor *self, - CcDisplayMode *m); - void (*set_mode) (CcDisplayMonitor *self, - CcDisplayMode *m); - void (*set_position) (CcDisplayMonitor *self, - int x, - int y); - double (*get_scale) (CcDisplayMonitor *self); - void (*set_scale) (CcDisplayMonitor *self, - double s); -}; +G_DECLARE_FINAL_TYPE (CcDisplayMonitor, cc_display_monitor, + CC, DISPLAY_MONITOR, GObject) +#define CC_TYPE_DISPLAY_LOGICAL_MONITOR (cc_display_logical_monitor_get_type ()) +G_DECLARE_FINAL_TYPE (CcDisplayLogicalMonitor, cc_display_logical_monitor, + CC, DISPLAY_LOGICAL_MONITOR, GObject) #define CC_TYPE_DISPLAY_CONFIG (cc_display_config_get_type ()) -G_DECLARE_DERIVABLE_TYPE (CcDisplayConfig, cc_display_config, - CC, DISPLAY_CONFIG, GObject) - -struct _CcDisplayConfigClass -{ - GObjectClass parent_class; - - GList* (*get_monitors) (CcDisplayConfig *self); - gboolean (*is_applicable) (CcDisplayConfig *self); - gboolean (*equal) (CcDisplayConfig *self, - CcDisplayConfig *other); - gboolean (*apply) (CcDisplayConfig *self, - GError **error); - gboolean (*is_cloning) (CcDisplayConfig *self); - void (*set_cloning) (CcDisplayConfig *self, - gboolean clone); - GList* (*generate_cloning_modes) (CcDisplayConfig *self); - gboolean (*is_layout_logical) (CcDisplayConfig *self); - void (*set_minimum_size) (CcDisplayConfig *self, - int width, - int height); - gboolean (*is_scaled_mode_valid) (CcDisplayConfig *self, - CcDisplayMode *mode, - double scale); - gboolean (* get_panel_orientation_managed) (CcDisplayConfig *self); -}; - +G_DECLARE_FINAL_TYPE (CcDisplayConfig, cc_display_config, + CC, DISPLAY_CONFIG, GObject) GList* cc_display_config_get_monitors (CcDisplayConfig *config); GList* cc_display_config_get_ui_sorted_monitors (CcDisplayConfig *config); diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c index 07393d1079..5a96aa65b9 100644 --- a/panels/display/cc-display-panel.c +++ b/panels/display/cc-display-panel.c @@ -30,7 +30,7 @@ #include #include "cc-list-row.h" -#include "cc-display-config-manager-dbus.h" +#include "cc-display-config-manager.h" #include "cc-display-config.h" #include "cc-display-arrangement.h" #include "cc-night-light-page.h" @@ -1067,7 +1067,7 @@ session_bus_ready (GObject *source, return; } - self->manager = cc_display_config_manager_dbus_new (); + self->manager = cc_display_config_manager_new (); g_signal_connect_object (self->manager, "changed", G_CALLBACK (on_screen_changed), self, diff --git a/panels/display/cc-night-light-page.c b/panels/display/cc-night-light-page.c index 93ab80e2b9..65f2244ff9 100644 --- a/panels/display/cc-night-light-page.c +++ b/panels/display/cc-night-light-page.c @@ -28,7 +28,7 @@ #include "cc-night-light-page.h" #include "shell/cc-object-storage.h" -#include "cc-display-config-manager-dbus.h" +#include "cc-display-config-manager.h" struct _CcNightLightPage { AdwBin parent; @@ -797,7 +797,7 @@ cc_night_light_page_init (CcNightLightPage *self) gtk_widget_set_direction (self->from_spinbuttons_box, GTK_TEXT_DIR_LTR); } - self->config_manager = cc_display_config_manager_dbus_new (); + self->config_manager = cc_display_config_manager_new (); g_signal_connect_object (self->config_manager, "changed", G_CALLBACK (config_manager_changed_cb), self, G_CONNECT_SWAPPED); diff --git a/panels/display/meson.build b/panels/display/meson.build index 895f73bf36..64d6075d40 100644 --- a/panels/display/meson.build +++ b/panels/display/meson.build @@ -14,8 +14,6 @@ sources = files( 'cc-display-panel.c', 'cc-display-arrangement.c', 'cc-display-config.c', - 'cc-display-config-dbus.c', - 'cc-display-config-manager-dbus.c', 'cc-display-config-manager.c', 'cc-display-settings.c', 'cc-night-light-page.c', diff --git a/panels/screen/cc-screen-panel.c b/panels/screen/cc-screen-panel.c index 3c6f35b39a..6ca3508f0e 100644 --- a/panels/screen/cc-screen-panel.c +++ b/panels/screen/cc-screen-panel.c @@ -25,7 +25,7 @@ #include "cc-screen-resources.h" #include "cc-util.h" -#include "panels/display/cc-display-config-manager-dbus.h" +#include "panels/display/cc-display-config-manager.h" #include @@ -363,7 +363,7 @@ cc_screen_panel_init (CcScreenPanel *self) "active", G_SETTINGS_BIND_DEFAULT); - self->display_config_manager = cc_display_config_manager_dbus_new (); + self->display_config_manager = cc_display_config_manager_new (); g_signal_connect_object (self->display_config_manager, "changed", G_CALLBACK (update_display_config), self, G_CONNECT_SWAPPED); diff --git a/subprojects/gvc b/subprojects/gvc index 8e7a5a4c3e..7a621180b4 160000 --- a/subprojects/gvc +++ b/subprojects/gvc @@ -1 +1 @@ -Subproject commit 8e7a5a4c3e51007ce6579292642517e3d3eb9c50 +Subproject commit 7a621180b46421e356b33972e3446775a504139c -- GitLab From 519df314d0ba6844ebc115184d10e44d0894264a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 5 Jun 2023 17:48:40 +0200 Subject: [PATCH 2/7] display/config: Use 'l' as GList iterator --- panels/display/cc-display-config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/panels/display/cc-display-config.c b/panels/display/cc-display-config.c index 7b0759298c..937e27563f 100644 --- a/panels/display/cc-display-config.c +++ b/panels/display/cc-display-config.c @@ -2244,12 +2244,12 @@ cc_display_config_get_panel_orientation_managed (CcDisplayConfig *self) void cc_display_config_update_ui_numbers_names (CcDisplayConfig *self) { - GList *item; + GList *l; gint ui_number = 1; - for (item = self->ui_sorted_monitors; item != NULL; item = item->next) + for (l = self->ui_sorted_monitors; l; l = l->next) { - CcDisplayMonitor *monitor = item->data; + CcDisplayMonitor *monitor = l->data; char *ui_name; gint current_ui_number = 0; -- GitLab From 16314ce29422f2304b1d5de771e4def3541171bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 5 Jun 2023 17:53:49 +0200 Subject: [PATCH 3/7] display/config: Remove handling not supporting rotations In the past the rotation depended on display hardware capabilities; now we can always rotate ourself if the display hardware can't do it for us. --- panels/display/cc-display-config.c | 6 ------ panels/display/cc-display-config.h | 2 -- panels/display/cc-display-settings.c | 14 -------------- 3 files changed, 22 deletions(-) diff --git a/panels/display/cc-display-config.c b/panels/display/cc-display-config.c index 937e27563f..1042a75282 100644 --- a/panels/display/cc-display-config.c +++ b/panels/display/cc-display-config.c @@ -813,12 +813,6 @@ cc_display_monitor_set_rotation (CcDisplayMonitor *self, } } -gboolean -cc_display_monitor_supports_rotation (CcDisplayMonitor *self, CcDisplayRotation r) -{ - return TRUE; -} - void cc_display_monitor_get_physical_size (CcDisplayMonitor *self, int *w, int *h) { diff --git a/panels/display/cc-display-config.h b/panels/display/cc-display-config.h index e8ae6278ea..3ddddc0c64 100644 --- a/panels/display/cc-display-config.h +++ b/panels/display/cc-display-config.h @@ -127,8 +127,6 @@ const char* cc_display_monitor_get_connector_name (CcDisplayMonitor CcDisplayRotation cc_display_monitor_get_rotation (CcDisplayMonitor *monitor); void cc_display_monitor_set_rotation (CcDisplayMonitor *monitor, CcDisplayRotation r); -gboolean cc_display_monitor_supports_rotation (CcDisplayMonitor *monitor, - CcDisplayRotation rotation); void cc_display_monitor_get_physical_size (CcDisplayMonitor *monitor, int *w, int *h); diff --git a/panels/display/cc-display-settings.c b/panels/display/cc-display-settings.c index 79de8b657a..a55f59c80d 100644 --- a/panels/display/cc-display-settings.c +++ b/panels/display/cc-display-settings.c @@ -80,17 +80,6 @@ static void on_scale_btn_active_changed_cb (CcDisplaySettings *self, static gboolean should_show_rotation (CcDisplaySettings *self) { - gboolean supports_rotation; - - supports_rotation = cc_display_monitor_supports_rotation (self->selected_output, - CC_DISPLAY_ROTATION_90 | - CC_DISPLAY_ROTATION_180 | - CC_DISPLAY_ROTATION_270); - - /* Doesn't support rotation at all */ - if (!supports_rotation) - return FALSE; - /* We can always rotate displays that aren't builtin */ if (!cc_display_monitor_is_builtin (self->selected_output)) return TRUE; @@ -305,9 +294,6 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) { g_autoptr(GObject) obj = NULL; - if (!cc_display_monitor_supports_rotation (self->selected_output, rotations[i])) - continue; - gtk_string_list_append (GTK_STRING_LIST (self->orientation_list), string_for_rotation (rotations[i])); obj = g_list_model_get_item (self->orientation_list, i); -- GitLab From 05b0e97c0ee5703f6cdb674ab8ac8fdc0216f6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 6 Jun 2023 01:10:22 +0200 Subject: [PATCH 4/7] display/config: Allow changing layout mode This will be used to change the HiDPI compatibility mode. --- panels/display/cc-display-config.c | 51 ++++++++++++++++++++++++++---- panels/display/cc-display-config.h | 11 +++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/panels/display/cc-display-config.c b/panels/display/cc-display-config.c index 1042a75282..e74e5b3665 100644 --- a/panels/display/cc-display-config.c +++ b/panels/display/cc-display-config.c @@ -48,12 +48,6 @@ typedef enum _CcDisplayModeFlags MODE_INTERLACED = 1 << 2, } CcDisplayModeFlags; -typedef enum _CcDisplayLayoutMode -{ - CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1, - CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2 -} CcDisplayLayoutMode; - typedef enum _CcDisplayConfigMethod { CC_DISPLAY_CONFIG_METHOD_VERIFY = 0, @@ -1690,6 +1684,14 @@ cc_display_config_get_ui_sorted_monitors (CcDisplayConfig *self) return self->ui_sorted_monitors; } +gboolean +cc_display_config_is_changing_layout_mode_supported (CcDisplayConfig *self) +{ + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); + + return self->supports_changing_layout_mode; +} + int cc_display_config_count_useful_monitors (CcDisplayConfig *self) { @@ -2189,6 +2191,43 @@ cc_display_config_generate_cloning_modes (CcDisplayConfig *self) return clone_modes; } +CcDisplayLayoutMode +cc_display_config_get_layout_mode (CcDisplayConfig *self) +{ + g_return_val_if_fail (CC_IS_DISPLAY_CONFIG (self), FALSE); + + return self->layout_mode; +} + +static void +ensure_scales_valid_int_func (gpointer key, + gpointer data, + gpointer user_data) +{ + CcDisplayLogicalMonitor *logical_monitor = data; + const float scale_epsilon = 0.2; + + logical_monitor->scale = floorf (logical_monitor->scale + + 0.25 + + scale_epsilon); +} + +void +cc_display_config_set_layout_mode (CcDisplayConfig *self, + CcDisplayLayoutMode layout_mode) +{ + g_return_if_fail (CC_IS_DISPLAY_CONFIG (self)); + g_return_if_fail (self->supports_changing_layout_mode); + + self->layout_mode = layout_mode; + + if (layout_mode == CC_DISPLAY_LAYOUT_MODE_PHYSICAL) + { + g_hash_table_foreach (self->logical_monitors, + ensure_scales_valid_int_func, NULL); + } +} + gboolean cc_display_config_is_layout_logical (CcDisplayConfig *self) { diff --git a/panels/display/cc-display-config.h b/panels/display/cc-display-config.h index 3ddddc0c64..dc0fcf4145 100644 --- a/panels/display/cc-display-config.h +++ b/panels/display/cc-display-config.h @@ -76,6 +76,12 @@ typedef enum _CcDisplayMonitorPrivacy CC_DISPLAY_MONITOR_PRIVACY_LOCKED = 1 << 2, } CcDisplayMonitorPrivacy; +typedef enum _CcDisplayLayoutMode +{ + CC_DISPLAY_LAYOUT_MODE_LOGICAL = 1, + CC_DISPLAY_LAYOUT_MODE_PHYSICAL = 2 +} CcDisplayLayoutMode; + #define CC_TYPE_DISPLAY_MODE (cc_display_mode_get_type ()) G_DECLARE_FINAL_TYPE (CcDisplayMode, cc_display_mode, CC, DISPLAY_MODE, GObject) @@ -108,6 +114,9 @@ GList* cc_display_config_generate_cloning_modes (CcDisplayConfig void cc_display_config_set_mode_on_all_outputs (CcDisplayConfig *config, CcDisplayMode *mode); +CcDisplayLayoutMode cc_display_config_get_layout_mode (CcDisplayConfig *self); +void cc_display_config_set_layout_mode (CcDisplayConfig *self, + CcDisplayLayoutMode layout_mode); gboolean cc_display_config_is_layout_logical (CcDisplayConfig *self); void cc_display_config_set_minimum_size (CcDisplayConfig *self, int width, @@ -119,6 +128,8 @@ gboolean cc_display_config_get_panel_orientation_managed (CcDisplayConfig *self); void cc_display_config_update_ui_numbers_names (CcDisplayConfig *self); +gboolean cc_display_config_is_changing_layout_mode_supported (CcDisplayConfig *self); + const char* cc_display_monitor_get_display_name (CcDisplayMonitor *monitor); gboolean cc_display_monitor_is_active (CcDisplayMonitor *monitor); void cc_display_monitor_set_active (CcDisplayMonitor *monitor, -- GitLab From c8d9083caff1c64b80d022b6c710e533674bc2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 6 Jun 2023 01:10:56 +0200 Subject: [PATCH 5/7] display/panel: Remove unused variables --- panels/display/cc-display-panel.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c index 5a96aa65b9..0f4376d3b4 100644 --- a/panels/display/cc-display-panel.c +++ b/panels/display/cc-display-panel.c @@ -726,7 +726,7 @@ move_display_settings_to_separate_page (CcDisplayPanel *self) static void rebuild_ui (CcDisplayPanel *self) { - guint n_outputs, n_active_outputs, n_usable_outputs; + guint n_usable_outputs; GList *outputs, *l; CcDisplayConfigType type; @@ -758,7 +758,6 @@ rebuild_ui (CcDisplayPanel *self) gtk_widget_set_visible (self->display_settings_disabled_group, FALSE); - n_active_outputs = 0; n_usable_outputs = 0; outputs = cc_display_config_get_ui_sorted_monitors (self->current_config); for (l = outputs; l; l = l->next) @@ -772,8 +771,6 @@ rebuild_ui (CcDisplayPanel *self) if (cc_display_monitor_is_active (output)) { - n_active_outputs += 1; - g_list_store_append (self->primary_display_list, output); if (cc_display_monitor_is_primary (output)) adw_combo_row_set_selected (self->primary_display_row, @@ -792,7 +789,6 @@ rebuild_ui (CcDisplayPanel *self) /* Sync the rebuild lists/buttons */ set_current_output (self, self->current_output, TRUE); - n_outputs = g_list_length (outputs); type = config_get_current_type (self); if (n_usable_outputs > 1) -- GitLab From 2b09b513b3e5457c7ea89a9a91a6ddb4176ef32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 6 Jun 2023 01:12:18 +0200 Subject: [PATCH 6/7] display/panel: Add switch to toggle compatibility mode In practice this switches between "physical" and "logical" layout mode. The "legacy compat" mode translates to "physical" which mimics how Xorg work, while "logical" allows for more advanced features, such as fractional scaling. --- panels/display/cc-display-panel.c | 42 ++++++++++++++++++++++++++++++ panels/display/cc-display-panel.ui | 24 ++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c index 0f4376d3b4..b8cf1323c6 100644 --- a/panels/display/cc-display-panel.c +++ b/panels/display/cc-display-panel.c @@ -30,6 +30,7 @@ #include #include "cc-list-row.h" +#include "cc-list-row-info-button.h" #include "cc-display-config-manager.h" #include "cc-display-config.h" #include "cc-display-arrangement.h" @@ -77,6 +78,8 @@ struct _CcDisplayPanel CcNightLightPage *night_light_page; CcListRow *night_light_row; + AdwActionRow *legacy_hidpi_row; + UpClient *up_client; gboolean lid_is_closed; @@ -503,6 +506,25 @@ on_night_light_row_activated_cb (CcDisplayPanel *self) adw_leaflet_set_visible_child_name (self->leaflet, "night-light"); } +static void +on_legacy_hidpi_row_activated_cb (CcDisplayPanel *self) +{ + GtkWidget *legacy_hidpi_switch; + CcDisplayLayoutMode layout_mode; + + legacy_hidpi_switch = + adw_action_row_get_activatable_widget (self->legacy_hidpi_row); + if (gtk_switch_get_active (GTK_SWITCH (legacy_hidpi_switch))) + layout_mode = CC_DISPLAY_LAYOUT_MODE_PHYSICAL; + else + layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL; + + cc_display_config_set_layout_mode (self->current_config, layout_mode); + cc_display_config_snap_outputs (self->current_config); + cc_display_arrangement_set_config (self->arrangement, self->current_config); + update_apply_button (self); +} + static void on_primary_display_selected_item_changed_cb (CcDisplayPanel *self) { @@ -574,6 +596,7 @@ cc_display_panel_class_init (CcDisplayPanelClass *klass) g_type_ensure (CC_TYPE_LIST_ROW); g_type_ensure (CC_TYPE_NIGHT_LIGHT_PAGE); + g_type_ensure (CC_TYPE_LIST_ROW_INFO_BUTTON); panel_class->get_help_uri = cc_display_panel_get_help_uri; @@ -600,6 +623,7 @@ cc_display_panel_class_init (CcDisplayPanelClass *klass) gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, leaflet); gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, night_light_page); gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, night_light_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, legacy_hidpi_row); gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, primary_display_row); gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, single_display_settings_group); gtk_widget_class_bind_template_child (widget_class, CcDisplayPanel, toplevel_shortcuts); @@ -609,6 +633,7 @@ cc_display_panel_class_init (CcDisplayPanelClass *klass) gtk_widget_class_bind_template_callback (widget_class, on_back_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, on_config_type_toggled_cb); gtk_widget_class_bind_template_callback (widget_class, on_night_light_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_legacy_hidpi_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, on_primary_display_selected_item_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_screen_changed); gtk_widget_class_bind_template_callback (widget_class, on_toplevel_escape_pressed_cb); @@ -729,6 +754,8 @@ rebuild_ui (CcDisplayPanel *self) guint n_usable_outputs; GList *outputs, *l; CcDisplayConfigType type; + gboolean show_legacy_hidpi_switch; + GtkWidget *legacy_hidpi_switch; if (!cc_display_config_manager_get_apply_allowed (self->manager)) { @@ -822,6 +849,21 @@ rebuild_ui (CcDisplayPanel *self) cc_panel_set_selected_type (self, type); + show_legacy_hidpi_switch = + cc_display_config_is_changing_layout_mode_supported (self->current_config); + gtk_widget_set_visible (GTK_WIDGET (self->legacy_hidpi_row), + show_legacy_hidpi_switch); + legacy_hidpi_switch = adw_action_row_get_activatable_widget (self->legacy_hidpi_row); + switch (cc_display_config_get_layout_mode (self->current_config)) + { + case CC_DISPLAY_LAYOUT_MODE_LOGICAL: + gtk_switch_set_active (GTK_SWITCH (legacy_hidpi_switch), FALSE); + break; + case CC_DISPLAY_LAYOUT_MODE_PHYSICAL: + gtk_switch_set_active (GTK_SWITCH (legacy_hidpi_switch), TRUE); + break; + } + self->rebuilding_counter--; update_apply_button (self); } diff --git a/panels/display/cc-display-panel.ui b/panels/display/cc-display-panel.ui index 535f06cc99..9cd1719994 100644 --- a/panels/display/cc-display-panel.ui +++ b/panels/display/cc-display-panel.ui @@ -180,9 +180,10 @@ - + + True @@ -191,6 +192,27 @@ + + + + + Legacy HiDPI Compatibility + legacy_hidpi_switch + + + center + Allows apps which use legacy technologies to display correctly on HiDPI screens. This can affect other aspects of display handling, including display scaling. + + + + + center + + + + + + -- GitLab From 98aece7d652e3696eb15961f26c4ec6aab243b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 20 Jun 2023 14:51:16 +0200 Subject: [PATCH 7/7] display: Implement new display scale setting mockup This uses a "preview" like widget that shows an illustration together with the scaling factor presented just as before as e.g. 150%. Closes: #2516 --- panels/display/cc-display-scale.c | 500 +++++++++++++++++++++++++ panels/display/cc-display-scale.css | 19 + panels/display/cc-display-scale.h | 38 ++ panels/display/cc-display-scale.ui | 33 ++ panels/display/cc-display-settings.c | 217 +++++------ panels/display/cc-display-settings.css | 3 + panels/display/cc-display-settings.ui | 38 +- panels/display/display.gresource.xml | 3 + panels/display/meson.build | 1 + 9 files changed, 711 insertions(+), 141 deletions(-) create mode 100644 panels/display/cc-display-scale.c create mode 100644 panels/display/cc-display-scale.css create mode 100644 panels/display/cc-display-scale.h create mode 100644 panels/display/cc-display-scale.ui create mode 100644 panels/display/cc-display-settings.css diff --git a/panels/display/cc-display-scale.c b/panels/display/cc-display-scale.c new file mode 100644 index 0000000000..3cb6d58849 --- /dev/null +++ b/panels/display/cc-display-scale.c @@ -0,0 +1,500 @@ +/* cc-display-scale.c + * + * Copyright 2023 Red Hat, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "cc-display-scale.h" + +enum +{ + PROP_0, + + PROP_IS_ACTIVE, + PROP_SCALE, + PROP_CURRENT_SCALE, + PROP_IS_DEFAULT, + PROP_GROUP, + + N_PROPS +}; + +static GParamSpec *props[N_PROPS]; + +enum +{ + ACTIVATE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +struct _CcDisplayScale +{ + GtkBox parent; + + CcDisplayScale *group; + GPtrArray *scale_siblings; + + gboolean is_active; + + double scale; + double current_scale; + gboolean is_default; + + GtkBox *text_preview; + GtkInscription *text_preview_text; + GtkLabel *scale_label; +}; + +G_DEFINE_FINAL_TYPE (CcDisplayScale, cc_display_scale, GTK_TYPE_BOX) + +void +cc_display_scale_set_is_active (CcDisplayScale *self, + gboolean is_active) +{ + if (self->is_active == is_active) + return; + + self->is_active = is_active; + + if (is_active) + { + gtk_widget_add_css_class (GTK_WIDGET (self->text_preview), + "cc-display-scale-preview-active"); + } + else + { + gtk_widget_remove_css_class (GTK_WIDGET (self->text_preview), + "cc-display-scale-preview-active"); + } + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_ACTIVE]); +} + +gboolean +cc_display_scale_get_is_active (CcDisplayScale *self) +{ + return self->is_active; +} + +double +cc_display_scale_get_scale (CcDisplayScale *self) +{ + return self->scale; +} + +static void +set_active (CcDisplayScale *self) +{ + CcDisplayScale *leader; + int i; + + if (!self->group) + leader = self; + else + leader = self->group; + + cc_display_scale_set_is_active (leader, leader == self); + if (!leader->scale_siblings) + return; + + for (i = 0; i < leader->scale_siblings->len; i++) + { + CcDisplayScale *display_scale = + g_ptr_array_index (leader->scale_siblings, i); + + cc_display_scale_set_is_active (display_scale, display_scale == self); + } +} + +static void +set_group (CcDisplayScale *self, + CcDisplayScale *group) +{ + CcDisplayScale *leader = group; + + if (!group) + return; + + if (self == group) + return; + + if (!leader->scale_siblings) + leader->scale_siblings = g_ptr_array_new (); + + if (g_ptr_array_find (leader->scale_siblings, self, NULL)) + return; + + g_ptr_array_add (leader->scale_siblings, self); + + self->group = leader; +} + +static CcDisplayScale * +find_next_display_scale (CcDisplayScale *self) +{ + CcDisplayScale *leader; + unsigned int index; + + if (!self->group) + leader = self; + else + leader = self->group; + + if (!leader->scale_siblings) + return NULL; + + if (self == leader) + return g_ptr_array_index (leader->scale_siblings, 0); + + if (!g_ptr_array_find (leader->scale_siblings, self, &index)) + { + g_warn_if_reached (); + return NULL; + } + + if (index == leader->scale_siblings->len - 1) + return NULL; + + return g_ptr_array_index (leader->scale_siblings, index + 1); +} + +static CcDisplayScale * +find_prev_display_scale (CcDisplayScale *self) +{ + CcDisplayScale *leader; + unsigned int index; + + if (!self->group) + leader = self; + else + leader = self->group; + + if (!leader->scale_siblings) + return NULL; + + if (self == leader) + return NULL; + + if (!g_ptr_array_find (leader->scale_siblings, self, &index)) + { + g_warn_if_reached (); + return NULL; + } + + if (index == 0) + return leader; + + return g_ptr_array_index (leader->scale_siblings, index - 1); +} + +static CcDisplayScale * +find_active_display_scale (CcDisplayScale *self) +{ + CcDisplayScale *leader; + int i; + + if (!self->group) + leader = self; + else + leader = self->group; + + if (leader->is_active) + return leader; + + for (i = 0; i < leader->scale_siblings->len; i++) + { + CcDisplayScale *display_scale = + g_ptr_array_index (leader->scale_siblings, i); + + if (display_scale->is_active) + return display_scale; + } + + g_return_val_if_reached (NULL); +} + +static gboolean +cc_display_scale_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + CcDisplayScale *self = CC_DISPLAY_SCALE (widget); + CcDisplayScale *active_display_scale; + CcDisplayScale *next_focus = NULL; + + switch (direction) + { + case GTK_DIR_RIGHT: + active_display_scale = find_active_display_scale (self); + next_focus = find_next_display_scale (active_display_scale); + break; + case GTK_DIR_LEFT: + active_display_scale = find_active_display_scale (self); + next_focus = find_prev_display_scale (active_display_scale); + break; + default: + break; + } + + if (next_focus) + { + gtk_widget_grab_focus (GTK_WIDGET (next_focus->text_preview)); + return TRUE; + } + + return FALSE; +} + +static void +cc_display_scale_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcDisplayScale *self = CC_DISPLAY_SCALE (object); + + switch (prop_id) + { + case PROP_IS_ACTIVE: + g_value_set_boolean (value, self->is_active); + break; + case PROP_SCALE: + g_value_set_double (value, self->scale); + break; + case PROP_CURRENT_SCALE: + g_value_set_double (value, self->current_scale); + break; + case PROP_IS_DEFAULT: + g_value_set_boolean (value, self->is_default); + break; + case PROP_GROUP: + g_value_set_object (value, self->group); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_display_scale_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcDisplayScale *self = CC_DISPLAY_SCALE (object); + + switch (prop_id) + { + case PROP_IS_ACTIVE: + cc_display_scale_set_is_active (self, g_value_get_boolean (value)); + break; + case PROP_SCALE: + self->scale = g_value_get_double (value); + break; + case PROP_CURRENT_SCALE: + self->current_scale = g_value_get_double (value); + break; + case PROP_IS_DEFAULT: + self->is_default = g_value_get_boolean (value); + break; + case PROP_GROUP: + set_group (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static int +round_scale_for_ui (double scale) +{ + /* Keep in sync with mutter */ + + return (int) ((round (scale * 4) / 4) * 100); +} + +static void +cc_display_scale_constructed (GObject *object) +{ + CcDisplayScale *self = CC_DISPLAY_SCALE (object); + PangoContext *pango_context = + gtk_widget_get_pango_context (GTK_WIDGET (self)); + PangoLanguage *pango_language; + const char *sample_string; + g_autoptr(PangoAttrList) attr_list = NULL; + g_autofree char *scale_label_text = NULL; + + pango_language = pango_context_get_language (pango_context); + sample_string = pango_language_get_sample_string (pango_language); + gtk_inscription_set_text (self->text_preview_text, sample_string); + + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, + pango_attr_scale_new (self->scale / self->current_scale)); + gtk_inscription_set_attributes (self->text_preview_text, attr_list); + + g_object_set (self->text_preview_text, + "width-request", (int) (100 * (self->scale / self->current_scale)), + "height-request", (int) (100 * (self->scale / self->current_scale)), + NULL); + + if (self->is_default) + { + scale_label_text = g_strdup_printf ("%d%%", + (int) round_scale_for_ui (self->scale)); + } + else + { + scale_label_text = g_strdup_printf ("%d%%", + (int) round_scale_for_ui (self->scale)); + } + gtk_label_set_label (self->scale_label, scale_label_text); + + G_OBJECT_CLASS (cc_display_scale_parent_class)->constructed (object); +} + +static void +cc_display_scale_finalize (GObject *object) +{ + CcDisplayScale *self = CC_DISPLAY_SCALE (object); + + g_clear_pointer (&self->scale_siblings, g_ptr_array_unref); + + G_OBJECT_CLASS (cc_display_scale_parent_class)->finalize (object); +} + +static void +cc_display_scale_class_init (CcDisplayScaleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = cc_display_scale_get_property; + object_class->set_property = cc_display_scale_set_property; + object_class->constructed = cc_display_scale_constructed; + object_class->finalize = cc_display_scale_finalize; + + widget_class->focus = cc_display_scale_focus; + + props[PROP_IS_ACTIVE] = + g_param_spec_boolean ("is-active", + "Is active", + "Is active", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + props[PROP_SCALE] = + g_param_spec_double ("scale", + "Scale", + "Scale", + 0.25, 10.0, + 1.0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + props[PROP_CURRENT_SCALE] = + g_param_spec_double ("current-scale", + "Current scale", + "Current scale", + 0.25, 10.0, + 1.0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + props[PROP_IS_DEFAULT] = + g_param_spec_boolean ("is-default", + "Is default", + "Is default", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + props[PROP_GROUP] = + g_param_spec_object ("group", + "Scale group", + "Scale group", + CC_TYPE_DISPLAY_SCALE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, props); + + signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 0); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/control-center/display/cc-display-scale.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcDisplayScale, text_preview); + gtk_widget_class_bind_template_child (widget_class, CcDisplayScale, text_preview_text); + gtk_widget_class_bind_template_child (widget_class, CcDisplayScale, scale_label); +} + +static void +load_custom_css (const char *path) +{ + g_autoptr (GtkCssProvider) provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, path); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); +} + +static void +scale_pressed_cb (GtkGestureClick *gesture, + guint n_press, + double x, + double y, + CcDisplayScale *self) +{ + set_active (self); +} + +static void +has_focus_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplayScale *self) +{ + set_active (self); +} + +static void +cc_display_scale_init (CcDisplayScale *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + gtk_widget_set_name (GTK_WIDGET (self), "display-scale"); + + load_custom_css ("/org/gnome/control-center/display/cc-display-scale.css"); + + GtkGesture * gesture; + + g_signal_connect (self->text_preview, "notify::has-focus", + G_CALLBACK (has_focus_cb), self); + + gesture = gtk_gesture_click_new (); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_PRIMARY); + g_signal_connect (gesture, "pressed", G_CALLBACK (scale_pressed_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); +} diff --git a/panels/display/cc-display-scale.css b/panels/display/cc-display-scale.css new file mode 100644 index 0000000000..93d55fffd3 --- /dev/null +++ b/panels/display/cc-display-scale.css @@ -0,0 +1,19 @@ +.cc-display-scale-preview { + border-radius: 12px; + outline: solid 1px; + outline-color: lightgrey; + padding: 6px 0px 0px 10px; + margin: 6px; +} + +.cc-display-scale-preview-active { + outline: solid 4px; + outline-color: @accent_color; + border-radius: 12px; + padding: 6px 0px 0px 10px; + margin: 6px; +} + +.cc-display-scale-factor { + margin: 6px; +} diff --git a/panels/display/cc-display-scale.h b/panels/display/cc-display-scale.h new file mode 100644 index 0000000000..5153da6745 --- /dev/null +++ b/panels/display/cc-display-scale.h @@ -0,0 +1,38 @@ +/* cc-display-scale.h + * + * Copyright 2023 Red Hat, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CC_TYPE_DISPLAY_SCALE (cc_display_scale_get_type ()) +G_DECLARE_FINAL_TYPE (CcDisplayScale, cc_display_scale, + CC, DISPLAY_SCALE, GtkBox) + +void cc_display_scale_set_is_active (CcDisplayScale *self, + gboolean is_active); + +gboolean cc_display_scale_get_is_active (CcDisplayScale *self); + +double cc_display_scale_get_scale (CcDisplayScale *self); + +G_END_DECLS diff --git a/panels/display/cc-display-scale.ui b/panels/display/cc-display-scale.ui new file mode 100644 index 0000000000..8dbb791a8b --- /dev/null +++ b/panels/display/cc-display-scale.ui @@ -0,0 +1,33 @@ + + + + diff --git a/panels/display/cc-display-settings.c b/panels/display/cc-display-settings.c index a55f59c80d..94e8982326 100644 --- a/panels/display/cc-display-settings.c +++ b/panels/display/cc-display-settings.c @@ -23,8 +23,10 @@ #include #include #include +#include "cc-display-scale.h" #include "cc-display-settings.h" #include "cc-display-config.h" +#include "cc-vertical-row.h" #define MAX_SCALE_BUTTONS 5 @@ -52,9 +54,7 @@ struct _CcDisplaySettings GtkWidget *orientation_row; GtkWidget *refresh_rate_row; GtkWidget *resolution_row; - GtkWidget *scale_bbox; - GtkWidget *scale_buttons_row; - GtkWidget *scale_combo_row; + GtkBox *scale_options; GtkWidget *underscanning_row; GtkWidget *underscanning_switch; }; @@ -73,10 +73,6 @@ G_DEFINE_TYPE (CcDisplaySettings, cc_display_settings, GTK_TYPE_BOX) static GParamSpec *props[PROP_LAST]; -static void on_scale_btn_active_changed_cb (CcDisplaySettings *self, - GParamSpec *pspec, - GtkWidget *widget); - static gboolean should_show_rotation (CcDisplaySettings *self) { @@ -185,19 +181,6 @@ make_resolution_string (CcDisplayMode *mode) return g_strdup_printf ("%d × %d%s", width, height, interlaced); } -static double -round_scale_for_ui (double scale) -{ - /* Keep in sync with mutter */ - return round (scale*4)/4; -} - -static gchar * -make_scale_string (gdouble scale) -{ - return g_strdup_printf ("%d %%", (int) (round_scale_for_ui (scale)*100)); -} - static gint sort_modes_by_area_desc (CcDisplayMode *a, CcDisplayMode *b) { @@ -222,6 +205,37 @@ sort_modes_by_freq_desc (CcDisplayMode *a, CcDisplayMode *b) return delta; } +static void +display_scale_is_active_changed_cb (CcDisplayScale *display_scale, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + double scale; + + if (!cc_display_scale_get_is_active (display_scale)) + return; + + scale = cc_display_scale_get_scale (display_scale); + cc_display_monitor_set_scale (self->selected_output, scale); + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static int +find_default_scale_index (GArray *scales, + double default_scale) +{ + int i; + + for (i = 0; i < scales->len; i++) + { + double scale = g_array_index (scales, double, i); + if (G_APPROX_VALUE (scale, default_scale, DBL_EPSILON)) + return i; + } + + return -1; +} + static gboolean cc_display_settings_rebuild_ui (CcDisplaySettings *self) { @@ -231,8 +245,12 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) GList *item; gint width, height; CcDisplayMode *current_mode; - GtkToggleButton *group = NULL; g_autoptr(GArray) scales = NULL; + double default_scale; + CcDisplayScale *leader_scale = NULL; + int default_scale_index; + int first_scale_index; + int last_scale_index; gint i; self->idle_udpate_id = 0; @@ -243,8 +261,6 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) gtk_widget_set_visible (self->orientation_row, FALSE); gtk_widget_set_visible (self->refresh_rate_row, FALSE); gtk_widget_set_visible (self->resolution_row, FALSE); - gtk_widget_set_visible (self->scale_combo_row, FALSE); - gtk_widget_set_visible (self->scale_buttons_row, FALSE); gtk_widget_set_visible (self->underscanning_row, FALSE); return G_SOURCE_REMOVE; @@ -254,7 +270,6 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) g_object_freeze_notify ((GObject*) self->orientation_row); g_object_freeze_notify ((GObject*) self->refresh_rate_row); g_object_freeze_notify ((GObject*) self->resolution_row); - g_object_freeze_notify ((GObject*) self->scale_combo_row); g_object_freeze_notify ((GObject*) self->underscanning_switch); cc_display_monitor_get_geometry (self->selected_output, NULL, NULL, &width, &height); @@ -398,56 +413,48 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) g_list_store_insert (self->resolution_list, ins, mode); } - - /* Scale row is usually shown. */ - while ((child = gtk_widget_get_first_child (self->scale_bbox)) != NULL) - gtk_box_remove (GTK_BOX (self->scale_bbox), child); - - gtk_string_list_splice (GTK_STRING_LIST (self->scale_list), - 0, - g_list_model_get_n_items (self->scale_list), - NULL); scales = cc_display_mode_get_supported_scales (current_mode); + default_scale = cc_display_mode_get_preferred_scale (current_mode); self->num_scales = scales->len; - for (i = 0; i < scales->len; i++) + + gtk_box_set_homogeneous (self->scale_options, TRUE); + + while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->scale_options)))) + gtk_box_remove (GTK_BOX (self->scale_options), child); + + default_scale_index = find_default_scale_index (scales, default_scale); + g_warn_if_fail (default_scale_index >= 0); + + first_scale_index = MAX (0, default_scale_index - 2); + last_scale_index = MIN (scales->len - 1, first_scale_index + 4); + if (last_scale_index == scales->len -1) + first_scale_index = MAX (0, last_scale_index - 4); + + for (i = first_scale_index; i <= last_scale_index; i++) { - g_autofree gchar *scale_str = NULL; - g_autoptr(GObject) value_object = NULL; double scale = g_array_index (scales, double, i); - GtkWidget *scale_btn; - gboolean is_selected; - - /* ComboRow */ - scale_str = make_scale_string (scale); - is_selected = G_APPROX_VALUE (cc_display_monitor_get_scale (self->selected_output), - scale, DBL_EPSILON); - - gtk_string_list_append (GTK_STRING_LIST (self->scale_list), scale_str); - value_object = g_list_model_get_item (self->scale_list, i); - g_object_set_data_full (G_OBJECT (value_object), "scale", - g_memdup2 (&scale, sizeof (double)), g_free); - if (is_selected) - adw_combo_row_set_selected (ADW_COMBO_ROW (self->scale_combo_row), - g_list_model_get_n_items (G_LIST_MODEL (self->scale_list)) - 1); - - /* ButtonBox */ - scale_btn = gtk_toggle_button_new_with_label (scale_str); - gtk_toggle_button_set_group (GTK_TOGGLE_BUTTON (scale_btn), group); - g_object_set_data_full (G_OBJECT (scale_btn), "scale", - g_memdup2 (&scale, sizeof (double)), g_free); - - if (!group) - group = GTK_TOGGLE_BUTTON (scale_btn); - gtk_box_append (GTK_BOX (self->scale_bbox), scale_btn); - /* Set active before connecting the signal */ - if (is_selected) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scale_btn), TRUE); - - g_signal_connect_object (scale_btn, - "notify::active", - G_CALLBACK (on_scale_btn_active_changed_cb), - self, G_CONNECT_SWAPPED); + double current_scale; + CcDisplayScale *display_scale; + + current_scale = cc_display_monitor_get_scale (self->selected_output); + display_scale = g_object_new (CC_TYPE_DISPLAY_SCALE, + "is-active", G_APPROX_VALUE (current_scale, + scale, + FLT_EPSILON), + "is-default", default_scale == scale, + "scale", scale, + "current-scale", current_scale, + "group", leader_scale, + NULL); + g_signal_connect (display_scale, "notify::is-active", + G_CALLBACK (display_scale_is_active_changed_cb), + self); + gtk_box_append (self->scale_options, GTK_WIDGET (display_scale)); + + if (!leader_scale) + leader_scale = display_scale; } + cc_display_settings_refresh_layout (self, self->folded); gtk_widget_set_visible (self->underscanning_row, @@ -461,7 +468,6 @@ cc_display_settings_rebuild_ui (CcDisplaySettings *self) g_object_thaw_notify ((GObject*) self->orientation_row); g_object_thaw_notify ((GObject*) self->refresh_rate_row); g_object_thaw_notify ((GObject*) self->resolution_row); - g_object_thaw_notify ((GObject*) self->scale_combo_row); g_object_thaw_notify ((GObject*) self->underscanning_switch); self->updating = FALSE; @@ -559,46 +565,6 @@ on_resolution_selection_changed_cb (CcDisplaySettings *self) g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); } -static void -on_scale_btn_active_changed_cb (CcDisplaySettings *self, - GParamSpec *pspec, - GtkWidget *widget) -{ - gdouble scale; - if (self->updating) - return; - - if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) - return; - - scale = *(gdouble*) g_object_get_data (G_OBJECT (widget), "scale"); - cc_display_monitor_set_scale (self->selected_output, - scale); - - g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); -} - -static void -on_scale_selection_changed_cb (CcDisplaySettings *self) -{ - int idx; - double scale; - g_autoptr(GObject) obj = NULL; - - if (self->updating) - return; - - idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->scale_combo_row)); - obj = g_list_model_get_item (G_LIST_MODEL (self->scale_list), idx); - if (!obj) - return; - scale = *(gdouble*) g_object_get_data (G_OBJECT (obj), "scale"); - - cc_display_monitor_set_scale (self->selected_output, scale); - - g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); -} - static void on_underscanning_switch_active_changed_cb (CcDisplaySettings *self) { @@ -675,7 +641,6 @@ cc_display_settings_finalize (GObject *object) g_clear_object (&self->orientation_list); g_clear_object (&self->refresh_rate_list); g_clear_object (&self->resolution_list); - g_clear_object (&self->scale_list); if (self->idle_udpate_id) g_source_remove (self->idle_udpate_id); @@ -690,6 +655,8 @@ cc_display_settings_class_init (CcDisplaySettingsClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + g_type_ensure (CC_TYPE_VERTICAL_ROW); + gobject_class->finalize = cc_display_settings_finalize; gobject_class->get_property = cc_display_settings_get_property; gobject_class->set_property = cc_display_settings_set_property; @@ -730,9 +697,7 @@ cc_display_settings_class_init (CcDisplaySettingsClass *klass) gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, orientation_row); gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, refresh_rate_row); gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, resolution_row); - gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_bbox); - gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_buttons_row); - gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_combo_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_options); gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_row); gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_switch); @@ -740,10 +705,19 @@ cc_display_settings_class_init (CcDisplaySettingsClass *klass) gtk_widget_class_bind_template_callback (widget_class, on_orientation_selection_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_refresh_rate_selection_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_resolution_selection_changed_cb); - gtk_widget_class_bind_template_callback (widget_class, on_scale_selection_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_underscanning_switch_active_changed_cb); } +static void +load_custom_css (const char *path) +{ + g_autoptr (GtkCssProvider) provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, path); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); +} + static void cc_display_settings_init (CcDisplaySettings *self) { @@ -754,14 +728,11 @@ cc_display_settings_init (CcDisplaySettings *self) self->orientation_list = G_LIST_MODEL (gtk_string_list_new (NULL)); self->refresh_rate_list = g_list_store_new (CC_TYPE_DISPLAY_MODE); self->resolution_list = g_list_store_new (CC_TYPE_DISPLAY_MODE); - self->scale_list = G_LIST_MODEL (gtk_string_list_new (NULL)); self->updating = TRUE; adw_combo_row_set_model (ADW_COMBO_ROW (self->orientation_row), G_LIST_MODEL (self->orientation_list)); - adw_combo_row_set_model (ADW_COMBO_ROW (self->scale_combo_row), - G_LIST_MODEL (self->scale_list)); expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, @@ -780,6 +751,8 @@ cc_display_settings_init (CcDisplaySettings *self) G_LIST_MODEL (self->resolution_list)); self->updating = FALSE; + + load_custom_css ("/org/gnome/control-center/display/cc-display-settings.css"); } CcDisplaySettings* @@ -815,7 +788,7 @@ void cc_display_settings_set_config (CcDisplaySettings *self, CcDisplayConfig *config) { - const gchar *signals[] = { "rotation", "mode", "scale", "is-usable", "active" }; + const gchar *signals[] = { "rotation", "mode", "is-usable", "active" }; GList *outputs, *l; guint i; @@ -872,13 +845,7 @@ void cc_display_settings_refresh_layout (CcDisplaySettings *self, gboolean folded) { - gboolean use_combo; - self->folded = folded; - use_combo = self->num_scales > MAX_SCALE_BUTTONS || (self->num_scales > 2 && folded); - - gtk_widget_set_visible (self->scale_combo_row, use_combo); - gtk_widget_set_visible (self->scale_buttons_row, self->num_scales > 1 && !use_combo); } void diff --git a/panels/display/cc-display-settings.css b/panels/display/cc-display-settings.css new file mode 100644 index 0000000000..232bcb450c --- /dev/null +++ b/panels/display/cc-display-settings.css @@ -0,0 +1,3 @@ +.cc-display-scale-disclaimer { + margin: 18px 0px 18px 0px; +} diff --git a/panels/display/cc-display-settings.ui b/panels/display/cc-display-settings.ui index 6de2ba6ee1..1e6b7c53ee 100644 --- a/panels/display/cc-display-settings.ui +++ b/panels/display/cc-display-settings.ui @@ -74,27 +74,33 @@ - + 100 - Scale - - - end - center - + Scaling + + + vertical + + + horizontal + True + fill + center + + + + + start + Some apps may not look sharp when scaling is set over 100% + + + - - - 100 - Scale - - - diff --git a/panels/display/display.gresource.xml b/panels/display/display.gresource.xml index fcac1a274e..d222e7b6b1 100644 --- a/panels/display/display.gresource.xml +++ b/panels/display/display.gresource.xml @@ -1,9 +1,12 @@ + cc-display-scale.ui cc-display-panel.ui cc-display-settings.ui cc-night-light-page.ui + cc-display-scale.css + cc-display-settings.css display-arrangement.css night-light.css diff --git a/panels/display/meson.build b/panels/display/meson.build index 64d6075d40..03e4164cb6 100644 --- a/panels/display/meson.build +++ b/panels/display/meson.build @@ -15,6 +15,7 @@ sources = files( 'cc-display-arrangement.c', 'cc-display-config.c', 'cc-display-config-manager.c', + 'cc-display-scale.c', 'cc-display-settings.c', 'cc-night-light-page.c', ) -- GitLab