5e005316ef
We need a 64 bit field to combine the device id and connector id into the output id (winsys_id).
1784 lines
50 KiB
C
1784 lines
50 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"
|
|
|
|
#define SCALE_FACTORS_PER_INTEGER 4
|
|
#define MINIMUM_SCALE_FACTOR 1.0f
|
|
#define MAXIMUM_SCALE_FACTOR 4.0f
|
|
#define MINIMUM_LOGICAL_WIDTH 800
|
|
#define MINIMUM_LOGICAL_HEIGHT 480
|
|
#define MAXIMUM_REFRESH_RATE_DIFF 0.001
|
|
|
|
typedef struct _MetaMonitorMode
|
|
{
|
|
char *id;
|
|
MetaMonitorModeSpec spec;
|
|
MetaMonitorCrtcMode *crtc_modes;
|
|
} MetaMonitorMode;
|
|
|
|
typedef struct _MetaMonitorModeTiled
|
|
{
|
|
MetaMonitorMode parent;
|
|
|
|
gboolean is_tiled;
|
|
} MetaMonitorModeTiled;
|
|
|
|
typedef struct _MetaMonitorPrivate
|
|
{
|
|
MetaGpu *gpu;
|
|
|
|
GList *outputs;
|
|
GList *modes;
|
|
GHashTable *mode_ids;
|
|
|
|
MetaMonitorMode *preferred_mode;
|
|
MetaMonitorMode *current_mode;
|
|
|
|
MetaMonitorSpec *spec;
|
|
|
|
/*
|
|
* 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 extened, 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;
|
|
} 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;
|
|
|
|
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;
|
|
}
|
|
|
|
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 void
|
|
meta_monitor_generate_spec (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
MetaMonitorSpec *monitor_spec;
|
|
|
|
monitor_spec = g_new0 (MetaMonitorSpec, 1);
|
|
*monitor_spec = (MetaMonitorSpec) {
|
|
.connector = g_strdup (output->name),
|
|
.vendor = g_strdup (output->vendor),
|
|
.product = g_strdup (output->product),
|
|
.serial = g_strdup (output->serial),
|
|
};
|
|
|
|
priv->spec = monitor_spec;
|
|
}
|
|
|
|
MetaGpu *
|
|
meta_monitor_get_gpu (MetaMonitor *monitor)
|
|
{
|
|
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
|
|
|
|
return priv->gpu;
|
|
}
|
|
|
|
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 output->is_primary;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_supports_underscanning (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
return output->supports_underscanning;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_underscanning (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
return output->is_underscanning;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_is_laptop_panel (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
|
|
switch (output->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_same_as (MetaMonitor *monitor,
|
|
MetaMonitor *other_monitor)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
*width_mm = output->width_mm;
|
|
*height_mm = output->height_mm;
|
|
}
|
|
|
|
CoglSubpixelOrder
|
|
meta_monitor_get_subpixel_order (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->subpixel_order;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_connector (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->name;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_vendor (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->vendor;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_product (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->product;
|
|
}
|
|
|
|
const char *
|
|
meta_monitor_get_serial (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->serial;
|
|
}
|
|
|
|
MetaConnectorType
|
|
meta_monitor_get_connector_type (MetaMonitor *monitor)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
return output->connector_type;
|
|
}
|
|
|
|
MetaMonitorTransform
|
|
meta_monitor_logical_to_crtc_transform (MetaMonitor *monitor,
|
|
MetaMonitorTransform transform)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
MetaMonitorTransform new_transform;
|
|
|
|
new_transform = (transform + output->panel_orientation_transform) %
|
|
META_MONITOR_TRANSFORM_FLIPPED;
|
|
if (meta_monitor_transform_is_flipped (transform))
|
|
new_transform += META_MONITOR_TRANSFORM_FLIPPED;
|
|
|
|
return new_transform;
|
|
}
|
|
|
|
MetaMonitorTransform
|
|
meta_monitor_crtc_to_logical_transform (MetaMonitor *monitor,
|
|
MetaMonitorTransform transform)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
MetaMonitorTransform new_transform;
|
|
|
|
new_transform = (transform + META_MONITOR_TRANSFORM_FLIPPED -
|
|
output->panel_orientation_transform) %
|
|
META_MONITOR_TRANSFORM_FLIPPED;
|
|
if (meta_monitor_transform_is_flipped (transform))
|
|
new_transform += META_MONITOR_TRANSFORM_FLIPPED;
|
|
|
|
return new_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_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_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 refresh_rate_str[G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
is_interlaced = !!(monitor_mode_spec->flags & META_CRTC_MODE_FLAG_INTERLACE);
|
|
g_ascii_dtostr (refresh_rate_str, G_ASCII_DTOSTR_BUF_SIZE,
|
|
monitor_mode_spec->refresh_rate);
|
|
|
|
return g_strdup_printf ("%dx%d%s@%s",
|
|
monitor_mode_spec->width,
|
|
monitor_mode_spec->height,
|
|
is_interlaced ? "i" : "",
|
|
refresh_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)
|
|
{
|
|
MetaOutput *output = meta_monitor_get_main_output (monitor);
|
|
|
|
if (meta_monitor_transform_is_rotated (output->panel_orientation_transform))
|
|
{
|
|
int temp = width;
|
|
width = height;
|
|
height = temp;
|
|
}
|
|
|
|
return (MetaMonitorModeSpec) {
|
|
.width = width,
|
|
.height = height,
|
|
.refresh_rate = crtc_mode->refresh_rate,
|
|
.flags = crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS
|
|
};
|
|
}
|
|
|
|
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;
|
|
MetaCrtcModeFlag preferred_mode_flags;
|
|
unsigned int i;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
preferred_mode_flags = output->preferred_mode->flags;
|
|
|
|
for (i = 0; i < output->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = output->modes[i];
|
|
MetaCrtc *crtc;
|
|
MetaMonitorMode *mode;
|
|
gboolean replace;
|
|
|
|
mode = g_new0 (MetaMonitorMode, 1);
|
|
mode->spec = meta_monitor_create_spec (monitor,
|
|
crtc_mode->width,
|
|
crtc_mode->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->flags == preferred_mode_flags;
|
|
|
|
if (!meta_monitor_add_mode (monitor, mode, replace))
|
|
{
|
|
g_assert (crtc_mode != output->preferred_mode);
|
|
meta_monitor_mode_free (mode);
|
|
continue;
|
|
}
|
|
|
|
if (crtc_mode == output->preferred_mode)
|
|
monitor_priv->preferred_mode = mode;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
if (crtc && crtc_mode == crtc->current_mode)
|
|
monitor_priv->current_mode = mode;
|
|
}
|
|
}
|
|
|
|
MetaMonitorNormal *
|
|
meta_monitor_normal_new (MetaGpu *gpu,
|
|
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->gpu = gpu;
|
|
|
|
monitor_priv->outputs = g_list_append (NULL, g_object_ref (output));
|
|
monitor_priv->winsys_id = output->winsys_id;
|
|
meta_monitor_generate_spec (monitor);
|
|
|
|
meta_monitor_normal_generate_modes (monitor_normal);
|
|
|
|
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;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
*layout = (MetaRectangle) {
|
|
.x = crtc->rect.x,
|
|
.y = crtc->rect.y,
|
|
.width = crtc->rect.width,
|
|
.height = crtc->rect.height
|
|
};
|
|
}
|
|
|
|
static gboolean
|
|
meta_monitor_normal_get_suggested_position (MetaMonitor *monitor,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
MetaOutput *output;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
if (output->suggested_x < 0 && output->suggested_y < 0)
|
|
return FALSE;
|
|
|
|
*x = output->suggested_x;
|
|
*y = output->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;
|
|
|
|
if (output->tile_info.group_id != monitor_tiled->tile_group_id)
|
|
continue;
|
|
|
|
g_warn_if_fail (output->subpixel_order ==
|
|
monitor_tiled->origin_output->subpixel_order);
|
|
|
|
monitor_priv->outputs = g_list_append (monitor_priv->outputs,
|
|
g_object_ref (output));
|
|
}
|
|
}
|
|
|
|
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);
|
|
GList *l;
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *other_output = l->data;
|
|
|
|
switch (crtc_transform)
|
|
{
|
|
case META_MONITOR_TRANSFORM_NORMAL:
|
|
case META_MONITOR_TRANSFORM_FLIPPED:
|
|
if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
|
|
other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile)
|
|
x += other_output->tile_info.tile_w;
|
|
if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
|
|
other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile)
|
|
y += other_output->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_180:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_180:
|
|
if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
|
|
other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile)
|
|
x += other_output->tile_info.tile_w;
|
|
if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
|
|
other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile)
|
|
y += other_output->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_270:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_270:
|
|
if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
|
|
other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile)
|
|
y += other_output->tile_info.tile_w;
|
|
if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
|
|
other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile)
|
|
x += other_output->tile_info.tile_h;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_90:
|
|
case META_MONITOR_TRANSFORM_FLIPPED_90:
|
|
if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile &&
|
|
other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile)
|
|
y += other_output->tile_info.tile_w;
|
|
if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile &&
|
|
other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile)
|
|
x += other_output->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)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
|
|
if (output->tile_info.loc_v_tile == 0)
|
|
width += output->tile_info.tile_w;
|
|
|
|
if (output->tile_info.loc_h_tile == 0)
|
|
height += output->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;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
if (monitor_crtc_mode->crtc_mode &&
|
|
(!crtc || crtc->current_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)
|
|
{
|
|
return (crtc_mode->width == (int) output->tile_info.tile_w &&
|
|
crtc_mode->height == (int) output->tile_info.tile_h);
|
|
}
|
|
|
|
static MetaCrtcMode *
|
|
find_tiled_crtc_mode (MetaOutput *output,
|
|
MetaCrtcMode *reference_crtc_mode)
|
|
{
|
|
MetaCrtcMode *crtc_mode;
|
|
unsigned int i;
|
|
|
|
crtc_mode = output->preferred_mode;
|
|
if (is_crtc_mode_tiled (output, crtc_mode))
|
|
return crtc_mode;
|
|
|
|
for (i = 0; i < output->n_modes; i++)
|
|
{
|
|
crtc_mode = output->modes[i];
|
|
|
|
if (!is_crtc_mode_tiled (output, crtc_mode))
|
|
continue;
|
|
|
|
if (crtc_mode->refresh_rate != reference_crtc_mode->refresh_rate)
|
|
continue;
|
|
|
|
if (crtc_mode->flags != reference_crtc_mode->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.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;
|
|
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", output->name);
|
|
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->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;
|
|
GList *tiled_modes = NULL;
|
|
unsigned int i;
|
|
MetaMonitorMode *best_mode = NULL;
|
|
GList *l;
|
|
|
|
main_output = meta_monitor_get_main_output (META_MONITOR (monitor_tiled));
|
|
|
|
for (i = 0; i < main_output->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *reference_crtc_mode = main_output->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;
|
|
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.spec = meta_monitor_create_spec (monitor,
|
|
crtc_mode->width,
|
|
crtc_mode->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)
|
|
{
|
|
int count;
|
|
unsigned int i;
|
|
|
|
count = 0;
|
|
for (i = 0; i < output->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = output->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;
|
|
unsigned int i;
|
|
|
|
main_output = meta_monitor_get_main_output (monitor);
|
|
|
|
for (i = 0; i < main_output->n_modes; i++)
|
|
{
|
|
MetaCrtcMode *crtc_mode = main_output->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->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 (MetaGpu *gpu,
|
|
MetaOutput *output)
|
|
{
|
|
MetaMonitorManager *monitor_manager;
|
|
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->gpu = gpu;
|
|
|
|
monitor_tiled->tile_group_id = output->tile_info.group_id;
|
|
monitor_priv->winsys_id = output->winsys_id;
|
|
|
|
monitor_tiled->origin_output = output;
|
|
add_tiled_monitor_outputs (gpu, monitor_tiled);
|
|
|
|
monitor_tiled->main_output = find_untiled_output (monitor_tiled);
|
|
|
|
meta_monitor_generate_spec (monitor);
|
|
|
|
monitor_manager = meta_gpu_get_monitor_manager (gpu);
|
|
meta_monitor_manager_tiled_monitor_added (monitor_manager,
|
|
META_MONITOR (monitor_tiled));
|
|
|
|
meta_monitor_tiled_generate_modes (monitor_tiled);
|
|
|
|
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;
|
|
int min_x, min_y, max_x, max_y;
|
|
|
|
min_x = INT_MAX;
|
|
min_y = INT_MAX;
|
|
max_x = 0;
|
|
max_y = 0;
|
|
for (l = monitor_priv->outputs; l; l = l->next)
|
|
{
|
|
MetaOutput *output = l->data;
|
|
MetaCrtc *crtc;
|
|
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
if (!crtc)
|
|
continue;
|
|
|
|
min_x = MIN (crtc->rect.x, min_x);
|
|
min_y = MIN (crtc->rect.y, min_y);
|
|
max_x = MAX (crtc->rect.x + crtc->rect.width, max_x);
|
|
max_y = MAX (crtc->rect.y + crtc->rect.height, max_y);
|
|
}
|
|
|
|
*layout = (MetaRectangle) {
|
|
.x = min_x,
|
|
.y = min_y,
|
|
.width = max_x - min_x,
|
|
.height = 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)
|
|
{
|
|
MetaMonitor *monitor = META_MONITOR (object);
|
|
MetaMonitorPrivate *monitor_priv =
|
|
meta_monitor_get_instance_private (monitor);
|
|
MetaMonitorManager *monitor_manager;
|
|
|
|
monitor_manager = meta_gpu_get_monitor_manager (monitor_priv->gpu);
|
|
meta_monitor_manager_tiled_monitor_removed (monitor_manager,
|
|
monitor);
|
|
|
|
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)
|
|
{
|
|
MetaOutput *output;
|
|
MetaCrtc *crtc;
|
|
|
|
output = meta_monitor_get_main_output (monitor);
|
|
crtc = meta_output_get_assigned_crtc (output);
|
|
|
|
if (crtc)
|
|
return crtc->logical_monitor;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 && crtc->current_mode);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* The minimum resolution at which we turn on a window-scale of 2 */
|
|
#define HIDPI_LIMIT 192
|
|
|
|
/*
|
|
* The minimum screen height at which we turn on a window-scale of 2;
|
|
* below this there just isn't enough vertical real estate for GNOME
|
|
* apps to work, and it's better to just be tiny
|
|
*/
|
|
#define HIDPI_MIN_HEIGHT 1200
|
|
|
|
/* From http://en.wikipedia.org/wiki/4K_resolution#Resolutions_of_common_formats */
|
|
#define SMALLEST_4K_WIDTH 3656
|
|
|
|
static float
|
|
calculate_scale (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode)
|
|
{
|
|
int resolution_width, resolution_height;
|
|
int width_mm, height_mm;
|
|
int scale;
|
|
|
|
scale = 1.0;
|
|
|
|
meta_monitor_mode_get_resolution (monitor_mode,
|
|
&resolution_width,
|
|
&resolution_height);
|
|
|
|
if (resolution_height < HIDPI_MIN_HEIGHT)
|
|
goto out;
|
|
|
|
/* 4K TV */
|
|
switch (meta_monitor_get_connector_type (monitor))
|
|
{
|
|
case META_CONNECTOR_TYPE_HDMIA:
|
|
case META_CONNECTOR_TYPE_HDMIB:
|
|
if (resolution_width < SMALLEST_4K_WIDTH)
|
|
goto out;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm);
|
|
|
|
/*
|
|
* Somebody encoded the aspect ratio (16/9 or 16/10) instead of the physical
|
|
* size.
|
|
*/
|
|
if ((width_mm == 160 && height_mm == 90) ||
|
|
(width_mm == 160 && height_mm == 100) ||
|
|
(width_mm == 16 && height_mm == 9) ||
|
|
(width_mm == 16 && height_mm == 10))
|
|
goto out;
|
|
|
|
if (width_mm > 0 && height_mm > 0)
|
|
{
|
|
double dpi_x, dpi_y;
|
|
|
|
dpi_x = (double) resolution_width / (width_mm / 25.4);
|
|
dpi_y = (double) resolution_height / (height_mm / 25.4);
|
|
|
|
/*
|
|
* We don't completely trust these values so both must be high, and never
|
|
* pick higher ratio than 2 automatically.
|
|
*/
|
|
if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT)
|
|
scale = 2.0;
|
|
}
|
|
|
|
out:
|
|
return scale;
|
|
}
|
|
|
|
float
|
|
meta_monitor_calculate_mode_scale (MetaMonitor *monitor,
|
|
MetaMonitorMode *monitor_mode)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
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);
|
|
}
|
|
|
|
static gboolean
|
|
is_logical_size_large_enough (gint width, gint height)
|
|
{
|
|
return width >= MINIMUM_LOGICAL_WIDTH &&
|
|
height >= MINIMUM_LOGICAL_HEIGHT;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_mode_should_be_advertised (MetaMonitorMode *monitor_mode)
|
|
{
|
|
g_return_val_if_fail (monitor_mode != NULL, FALSE);
|
|
|
|
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 scale_step)
|
|
{
|
|
unsigned int i, j;
|
|
float scaled_h;
|
|
float scaled_w;
|
|
float best_scale;
|
|
int base_scaled_w;
|
|
gboolean limit_exceeded;
|
|
gboolean found_one;
|
|
|
|
best_scale = 0;
|
|
scaled_w = width / scale;
|
|
scaled_h = height / scale;
|
|
|
|
if (scale < MINIMUM_SCALE_FACTOR ||
|
|
scale > MAXIMUM_SCALE_FACTOR ||
|
|
!is_logical_size_large_enough (floorf (scaled_w), floorf (scaled_h)))
|
|
goto out;
|
|
|
|
if (floorf (scaled_w) == scaled_w && floorf (scaled_h) == scaled_h)
|
|
return scale;
|
|
|
|
i = 0;
|
|
found_one = FALSE;
|
|
limit_exceeded = FALSE;
|
|
base_scaled_w = floorf (scaled_w);
|
|
|
|
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 + scale_step ||
|
|
current_scale <= scale - scale_step ||
|
|
current_scale < MINIMUM_SCALE_FACTOR ||
|
|
current_scale > MAXIMUM_SCALE_FACTOR)
|
|
{
|
|
limit_exceeded = TRUE;
|
|
continue;
|
|
}
|
|
|
|
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 && !limit_exceeded);
|
|
|
|
out:
|
|
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;
|
|
float scale_steps;
|
|
GArray *supported_scales;
|
|
|
|
scale_steps = 1.0 / (float) SCALE_FACTORS_PER_INTEGER;
|
|
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++)
|
|
{
|
|
for (j = 0; j < SCALE_FACTORS_PER_INTEGER; j++)
|
|
{
|
|
float scale;
|
|
float scale_value = i + j * scale_steps;
|
|
|
|
if ((constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) &&
|
|
fmodf (scale_value, 1.0) != 0.0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
scale = get_closest_scale_factor_for_resolution (width,
|
|
height,
|
|
scale_value,
|
|
scale_steps);
|
|
|
|
if (scale > 0.0f)
|
|
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;
|
|
}
|