Mutter sends modes in descending order of preference. By reverting the order via `g_list_prepend`, we get unintended side effects such as choosing the least preferred refresh rate by default (if the selected mode is not marked as preferred). Instead of adding complex logic in several places, make sure that the assumption of descending preference is kept by simply not inverting the order. Closes https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/1562
1881 lines
53 KiB
C
1881 lines
53 KiB
C
/*
|
|
* 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 <float.h>
|
|
#include <math.h>
|
|
#include <gio/gio.h>
|
|
|
|
#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 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 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->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->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 (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;
|
|
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 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_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->set_mode = cc_display_monitor_dbus_set_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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
GList *clone_modes;
|
|
};
|
|
|
|
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)
|
|
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 GList *
|
|
cc_display_config_dbus_get_cloning_modes (CcDisplayConfig *pself)
|
|
{
|
|
CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
|
|
|
|
return self->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
|
|
gather_clone_modes (CcDisplayConfigDBus *self)
|
|
{
|
|
guint n_monitors = g_list_length (self->monitors);
|
|
CcDisplayMonitorDBus *monitor;
|
|
GList *l;
|
|
|
|
if (n_monitors < 2)
|
|
return;
|
|
|
|
monitor = self->monitors->data;
|
|
for (l = monitor->modes; l != NULL; l = l->next)
|
|
{
|
|
CcDisplayModeDBus *mode = l->data;
|
|
gboolean valid = TRUE;
|
|
GList *ll;
|
|
for (ll = self->monitors->next; ll != NULL; ll = ll->next)
|
|
{
|
|
CcDisplayMonitorDBus *other_monitor = ll->data;
|
|
if (!cc_display_monitor_dbus_get_closest_mode (other_monitor, mode))
|
|
{
|
|
valid = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (valid)
|
|
self->clone_modes = g_list_prepend (self->clone_modes, mode);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
gather_clone_modes (self);
|
|
}
|
|
|
|
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 (GDBusProxy *proxy,
|
|
GVariant *changed_properties,
|
|
GStrv invalidated_properties,
|
|
CcDisplayConfigDBus *self)
|
|
{
|
|
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 (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_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_list_foreach (self->monitors, (GFunc) g_object_unref, NULL);
|
|
g_clear_pointer (&self->monitors, g_list_free);
|
|
g_clear_pointer (&self->logical_monitors, g_hash_table_destroy);
|
|
g_clear_pointer (&self->clone_modes, g_list_free);
|
|
|
|
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->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->get_cloning_modes = cc_display_config_dbus_get_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);
|
|
}
|