mirror of
https://github.com/brl/mutter.git
synced 2024-12-04 22:00:42 -05:00
97f0e2e691
If there are two display modes present with the same flags, and one of them is the preferred mode, it could have been excluded from the resulting list of display modes. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2858 Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3061>
2358 lines
67 KiB
C
2358 lines
67 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2016 Red Hat
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/meta-monitor.h"
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/meta-crtc.h"
|
|
#include "backends/meta-gpu.h"
|
|
#include "backends/meta-monitor-manager-private.h"
|
|
#include "backends/meta-settings-private.h"
|
|
#include "backends/meta-output.h"
|
|
#include "core/boxes-private.h"
|
|
|
|
#define SCALE_FACTORS_PER_INTEGER 4
|
|
#define SCALE_FACTORS_STEPS (1.0 / (float) SCALE_FACTORS_PER_INTEGER)
|
|
#define MINIMUM_SCALE_FACTOR 1.0f
|
|
#define MAXIMUM_SCALE_FACTOR 4.0f
|
|
#define MINIMUM_LOGICAL_AREA (800 * 480)
|
|
#define MAXIMUM_REFRESH_RATE_DIFF 0.001
|
|
|
|
typedef struct _MetaMonitorMode
|
|
{
|
|
MetaMonitor *monitor;
|
|
char *id;
|
|
MetaMonitorModeSpec spec;
|
|
MetaMonitorCrtcMode *crtc_modes;
|
|
} MetaMonitorMode;
|
|
|
|
typedef struct _MetaMonitorModeTiled
|
|
{
|
|
MetaMonitorMode parent;
|
|
|
|
gboolean is_tiled;
|
|
} MetaMonitorModeTiled;
|
|
|
|
typedef struct _MetaMonitorPrivate
|
|
{
|
|
MetaBackend *backend;
|
|
|
|
GList *outputs;
|
|
GList *modes;
|
|
GHashTable *mode_ids;
|
|
|
|
MetaMonitorMode *preferred_mode;
|
|
MetaMonitorMode *current_mode;
|
|
|
|
MetaMonitorSpec *spec;
|
|
|
|
MetaLogicalMonitor *logical_monitor;
|
|
|
|
/*
|
|
* The primary or first output for this monitor, 0 if we can't figure out.
|
|
* It can be matched to a winsys_id of a MetaOutput.
|
|
*
|
|
* This is used as an opaque token on reconfiguration when switching from
|
|
* clone to extended, to decide on what output the windows should go next
|
|
* (it's an attempt to keep windows on the same monitor, and preferably on
|
|
* the primary one).
|
|
*/
|
|
uint64_t winsys_id;
|
|
|
|
char *display_name;
|
|
} MetaMonitorPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (MetaMonitor, meta_monitor, G_TYPE_OBJECT)
|
|
|
|
struct _MetaMonitorNormal
|
|
{
|
|
MetaMonitor parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaMonitorNormal, meta_monitor_normal, META_TYPE_MONITOR)
|
|
|
|
struct _MetaMonitorTiled
|
|
{
|
|
MetaMonitor parent;
|
|
|
|
MetaMonitorManager *monitor_manager;
|
|
|
|
uint32_t tile_group_id;
|
|
|
|
/* The tile (0, 0) output. */
|
|
MetaOutput *origin_output;
|
|
|
|
/* The output enabled even when a non-tiled mode is used. */
|
|
MetaOutput *main_output;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaMonitorTiled, meta_monitor_tiled, META_TYPE_MONITOR)
|
|
|
|
static void
|
|
meta_monitor_mode_free (MetaMonitorMode *mode);
|
|
|
|
MetaMonitorSpec *
|
|
meta_monitor_spec_clone (MetaMonitorSpec *monitor_spec)
|
|
{
|
|
MetaMonitorSpec *new_monitor_spec;
|
|
|
|
new_monitor_spec = g_new0 (MetaMonitorSpec, 1);
|
|
*new_monitor_spec = (MetaMonitorSpec) {
|
|
.connector = g_strdup (monitor_spec->connector),
|
|
.vendor = g_strdup (monitor_spec->vendor),
|
|
.product = g_strdup (monitor_spec->product),
|
|
.serial = g_strdup (monitor_spec->serial),
|
|
};
|
|
|
|
return new_monitor_spec;
|
|
}
|
|
|
|
guint
|
|
meta_monitor_spec_hash (gconstpointer key)
|
|
{
|
|
const MetaMonitorSpec *monitor_spec = key;
|
|
|
|
return (g_str_hash (monitor_spec->connector) +
|
|
g_str_hash (monitor_spec->vendor) +
|
|
g_str_hash (monitor_spec->product) +
|
|
g_str_hash (monitor_spec->serial));
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_spec_equals (MetaMonitorSpec *monitor_spec,
|
|
MetaMonitorSpec *other_monitor_spec)
|
|
{
|
|
return (g_str_equal (monitor_spec->connector, other_monitor_spec->connector) &&
|
|
g_str_equal (monitor_spec->vendor, other_monitor_spec->vendor) &&
|
|
g_str_equal (monitor_spec->product, other_monitor_spec->product) &&
|
|
g_str_equal (monitor_spec->serial, other_monitor_spec->serial));
|
|
}
|
|
|
|
int
|
|
meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a,
|
|
MetaMonitorSpec *monitor_spec_b)
|
|
{
|
|
int ret;
|
|
|
|
ret = strcmp (monitor_spec_a->connector, monitor_spec_b->connector);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = strcmp (monitor_spec_a->vendor, monitor_spec_b->vendor);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = strcmp (monitor_spec_a->product, monitor_spec_b->product);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
return strcmp (monitor_spec_a->serial, monitor_spec_b->serial);
|
|
}
|
|
|
|
void
|
|
meta_monitor_spec_free (MetaMonitorSpec *monitor_spec)
|
|
{
|
|
g_free (monitor_spec->connector);
|
|
g_free (monitor_spec->vendor);
|
|
g_free (monitor_spec->product);
|
|
g_free (monitor_spec->serial);
|
|
g_free (monitor_spec);
|
|
}
|
|
|
|
static const MetaOutputInfo *
|
|
meta_monitor_get_main_output_info (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_get_info (output);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_generate_spec (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
MetaMonitorSpec *monitor_spec;
|
|
const char *vendor;
|
|
const char *product;
|
|
const char *serial;
|
|
|
|
vendor = output_info->vendor;
|
|
product = output_info->product;
|
|
serial = output_info->serial;
|
|
|
|
monitor_spec = g_new0 (MetaMonitorSpec, 1);
|
|
*monitor_spec = (MetaMonitorSpec) {
|
|
.connector = g_strdup (output_info->name),
|
|
.vendor = g_strdup (vendor ? vendor : "unknown"),
|
|
.product = g_strdup (product ? product : "unknown"),
|
|
.serial = g_strdup (serial ? serial : "unknown"),
|
|
};
|
|
|
|
priv->spec = monitor_spec;
|
|
}
|
|
|
|
static const double known_diagonals[] = {
|
|
12.1,
|
|
13.3,
|
|
15.6
|
|
};
|
|
|
|
static char *
|
|
diagonal_to_str (double d)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (known_diagonals); i++)
|
|
{
|
|
double delta;
|
|
|
|
delta = fabs(known_diagonals[i] - d);
|
|
if (delta < 0.1)
|
|
return g_strdup_printf ("%0.1lf\"", known_diagonals[i]);
|
|
}
|
|
|
|
return g_strdup_printf ("%d\"", (int) (d + 0.5));
|
|
}
|
|
|
|
static char *
|
|
meta_monitor_make_display_name (MetaMonitor *monitor)
|
|
{
|
|
MetaBackend *backend = meta_monitor_get_backend (monitor);
|
|
g_autofree char *inches = NULL;
|
|
g_autofree char *vendor_name = NULL;
|
|
const char *vendor = NULL;
|
|
const char *product_name = NULL;
|
|
int width_mm;
|
|
int height_mm;
|
|
|
|
meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm);
|
|
|
|
if (meta_monitor_is_laptop_panel (monitor))
|
|
return g_strdup (_("Built-in display"));
|
|
|
|
if (width_mm > 0 && height_mm > 0)
|
|
{
|
|
if (!meta_monitor_has_aspect_as_size (monitor))
|
|
{
|
|
double d = sqrt (width_mm * width_mm +
|
|
height_mm * height_mm);
|
|
inches = diagonal_to_str (d / 25.4);
|
|
}
|
|
else
|
|
{
|
|
product_name = meta_monitor_get_product (monitor);
|
|
}
|
|
}
|
|
|
|
vendor = meta_monitor_get_vendor (monitor);
|
|
if (vendor)
|
|
{
|
|
vendor_name = meta_backend_get_vendor_name (backend, vendor);
|
|
|
|
if (!vendor_name)
|
|
vendor_name = g_strdup (vendor);
|
|
}
|
|
else
|
|
{
|
|
if (inches != NULL)
|
|
vendor_name = g_strdup (_("Unknown"));
|
|
else
|
|
vendor_name = g_strdup (_("Unknown Display"));
|
|
}
|
|
|
|
if (inches != NULL)
|
|
{
|
|
/**/
|
|
return g_strdup_printf (C_("This is a monitor vendor name, followed by a "
|
|
"size in inches, like 'Dell 15\"'",
|
|
"%s %s"),
|
|
vendor_name, inches);
|
|
}
|
|
else if (product_name != NULL)
|
|
{
|
|
return g_strdup_printf (C_("This is a monitor vendor name followed by "
|
|
"product/model name where size in inches "
|
|
"could not be calculated, e.g. Dell U2414H",
|
|
"%s %s"),
|
|
vendor_name, product_name);
|
|
}
|
|
else
|
|
{
|
|
return g_strdup (vendor_name);
|
|
}
|
|
}
|
|
|
|
MetaBackend *
|
|
meta_monitor_get_backend (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->backend;
|
|
}
|
|
|
|
GList *
|
|
meta_monitor_get_outputs (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->outputs;
|
|
}
|
|
|
|
MetaOutput *
|
|
meta_monitor_get_main_output (MetaMonitor *monitor)
|
|
{
|
|
return META_MONITOR_GET_CLASS (monitor)->get_main_output (monitor);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_active (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return !!priv->current_mode;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_primary (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_is_primary (output);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_supports_underscanning (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->supports_underscanning;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_supports_color_transform (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->supports_color_transform;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_underscanning (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_is_underscanning (output);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_get_max_bpc (MetaMonitor *monitor,
|
|
unsigned int *max_bpc)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_get_max_bpc (output, max_bpc);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_laptop_panel (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
switch (output_info->connector_type)
|
|
{
|
|
case META_CONNECTOR_TYPE_eDP:
|
|
case META_CONNECTOR_TYPE_LVDS:
|
|
case META_CONNECTOR_TYPE_DSI:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_virtual (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->is_virtual;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_same_as (MetaMonitor *monitor,
|
|
MetaMonitor *other_monitor)
|
|
{
|
|
const MetaMonitorSpec *spec = meta_monitor_get_spec (monitor);
|
|
const MetaMonitorSpec *other_spec = meta_monitor_get_spec (other_monitor);
|
|
|
|
if ((g_strcmp0 (spec->vendor, "unknown") == 0 ||
|
|
g_strcmp0 (spec->product, "unknown") == 0 ||
|
|
g_strcmp0 (spec->serial, "unknown") == 0) &&
|
|
(g_strcmp0 (other_spec->vendor, "unknown") == 0 ||
|
|
g_strcmp0 (other_spec->product, "unknown") == 0 ||
|
|
g_strcmp0 (other_spec->serial, "unknown") == 0))
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorPrivate *other_priv =
|
|
meta_monitor_get_instance_private (other_monitor);
|
|
|
|
return priv->winsys_id == other_priv->winsys_id;
|
|
}
|
|
|
|
if (g_strcmp0 (spec->vendor, other_spec->vendor) != 0)
|
|
return FALSE;
|
|
|
|
if (g_strcmp0 (spec->product, other_spec->product) != 0)
|
|
return FALSE;
|
|
|
|
if (g_strcmp0 (spec->serial, other_spec->serial) != 0)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_monitor_get_current_resolution (MetaMonitor *monitor,
|
|
int *width,
|
|
int *height)
|
|
{
|
|
MetaMonitorMode *mode = meta_monitor_get_current_mode (monitor);
|
|
|
|
*width = mode->spec.width;
|
|
*height = mode->spec.height;
|
|
}
|
|
|
|
void
|
|
meta_monitor_derive_layout (MetaMonitor *monitor,
|
|
MetaRectangle *layout)
|
|
{
|
|
META_MONITOR_GET_CLASS (monitor)->derive_layout (monitor, layout);
|
|
}
|
|
|
|
void
|
|
meta_monitor_get_physical_dimensions (MetaMonitor *monitor,
|
|
int *width_mm,
|
|
int *height_mm)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
*width_mm = output_info->width_mm;
|
|
*height_mm = output_info->height_mm;
|
|
}
|
|
|
|
CoglSubpixelOrder
|
|
meta_monitor_get_subpixel_order (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->subpixel_order;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_connector (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->name;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_vendor (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->vendor;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_product (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->product;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_serial (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->serial;
|
|
}
|
|
|
|
const MetaEdidInfo *
|
|
meta_monitor_get_edid_info (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->edid_info;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_edid_checksum_md5 (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->edid_checksum_md5;
|
|
}
|
|
|
|
MetaConnectorType
|
|
meta_monitor_get_connector_type (MetaMonitor *monitor)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
return output_info->connector_type;
|
|
}
|
|
|
|
MetaMonitorTransform
|
|
meta_monitor_logical_to_crtc_transform (MetaMonitor *monitor,
|
|
MetaMonitorTransform transform)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_logical_to_crtc_transform (output, transform);
|
|
}
|
|
|
|
MetaMonitorTransform
|
|
meta_monitor_crtc_to_logical_transform (MetaMonitor *monitor,
|
|
MetaMonitorTransform transform)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
|
|
return meta_output_crtc_to_logical_transform (output, transform);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_dispose (GObject *object)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (object);
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
if (priv->outputs)
|
|
{
|
|
g_list_foreach (priv->outputs, (GFunc) meta_output_unset_monitor, NULL);
|
|
g_list_free_full (priv->outputs, g_object_unref);
|
|
priv->outputs = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (meta_monitor_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_finalize (GObject *object)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (object);
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
g_hash_table_destroy (priv->mode_ids);
|
|
g_list_free_full (priv->modes, (GDestroyNotify) meta_monitor_mode_free);
|
|
meta_monitor_spec_free (priv->spec);
|
|
g_free (priv->display_name);
|
|
|
|
G_OBJECT_CLASS (meta_monitor_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_init (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
priv->mode_ids = g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_class_init (MetaMonitorClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = meta_monitor_dispose;
|
|
object_class->finalize = meta_monitor_finalize;
|
|
}
|
|
|
|
static char *
|
|
generate_mode_id (MetaMonitorModeSpec *monitor_mode_spec)
|
|
{
|
|
gboolean is_interlaced;
|
|
char rate_str[G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
is_interlaced = !!(monitor_mode_spec->flags & META_CRTC_MODE_FLAG_INTERLACE);
|
|
g_ascii_formatd (rate_str, sizeof (rate_str),
|
|
"%.3f", monitor_mode_spec->refresh_rate);
|
|
|
|
return g_strdup_printf ("%dx%d%s@%s",
|
|
monitor_mode_spec->width,
|
|
monitor_mode_spec->height,
|
|
is_interlaced ? "i" : "",
|
|
rate_str);
|
|
}
|
|
|
|
static gboolean
|
|
meta_monitor_add_mode (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
gboolean replace)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorMode *existing_mode;
|
|
|
|
existing_mode = g_hash_table_lookup (priv->mode_ids,
|
|
meta_monitor_mode_get_id (monitor_mode));
|
|
if (existing_mode && !replace)
|
|
return FALSE;
|
|
|
|
if (existing_mode)
|
|
priv->modes = g_list_remove (priv->modes, existing_mode);
|
|
|
|
priv->modes = g_list_append (priv->modes, monitor_mode);
|
|
g_hash_table_replace (priv->mode_ids, monitor_mode->id, monitor_mode);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaMonitorModeSpec
|
|
meta_monitor_create_spec (MetaMonitor *monitor,
|
|
int width,
|
|
int height,
|
|
MetaCrtcMode *crtc_mode)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
const MetaCrtcModeInfo *crtc_mode_info =
|
|
meta_crtc_mode_get_info (crtc_mode);
|
|
|
|
if (meta_monitor_transform_is_rotated (output_info->panel_orientation_transform))
|
|
{
|
|
int temp = width;
|
|
width = height;
|
|
height = temp;
|
|
}
|
|
|
|
return (MetaMonitorModeSpec) {
|
|
.width = width,
|
|
.height = height,
|
|
.refresh_rate = crtc_mode_info->refresh_rate,
|
|
.flags = crtc_mode_info->flags & HANDLED_CRTC_MODE_FLAGS
|
|
};
|
|
}
|
|
|
|
/**
|
|
* meta_monitor_get_gamma_lut_size:
|
|
* @monitor: The MetaMonitor instance to retrieve the size from.
|
|
*
|
|
* Get the size of the look-up tables (LUTs) for the monitor.
|
|
*
|
|
* Retrieve the size of the LUT used to implement the encoding or decoding
|
|
* transfer functions ("gamma", "degamma") for the CRTC or CRTCs that backs
|
|
* this monitor.
|
|
*
|
|
* Returns: The number of look-up table entries possible for the monitor. It is
|
|
* assumed that each CRTC of a monitor has identical gamma LUT sizes.
|
|
*/
|
|
size_t
|
|
meta_monitor_get_gamma_lut_size (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
MetaCrtc *crtc;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
return meta_crtc_get_gamma_lut_size (crtc);
|
|
}
|
|
|
|
static gboolean
|
|
set_gamma_lut (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode,
|
|
MetaMonitorCrtcMode *monitor_crtc_mode,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
const MetaGammaLut *lut = user_data;
|
|
MetaCrtc *crtc;
|
|
|
|
crtc = meta_output_get_assigned_crtc (monitor_crtc_mode->output);
|
|
|
|
meta_crtc_set_gamma_lut (crtc, lut);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* meta_monitor_set_gamma_lut:
|
|
*
|
|
* Set a new gamma look-up table (LUT) for the given monitor's CRTCs.
|
|
*/
|
|
void
|
|
meta_monitor_set_gamma_lut (MetaMonitor *monitor,
|
|
const MetaGammaLut *lut)
|
|
{
|
|
MetaMonitorMode *current_mode;
|
|
|
|
current_mode = meta_monitor_get_current_mode (monitor);
|
|
g_return_if_fail (current_mode);
|
|
|
|
meta_monitor_mode_foreach_crtc (monitor,
|
|
current_mode,
|
|
set_gamma_lut,
|
|
(gpointer) lut,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_normal_generate_modes (MetaMonitorNormal *monitor_normal)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_normal);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaOutput *output;
|
|
const MetaOutputInfo *output_info;
|
|
MetaCrtcMode *preferred_mode;
|
|
MetaCrtcModeFlag preferred_mode_flags;
|
|
unsigned int i;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
output_info = meta_output_get_info (output);
|
|
preferred_mode = output_info->preferred_mode;
|
|
preferred_mode_flags = meta_crtc_mode_get_info (preferred_mode)->flags;
|
|
|
|
for (i = 0; i < output_info->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = output_info->modes[i];
|
|
const MetaCrtcModeInfo *crtc_mode_info =
|
|
meta_crtc_mode_get_info (crtc_mode);
|
|
MetaCrtc *crtc;
|
|
MetaMonitorMode *mode;
|
|
gboolean replace;
|
|
|
|
mode = g_new0 (MetaMonitorMode, 1);
|
|
mode->monitor = monitor;
|
|
mode->spec = meta_monitor_create_spec (monitor,
|
|
crtc_mode_info->width,
|
|
crtc_mode_info->height,
|
|
crtc_mode);
|
|
mode->id = generate_mode_id (&mode->spec);
|
|
mode->crtc_modes = g_new (MetaMonitorCrtcMode, 1);
|
|
mode->crtc_modes[0] = (MetaMonitorCrtcMode) {
|
|
.output = output,
|
|
.crtc_mode = crtc_mode
|
|
};
|
|
|
|
/*
|
|
* We don't distinguish between all available mode flags, just the ones
|
|
* that are configurable. We still need to pick some mode though, so
|
|
* prefer ones that has the same set of flags as the preferred mode;
|
|
* otherwise take the first one in the list. This guarantees that the
|
|
* preferred mode is always added.
|
|
*/
|
|
replace = (crtc_mode_info->flags == preferred_mode_flags &&
|
|
(!monitor_priv->preferred_mode ||
|
|
g_strcmp0 (meta_monitor_mode_get_id (monitor_priv->preferred_mode),
|
|
mode->id) != 0));
|
|
|
|
if (!meta_monitor_add_mode (monitor, mode, replace))
|
|
{
|
|
g_assert (crtc_mode != output_info->preferred_mode);
|
|
meta_monitor_mode_free (mode);
|
|
continue;
|
|
}
|
|
|
|
if (crtc_mode == output_info->preferred_mode)
|
|
monitor_priv->preferred_mode = mode;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
if (crtc)
|
|
{
|
|
const MetaCrtcConfig *crtc_config;
|
|
|
|
crtc_config = meta_crtc_get_config (crtc);
|
|
if (crtc_config && crtc_mode == crtc_config->mode)
|
|
monitor_priv->current_mode = mode;
|
|
}
|
|
}
|
|
}
|
|
|
|
MetaMonitorNormal *
|
|
meta_monitor_normal_new (MetaMonitorManager *monitor_manager,
|
|
MetaOutput *output)
|
|
{
|
|
MetaMonitorNormal *monitor_normal;
|
|
MetaMonitor *monitor;
|
|
MetaMonitorPrivate *monitor_priv;
|
|
|
|
monitor_normal = g_object_new (META_TYPE_MONITOR_NORMAL, NULL);
|
|
monitor = META_MONITOR (monitor_normal);
|
|
monitor_priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
monitor_priv->backend = meta_monitor_manager_get_backend (monitor_manager);
|
|
|
|
monitor_priv->outputs = g_list_append (NULL, g_object_ref (output));
|
|
meta_output_set_monitor (output, monitor);
|
|
|
|
monitor_priv->winsys_id = meta_output_get_id (output);
|
|
meta_monitor_generate_spec (monitor);
|
|
|
|
meta_monitor_normal_generate_modes (monitor_normal);
|
|
|
|
monitor_priv->display_name = meta_monitor_make_display_name (monitor);
|
|
|
|
return monitor_normal;
|
|
}
|
|
|
|
static MetaOutput *
|
|
meta_monitor_normal_get_main_output (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
|
|
return monitor_priv->outputs->data;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_normal_derive_layout (MetaMonitor *monitor,
|
|
MetaRectangle *layout)
|
|
{
|
|
MetaOutput *output;
|
|
MetaCrtc *crtc;
|
|
const MetaCrtcConfig *crtc_config;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
crtc_config = meta_crtc_get_config (crtc);
|
|
|
|
g_return_if_fail (crtc_config);
|
|
|
|
meta_rectangle_from_graphene_rect (&crtc_config->layout,
|
|
META_ROUNDING_STRATEGY_ROUND,
|
|
layout);
|
|
}
|
|
|
|
static gboolean
|
|
meta_monitor_normal_get_suggested_position (MetaMonitor *monitor,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
const MetaOutputInfo *output_info =
|
|
meta_monitor_get_main_output_info (monitor);
|
|
|
|
if (!output_info->hotplug_mode_update)
|
|
return FALSE;
|
|
|
|
if (output_info->suggested_x < 0 && output_info->suggested_y < 0)
|
|
return FALSE;
|
|
|
|
if (x)
|
|
*x = output_info->suggested_x;
|
|
|
|
if (y)
|
|
*y = output_info->suggested_y;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_normal_calculate_crtc_pos (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaOutput *output,
|
|
MetaMonitorTransform crtc_transform,
|
|
int *out_x,
|
|
int *out_y)
|
|
{
|
|
*out_x = 0;
|
|
*out_y = 0;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_normal_init (MetaMonitorNormal *monitor)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_monitor_normal_class_init (MetaMonitorNormalClass *klass)
|
|
{
|
|
MetaMonitorClass *monitor_class = META_MONITOR_CLASS (klass);
|
|
|
|
monitor_class->get_main_output = meta_monitor_normal_get_main_output;
|
|
monitor_class->derive_layout = meta_monitor_normal_derive_layout;
|
|
monitor_class->calculate_crtc_pos = meta_monitor_normal_calculate_crtc_pos;
|
|
monitor_class->get_suggested_position = meta_monitor_normal_get_suggested_position;
|
|
}
|
|
|
|
uint32_t
|
|
meta_monitor_tiled_get_tile_group_id (MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
return monitor_tiled->tile_group_id;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_get_suggested_position (MetaMonitor *monitor,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
return META_MONITOR_GET_CLASS (monitor)->get_suggested_position (monitor,
|
|
x, y);
|
|
}
|
|
|
|
static void
|
|
add_tiled_monitor_outputs (MetaGpu *gpu,
|
|
MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (META_MONITOR (monitor_tiled));
|
|
GList *outputs;
|
|
GList *l;
|
|
|
|
outputs = meta_gpu_get_outputs (gpu);
|
|
for (l = outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
const MetaOutputInfo *origin_output_info;
|
|
|
|
if (output_info->tile_info.group_id != monitor_tiled->tile_group_id)
|
|
continue;
|
|
|
|
origin_output_info = meta_output_get_info (monitor_tiled->origin_output);
|
|
g_warn_if_fail (output_info->subpixel_order ==
|
|
origin_output_info->subpixel_order);
|
|
|
|
monitor_priv->outputs = g_list_append (monitor_priv->outputs,
|
|
g_object_ref (output));
|
|
|
|
meta_output_set_monitor (output, META_MONITOR (monitor_tiled));
|
|
}
|
|
}
|
|
|
|
static void
|
|
calculate_tile_coordinate (MetaMonitor *monitor,
|
|
MetaOutput *output,
|
|
MetaMonitorTransform crtc_transform,
|
|
int *out_x,
|
|
int *out_y)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
GList *l;
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
const MetaOutputInfo *other_output_info = meta_output_get_info (l->data);
|
|
|
|
switch (crtc_transform)
|
|
{
|
|
case META_MONITOR_TRANSFORM_NORMAL:
|
|
case META_MONITOR_TRANSFORM_FLIPPED:
|
|
if ((other_output_info->tile_info.loc_v_tile ==
|
|
output_info->tile_info.loc_v_tile) &&
|
|
(other_output_info->tile_info.loc_h_tile <
|
|
output_info->tile_info.loc_h_tile))
|
|
x += other_output_info->tile_info.tile_w;
|
|
if ((other_output_info->tile_info.loc_h_tile ==
|
|
output_info->tile_info.loc_h_tile) &&
|
|
(other_output_info->tile_info.loc_v_tile <
|
|
output_info->tile_info.loc_v_tile))
|
|
y += other_output_info->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_180:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_180:
|
|
if ((other_output_info->tile_info.loc_v_tile ==
|
|
output_info->tile_info.loc_v_tile) &&
|
|
(other_output_info->tile_info.loc_h_tile >
|
|
output_info->tile_info.loc_h_tile))
|
|
x += other_output_info->tile_info.tile_w;
|
|
if ((other_output_info->tile_info.loc_h_tile ==
|
|
output_info->tile_info.loc_h_tile) &&
|
|
(other_output_info->tile_info.loc_v_tile >
|
|
output_info->tile_info.loc_v_tile))
|
|
y += other_output_info->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_270:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_270:
|
|
if ((other_output_info->tile_info.loc_v_tile ==
|
|
output_info->tile_info.loc_v_tile) &&
|
|
(other_output_info->tile_info.loc_h_tile >
|
|
output_info->tile_info.loc_h_tile))
|
|
y += other_output_info->tile_info.tile_w;
|
|
if ((other_output_info->tile_info.loc_h_tile ==
|
|
output_info->tile_info.loc_h_tile) &&
|
|
(other_output_info->tile_info.loc_v_tile >
|
|
output_info->tile_info.loc_v_tile))
|
|
x += other_output_info->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_90:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_90:
|
|
if ((other_output_info->tile_info.loc_v_tile ==
|
|
output_info->tile_info.loc_v_tile) &&
|
|
(other_output_info->tile_info.loc_h_tile <
|
|
output_info->tile_info.loc_h_tile))
|
|
y += other_output_info->tile_info.tile_w;
|
|
if ((other_output_info->tile_info.loc_h_tile ==
|
|
output_info->tile_info.loc_h_tile) &&
|
|
(other_output_info->tile_info.loc_v_tile <
|
|
output_info->tile_info.loc_v_tile))
|
|
x += other_output_info->tile_info.tile_h;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*out_x = x;
|
|
*out_y = y;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_calculate_tiled_size (MetaMonitor *monitor,
|
|
int *out_width,
|
|
int *out_height)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
int width;
|
|
int height;
|
|
|
|
width = 0;
|
|
height = 0;
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
const MetaOutputInfo *output_info = meta_output_get_info (l->data);
|
|
|
|
if (output_info->tile_info.loc_v_tile == 0)
|
|
width += output_info->tile_info.tile_w;
|
|
|
|
if (output_info->tile_info.loc_h_tile == 0)
|
|
height += output_info->tile_info.tile_h;
|
|
}
|
|
|
|
*out_width = width;
|
|
*out_height = height;
|
|
}
|
|
|
|
static gboolean
|
|
is_monitor_mode_assigned (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
int i;
|
|
|
|
for (l = priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i];
|
|
MetaCrtc *crtc;
|
|
const MetaCrtcConfig *crtc_config;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
crtc_config = crtc ? meta_crtc_get_config (crtc) : NULL;
|
|
|
|
if (monitor_crtc_mode->crtc_mode &&
|
|
(!crtc || !crtc_config ||
|
|
crtc_config->mode != monitor_crtc_mode->crtc_mode))
|
|
return FALSE;
|
|
else if (!monitor_crtc_mode->crtc_mode && crtc)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
is_crtc_mode_tiled (MetaOutput *output,
|
|
MetaCrtcMode *crtc_mode)
|
|
{
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
const MetaCrtcModeInfo *crtc_mode_info = meta_crtc_mode_get_info (crtc_mode);
|
|
|
|
return (crtc_mode_info->width == (int) output_info->tile_info.tile_w &&
|
|
crtc_mode_info->height == (int) output_info->tile_info.tile_h);
|
|
}
|
|
|
|
static MetaCrtcMode *
|
|
find_tiled_crtc_mode (MetaOutput *output,
|
|
MetaCrtcMode *reference_crtc_mode)
|
|
{
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
const MetaCrtcModeInfo *reference_crtc_mode_info =
|
|
meta_crtc_mode_get_info (reference_crtc_mode);
|
|
MetaCrtcMode *crtc_mode;
|
|
unsigned int i;
|
|
|
|
crtc_mode = output_info->preferred_mode;
|
|
if (is_crtc_mode_tiled (output, crtc_mode))
|
|
return crtc_mode;
|
|
|
|
for (i = 0; i < output_info->n_modes; i++)
|
|
{
|
|
const MetaCrtcModeInfo *crtc_mode_info;
|
|
|
|
crtc_mode = output_info->modes[i];
|
|
crtc_mode_info = meta_crtc_mode_get_info (crtc_mode);
|
|
|
|
if (!is_crtc_mode_tiled (output, crtc_mode))
|
|
continue;
|
|
|
|
if (crtc_mode_info->refresh_rate != reference_crtc_mode_info->refresh_rate)
|
|
continue;
|
|
|
|
if (crtc_mode_info->flags != reference_crtc_mode_info->flags)
|
|
continue;
|
|
|
|
return crtc_mode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static MetaMonitorMode *
|
|
create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
|
|
MetaCrtcMode *reference_crtc_mode,
|
|
gboolean *out_is_preferred)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorModeTiled *mode;
|
|
int width, height;
|
|
GList *l;
|
|
unsigned int i;
|
|
gboolean is_preferred = TRUE;
|
|
|
|
mode = g_new0 (MetaMonitorModeTiled, 1);
|
|
mode->is_tiled = TRUE;
|
|
meta_monitor_tiled_calculate_tiled_size (monitor, &width, &height);
|
|
mode->parent.monitor = monitor;
|
|
mode->parent.spec =
|
|
meta_monitor_create_spec (monitor, width, height, reference_crtc_mode);
|
|
mode->parent.id = generate_mode_id (&mode->parent.spec);
|
|
|
|
mode->parent.crtc_modes = g_new0 (MetaMonitorCrtcMode,
|
|
g_list_length (monitor_priv->outputs));
|
|
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
MetaCrtcMode *tiled_crtc_mode;
|
|
|
|
tiled_crtc_mode = find_tiled_crtc_mode (output, reference_crtc_mode);
|
|
if (!tiled_crtc_mode)
|
|
{
|
|
g_warning ("No tiled mode found on %s", meta_output_get_name (output));
|
|
meta_monitor_mode_free ((MetaMonitorMode *) mode);
|
|
return NULL;
|
|
}
|
|
|
|
mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) {
|
|
.output = output,
|
|
.crtc_mode = tiled_crtc_mode
|
|
};
|
|
|
|
is_preferred = (is_preferred &&
|
|
tiled_crtc_mode == output_info->preferred_mode);
|
|
}
|
|
|
|
*out_is_preferred = is_preferred;
|
|
|
|
return (MetaMonitorMode *) mode;
|
|
}
|
|
|
|
static void
|
|
generate_tiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaOutput *main_output;
|
|
const MetaOutputInfo *main_output_info;
|
|
GList *tiled_modes = NULL;
|
|
unsigned int i;
|
|
MetaMonitorMode *best_mode = NULL;
|
|
GList *l;
|
|
|
|
main_output = meta_monitor_get_main_output (META_MONITOR (monitor_tiled));
|
|
main_output_info = meta_output_get_info (main_output);
|
|
|
|
for (i = 0; i < main_output_info->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *reference_crtc_mode = main_output_info->modes[i];
|
|
MetaMonitorMode *mode;
|
|
gboolean is_preferred;
|
|
|
|
if (!is_crtc_mode_tiled (main_output, reference_crtc_mode))
|
|
continue;
|
|
|
|
mode = create_tiled_monitor_mode (monitor_tiled, reference_crtc_mode,
|
|
&is_preferred);
|
|
if (!mode)
|
|
continue;
|
|
|
|
tiled_modes = g_list_append (tiled_modes, mode);
|
|
|
|
if (is_monitor_mode_assigned (monitor, mode))
|
|
monitor_priv->current_mode = mode;
|
|
|
|
if (is_preferred)
|
|
monitor_priv->preferred_mode = mode;
|
|
}
|
|
|
|
while ((l = tiled_modes))
|
|
{
|
|
MetaMonitorMode *mode = l->data;
|
|
|
|
tiled_modes = g_list_remove_link (tiled_modes, l);
|
|
|
|
if (!meta_monitor_add_mode (monitor, mode, FALSE))
|
|
{
|
|
meta_monitor_mode_free (mode);
|
|
continue;
|
|
}
|
|
|
|
if (!monitor_priv->preferred_mode)
|
|
{
|
|
if (!best_mode ||
|
|
mode->spec.refresh_rate > best_mode->spec.refresh_rate)
|
|
best_mode = mode;
|
|
}
|
|
}
|
|
|
|
if (best_mode)
|
|
monitor_priv->preferred_mode = best_mode;
|
|
}
|
|
|
|
static MetaMonitorMode *
|
|
create_untiled_monitor_mode (MetaMonitorTiled *monitor_tiled,
|
|
MetaOutput *main_output,
|
|
MetaCrtcMode *crtc_mode)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorModeTiled *mode;
|
|
const MetaCrtcModeInfo *crtc_mode_info;
|
|
GList *l;
|
|
int i;
|
|
|
|
if (is_crtc_mode_tiled (main_output, crtc_mode))
|
|
return NULL;
|
|
|
|
mode = g_new0 (MetaMonitorModeTiled, 1);
|
|
mode->is_tiled = FALSE;
|
|
mode->parent.monitor = monitor;
|
|
|
|
crtc_mode_info = meta_crtc_mode_get_info (crtc_mode);
|
|
mode->parent.spec = meta_monitor_create_spec (monitor,
|
|
crtc_mode_info->width,
|
|
crtc_mode_info->height,
|
|
crtc_mode);
|
|
mode->parent.id = generate_mode_id (&mode->parent.spec);
|
|
mode->parent.crtc_modes = g_new0 (MetaMonitorCrtcMode,
|
|
g_list_length (monitor_priv->outputs));
|
|
|
|
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
if (output == main_output)
|
|
{
|
|
mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) {
|
|
.output = output,
|
|
.crtc_mode = crtc_mode
|
|
};
|
|
}
|
|
else
|
|
{
|
|
mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) {
|
|
.output = output,
|
|
.crtc_mode = NULL
|
|
};
|
|
}
|
|
}
|
|
|
|
return &mode->parent;
|
|
}
|
|
|
|
static int
|
|
count_untiled_crtc_modes (MetaOutput *output)
|
|
{
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
int count;
|
|
unsigned int i;
|
|
|
|
count = 0;
|
|
for (i = 0; i < output_info->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = output_info->modes[i];
|
|
|
|
if (!is_crtc_mode_tiled (output, crtc_mode))
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static MetaOutput *
|
|
find_untiled_output (MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaOutput *best_output;
|
|
int best_untiled_crtc_mode_count;
|
|
GList *l;
|
|
|
|
best_output = monitor_tiled->origin_output;
|
|
best_untiled_crtc_mode_count =
|
|
count_untiled_crtc_modes (monitor_tiled->origin_output);
|
|
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
int untiled_crtc_mode_count;
|
|
|
|
if (output == monitor_tiled->origin_output)
|
|
continue;
|
|
|
|
untiled_crtc_mode_count = count_untiled_crtc_modes (output);
|
|
if (untiled_crtc_mode_count > best_untiled_crtc_mode_count)
|
|
{
|
|
best_untiled_crtc_mode_count = untiled_crtc_mode_count;
|
|
best_output = output;
|
|
}
|
|
}
|
|
|
|
return best_output;
|
|
}
|
|
|
|
static void
|
|
generate_untiled_monitor_modes (MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaOutput *main_output;
|
|
const MetaOutputInfo *main_output_info;
|
|
unsigned int i;
|
|
|
|
main_output = meta_monitor_get_main_output (monitor);
|
|
main_output_info = meta_output_get_info (main_output);
|
|
|
|
for (i = 0; i < main_output_info->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = main_output_info->modes[i];
|
|
MetaMonitorMode *mode;
|
|
|
|
mode = create_untiled_monitor_mode (monitor_tiled,
|
|
main_output,
|
|
crtc_mode);
|
|
if (!mode)
|
|
continue;
|
|
|
|
if (!meta_monitor_add_mode (monitor, mode, FALSE))
|
|
{
|
|
meta_monitor_mode_free (mode);
|
|
continue;
|
|
}
|
|
|
|
if (is_monitor_mode_assigned (monitor, mode))
|
|
{
|
|
g_assert (!monitor_priv->current_mode);
|
|
monitor_priv->current_mode = mode;
|
|
}
|
|
|
|
if (!monitor_priv->preferred_mode &&
|
|
crtc_mode == main_output_info->preferred_mode)
|
|
monitor_priv->preferred_mode = mode;
|
|
}
|
|
}
|
|
|
|
static MetaMonitorMode *
|
|
find_best_mode (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorMode *best_mode = NULL;
|
|
GList *l;
|
|
|
|
for (l = monitor_priv->modes; l; l = l->next)
|
|
{
|
|
MetaMonitorMode *mode = l->data;
|
|
int area, best_area;
|
|
|
|
if (!best_mode)
|
|
{
|
|
best_mode = mode;
|
|
continue;
|
|
}
|
|
|
|
area = mode->spec.width * mode->spec.height;
|
|
best_area = best_mode->spec.width * best_mode->spec.height;
|
|
if (area > best_area)
|
|
{
|
|
best_mode = mode;
|
|
continue;
|
|
}
|
|
|
|
if (mode->spec.refresh_rate > best_mode->spec.refresh_rate)
|
|
{
|
|
best_mode = mode;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return best_mode;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_generate_modes (MetaMonitorTiled *monitor_tiled)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
|
|
/*
|
|
* Tiled monitors may look a bit different from each other, depending on the
|
|
* monitor itself, the driver, etc.
|
|
*
|
|
* On some, the tiled modes will be the preferred CRTC modes, and running
|
|
* untiled is done by only enabling (0, 0) tile. In this case, things are
|
|
* pretty straight forward.
|
|
*
|
|
* Other times a monitor may have some bogus mode preferred on the main tile,
|
|
* and an untiled mode preferred on the non-main tile, and there seems to be
|
|
* no guarantee that the (0, 0) tile is the one that should drive the
|
|
* non-tiled mode.
|
|
*
|
|
* To handle both these cases, the following hueristics are implemented:
|
|
*
|
|
* 1) Find all the tiled CRTC modes of the (0, 0) tile, and create tiled
|
|
* monitor modes for all tiles based on these.
|
|
* 2) If there is any tiled monitor mode combination where all CRTC modes
|
|
* are the preferred ones, that one is marked as preferred.
|
|
* 3) If there is no preferred mode determined so far, assume the tiled
|
|
* monitor mode with the highest refresh rate is preferred.
|
|
* 4) Find the tile with highest number of untiled CRTC modes available,
|
|
* assume this is the one driving the monitor in untiled mode, and
|
|
* create monitor modes for all untiled CRTC modes of that tile. If
|
|
* there is still no preferred mode, set any untiled mode as preferred
|
|
* if the CRTC mode is marked as such.
|
|
* 5) If at this point there is still no preferred mode, just pick the one
|
|
* with the highest number of pixels and highest refresh rate.
|
|
*
|
|
* Note that this ignores the preference if the preference is a non-tiled
|
|
* mode. This seems to be the case on some systems, where the user tends to
|
|
* manually set up the tiled mode anyway.
|
|
*/
|
|
|
|
generate_tiled_monitor_modes (monitor_tiled);
|
|
|
|
if (!monitor_priv->preferred_mode)
|
|
g_warning ("Tiled monitor on %s didn't have any tiled modes",
|
|
monitor_priv->spec->connector);
|
|
|
|
generate_untiled_monitor_modes (monitor_tiled);
|
|
|
|
if (!monitor_priv->preferred_mode)
|
|
{
|
|
g_warning ("Tiled monitor on %s didn't have a valid preferred mode",
|
|
monitor_priv->spec->connector);
|
|
monitor_priv->preferred_mode = find_best_mode (monitor);
|
|
}
|
|
}
|
|
|
|
MetaMonitorTiled *
|
|
meta_monitor_tiled_new (MetaMonitorManager *monitor_manager,
|
|
MetaOutput *output)
|
|
{
|
|
const MetaOutputInfo *output_info = meta_output_get_info (output);
|
|
MetaMonitorTiled *monitor_tiled;
|
|
MetaMonitor *monitor;
|
|
MetaMonitorPrivate *monitor_priv;
|
|
|
|
monitor_tiled = g_object_new (META_TYPE_MONITOR_TILED, NULL);
|
|
monitor = META_MONITOR (monitor_tiled);
|
|
monitor_priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
monitor_priv->backend = meta_monitor_manager_get_backend (monitor_manager);
|
|
|
|
monitor_tiled->tile_group_id = output_info->tile_info.group_id;
|
|
monitor_priv->winsys_id = meta_output_get_id (output);
|
|
|
|
monitor_tiled->origin_output = output;
|
|
add_tiled_monitor_outputs (meta_output_get_gpu (output), monitor_tiled);
|
|
|
|
monitor_tiled->main_output = find_untiled_output (monitor_tiled);
|
|
|
|
meta_monitor_generate_spec (monitor);
|
|
|
|
monitor_tiled->monitor_manager = monitor_manager;
|
|
meta_monitor_manager_tiled_monitor_added (monitor_manager,
|
|
META_MONITOR (monitor_tiled));
|
|
|
|
meta_monitor_tiled_generate_modes (monitor_tiled);
|
|
|
|
monitor_priv->display_name = meta_monitor_make_display_name (monitor);
|
|
|
|
return monitor_tiled;
|
|
}
|
|
|
|
static MetaOutput *
|
|
meta_monitor_tiled_get_main_output (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (monitor);
|
|
|
|
return monitor_tiled->main_output;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_derive_layout (MetaMonitor *monitor,
|
|
MetaRectangle *layout)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
float min_x, min_y, max_x, max_y;
|
|
|
|
min_x = FLT_MAX;
|
|
min_y = FLT_MAX;
|
|
max_x = 0.0;
|
|
max_y = 0.0;
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
MetaCrtc *crtc;
|
|
const MetaCrtcConfig *crtc_config;
|
|
const graphene_rect_t *crtc_layout;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
if (!crtc)
|
|
continue;
|
|
|
|
crtc_config = meta_crtc_get_config (crtc);
|
|
g_return_if_fail (crtc_config);
|
|
|
|
crtc_layout = &crtc_config->layout;
|
|
|
|
min_x = MIN (crtc_layout->origin.x, min_x);
|
|
min_y = MIN (crtc_layout->origin.y, min_y);
|
|
max_x = MAX (crtc_layout->origin.x + crtc_layout->size.width, max_x);
|
|
max_y = MAX (crtc_layout->origin.y + crtc_layout->size.height, max_y);
|
|
}
|
|
|
|
*layout = (MetaRectangle) {
|
|
.x = roundf (min_x),
|
|
.y = roundf (min_y),
|
|
.width = roundf (max_x - min_x),
|
|
.height = roundf (max_y - min_y)
|
|
};
|
|
}
|
|
|
|
static gboolean
|
|
meta_monitor_tiled_get_suggested_position (MetaMonitor *monitor,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_calculate_crtc_pos (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaOutput *output,
|
|
MetaMonitorTransform crtc_transform,
|
|
int *out_x,
|
|
int *out_y)
|
|
{
|
|
MetaMonitorModeTiled *mode_tiled = (MetaMonitorModeTiled *) monitor_mode;
|
|
|
|
if (mode_tiled->is_tiled)
|
|
{
|
|
calculate_tile_coordinate (monitor, output, crtc_transform,
|
|
out_x, out_y);
|
|
}
|
|
else
|
|
{
|
|
*out_x = 0;
|
|
*out_y = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_finalize (GObject *object)
|
|
{
|
|
MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (object);
|
|
|
|
meta_monitor_manager_tiled_monitor_removed (monitor_tiled->monitor_manager,
|
|
META_MONITOR (monitor_tiled));
|
|
|
|
G_OBJECT_CLASS (meta_monitor_tiled_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_init (MetaMonitorTiled *monitor)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_monitor_tiled_class_init (MetaMonitorTiledClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MetaMonitorClass *monitor_class = META_MONITOR_CLASS (klass);
|
|
|
|
object_class->finalize = meta_monitor_tiled_finalize;
|
|
|
|
monitor_class->get_main_output = meta_monitor_tiled_get_main_output;
|
|
monitor_class->derive_layout = meta_monitor_tiled_derive_layout;
|
|
monitor_class->calculate_crtc_pos = meta_monitor_tiled_calculate_crtc_pos;
|
|
monitor_class->get_suggested_position = meta_monitor_tiled_get_suggested_position;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_mode_free (MetaMonitorMode *monitor_mode)
|
|
{
|
|
g_free (monitor_mode->id);
|
|
g_free (monitor_mode->crtc_modes);
|
|
g_free (monitor_mode);
|
|
}
|
|
|
|
MetaMonitorSpec *
|
|
meta_monitor_get_spec (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->spec;
|
|
}
|
|
|
|
MetaLogicalMonitor *
|
|
meta_monitor_get_logical_monitor (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->logical_monitor;
|
|
}
|
|
|
|
MetaMonitorMode *
|
|
meta_monitor_get_mode_from_id (MetaMonitor *monitor,
|
|
const char *monitor_mode_id)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return g_hash_table_lookup (priv->mode_ids, monitor_mode_id);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_mode_spec_has_similar_size (MetaMonitorModeSpec *monitor_mode_spec,
|
|
MetaMonitorModeSpec *other_monitor_mode_spec)
|
|
{
|
|
const float target_ratio = 1.0;
|
|
/* The a size difference of 15% means e.g. 4K modes matches other 4K modes,
|
|
* FHD (2K) modes other FHD modes, and HD modes other HD modes, but not each
|
|
* other.
|
|
*/
|
|
const float epsilon = 0.15;
|
|
|
|
return G_APPROX_VALUE (((float) monitor_mode_spec->width /
|
|
other_monitor_mode_spec->width) *
|
|
((float) monitor_mode_spec->height /
|
|
other_monitor_mode_spec->height),
|
|
target_ratio, epsilon);
|
|
}
|
|
|
|
static gboolean
|
|
meta_monitor_mode_spec_equals (MetaMonitorModeSpec *monitor_mode_spec,
|
|
MetaMonitorModeSpec *other_monitor_mode_spec)
|
|
{
|
|
return (monitor_mode_spec->width == other_monitor_mode_spec->width &&
|
|
monitor_mode_spec->height == other_monitor_mode_spec->height &&
|
|
ABS (monitor_mode_spec->refresh_rate -
|
|
other_monitor_mode_spec->refresh_rate) < MAXIMUM_REFRESH_RATE_DIFF &&
|
|
monitor_mode_spec->flags == other_monitor_mode_spec->flags);
|
|
}
|
|
|
|
MetaMonitorMode *
|
|
meta_monitor_get_mode_from_spec (MetaMonitor *monitor,
|
|
MetaMonitorModeSpec *monitor_mode_spec)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
|
|
for (l = priv->modes; l; l = l->next)
|
|
{
|
|
MetaMonitorMode *monitor_mode = l->data;
|
|
|
|
if (meta_monitor_mode_spec_equals (monitor_mode_spec,
|
|
&monitor_mode->spec))
|
|
return monitor_mode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MetaMonitorMode *
|
|
meta_monitor_get_preferred_mode (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->preferred_mode;
|
|
}
|
|
|
|
MetaMonitorMode *
|
|
meta_monitor_get_current_mode (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->current_mode;
|
|
}
|
|
|
|
static gboolean
|
|
is_current_mode_known (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
MetaCrtc *crtc;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
|
|
return (meta_monitor_is_active (monitor) ==
|
|
(crtc && meta_crtc_get_config (crtc)));
|
|
}
|
|
|
|
void
|
|
meta_monitor_derive_current_mode (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorMode *current_mode = NULL;
|
|
GList *l;
|
|
|
|
for (l = priv->modes; l; l = l->next)
|
|
{
|
|
MetaMonitorMode *mode = l->data;
|
|
|
|
if (is_monitor_mode_assigned (monitor, mode))
|
|
{
|
|
current_mode = mode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
priv->current_mode = current_mode;
|
|
|
|
g_warn_if_fail (is_current_mode_known (monitor));
|
|
}
|
|
|
|
void
|
|
meta_monitor_set_current_mode (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
priv->current_mode = mode;
|
|
}
|
|
|
|
GList *
|
|
meta_monitor_get_modes (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->modes;
|
|
}
|
|
|
|
void
|
|
meta_monitor_calculate_crtc_pos (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaOutput *output,
|
|
MetaMonitorTransform crtc_transform,
|
|
int *out_x,
|
|
int *out_y)
|
|
{
|
|
META_MONITOR_GET_CLASS (monitor)->calculate_crtc_pos (monitor,
|
|
monitor_mode,
|
|
output,
|
|
crtc_transform,
|
|
out_x,
|
|
out_y);
|
|
}
|
|
|
|
/*
|
|
* We choose a default scale factor such that the UI is as big
|
|
* as it would be on a display with this DPI without scaling.
|
|
*
|
|
* Through experiementing, a value of 135 has been found to best
|
|
* line up with the UI size chosen as default by other operating
|
|
* systems (macOS, Android, iOS, Windows) and the community-decided
|
|
* "known-good" scale factors for GNOME for various mobile devices
|
|
* such as phones, tablets, and laptops
|
|
*/
|
|
#define UI_SCALE_MOBILE_TARGET_DPI 135
|
|
|
|
/*
|
|
* People tend to sit further away from larger stationary displays
|
|
* than they do from mobile displays, so a UI of an identical size to
|
|
* a mobile device has a smaller angular size and thus seems to be too
|
|
* small.
|
|
*
|
|
* The largest mainstream laptops have screens ~17in, and HiDPI external
|
|
* monitors start at ~23in, so 20in is a good boundary point
|
|
*/
|
|
#define UI_SCALE_LARGE_TARGET_DPI 110
|
|
#define UI_SCALE_LARGE_MIN_SIZE_INCHES 20
|
|
|
|
static float
|
|
calculate_scale (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaMonitorScalesConstraint constraints)
|
|
{
|
|
int width_px, height_px, width_mm, height_mm;
|
|
float diag_inches;
|
|
g_autofree float *scales = NULL;
|
|
int n_scales;
|
|
float best_scale, best_dpi;
|
|
int target_dpi;
|
|
const float scale_epsilon = 0.2;
|
|
|
|
/*
|
|
* Somebody encoded the aspect ratio (16/9 or 16/10) instead of the physical
|
|
* size. We'll be unable to select an appropriate scale factor.
|
|
*/
|
|
if (meta_monitor_has_aspect_as_size (monitor))
|
|
return 1.0;
|
|
|
|
/* Compute display's diagonal size in inches */
|
|
meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm);
|
|
if (width_mm == 0 || height_mm == 0)
|
|
return 1.0;
|
|
diag_inches = sqrtf (width_mm * width_mm + height_mm * height_mm) / 25.4;
|
|
|
|
/* Pick the appropriate target DPI based on screen size */
|
|
if (diag_inches < UI_SCALE_LARGE_MIN_SIZE_INCHES)
|
|
target_dpi = UI_SCALE_MOBILE_TARGET_DPI;
|
|
else
|
|
target_dpi = UI_SCALE_LARGE_TARGET_DPI;
|
|
|
|
meta_monitor_mode_get_resolution (monitor_mode, &width_px, &height_px);
|
|
|
|
/* We'll only be considering the supported scale factors */
|
|
scales = meta_monitor_calculate_supported_scales (monitor, monitor_mode,
|
|
META_MONITOR_SCALES_CONSTRAINT_NONE,
|
|
&n_scales);
|
|
best_scale = scales[0];
|
|
for (int i = 0; i < n_scales; i++)
|
|
{
|
|
float width_scaled, height_scaled, diag_scaled, dpi;
|
|
|
|
/*
|
|
* Compute the logical resolution of the display for this
|
|
* scale factor
|
|
*/
|
|
width_scaled = (float) width_px / scales[i];
|
|
height_scaled = (float) height_px / scales[i];
|
|
|
|
/* Compute the number of logical pixels across the display's diagonal */
|
|
diag_scaled = sqrtf (width_scaled * width_scaled +
|
|
height_scaled * height_scaled);
|
|
|
|
/*
|
|
* Computes the display's logical DPI - the number of logical pixels
|
|
* per inch on the display's diagonal
|
|
*/
|
|
dpi = diag_scaled / diag_inches;
|
|
|
|
/* Pick the scale factor whose logical DPI is closest to the optimal value */
|
|
if (i == 0 || fabsf (dpi - target_dpi) < fabsf (best_dpi - target_dpi))
|
|
{
|
|
best_scale = scales[i];
|
|
best_dpi = dpi;
|
|
}
|
|
}
|
|
|
|
if (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC)
|
|
{
|
|
best_scale = floorf (MIN (scales[n_scales - 1],
|
|
best_scale + 0.25 + scale_epsilon));
|
|
}
|
|
|
|
return best_scale;
|
|
}
|
|
|
|
float
|
|
meta_monitor_calculate_mode_scale (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaMonitorScalesConstraint constraints)
|
|
{
|
|
MetaBackend *backend = meta_monitor_get_backend (monitor);
|
|
MetaSettings *settings = meta_backend_get_settings (backend);
|
|
int global_scaling_factor;
|
|
|
|
if (meta_settings_get_global_scaling_factor (settings,
|
|
&global_scaling_factor))
|
|
return global_scaling_factor;
|
|
|
|
return calculate_scale (monitor, monitor_mode, constraints);
|
|
}
|
|
|
|
static gboolean
|
|
is_logical_size_large_enough (int width,
|
|
int height)
|
|
{
|
|
return width * height >= MINIMUM_LOGICAL_AREA;
|
|
}
|
|
|
|
static gboolean
|
|
is_scale_valid_for_size (float width,
|
|
float height,
|
|
float scale)
|
|
{
|
|
if (scale < MINIMUM_SCALE_FACTOR || scale > MAXIMUM_SCALE_FACTOR)
|
|
return FALSE;
|
|
|
|
return is_logical_size_large_enough (floorf (width / scale),
|
|
floorf (height / scale));
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_mode_should_be_advertised (MetaMonitorMode *monitor_mode)
|
|
{
|
|
MetaMonitorMode *preferred_mode;
|
|
|
|
g_return_val_if_fail (monitor_mode != NULL, FALSE);
|
|
|
|
preferred_mode = meta_monitor_get_preferred_mode (monitor_mode->monitor);
|
|
if (monitor_mode->spec.width == preferred_mode->spec.width &&
|
|
monitor_mode->spec.height == preferred_mode->spec.height)
|
|
return TRUE;
|
|
|
|
return is_logical_size_large_enough (monitor_mode->spec.width,
|
|
monitor_mode->spec.height);
|
|
}
|
|
|
|
static float
|
|
get_closest_scale_factor_for_resolution (float width,
|
|
float height,
|
|
float scale,
|
|
float threshold)
|
|
{
|
|
unsigned int i, j;
|
|
float scaled_h;
|
|
float scaled_w;
|
|
float best_scale;
|
|
int base_scaled_w;
|
|
gboolean found_one;
|
|
|
|
best_scale = 0;
|
|
|
|
if (!is_scale_valid_for_size (width, height, scale))
|
|
return best_scale;
|
|
|
|
if (fmodf (width, scale) == 0.0 && fmodf (height, scale) == 0.0)
|
|
return scale;
|
|
|
|
i = 0;
|
|
found_one = FALSE;
|
|
base_scaled_w = floorf (width / scale);
|
|
|
|
do
|
|
{
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
float current_scale;
|
|
int offset = i * (j ? 1 : -1);
|
|
|
|
scaled_w = base_scaled_w + offset;
|
|
current_scale = width / scaled_w;
|
|
scaled_h = height / current_scale;
|
|
|
|
if (current_scale >= scale + threshold ||
|
|
current_scale <= scale - threshold ||
|
|
current_scale < MINIMUM_SCALE_FACTOR ||
|
|
current_scale > MAXIMUM_SCALE_FACTOR)
|
|
{
|
|
return best_scale;
|
|
}
|
|
|
|
if (floorf (scaled_h) == scaled_h)
|
|
{
|
|
found_one = TRUE;
|
|
|
|
if (fabsf (current_scale - scale) < fabsf (best_scale - scale))
|
|
best_scale = current_scale;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
while (!found_one);
|
|
|
|
return best_scale;
|
|
}
|
|
|
|
float *
|
|
meta_monitor_calculate_supported_scales (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode,
|
|
MetaMonitorScalesConstraint constraints,
|
|
int *n_supported_scales)
|
|
{
|
|
unsigned int i, j;
|
|
int width, height;
|
|
GArray *supported_scales;
|
|
|
|
supported_scales = g_array_new (FALSE, FALSE, sizeof (float));
|
|
|
|
meta_monitor_mode_get_resolution (monitor_mode, &width, &height);
|
|
|
|
for (i = floorf (MINIMUM_SCALE_FACTOR);
|
|
i <= ceilf (MAXIMUM_SCALE_FACTOR);
|
|
i++)
|
|
{
|
|
if (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC)
|
|
{
|
|
if (is_scale_valid_for_size (width, height, i))
|
|
{
|
|
float scale = i;
|
|
g_array_append_val (supported_scales, scale);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float max_bound;
|
|
|
|
if (i == floorf (MINIMUM_SCALE_FACTOR) ||
|
|
i == ceilf (MAXIMUM_SCALE_FACTOR))
|
|
max_bound = SCALE_FACTORS_STEPS;
|
|
else
|
|
max_bound = SCALE_FACTORS_STEPS / 2.0;
|
|
|
|
for (j = 0; j < SCALE_FACTORS_PER_INTEGER; j++)
|
|
{
|
|
float scale;
|
|
float scale_value = i + j * SCALE_FACTORS_STEPS;
|
|
|
|
scale = get_closest_scale_factor_for_resolution (width, height,
|
|
scale_value,
|
|
max_bound);
|
|
if (scale > 0.0)
|
|
g_array_append_val (supported_scales, scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (supported_scales->len == 0)
|
|
{
|
|
float fallback_scale;
|
|
|
|
fallback_scale = 1.0;
|
|
g_array_append_val (supported_scales, fallback_scale);
|
|
}
|
|
|
|
*n_supported_scales = supported_scales->len;
|
|
return (float *) g_array_free (supported_scales, FALSE);
|
|
}
|
|
|
|
MetaMonitorModeSpec *
|
|
meta_monitor_mode_get_spec (MetaMonitorMode *monitor_mode)
|
|
{
|
|
return &monitor_mode->spec;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_mode_get_id (MetaMonitorMode *monitor_mode)
|
|
{
|
|
return monitor_mode->id;
|
|
}
|
|
|
|
void
|
|
meta_monitor_mode_get_resolution (MetaMonitorMode *monitor_mode,
|
|
int *width,
|
|
int *height)
|
|
{
|
|
*width = monitor_mode->spec.width;
|
|
*height = monitor_mode->spec.height;
|
|
}
|
|
|
|
float
|
|
meta_monitor_mode_get_refresh_rate (MetaMonitorMode *monitor_mode)
|
|
{
|
|
return monitor_mode->spec.refresh_rate;
|
|
}
|
|
|
|
MetaCrtcModeFlag
|
|
meta_monitor_mode_get_flags (MetaMonitorMode *monitor_mode)
|
|
{
|
|
return monitor_mode->spec.flags;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_mode_foreach_crtc (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode,
|
|
MetaMonitorModeFunc func,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
int i;
|
|
|
|
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i];
|
|
|
|
if (!monitor_crtc_mode->crtc_mode)
|
|
continue;
|
|
|
|
if (!func (monitor, mode, monitor_crtc_mode, user_data, error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_mode_foreach_output (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode,
|
|
MetaMonitorModeFunc func,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
int i;
|
|
|
|
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i];
|
|
|
|
if (!func (monitor, mode, monitor_crtc_mode, user_data, error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
MetaMonitorCrtcMode *
|
|
meta_monitor_get_crtc_mode_for_output (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode,
|
|
MetaOutput *output)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
int i;
|
|
|
|
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
|
|
{
|
|
MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i];
|
|
|
|
if (monitor_crtc_mode->output == output)
|
|
return monitor_crtc_mode;
|
|
}
|
|
|
|
g_warn_if_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_display_name (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
|
|
return monitor_priv->display_name;
|
|
}
|
|
|
|
void
|
|
meta_monitor_set_logical_monitor (MetaMonitor *monitor,
|
|
MetaLogicalMonitor *logical_monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
priv->logical_monitor = logical_monitor;
|
|
}
|
|
|
|
static MetaOutput *
|
|
maybe_get_privacy_screen_output (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
if (priv->outputs && priv->outputs->next)
|
|
return NULL;
|
|
|
|
return meta_monitor_get_main_output (monitor);
|
|
}
|
|
|
|
MetaPrivacyScreenState
|
|
meta_monitor_get_privacy_screen_state (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = maybe_get_privacy_screen_output (monitor);
|
|
|
|
if (!output)
|
|
return META_PRIVACY_SCREEN_UNAVAILABLE;
|
|
|
|
return meta_output_get_privacy_screen_state (output);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_set_privacy_screen_enabled (MetaMonitor *monitor,
|
|
gboolean enabled,
|
|
GError **error)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = maybe_get_privacy_screen_output (monitor);
|
|
|
|
if (!output)
|
|
{
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"The privacy screen is not supported by this output");
|
|
return FALSE;
|
|
}
|
|
|
|
return meta_output_set_privacy_screen_enabled (output, enabled, error);
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_set_color_space (MetaMonitor *monitor,
|
|
MetaOutputColorspace color_space,
|
|
GError **error)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
|
|
for (l = priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
if (!meta_output_is_color_space_supported (output, color_space))
|
|
{
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"The color space is not supported by this monitor");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for (l = priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
meta_output_set_color_space (output, color_space);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_set_hdr_metadata (MetaMonitor *monitor,
|
|
MetaOutputHdrMetadata *metadata,
|
|
GError **error)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
GList *l;
|
|
|
|
for (l = priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
if (!meta_output_is_hdr_metadata_supported (output, metadata->eotf))
|
|
{
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"HDR metadata is not supported by this monitor");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for (l = priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
meta_output_set_hdr_metadata (output, metadata);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_parse_monitor_mode (const char *string,
|
|
int *out_width,
|
|
int *out_height,
|
|
float *out_refresh_rate,
|
|
float fallback_refresh_rate)
|
|
{
|
|
char *ptr = (char *) string;
|
|
int width, height;
|
|
float refresh_rate;
|
|
|
|
width = g_ascii_strtoull (ptr, &ptr, 10);
|
|
if (width == 0)
|
|
return FALSE;
|
|
|
|
if (ptr[0] != 'x')
|
|
return FALSE;
|
|
ptr++;
|
|
|
|
height = g_ascii_strtoull (ptr, &ptr, 10);
|
|
if (height == 0)
|
|
return FALSE;
|
|
|
|
if (ptr[0] == '\0')
|
|
{
|
|
refresh_rate = fallback_refresh_rate;
|
|
goto out;
|
|
}
|
|
|
|
if (ptr[0] != '@')
|
|
return FALSE;
|
|
ptr++;
|
|
|
|
refresh_rate = g_ascii_strtod (ptr, &ptr);
|
|
if (refresh_rate == 0.0)
|
|
return FALSE;
|
|
|
|
if (ptr[0] != '\0')
|
|
return FALSE;
|
|
|
|
out:
|
|
*out_width = width;
|
|
*out_height = height;
|
|
*out_refresh_rate = refresh_rate;
|
|
return TRUE;
|
|
}
|