mutter/src/backends/meta-monitor.c
Jonas Ådahl e8197e8e05 monitor-config-manager: Add underscanning to MetaMonitorConfig
Set the underscanning state via the monitor config struct so that it
can be configured.

https://bugzilla.gnome.org/show_bug.cgi?id=777732
2017-01-25 16:28:56 +08:00

790 lines
20 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-monitor-manager-private.h"
typedef struct _MetaMonitorMode
{
MetaMonitorModeSpec spec;
MetaMonitorCrtcMode *crtc_modes;
} MetaMonitorMode;
typedef struct _MetaMonitorPrivate
{
GList *outputs;
GList *modes;
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).
*/
long 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;
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_id (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;
}
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)
{
MetaOutput *output;
output = meta_monitor_get_main_output (monitor);
return output->crtc && output->crtc->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_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;
}
}
void
meta_monitor_get_dimensions (MetaMonitor *monitor,
int *width,
int *height)
{
META_MONITOR_GET_CLASS (monitor)->get_dimensions (monitor, width, height);
}
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_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;
}
static void
meta_monitor_finalize (GObject *object)
{
MetaMonitor *monitor = META_MONITOR (object);
MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor);
g_list_free_full (priv->modes, (GDestroyNotify) meta_monitor_mode_free);
g_clear_pointer (&priv->outputs, g_list_free);
meta_monitor_spec_free (priv->spec);
}
static void
meta_monitor_init (MetaMonitor *monitor)
{
}
static void
meta_monitor_class_init (MetaMonitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_monitor_finalize;
}
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;
unsigned int i;
output = meta_monitor_get_main_output (monitor);
for (i = 0; i < output->n_modes; i++)
{
MetaCrtcMode *crtc_mode = output->modes[i];
MetaMonitorMode *mode;
mode = g_new0 (MetaMonitorMode, 1);
mode->spec = (MetaMonitorModeSpec) {
.width = crtc_mode->width,
.height = crtc_mode->height,
.refresh_rate = crtc_mode->refresh_rate
},
mode->crtc_modes = g_new (MetaMonitorCrtcMode, 1);
mode->crtc_modes[0] = (MetaMonitorCrtcMode) {
.x = 0,
.y = 0,
.output = output,
.crtc_mode = crtc_mode
};
if (crtc_mode == output->preferred_mode)
monitor_priv->preferred_mode = mode;
if (output->crtc && crtc_mode == output->crtc->current_mode)
monitor_priv->current_mode = mode;
monitor_priv->modes = g_list_append (monitor_priv->modes, mode);
}
}
MetaMonitorNormal *
meta_monitor_normal_new (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->outputs = g_list_append (NULL, output);
monitor_priv->winsys_id = output->winsys_id;
meta_monitor_normal_generate_modes (monitor_normal);
meta_monitor_generate_id (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_get_dimensions (MetaMonitor *monitor,
int *width,
int *height)
{
MetaOutput *output;
output = meta_monitor_get_main_output (monitor);
*width = output->crtc->rect.width;
*height = output->crtc->rect.height;
}
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->get_dimensions = meta_monitor_normal_get_dimensions;
}
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)
{
MetaOutput *main_output;
main_output = meta_monitor_get_main_output (monitor);
if (main_output->suggested_x < 0 && main_output->suggested_y < 0)
return FALSE;
*x = main_output->suggested_x;
*y = main_output->suggested_y;
return TRUE;
}
static void
add_tiled_monitor_outputs (MetaMonitorManager *monitor_manager,
MetaMonitorTiled *monitor_tiled)
{
MetaMonitorPrivate *monitor_priv =
meta_monitor_get_instance_private (META_MONITOR (monitor_tiled));
unsigned int i;
for (i = 0; i < monitor_manager->n_outputs; i++)
{
MetaOutput *output = &monitor_manager->outputs[i];
if (output->tile_info.group_id != monitor_tiled->tile_group_id)
continue;
g_warn_if_fail (output->subpixel_order ==
monitor_tiled->main_output->subpixel_order);
monitor_priv->outputs = g_list_append (monitor_priv->outputs, output);
}
}
static void
calculate_tile_coordinate (MetaMonitor *monitor,
MetaOutput *output,
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;
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;
}
*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 void
meta_monitor_tiled_generate_modes (MetaMonitorTiled *monitor_tiled)
{
MetaMonitor *monitor = META_MONITOR (monitor_tiled);
MetaMonitorPrivate *monitor_priv =
meta_monitor_get_instance_private (monitor);
MetaMonitorMode *mode;
gboolean preferred_mode_is_current;
GList *l;
int i;
mode = g_new0 (MetaMonitorMode, 1);
meta_monitor_tiled_calculate_tiled_size (monitor,
&mode->spec.width,
&mode->spec.height);
mode->crtc_modes = g_new (MetaMonitorCrtcMode,
g_list_length (monitor_priv->outputs));
preferred_mode_is_current = TRUE;
for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++)
{
MetaOutput *output = l->data;
MetaCrtcMode *preferred_crtc_mode = output->preferred_mode;
int x;
int y;
calculate_tile_coordinate (monitor, output, &x, &y);
mode->crtc_modes[i] = (MetaMonitorCrtcMode) {
.x = x,
.y = y,
.output = output,
.crtc_mode = preferred_crtc_mode
};
g_warn_if_fail (mode->spec.refresh_rate == 0.0f ||
(mode->spec.refresh_rate ==
preferred_crtc_mode->refresh_rate));
mode->spec.refresh_rate = preferred_crtc_mode->refresh_rate;
if (!output->crtc ||
mode->crtc_modes[i].crtc_mode != output->crtc->current_mode)
preferred_mode_is_current = FALSE;
}
monitor_priv->modes = g_list_append (monitor_priv->modes, mode);
monitor_priv->preferred_mode = mode;
if (preferred_mode_is_current)
monitor_priv->current_mode = mode;
/* TODO: Add single tile modes */
}
MetaMonitorTiled *
meta_monitor_tiled_new (MetaMonitorManager *monitor_manager,
MetaOutput *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_tiled->tile_group_id = output->tile_info.group_id;
monitor_priv->winsys_id = output->winsys_id;
monitor_tiled->main_output = output;
add_tiled_monitor_outputs (monitor_manager, monitor_tiled);
meta_monitor_manager_tiled_monitor_added (monitor_manager,
META_MONITOR (monitor_tiled));
meta_monitor_tiled_generate_modes (monitor_tiled);
meta_monitor_generate_id (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_get_dimensions (MetaMonitor *monitor,
int *width,
int *height)
{
meta_monitor_tiled_calculate_tiled_size (monitor, width, height);
}
static void
meta_monitor_tiled_finalize (GObject *object)
{
MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (object);
MetaBackend *backend = meta_get_backend ();
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
meta_monitor_manager_tiled_monitor_removed (monitor_manager,
META_MONITOR (monitor_tiled));
}
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->get_dimensions = meta_monitor_tiled_get_dimensions;
}
static void
meta_monitor_mode_free (MetaMonitorMode *monitor_mode)
{
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 = meta_monitor_get_main_output (monitor);
if (output->crtc)
return output->crtc->logical_monitor;
else
return NULL;
}
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 &&
(monitor_mode_spec->refresh_rate ==
other_monitor_mode_spec->refresh_rate));
}
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_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];
if (!output->crtc ||
output->crtc->current_mode != monitor_crtc_mode->crtc_mode)
return FALSE;
}
return TRUE;
}
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;
}
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;
}
MetaMonitorModeSpec *
meta_monitor_mode_get_spec (MetaMonitorMode *monitor_mode)
{
return &monitor_mode->spec;
}
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;
}
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 (!func (monitor, mode, monitor_crtc_mode, user_data, error))
return FALSE;
}
return TRUE;
}