/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2001, 2002 Havoc Pennington
* Copyright (C) 2002, 2003 Red Hat Inc.
* Some ICCCM manager selection code derived from fvwm2,
* Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
* Copyright (C) 2003 Rob Adams
* Copyright (C) 2004-2006 Elijah Newren
* Copyright (C) 2013 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include "config.h"
#include "meta-monitor-manager-private.h"
#include
#include
#include
#include
#include
#include "util-private.h"
#include
#include "edid.h"
#include "backends/meta-crtc.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor.h"
#include "backends/meta-monitor-config-manager.h"
#include "backends/meta-orientation-manager.h"
#include "backends/meta-output.h"
#include "backends/x11/meta-monitor-manager-xrandr.h"
#include "meta-backend-private.h"
#define DEFAULT_DISPLAY_CONFIGURATION_TIMEOUT 20
enum
{
PROP_0,
PROP_BACKEND,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
enum {
MONITORS_CHANGED_INTERNAL,
CONFIRM_DISPLAY_CHANGE,
SIGNALS_LAST
};
/* Array index matches MetaMonitorTransform */
static gfloat transform_matrices[][6] = {
{ 1, 0, 0, 0, 1, 0 }, /* normal */
{ 0, -1, 1, 1, 0, 0 }, /* 90° */
{ -1, 0, 1, 0, -1, 1 }, /* 180° */
{ 0, 1, 0, -1, 0, 1 }, /* 270° */
{ -1, 0, 1, 0, 1, 0 }, /* normal flipped */
{ 0, 1, 0, 1, 0, 0 }, /* 90° flipped */
{ 1, 0, 0, 0, -1, 1 }, /* 180° flipped */
{ 0, -1, 1, -1, 0, 1 }, /* 270° flipped */
};
static int signals[SIGNALS_LAST];
static void meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaMonitorManager, meta_monitor_manager, META_DBUS_TYPE_DISPLAY_CONFIG_SKELETON,
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_DISPLAY_CONFIG, meta_monitor_manager_display_config_init));
static void initialize_dbus_interface (MetaMonitorManager *manager);
static gboolean
meta_monitor_manager_is_config_complete (MetaMonitorManager *manager,
MetaMonitorsConfig *config);
MetaBackend *
meta_monitor_manager_get_backend (MetaMonitorManager *manager)
{
return manager->backend;
}
static void
meta_monitor_manager_init (MetaMonitorManager *manager)
{
}
static void
meta_monitor_manager_set_primary_logical_monitor (MetaMonitorManager *manager,
MetaLogicalMonitor *logical_monitor)
{
manager->primary_logical_monitor = logical_monitor;
if (logical_monitor)
meta_logical_monitor_make_primary (logical_monitor);
}
static gboolean
is_main_tiled_monitor_output (MetaOutput *output)
{
return output->tile_info.loc_h_tile == 0 && output->tile_info.loc_v_tile == 0;
}
static MetaLogicalMonitor *
logical_monitor_from_layout (MetaMonitorManager *manager,
GList *logical_monitors,
MetaRectangle *layout)
{
GList *l;
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
if (meta_rectangle_equal (layout, &logical_monitor->rect))
return logical_monitor;
}
return NULL;
}
static void
meta_monitor_manager_rebuild_logical_monitors (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *logical_monitor_configs;
GList *logical_monitors = NULL;
GList *l;
int monitor_number = 0;
MetaLogicalMonitor *primary_logical_monitor = NULL;
logical_monitor_configs = config ? config->logical_monitor_configs : NULL;
for (l = logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
MetaLogicalMonitor *logical_monitor;
logical_monitor = meta_logical_monitor_new (manager,
logical_monitor_config,
monitor_number);
monitor_number++;
if (logical_monitor_config->is_primary)
primary_logical_monitor = logical_monitor;
logical_monitors = g_list_append (logical_monitors, logical_monitor);
}
/*
* If no monitor was marked as primary, fall back on marking the first
* logical monitor the primary one.
*/
if (!primary_logical_monitor && logical_monitors)
primary_logical_monitor = g_list_first (logical_monitors)->data;
manager->logical_monitors = logical_monitors;
meta_monitor_manager_set_primary_logical_monitor (manager,
primary_logical_monitor);
}
static float
derive_configured_global_scale (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
MetaLogicalMonitorConfig *logical_monitor_config;
logical_monitor_config = config->logical_monitor_configs->data;
return logical_monitor_config->scale;
}
static float
calculate_monitor_scale (MetaMonitorManager *manager,
MetaMonitor *monitor)
{
MetaMonitorMode *monitor_mode;
monitor_mode = meta_monitor_get_current_mode (monitor);
return meta_monitor_manager_calculate_monitor_mode_scale (manager,
monitor,
monitor_mode);
}
static float
derive_calculated_global_scale (MetaMonitorManager *manager)
{
MetaMonitor *primary_monitor;
primary_monitor = meta_monitor_manager_get_primary_monitor (manager);
if (!primary_monitor)
return 1.0;
return calculate_monitor_scale (manager, primary_monitor);
}
static float
derive_scale_from_config (MetaMonitorManager *manager,
MetaMonitorsConfig *config,
MetaRectangle *layout)
{
GList *l;
for (l = config->logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
if (meta_rectangle_equal (layout, &logical_monitor_config->layout))
return logical_monitor_config->scale;
}
g_warning ("Missing logical monitor, using scale 1");
return 1.0;
}
static void
meta_monitor_manager_rebuild_logical_monitors_derived (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *logical_monitors = NULL;
GList *l;
int monitor_number;
MetaLogicalMonitor *primary_logical_monitor = NULL;
gboolean use_global_scale;
float global_scale = 0.0;
MetaMonitorManagerCapability capabilities;
monitor_number = 0;
capabilities = meta_monitor_manager_get_capabilities (manager);
use_global_scale =
!!(capabilities & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED);
if (use_global_scale)
{
if (config)
global_scale = derive_configured_global_scale (manager, config);
else
global_scale = derive_calculated_global_scale (manager);
}
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
MetaLogicalMonitor *logical_monitor;
MetaRectangle layout;
if (!meta_monitor_is_active (monitor))
continue;
meta_monitor_derive_layout (monitor, &layout);
logical_monitor = logical_monitor_from_layout (manager, logical_monitors,
&layout);
if (logical_monitor)
{
meta_logical_monitor_add_monitor (logical_monitor, monitor);
}
else
{
float scale;
if (use_global_scale)
scale = global_scale;
else if (config)
scale = derive_scale_from_config (manager, config, &layout);
else
scale = calculate_monitor_scale (manager, monitor);
g_assert (scale > 0);
logical_monitor = meta_logical_monitor_new_derived (manager,
monitor,
&layout,
scale,
monitor_number);
logical_monitors = g_list_append (logical_monitors, logical_monitor);
monitor_number++;
}
if (meta_monitor_is_primary (monitor))
primary_logical_monitor = logical_monitor;
}
manager->logical_monitors = logical_monitors;
/*
* If no monitor was marked as primary, fall back on marking the first
* logical monitor the primary one.
*/
if (!primary_logical_monitor && manager->logical_monitors)
primary_logical_monitor = g_list_first (manager->logical_monitors)->data;
meta_monitor_manager_set_primary_logical_monitor (manager,
primary_logical_monitor);
}
static void
power_save_mode_changed (MetaMonitorManager *manager,
GParamSpec *pspec,
gpointer user_data)
{
MetaMonitorManagerClass *klass;
int mode = meta_dbus_display_config_get_power_save_mode (META_DBUS_DISPLAY_CONFIG (manager));
if (mode == META_POWER_SAVE_UNSUPPORTED)
return;
/* If DPMS is unsupported, force the property back. */
if (manager->power_save_mode == META_POWER_SAVE_UNSUPPORTED)
{
meta_dbus_display_config_set_power_save_mode (META_DBUS_DISPLAY_CONFIG (manager), META_POWER_SAVE_UNSUPPORTED);
return;
}
klass = META_MONITOR_MANAGER_GET_CLASS (manager);
if (klass->set_power_save_mode)
klass->set_power_save_mode (manager, mode);
manager->power_save_mode = mode;
}
void
meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager)
{
meta_monitor_manager_ensure_configured (manager);
}
static void
lid_is_closed_changed (UpClient *client,
GParamSpec *pspec,
gpointer user_data)
{
MetaMonitorManager *manager = user_data;
meta_monitor_manager_lid_is_closed_changed (manager);
}
static gboolean
meta_monitor_manager_real_is_lid_closed (MetaMonitorManager *manager)
{
if (!manager->up_client)
return FALSE;
return up_client_get_lid_is_closed (manager->up_client);
}
gboolean
meta_monitor_manager_is_lid_closed (MetaMonitorManager *manager)
{
return META_MONITOR_MANAGER_GET_CLASS (manager)->is_lid_closed (manager);
}
gboolean
meta_monitor_manager_is_headless (MetaMonitorManager *manager)
{
return !manager->logical_monitors;
}
float
meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager,
MetaMonitor *monitor,
MetaMonitorMode *monitor_mode)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->calculate_monitor_mode_scale (manager,
monitor,
monitor_mode);
}
float *
meta_monitor_manager_calculate_supported_scales (MetaMonitorManager *manager,
MetaLogicalMonitorLayoutMode layout_mode,
MetaMonitor *monitor,
MetaMonitorMode *monitor_mode,
int *n_supported_scales)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->calculate_supported_scales (manager,
layout_mode,
monitor,
monitor_mode,
n_supported_scales);
}
MetaMonitorManagerCapability
meta_monitor_manager_get_capabilities (MetaMonitorManager *manager)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->get_capabilities (manager);
}
gboolean
meta_monitor_manager_get_max_screen_size (MetaMonitorManager *manager,
int *max_width,
int *max_height)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->get_max_screen_size (manager, max_width, max_height);
}
MetaLogicalMonitorLayoutMode
meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->get_default_layout_mode (manager);
}
static void
meta_monitor_manager_ensure_initial_config (MetaMonitorManager *manager)
{
META_MONITOR_MANAGER_GET_CLASS (manager)->ensure_initial_config (manager);
}
static gboolean
meta_monitor_manager_apply_monitors_config (MetaMonitorManager *manager,
MetaMonitorsConfig *config,
MetaMonitorsConfigMethod method,
GError **error)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
g_assert (!config ||
!(config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED));
if (!manager_class->apply_monitors_config (manager, config, method, error))
return FALSE;
switch (method)
{
case META_MONITORS_CONFIG_METHOD_TEMPORARY:
case META_MONITORS_CONFIG_METHOD_PERSISTENT:
meta_monitor_config_manager_set_current (manager->config_manager, config);
break;
case META_MONITORS_CONFIG_METHOD_VERIFY:
break;
}
return TRUE;
}
gboolean
meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager)
{
GList *l;
for (l = manager->gpus; l; l = l->next)
{
MetaGpu *gpu = l->data;
if (meta_gpu_has_hotplug_mode_update (gpu))
return TRUE;
}
return FALSE;
}
static gboolean
should_use_stored_config (MetaMonitorManager *manager)
{
return (manager->in_init ||
!meta_monitor_manager_has_hotplug_mode_update (manager));
}
MetaMonitorsConfig *
meta_monitor_manager_ensure_configured (MetaMonitorManager *manager)
{
MetaMonitorsConfig *config = NULL;
GError *error = NULL;
gboolean use_stored_config;
MetaMonitorsConfigMethod method;
MetaMonitorsConfigMethod fallback_method =
META_MONITORS_CONFIG_METHOD_TEMPORARY;
use_stored_config = should_use_stored_config (manager);
if (use_stored_config)
method = META_MONITORS_CONFIG_METHOD_PERSISTENT;
else
method = META_MONITORS_CONFIG_METHOD_TEMPORARY;
if (use_stored_config)
{
config = meta_monitor_config_manager_get_stored (manager->config_manager);
if (config)
{
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
method,
&error))
{
config = NULL;
g_warning ("Failed to use stored monitor configuration: %s",
error->message);
g_clear_error (&error);
}
else
{
g_object_ref (config);
goto done;
}
}
}
config = meta_monitor_config_manager_create_suggested (manager->config_manager);
if (config)
{
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
method,
&error))
{
g_clear_object (&config);
g_warning ("Failed to use suggested monitor configuration: %s",
error->message);
g_clear_error (&error);
}
else
{
goto done;
}
}
config = meta_monitor_config_manager_get_previous (manager->config_manager);
if (config)
{
config = g_object_ref (config);
if (meta_monitor_manager_is_config_complete (manager, config))
{
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
method,
&error))
{
g_warning ("Failed to use suggested monitor configuration: %s",
error->message);
g_clear_error (&error);
}
else
{
goto done;
}
}
g_clear_object (&config);
}
config = meta_monitor_config_manager_create_linear (manager->config_manager);
if (config)
{
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
method,
&error))
{
g_clear_object (&config);
g_warning ("Failed to use linear monitor configuration: %s",
error->message);
g_clear_error (&error);
}
else
{
goto done;
}
}
config = meta_monitor_config_manager_create_fallback (manager->config_manager);
if (config)
{
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
fallback_method,
&error))
{
g_clear_object (&config);
g_warning ("Failed to use fallback monitor configuration: %s",
error->message);
g_clear_error (&error);
}
else
{
goto done;
}
}
done:
if (!config)
{
meta_monitor_manager_apply_monitors_config (manager,
NULL,
fallback_method,
&error);
return NULL;
}
g_object_unref (config);
return config;
}
static void
orientation_changed (MetaOrientationManager *orientation_manager,
MetaMonitorManager *manager)
{
MetaMonitorTransform transform;
GError *error = NULL;
MetaMonitorsConfig *config;
switch (meta_orientation_manager_get_orientation (orientation_manager))
{
case META_ORIENTATION_NORMAL:
transform = META_MONITOR_TRANSFORM_NORMAL;
break;
case META_ORIENTATION_BOTTOM_UP:
transform = META_MONITOR_TRANSFORM_180;
break;
case META_ORIENTATION_LEFT_UP:
transform = META_MONITOR_TRANSFORM_90;
break;
case META_ORIENTATION_RIGHT_UP:
transform = META_MONITOR_TRANSFORM_270;
break;
case META_ORIENTATION_UNDEFINED:
default:
return;
}
config =
meta_monitor_config_manager_create_for_orientation (manager->config_manager,
transform);
if (!config)
return;
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
META_MONITORS_CONFIG_METHOD_TEMPORARY,
&error))
{
g_warning ("Failed to use orientation monitor configuration: %s",
error->message);
g_error_free (error);
}
g_object_unref (config);
}
static void
experimental_features_changed (MetaSettings *settings,
MetaExperimentalFeature old_experimental_features,
MetaMonitorManager *manager)
{
gboolean was_stage_views_scaled;
gboolean is_stage_views_scaled;
gboolean should_reconfigure = FALSE;
was_stage_views_scaled =
!!(old_experimental_features &
META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER);
is_stage_views_scaled =
meta_settings_is_experimental_feature_enabled (
settings,
META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER);
if (is_stage_views_scaled != was_stage_views_scaled)
should_reconfigure = TRUE;
if (should_reconfigure)
meta_monitor_manager_on_hotplug (manager);
meta_settings_update_ui_scaling_factor (settings);
}
void
meta_monitor_manager_setup (MetaMonitorManager *manager)
{
manager->in_init = TRUE;
manager->config_manager = meta_monitor_config_manager_new (manager);
meta_monitor_manager_read_current_state (manager);
meta_monitor_manager_ensure_initial_config (manager);
manager->in_init = FALSE;
}
static void
meta_monitor_manager_constructed (GObject *object)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
MetaBackend *backend = manager->backend;
MetaSettings *settings = meta_backend_get_settings (backend);
manager->experimental_features_changed_handler_id =
g_signal_connect (settings,
"experimental-features-changed",
G_CALLBACK (experimental_features_changed),
manager);
if (manager_class->is_lid_closed == meta_monitor_manager_real_is_lid_closed)
{
manager->up_client = up_client_new ();
g_signal_connect_object (manager->up_client, "notify::lid-is-closed",
G_CALLBACK (lid_is_closed_changed), manager, 0);
}
g_signal_connect_object (manager, "notify::power-save-mode",
G_CALLBACK (power_save_mode_changed), manager, 0);
g_signal_connect_object (meta_backend_get_orientation_manager (backend),
"orientation-changed",
G_CALLBACK (orientation_changed),
manager, 0);
manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
initialize_dbus_interface (manager);
}
static void
meta_monitor_manager_finalize (GObject *object)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
g_list_free_full (manager->gpus, g_object_unref);
g_list_free_full (manager->logical_monitors, g_object_unref);
g_signal_handler_disconnect (manager->backend,
manager->experimental_features_changed_handler_id);
G_OBJECT_CLASS (meta_monitor_manager_parent_class)->finalize (object);
}
static void
meta_monitor_manager_dispose (GObject *object)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
if (manager->dbus_name_id != 0)
{
g_bus_unown_name (manager->dbus_name_id);
manager->dbus_name_id = 0;
}
g_clear_object (&manager->config_manager);
g_clear_object (&manager->up_client);
G_OBJECT_CLASS (meta_monitor_manager_parent_class)->dispose (object);
}
static GBytes *
meta_monitor_manager_real_read_edid (MetaMonitorManager *manager,
MetaOutput *output)
{
return NULL;
}
static char *
meta_monitor_manager_real_get_edid_file (MetaMonitorManager *manager,
MetaOutput *output)
{
return NULL;
}
static void
meta_monitor_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
switch (prop_id)
{
case PROP_BACKEND:
manager->backend = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
meta_monitor_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
switch (prop_id)
{
case PROP_BACKEND:
g_value_set_object (value, manager->backend);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = meta_monitor_manager_constructed;
object_class->dispose = meta_monitor_manager_dispose;
object_class->finalize = meta_monitor_manager_finalize;
object_class->get_property = meta_monitor_manager_get_property;
object_class->set_property = meta_monitor_manager_set_property;
klass->get_edid_file = meta_monitor_manager_real_get_edid_file;
klass->read_edid = meta_monitor_manager_real_read_edid;
klass->is_lid_closed = meta_monitor_manager_real_is_lid_closed;
signals[MONITORS_CHANGED_INTERNAL] =
g_signal_new ("monitors-changed-internal",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[CONFIRM_DISPLAY_CHANGE] =
g_signal_new ("confirm-display-change",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
obj_props[PROP_BACKEND] =
g_param_spec_object ("backend",
"backend",
"MetaBackend",
META_TYPE_BACKEND,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
}
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 *
make_display_name (MetaMonitorManager *manager,
MetaOutput *output)
{
g_autofree char *inches = NULL;
g_autofree char *vendor_name = NULL;
if (meta_output_is_laptop (output))
return g_strdup (_("Built-in display"));
if (output->width_mm > 0 && output->height_mm > 0)
{
double d = sqrt (output->width_mm * output->width_mm +
output->height_mm * output->height_mm);
inches = diagonal_to_str (d / 25.4);
}
if (g_strcmp0 (output->vendor, "unknown") != 0)
{
if (!manager->pnp_ids)
manager->pnp_ids = gnome_pnp_ids_new ();
vendor_name = gnome_pnp_ids_get_pnp_id (manager->pnp_ids,
output->vendor);
if (!vendor_name)
vendor_name = g_strdup (output->vendor);
}
else
{
if (inches != NULL)
vendor_name = g_strdup (_("Unknown"));
else
vendor_name = g_strdup (_("Unknown Display"));
}
if (inches != NULL)
{
/* TRANSLATORS: this is a monitor vendor name, followed by a
* size in inches, like 'Dell 15"'
*/
return g_strdup_printf (_("%s %s"), vendor_name, inches);
}
else
{
return g_strdup (vendor_name);
}
}
static const char *
get_connector_type_name (MetaConnectorType connector_type)
{
switch (connector_type)
{
case META_CONNECTOR_TYPE_Unknown: return "Unknown";
case META_CONNECTOR_TYPE_VGA: return "VGA";
case META_CONNECTOR_TYPE_DVII: return "DVII";
case META_CONNECTOR_TYPE_DVID: return "DVID";
case META_CONNECTOR_TYPE_DVIA: return "DVIA";
case META_CONNECTOR_TYPE_Composite: return "Composite";
case META_CONNECTOR_TYPE_SVIDEO: return "SVIDEO";
case META_CONNECTOR_TYPE_LVDS: return "LVDS";
case META_CONNECTOR_TYPE_Component: return "Component";
case META_CONNECTOR_TYPE_9PinDIN: return "9PinDIN";
case META_CONNECTOR_TYPE_DisplayPort: return "DisplayPort";
case META_CONNECTOR_TYPE_HDMIA: return "HDMIA";
case META_CONNECTOR_TYPE_HDMIB: return "HDMIB";
case META_CONNECTOR_TYPE_TV: return "TV";
case META_CONNECTOR_TYPE_eDP: return "eDP";
case META_CONNECTOR_TYPE_VIRTUAL: return "VIRTUAL";
case META_CONNECTOR_TYPE_DSI: return "DSI";
default: g_assert_not_reached ();
}
}
static GList *
combine_gpu_lists (MetaMonitorManager *manager,
GList * (*list_getter) (MetaGpu *gpu))
{
GList *list = NULL;
GList *l;
for (l = manager->gpus; l; l = l->next)
{
MetaGpu *gpu = l->data;
list = g_list_concat (list, g_list_copy (list_getter (gpu)));
}
return list;
}
static gboolean
meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (skeleton);
GList *combined_modes;
GList *combined_outputs;
GList *combined_crtcs;
GVariantBuilder crtc_builder, output_builder, mode_builder;
GList *l;
unsigned int i, j;
int max_screen_width;
int max_screen_height;
combined_modes = combine_gpu_lists (manager, meta_gpu_get_modes);
combined_outputs = combine_gpu_lists (manager, meta_gpu_get_outputs);
combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs);
g_variant_builder_init (&crtc_builder, G_VARIANT_TYPE ("a(uxiiiiiuaua{sv})"));
g_variant_builder_init (&output_builder, G_VARIANT_TYPE ("a(uxiausauaua{sv})"));
g_variant_builder_init (&mode_builder, G_VARIANT_TYPE ("a(uxuudu)"));
for (l = combined_crtcs, i = 0; l; l = l->next, i++)
{
MetaCrtc *crtc = l->data;
GVariantBuilder transforms;
int current_mode_index;
g_variant_builder_init (&transforms, G_VARIANT_TYPE ("au"));
for (j = 0; j <= META_MONITOR_TRANSFORM_FLIPPED_270; j++)
if (crtc->all_transforms & (1 << j))
g_variant_builder_add (&transforms, "u", j);
if (crtc->current_mode)
current_mode_index = g_list_index (combined_modes, crtc->current_mode);
else
current_mode_index = -1;
g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})",
i, /* ID */
(gint64)crtc->crtc_id,
(int)crtc->rect.x,
(int)crtc->rect.y,
(int)crtc->rect.width,
(int)crtc->rect.height,
current_mode_index,
(guint32)crtc->transform,
&transforms,
NULL /* properties */);
}
for (l = combined_outputs; l; l = l->next)
{
MetaOutput *output = l->data;
GVariantBuilder crtcs, modes, clones, properties;
GBytes *edid;
char *edid_file;
int crtc_index;
g_variant_builder_init (&crtcs, G_VARIANT_TYPE ("au"));
for (j = 0; j < output->n_possible_crtcs; j++)
{
MetaCrtc *possible_crtc = output->possible_crtcs[j];
unsigned possible_crtc_index;
possible_crtc_index = g_list_index (combined_crtcs, possible_crtc);
g_variant_builder_add (&crtcs, "u", possible_crtc_index);
}
g_variant_builder_init (&modes, G_VARIANT_TYPE ("au"));
for (j = 0; j < output->n_modes; j++)
{
unsigned mode_index;
mode_index = g_list_index (combined_modes, output->modes[j]);
g_variant_builder_add (&modes, "u", mode_index);
}
g_variant_builder_init (&clones, G_VARIANT_TYPE ("au"));
for (j = 0; j < output->n_possible_clones; j++)
{
unsigned int possible_clone_index;
possible_clone_index = g_list_index (combined_outputs,
output->possible_clones[j]);
g_variant_builder_add (&clones, "u", possible_clone_index);
}
g_variant_builder_init (&properties, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&properties, "{sv}", "vendor",
g_variant_new_string (output->vendor));
g_variant_builder_add (&properties, "{sv}", "product",
g_variant_new_string (output->product));
g_variant_builder_add (&properties, "{sv}", "serial",
g_variant_new_string (output->serial));
g_variant_builder_add (&properties, "{sv}", "width-mm",
g_variant_new_int32 (output->width_mm));
g_variant_builder_add (&properties, "{sv}", "height-mm",
g_variant_new_int32 (output->height_mm));
g_variant_builder_add (&properties, "{sv}", "display-name",
g_variant_new_take_string (make_display_name (manager, output)));
g_variant_builder_add (&properties, "{sv}", "backlight",
g_variant_new_int32 (output->backlight));
g_variant_builder_add (&properties, "{sv}", "min-backlight-step",
g_variant_new_int32 ((output->backlight_max - output->backlight_min) ?
100 / (output->backlight_max - output->backlight_min) : -1));
g_variant_builder_add (&properties, "{sv}", "primary",
g_variant_new_boolean (output->is_primary));
g_variant_builder_add (&properties, "{sv}", "presentation",
g_variant_new_boolean (output->is_presentation));
g_variant_builder_add (&properties, "{sv}", "connector-type",
g_variant_new_string (get_connector_type_name (output->connector_type)));
g_variant_builder_add (&properties, "{sv}", "underscanning",
g_variant_new_boolean (output->is_underscanning));
g_variant_builder_add (&properties, "{sv}", "supports-underscanning",
g_variant_new_boolean (output->supports_underscanning));
edid_file = manager_class->get_edid_file (manager, output);
if (edid_file)
{
g_variant_builder_add (&properties, "{sv}", "edid-file",
g_variant_new_take_string (edid_file));
}
else
{
edid = manager_class->read_edid (manager, output);
if (edid)
{
g_variant_builder_add (&properties, "{sv}", "edid",
g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"),
edid, TRUE));
g_bytes_unref (edid);
}
}
if (output->tile_info.group_id)
{
g_variant_builder_add (&properties, "{sv}", "tile",
g_variant_new ("(uuuuuuuu)",
output->tile_info.group_id,
output->tile_info.flags,
output->tile_info.max_h_tiles,
output->tile_info.max_v_tiles,
output->tile_info.loc_h_tile,
output->tile_info.loc_v_tile,
output->tile_info.tile_w,
output->tile_info.tile_h));
}
crtc_index = output->crtc ? g_list_index (combined_crtcs, output->crtc)
: -1;
g_variant_builder_add (&output_builder, "(uxiausauaua{sv})",
i, /* ID */
(gint64)output->winsys_id,
crtc_index,
&crtcs,
output->name,
&modes,
&clones,
&properties);
}
for (l = combined_modes, i = 0; l; l = l->next, i++)
{
MetaCrtcMode *mode = l->data;
g_variant_builder_add (&mode_builder, "(uxuudu)",
i, /* ID */
(gint64)mode->mode_id,
(guint32)mode->width,
(guint32)mode->height,
(double)mode->refresh_rate,
(guint32)mode->flags);
}
if (!meta_monitor_manager_get_max_screen_size (manager,
&max_screen_width,
&max_screen_height))
{
/* No max screen size, just send something large */
max_screen_width = 65535;
max_screen_height = 65535;
}
meta_dbus_display_config_complete_get_resources (skeleton,
invocation,
manager->serial,
g_variant_builder_end (&crtc_builder),
g_variant_builder_end (&output_builder),
g_variant_builder_end (&mode_builder),
max_screen_width,
max_screen_height);
g_list_free (combined_modes);
g_list_free (combined_outputs);
g_list_free (combined_crtcs);
return TRUE;
}
static void
restore_previous_config (MetaMonitorManager *manager)
{
MetaMonitorsConfig *previous_config;
GError *error = NULL;
previous_config =
meta_monitor_config_manager_pop_previous (manager->config_manager);
if (previous_config)
{
MetaMonitorsConfigMethod method;
method = META_MONITORS_CONFIG_METHOD_TEMPORARY;
if (meta_monitor_manager_apply_monitors_config (manager,
previous_config,
method,
&error))
{
g_object_unref (previous_config);
return;
}
else
{
g_object_unref (previous_config);
g_warning ("Failed to restore previous configuration: %s",
error->message);
g_error_free (error);
}
}
meta_monitor_manager_ensure_configured (manager);
}
gint
meta_monitor_manager_get_display_configuration_timeout (void)
{
return DEFAULT_DISPLAY_CONFIGURATION_TIMEOUT;
}
static gboolean
save_config_timeout (gpointer user_data)
{
MetaMonitorManager *manager = user_data;
restore_previous_config (manager);
manager->persistent_timeout_id = 0;
return G_SOURCE_REMOVE;
}
static void
cancel_persistent_confirmation (MetaMonitorManager *manager)
{
g_source_remove (manager->persistent_timeout_id);
manager->persistent_timeout_id = 0;
}
static void
request_persistent_confirmation (MetaMonitorManager *manager)
{
manager->persistent_timeout_id = g_timeout_add_seconds (meta_monitor_manager_get_display_configuration_timeout (),
save_config_timeout,
manager);
g_source_set_name_by_id (manager->persistent_timeout_id,
"[mutter] save_config_timeout");
g_signal_emit (manager, signals[CONFIRM_DISPLAY_CHANGE], 0);
}
#define META_DISPLAY_CONFIG_MODE_FLAGS_PREFERRED (1 << 0)
#define META_DISPLAY_CONFIG_MODE_FLAGS_CURRENT (1 << 1)
#define MODE_FORMAT "(siiddada{sv})"
#define MODES_FORMAT "a" MODE_FORMAT
#define MONITOR_SPEC_FORMAT "(ssss)"
#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
#define MONITORS_FORMAT "a" MONITOR_FORMAT
#define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT
#define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})"
#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
static gboolean
meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
MetaSettings *settings = meta_backend_get_settings (manager->backend);
GVariantBuilder monitors_builder;
GVariantBuilder logical_monitors_builder;
GVariantBuilder properties_builder;
GList *l;
int i;
MetaMonitorManagerCapability capabilities;
int ui_scaling_factor;
int max_screen_width, max_screen_height;
g_variant_builder_init (&monitors_builder,
G_VARIANT_TYPE (MONITORS_FORMAT));
g_variant_builder_init (&logical_monitors_builder,
G_VARIANT_TYPE (LOGICAL_MONITORS_FORMAT));
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor);
MetaMonitorMode *current_mode;
MetaMonitorMode *preferred_mode;
GVariantBuilder modes_builder;
GVariantBuilder monitor_properties_builder;
GList *k;
gboolean is_builtin;
MetaOutput *main_output;
char *display_name;
current_mode = meta_monitor_get_current_mode (monitor);
preferred_mode = meta_monitor_get_preferred_mode (monitor);
g_variant_builder_init (&modes_builder, G_VARIANT_TYPE (MODES_FORMAT));
for (k = meta_monitor_get_modes (monitor); k; k = k->next)
{
MetaMonitorMode *monitor_mode = k->data;
GVariantBuilder supported_scales_builder;
const char *mode_id;
int mode_width, mode_height;
float refresh_rate;
float preferred_scale;
float *supported_scales;
int n_supported_scales;
GVariantBuilder mode_properties_builder;
MetaCrtcModeFlag mode_flags;
mode_id = meta_monitor_mode_get_id (monitor_mode);
meta_monitor_mode_get_resolution (monitor_mode,
&mode_width, &mode_height);
refresh_rate = meta_monitor_mode_get_refresh_rate (monitor_mode);
preferred_scale =
meta_monitor_manager_calculate_monitor_mode_scale (manager,
monitor,
monitor_mode);
g_variant_builder_init (&supported_scales_builder,
G_VARIANT_TYPE ("ad"));
supported_scales =
meta_monitor_manager_calculate_supported_scales (manager,
manager->layout_mode,
monitor,
monitor_mode,
&n_supported_scales);
for (i = 0; i < n_supported_scales; i++)
g_variant_builder_add (&supported_scales_builder, "d",
(double) supported_scales[i]);
g_free (supported_scales);
mode_flags = meta_monitor_mode_get_flags (monitor_mode);
g_variant_builder_init (&mode_properties_builder,
G_VARIANT_TYPE ("a{sv}"));
if (monitor_mode == current_mode)
g_variant_builder_add (&mode_properties_builder, "{sv}",
"is-current",
g_variant_new_boolean (TRUE));
if (monitor_mode == preferred_mode)
g_variant_builder_add (&mode_properties_builder, "{sv}",
"is-preferred",
g_variant_new_boolean (TRUE));
if (mode_flags & META_CRTC_MODE_FLAG_INTERLACE)
g_variant_builder_add (&mode_properties_builder, "{sv}",
"is-interlaced",
g_variant_new_boolean (TRUE));
g_variant_builder_add (&modes_builder, MODE_FORMAT,
mode_id,
mode_width,
mode_height,
refresh_rate,
(double) preferred_scale,
&supported_scales_builder,
&mode_properties_builder);
}
g_variant_builder_init (&monitor_properties_builder,
G_VARIANT_TYPE ("a{sv}"));
if (meta_monitor_supports_underscanning (monitor))
{
gboolean is_underscanning = meta_monitor_is_underscanning (monitor);
g_variant_builder_add (&monitor_properties_builder, "{sv}",
"is-underscanning",
g_variant_new_boolean (is_underscanning));
}
is_builtin = meta_monitor_is_laptop_panel (monitor);
g_variant_builder_add (&monitor_properties_builder, "{sv}",
"is-builtin",
g_variant_new_boolean (is_builtin));
main_output = meta_monitor_get_main_output (monitor);
display_name = make_display_name (manager, main_output);
g_variant_builder_add (&monitor_properties_builder, "{sv}",
"display-name",
g_variant_new_take_string (display_name));
g_variant_builder_add (&monitors_builder, MONITOR_FORMAT,
monitor_spec->connector,
monitor_spec->vendor,
monitor_spec->product,
monitor_spec->serial,
&modes_builder,
&monitor_properties_builder);
}
for (l = manager->logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
GVariantBuilder logical_monitor_monitors_builder;
GList *k;
g_variant_builder_init (&logical_monitor_monitors_builder,
G_VARIANT_TYPE (LOGICAL_MONITOR_MONITORS_FORMAT));
for (k = logical_monitor->monitors; k; k = k->next)
{
MetaMonitor *monitor = k->data;
MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor);
g_variant_builder_add (&logical_monitor_monitors_builder,
MONITOR_SPEC_FORMAT,
monitor_spec->connector,
monitor_spec->vendor,
monitor_spec->product,
monitor_spec->serial);
}
g_variant_builder_add (&logical_monitors_builder,
LOGICAL_MONITOR_FORMAT,
logical_monitor->rect.x,
logical_monitor->rect.y,
(double) logical_monitor->scale,
logical_monitor->transform,
logical_monitor->is_primary,
&logical_monitor_monitors_builder,
NULL);
}
g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}"));
capabilities = meta_monitor_manager_get_capabilities (manager);
if ((capabilities & META_MONITOR_MANAGER_CAPABILITY_MIRRORING) == 0)
{
g_variant_builder_add (&properties_builder, "{sv}",
"supports-mirroring",
g_variant_new_boolean (FALSE));
}
g_variant_builder_add (&properties_builder, "{sv}",
"layout-mode",
g_variant_new_uint32 (manager->layout_mode));
if (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)
{
g_variant_builder_add (&properties_builder, "{sv}",
"supports-changing-layout-mode",
g_variant_new_boolean (TRUE));
}
if (capabilities & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED)
{
g_variant_builder_add (&properties_builder, "{sv}",
"global-scale-required",
g_variant_new_boolean (TRUE));
}
ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings);
g_variant_builder_add (&properties_builder, "{sv}",
"legacy-ui-scaling-factor",
g_variant_new_int32 (ui_scaling_factor));
if (meta_monitor_manager_get_max_screen_size (manager,
&max_screen_width,
&max_screen_height))
{
GVariantBuilder max_screen_size_builder;
g_variant_builder_init (&max_screen_size_builder,
G_VARIANT_TYPE ("(ii)"));
g_variant_builder_add (&max_screen_size_builder, "i",
max_screen_width);
g_variant_builder_add (&max_screen_size_builder, "i",
max_screen_height);
g_variant_builder_add (&properties_builder, "{sv}",
"max-screen-size",
g_variant_builder_end (&max_screen_size_builder));
}
meta_dbus_display_config_complete_get_current_state (
skeleton,
invocation,
manager->serial,
g_variant_builder_end (&monitors_builder),
g_variant_builder_end (&logical_monitors_builder),
g_variant_builder_end (&properties_builder));
return TRUE;
}
#undef MODE_FORMAT
#undef MODES_FORMAT
#undef MONITOR_SPEC_FORMAT
#undef MONITOR_FORMAT
#undef MONITORS_FORMAT
#undef LOGICAL_MONITOR_MONITORS_FORMAT
#undef LOGICAL_MONITOR_FORMAT
#undef LOGICAL_MONITORS_FORMAT
gboolean
meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager,
MetaLogicalMonitorLayoutMode layout_mode,
MetaMonitor *monitor,
MetaMonitorMode *monitor_mode,
float scale)
{
g_autofree float *supported_scales = NULL;
int n_supported_scales;
int i;
supported_scales =
meta_monitor_manager_calculate_supported_scales (manager,
layout_mode,
monitor,
monitor_mode,
&n_supported_scales);
for (i = 0; i < n_supported_scales; i++)
{
if (supported_scales[i] == scale)
return TRUE;
}
return FALSE;
}
static gboolean
meta_monitor_manager_is_config_applicable (MetaMonitorManager *manager,
MetaMonitorsConfig *config,
GError **error)
{
GList *l;
for (l = config->logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
float scale = logical_monitor_config->scale;
GList *k;
for (k = logical_monitor_config->monitor_configs; k; k = k->next)
{
MetaMonitorConfig *monitor_config = k->data;
MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec;
MetaMonitorModeSpec *mode_spec = monitor_config->mode_spec;
MetaMonitor *monitor;
MetaMonitorMode *monitor_mode;
monitor = meta_monitor_manager_get_monitor_from_spec (manager,
monitor_spec);
if (!monitor)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Specified monitor not found");
return FALSE;
}
monitor_mode = meta_monitor_get_mode_from_spec (monitor, mode_spec);
if (!monitor_mode)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Specified monitor mode not available");
return FALSE;
}
if (!meta_monitor_manager_is_scale_supported (manager,
config->layout_mode,
monitor,
monitor_mode,
scale))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Scale not supported by backend");
return FALSE;
}
}
}
return TRUE;
}
static gboolean
meta_monitor_manager_is_config_complete (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *l;
unsigned int configured_monitor_count = 0;
unsigned int expected_monitor_count = 0;
for (l = config->logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
GList *k;
for (k = logical_monitor_config->monitor_configs; k; k = k->next)
configured_monitor_count++;
}
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (meta_monitor_is_laptop_panel (monitor))
{
if (!meta_monitor_manager_is_lid_closed (manager))
expected_monitor_count++;
}
else
{
expected_monitor_count++;
}
}
if (configured_monitor_count != expected_monitor_count)
return FALSE;
return meta_monitor_manager_is_config_applicable (manager, config, NULL);
}
static MetaMonitor *
find_monitor_from_connector (MetaMonitorManager *manager,
char *connector)
{
GList *monitors;
GList *l;
if (!connector)
return NULL;
monitors = meta_monitor_manager_get_monitors (manager);
for (l = monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor);
if (g_str_equal (connector, monitor_spec->connector))
return monitor;
}
return NULL;
}
#define MONITOR_CONFIG_FORMAT "(ssa{sv})"
#define MONITOR_CONFIGS_FORMAT "a" MONITOR_CONFIG_FORMAT
#define LOGICAL_MONITOR_CONFIG_FORMAT "(iidub" MONITOR_CONFIGS_FORMAT ")"
static MetaMonitorConfig *
create_monitor_config_from_variant (MetaMonitorManager *manager,
GVariant *monitor_config_variant,
GError **error)
{
MetaMonitorConfig *monitor_config = NULL;
g_autofree char *connector = NULL;
g_autofree char *mode_id = NULL;
MetaMonitorMode *monitor_mode;
MetaMonitor *monitor;
MetaMonitorSpec *monitor_spec;
MetaMonitorModeSpec *monitor_mode_spec;
g_autoptr (GVariant) properties_variant = NULL;
gboolean enable_underscanning = FALSE;
g_variant_get (monitor_config_variant, "(ss@a{sv})",
&connector,
&mode_id,
&properties_variant);
monitor = find_monitor_from_connector (manager, connector);
if (!monitor)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid connector '%s' specified", connector);
return NULL;
}
monitor_mode = meta_monitor_get_mode_from_id (monitor, mode_id);
if (!monitor_mode)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid mode '%s' specified", mode_id);
return NULL;
}
g_variant_lookup (properties_variant, "underscanning", "b", &enable_underscanning);
monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor));
monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1);
*monitor_mode_spec = *meta_monitor_mode_get_spec (monitor_mode);
monitor_config = g_new0 (MetaMonitorConfig, 1);
*monitor_config = (MetaMonitorConfig) {
.monitor_spec = monitor_spec,
.mode_spec = monitor_mode_spec,
.enable_underscanning = enable_underscanning
};
return monitor_config;
}
static gboolean
find_monitor_mode_scale (MetaMonitorManager *manager,
MetaLogicalMonitorLayoutMode layout_mode,
MetaMonitorConfig *monitor_config,
float scale,
float *out_scale,
GError **error)
{
MetaMonitorSpec *monitor_spec;
MetaMonitor *monitor;
MetaMonitorModeSpec *monitor_mode_spec;
MetaMonitorMode *monitor_mode;
g_autofree float *supported_scales = NULL;
int n_supported_scales;
int i;
monitor_spec = monitor_config->monitor_spec;
monitor = meta_monitor_manager_get_monitor_from_spec (manager,
monitor_spec);
if (!monitor)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Monitor not found");
return FALSE;
}
monitor_mode_spec = monitor_config->mode_spec;
monitor_mode = meta_monitor_get_mode_from_spec (monitor,
monitor_mode_spec);
if (!monitor_mode)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Monitor mode not found");
return FALSE;
}
supported_scales =
meta_monitor_manager_calculate_supported_scales (manager, layout_mode,
monitor, monitor_mode,
&n_supported_scales);
for (i = 0; i < n_supported_scales; i++)
{
float supported_scale = supported_scales[i];
if (fabsf (supported_scale - scale) < FLT_EPSILON)
{
*out_scale = supported_scale;
return TRUE;
}
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Scale %g not valid for resolution %dx%d",
scale,
monitor_mode_spec->width,
monitor_mode_spec->height);
return FALSE;
}
static gboolean
derive_logical_monitor_size (MetaMonitorConfig *monitor_config,
int *out_width,
int *out_height,
float scale,
MetaMonitorTransform transform,
MetaLogicalMonitorLayoutMode layout_mode,
GError **error)
{
int width, height;
if (meta_monitor_transform_is_rotated (transform))
{
width = monitor_config->mode_spec->height;
height = monitor_config->mode_spec->width;
}
else
{
width = monitor_config->mode_spec->width;
height = monitor_config->mode_spec->height;
}
switch (layout_mode)
{
case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
width = roundf (width / scale);
height = roundf (height / scale);
break;
case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
break;
}
*out_width = width;
*out_height = height;
return TRUE;
}
static MetaLogicalMonitorConfig *
create_logical_monitor_config_from_variant (MetaMonitorManager *manager,
GVariant *logical_monitor_config_variant,
MetaLogicalMonitorLayoutMode layout_mode,
GError **error)
{
MetaLogicalMonitorConfig *logical_monitor_config;
int x, y, width, height;
double scale_d;
float scale;
MetaMonitorTransform transform;
gboolean is_primary;
GVariantIter *monitor_configs_iter;
GList *monitor_configs = NULL;
MetaMonitorConfig *monitor_config;
g_variant_get (logical_monitor_config_variant, LOGICAL_MONITOR_CONFIG_FORMAT,
&x,
&y,
&scale_d,
&transform,
&is_primary,
&monitor_configs_iter);
scale = (float) scale_d;
while (TRUE)
{
GVariant *monitor_config_variant =
g_variant_iter_next_value (monitor_configs_iter);
MetaMonitorConfig *monitor_config;
if (!monitor_config_variant)
break;
monitor_config =
create_monitor_config_from_variant (manager,
monitor_config_variant, error);
g_variant_unref (monitor_config_variant);
if (!monitor_config)
goto err;
if (!meta_verify_monitor_config (monitor_config, error))
{
meta_monitor_config_free (monitor_config);
goto err;
}
monitor_configs = g_list_append (monitor_configs, monitor_config);
}
g_variant_iter_free (monitor_configs_iter);
if (!monitor_configs)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Empty logical monitor");
goto err;
}
monitor_config = monitor_configs->data;
if (!find_monitor_mode_scale (manager,
layout_mode,
monitor_config,
scale,
&scale,
error))
goto err;
if (!derive_logical_monitor_size (monitor_config, &width, &height,
scale, transform, layout_mode, error))
goto err;
logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
*logical_monitor_config = (MetaLogicalMonitorConfig) {
.layout = {
.x = x,
.y = y,
.width = width,
.height = height
},
.transform = transform,
.scale = scale,
.is_primary = is_primary,
.monitor_configs = monitor_configs
};
if (!meta_verify_logical_monitor_config (logical_monitor_config,
layout_mode,
manager,
error))
{
meta_logical_monitor_config_free (logical_monitor_config);
return NULL;
}
return logical_monitor_config;
err:
g_list_free_full (monitor_configs, (GDestroyNotify) meta_monitor_config_free);
return NULL;
}
static gboolean
is_valid_layout_mode (MetaLogicalMonitorLayoutMode layout_mode)
{
switch (layout_mode)
{
case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
return TRUE;
}
return FALSE;
}
static gboolean
meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation,
guint serial,
guint method,
GVariant *logical_monitor_configs_variant,
GVariant *properties_variant)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
MetaMonitorManagerCapability capabilities;
GVariant *layout_mode_variant = NULL;
MetaLogicalMonitorLayoutMode layout_mode;
GVariantIter logical_monitor_configs_iter;
MetaMonitorsConfig *config;
GList *logical_monitor_configs = NULL;
GError *error = NULL;
if (serial != manager->serial)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"The requested configuration is based on stale information");
return TRUE;
}
capabilities = meta_monitor_manager_get_capabilities (manager);
if (properties_variant)
layout_mode_variant = g_variant_lookup_value (properties_variant,
"layout-mode",
G_VARIANT_TYPE ("u"));
if (layout_mode_variant &&
capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)
{
g_variant_get (layout_mode_variant, "u", &layout_mode);
}
else if (!layout_mode_variant)
{
layout_mode =
meta_monitor_manager_get_default_layout_mode (manager);
}
else
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Can't set layout mode");
return TRUE;
}
if (!is_valid_layout_mode (layout_mode))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Invalid layout mode specified");
return TRUE;
}
g_variant_iter_init (&logical_monitor_configs_iter,
logical_monitor_configs_variant);
while (TRUE)
{
GVariant *logical_monitor_config_variant =
g_variant_iter_next_value (&logical_monitor_configs_iter);
MetaLogicalMonitorConfig *logical_monitor_config;
if (!logical_monitor_config_variant)
break;
logical_monitor_config =
create_logical_monitor_config_from_variant (manager,
logical_monitor_config_variant,
layout_mode,
&error);
g_variant_unref (logical_monitor_config_variant);
if (!logical_monitor_config)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"%s", error->message);
g_error_free (error);
g_list_free_full (logical_monitor_configs,
(GDestroyNotify) meta_logical_monitor_config_free);
return TRUE;
}
logical_monitor_configs = g_list_append (logical_monitor_configs,
logical_monitor_config);
}
config = meta_monitors_config_new (manager,
logical_monitor_configs,
layout_mode,
META_MONITORS_CONFIG_FLAG_NONE);
if (!meta_verify_monitors_config (config, manager, &error))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"%s", error->message);
g_error_free (error);
g_object_unref (config);
return TRUE;
}
if (!meta_monitor_manager_is_config_applicable (manager, config, &error))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"%s", error->message);
g_error_free (error);
g_object_unref (config);
return TRUE;
}
if (manager->persistent_timeout_id &&
method != META_MONITORS_CONFIG_METHOD_VERIFY)
cancel_persistent_confirmation (manager);
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
method,
&error))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"%s", error->message);
g_error_free (error);
g_object_unref (config);
return TRUE;
}
if (method == META_MONITORS_CONFIG_METHOD_PERSISTENT)
request_persistent_confirmation (manager);
meta_dbus_display_config_complete_apply_monitors_config (skeleton, invocation);
return TRUE;
}
#undef MONITOR_MODE_SPEC_FORMAT
#undef MONITOR_CONFIG_FORMAT
#undef MONITOR_CONFIGS_FORMAT
#undef LOGICAL_MONITOR_CONFIG_FORMAT
static void
confirm_configuration (MetaMonitorManager *manager,
gboolean confirmed)
{
if (confirmed)
meta_monitor_config_manager_save_current (manager->config_manager);
else
restore_previous_config (manager);
}
void
meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
gboolean ok)
{
if (!manager->persistent_timeout_id)
{
/* too late */
return;
}
cancel_persistent_confirmation (manager);
confirm_configuration (manager, ok);
}
static gboolean
meta_monitor_manager_handle_change_backlight (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation,
guint serial,
guint output_index,
gint value)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
GList *combined_outputs;
MetaOutput *output;
if (serial != manager->serial)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"The requested configuration is based on stale information");
return TRUE;
}
combined_outputs = combine_gpu_lists (manager, meta_gpu_get_outputs);
if (output_index >= g_list_length (combined_outputs))
{
g_list_free (combined_outputs);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid output id");
return TRUE;
}
output = g_list_nth_data (combined_outputs, output_index);
g_list_free (combined_outputs);
if (value < 0 || value > 100)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid backlight value");
return TRUE;
}
if (output->backlight == -1 ||
(output->backlight_min == 0 && output->backlight_max == 0))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Output does not support changing backlight");
return TRUE;
}
META_MONITOR_MANAGER_GET_CLASS (manager)->change_backlight (manager, output, value);
meta_dbus_display_config_complete_change_backlight (skeleton, invocation, output->backlight);
return TRUE;
}
static gboolean
meta_monitor_manager_handle_get_crtc_gamma (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation,
guint serial,
guint crtc_id)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
MetaMonitorManagerClass *klass;
GList *combined_crtcs;
MetaCrtc *crtc;
gsize size;
unsigned short *red;
unsigned short *green;
unsigned short *blue;
GBytes *red_bytes, *green_bytes, *blue_bytes;
GVariant *red_v, *green_v, *blue_v;
if (serial != manager->serial)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"The requested configuration is based on stale information");
return TRUE;
}
combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs);
if (crtc_id >= g_list_length (combined_crtcs))
{
g_list_free (combined_crtcs);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid crtc id");
return TRUE;
}
crtc = g_list_nth_data (combined_crtcs, crtc_id);
g_list_free (combined_crtcs);
klass = META_MONITOR_MANAGER_GET_CLASS (manager);
if (klass->get_crtc_gamma)
klass->get_crtc_gamma (manager, crtc, &size, &red, &green, &blue);
else
{
size = 0;
red = green = blue = NULL;
}
red_bytes = g_bytes_new_take (red, size * sizeof (unsigned short));
green_bytes = g_bytes_new_take (green, size * sizeof (unsigned short));
blue_bytes = g_bytes_new_take (blue, size * sizeof (unsigned short));
red_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), red_bytes, TRUE);
green_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), green_bytes, TRUE);
blue_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), blue_bytes, TRUE);
meta_dbus_display_config_complete_get_crtc_gamma (skeleton, invocation,
red_v, green_v, blue_v);
g_bytes_unref (red_bytes);
g_bytes_unref (green_bytes);
g_bytes_unref (blue_bytes);
return TRUE;
}
static gboolean
meta_monitor_manager_handle_set_crtc_gamma (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation,
guint serial,
guint crtc_id,
GVariant *red_v,
GVariant *green_v,
GVariant *blue_v)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
MetaMonitorManagerClass *klass;
GList *combined_crtcs;
MetaCrtc *crtc;
gsize size, dummy;
unsigned short *red;
unsigned short *green;
unsigned short *blue;
GBytes *red_bytes, *green_bytes, *blue_bytes;
if (serial != manager->serial)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"The requested configuration is based on stale information");
return TRUE;
}
combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs);
if (crtc_id >= g_list_length (combined_crtcs))
{
g_list_free (combined_crtcs);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid crtc id");
return TRUE;
}
crtc = g_list_nth_data (combined_crtcs, crtc_id);
g_list_free (combined_crtcs);
red_bytes = g_variant_get_data_as_bytes (red_v);
green_bytes = g_variant_get_data_as_bytes (green_v);
blue_bytes = g_variant_get_data_as_bytes (blue_v);
size = g_bytes_get_size (red_bytes) / sizeof (unsigned short);
red = (unsigned short*) g_bytes_get_data (red_bytes, &dummy);
green = (unsigned short*) g_bytes_get_data (green_bytes, &dummy);
blue = (unsigned short*) g_bytes_get_data (blue_bytes, &dummy);
klass = META_MONITOR_MANAGER_GET_CLASS (manager);
if (klass->set_crtc_gamma)
klass->set_crtc_gamma (manager, crtc, size, red, green, blue);
meta_dbus_display_config_complete_set_crtc_gamma (skeleton, invocation);
g_bytes_unref (red_bytes);
g_bytes_unref (green_bytes);
g_bytes_unref (blue_bytes);
return TRUE;
}
static void
meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface)
{
iface->handle_get_resources = meta_monitor_manager_handle_get_resources;
iface->handle_change_backlight = meta_monitor_manager_handle_change_backlight;
iface->handle_get_crtc_gamma = meta_monitor_manager_handle_get_crtc_gamma;
iface->handle_set_crtc_gamma = meta_monitor_manager_handle_set_crtc_gamma;
iface->handle_get_current_state = meta_monitor_manager_handle_get_current_state;
iface->handle_apply_monitors_config = meta_monitor_manager_handle_apply_monitors_config;
}
static void
on_bus_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
MetaMonitorManager *manager = user_data;
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager),
connection,
"/org/gnome/Mutter/DisplayConfig",
NULL);
}
static void
on_name_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
meta_topic (META_DEBUG_DBUS, "Acquired name %s\n", name);
}
static void
on_name_lost (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
meta_topic (META_DEBUG_DBUS, "Lost or failed to acquire name %s\n", name);
}
static void
initialize_dbus_interface (MetaMonitorManager *manager)
{
manager->dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.gnome.Mutter.DisplayConfig",
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
(meta_get_replace_current_wm () ?
G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
on_bus_acquired,
on_name_acquired,
on_name_lost,
g_object_ref (manager),
g_object_unref);
}
/**
* meta_monitor_manager_get:
*
* Accessor for the singleton MetaMonitorManager.
*
* Returns: (transfer none): The only #MetaMonitorManager there is.
*/
MetaMonitorManager *
meta_monitor_manager_get (void)
{
MetaBackend *backend = meta_get_backend ();
return meta_backend_get_monitor_manager (backend);
}
int
meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager)
{
return g_list_length (manager->logical_monitors);
}
GList *
meta_monitor_manager_get_logical_monitors (MetaMonitorManager *manager)
{
return manager->logical_monitors;
}
MetaLogicalMonitor *
meta_monitor_manager_get_logical_monitor_from_number (MetaMonitorManager *manager,
int number)
{
g_return_val_if_fail ((unsigned int) number < g_list_length (manager->logical_monitors), NULL);
return g_list_nth (manager->logical_monitors, number)->data;
}
MetaLogicalMonitor *
meta_monitor_manager_get_primary_logical_monitor (MetaMonitorManager *manager)
{
return manager->primary_logical_monitor;
}
static MetaMonitor *
find_monitor (MetaMonitorManager *monitor_manager,
gboolean (*match_func) (MetaMonitor *monitor))
{
GList *monitors;
GList *l;
monitors = meta_monitor_manager_get_monitors (monitor_manager);
for (l = monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (match_func (monitor))
return monitor;
}
return NULL;
}
MetaMonitor *
meta_monitor_manager_get_primary_monitor (MetaMonitorManager *manager)
{
return find_monitor (manager, meta_monitor_is_primary);
}
MetaMonitor *
meta_monitor_manager_get_laptop_panel (MetaMonitorManager *manager)
{
return find_monitor (manager, meta_monitor_is_laptop_panel);
}
MetaMonitor *
meta_monitor_manager_get_monitor_from_connector (MetaMonitorManager *manager,
const char *connector)
{
GList *l;
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (g_str_equal (meta_monitor_get_connector (monitor),
connector))
return monitor;
}
return NULL;
}
MetaMonitor *
meta_monitor_manager_get_monitor_from_spec (MetaMonitorManager *manager,
MetaMonitorSpec *monitor_spec)
{
GList *l;
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (meta_monitor_spec_equals (meta_monitor_get_spec (monitor),
monitor_spec))
return monitor;
}
return NULL;
}
MetaLogicalMonitor *
meta_monitor_manager_get_logical_monitor_at (MetaMonitorManager *manager,
float x,
float y)
{
GList *l;
for (l = manager->logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
if (POINT_IN_RECT (x, y, logical_monitor->rect))
return logical_monitor;
}
return NULL;
}
MetaLogicalMonitor *
meta_monitor_manager_get_logical_monitor_from_rect (MetaMonitorManager *manager,
MetaRectangle *rect)
{
MetaLogicalMonitor *best_logical_monitor;
int best_logical_monitor_area;
GList *l;
best_logical_monitor = NULL;
best_logical_monitor_area = 0;
for (l = manager->logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
MetaRectangle intersection;
int intersection_area;
if (!meta_rectangle_intersect (&logical_monitor->rect,
rect,
&intersection))
continue;
intersection_area = meta_rectangle_area (&intersection);
if (intersection_area > best_logical_monitor_area)
{
best_logical_monitor = logical_monitor;
best_logical_monitor_area = intersection_area;
}
}
if (!best_logical_monitor && (rect->width == 0 || rect->height == 0))
best_logical_monitor =
meta_monitor_manager_get_logical_monitor_at (manager, rect->x, rect->y);
if (!best_logical_monitor)
best_logical_monitor = manager->primary_logical_monitor;
return best_logical_monitor;
}
MetaLogicalMonitor *
meta_monitor_manager_get_logical_monitor_neighbor (MetaMonitorManager *manager,
MetaLogicalMonitor *logical_monitor,
MetaScreenDirection direction)
{
GList *l;
for (l = manager->logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *other = l->data;
if (meta_logical_monitor_has_neighbor (logical_monitor, other, direction))
return other;
}
return NULL;
}
GList *
meta_monitor_manager_get_monitors (MetaMonitorManager *manager)
{
return manager->monitors;
}
void
meta_monitor_manager_add_gpu (MetaMonitorManager *manager,
MetaGpu *gpu)
{
manager->gpus = g_list_append (manager->gpus, gpu);
}
GList *
meta_monitor_manager_get_gpus (MetaMonitorManager *manager)
{
return manager->gpus;
}
void
meta_monitor_manager_get_screen_size (MetaMonitorManager *manager,
int *width,
int *height)
{
*width = manager->screen_width;
*height = manager->screen_height;
}
static void
rebuild_monitors (MetaMonitorManager *manager)
{
GList *l;
if (manager->monitors)
{
g_list_free_full (manager->monitors, g_object_unref);
manager->monitors = NULL;
}
for (l = manager->gpus; l; l = l->next)
{
MetaGpu *gpu = l->data;
GList *k;
for (k = meta_gpu_get_outputs (gpu); k; k = k->next)
{
MetaOutput *output = k->data;
if (output->tile_info.group_id)
{
if (is_main_tiled_monitor_output (output))
{
MetaMonitorTiled *monitor_tiled;
monitor_tiled = meta_monitor_tiled_new (gpu, output);
manager->monitors = g_list_append (manager->monitors,
monitor_tiled);
}
}
else
{
MetaMonitorNormal *monitor_normal;
monitor_normal = meta_monitor_normal_new (gpu, output);
manager->monitors = g_list_append (manager->monitors,
monitor_normal);
}
}
}
}
void
meta_monitor_manager_tiled_monitor_added (MetaMonitorManager *manager,
MetaMonitor *monitor)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
if (manager_class->tiled_monitor_added)
manager_class->tiled_monitor_added (manager, monitor);
}
void
meta_monitor_manager_tiled_monitor_removed (MetaMonitorManager *manager,
MetaMonitor *monitor)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
if (manager_class->tiled_monitor_removed)
manager_class->tiled_monitor_removed (manager, monitor);
}
gboolean
meta_monitor_manager_is_transform_handled (MetaMonitorManager *manager,
MetaCrtc *crtc,
MetaMonitorTransform transform)
{
MetaMonitorManagerClass *manager_class =
META_MONITOR_MANAGER_GET_CLASS (manager);
return manager_class->is_transform_handled (manager, crtc, transform);
}
void
meta_monitor_manager_read_current_state (MetaMonitorManager *manager)
{
GList *l;
manager->serial++;
for (l = manager->gpus; l; l = l->next)
{
MetaGpu *gpu = l->data;
GError *error = NULL;
if (!meta_gpu_read_current (gpu, &error))
{
g_warning ("Failed to read current KMS state: %s", error->message);
g_clear_error (&error);
}
}
rebuild_monitors (manager);
}
static void
meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager)
{
manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN;
meta_backend_monitors_changed (manager->backend);
g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0);
g_signal_emit_by_name (manager, "monitors-changed");
}
static void
set_logical_monitor_modes (MetaMonitorManager *manager,
MetaLogicalMonitorConfig *logical_monitor_config)
{
GList *l;
for (l = logical_monitor_config->monitor_configs; l; l = l->next)
{
MetaMonitorConfig *monitor_config = l->data;
MetaMonitorSpec *monitor_spec;
MetaMonitor *monitor;
MetaMonitorModeSpec *monitor_mode_spec;
MetaMonitorMode *monitor_mode;
monitor_spec = monitor_config->monitor_spec;
monitor = meta_monitor_manager_get_monitor_from_spec (manager,
monitor_spec);
monitor_mode_spec = monitor_config->mode_spec;
monitor_mode = meta_monitor_get_mode_from_spec (monitor,
monitor_mode_spec);
meta_monitor_set_current_mode (monitor, monitor_mode);
}
}
static void
meta_monitor_manager_update_monitor_modes (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *logical_monitor_configs;
GList *l;
g_list_foreach (manager->monitors,
(GFunc) meta_monitor_set_current_mode,
NULL);
logical_monitor_configs = config ? config->logical_monitor_configs : NULL;
for (l = logical_monitor_configs; l; l = l->next)
{
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
set_logical_monitor_modes (manager, logical_monitor_config);
}
}
void
meta_monitor_manager_update_logical_state (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
if (config)
manager->layout_mode = config->layout_mode;
else
manager->layout_mode =
meta_monitor_manager_get_default_layout_mode (manager);
meta_monitor_manager_rebuild_logical_monitors (manager, config);
}
void
meta_monitor_manager_rebuild (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *old_logical_monitors;
meta_monitor_manager_update_monitor_modes (manager, config);
if (manager->in_init)
return;
old_logical_monitors = manager->logical_monitors;
meta_monitor_manager_update_logical_state (manager, config);
meta_monitor_manager_notify_monitors_changed (manager);
g_list_free_full (old_logical_monitors, g_object_unref);
}
static void
meta_monitor_manager_update_monitor_modes_derived (MetaMonitorManager *manager)
{
GList *l;
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
meta_monitor_derive_current_mode (monitor);
}
}
void
meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
manager->layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL;
meta_monitor_manager_rebuild_logical_monitors_derived (manager, config);
}
void
meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager,
MetaMonitorsConfig *config)
{
GList *old_logical_monitors;
meta_monitor_manager_update_monitor_modes_derived (manager);
if (manager->in_init)
return;
old_logical_monitors = manager->logical_monitors;
meta_monitor_manager_update_logical_state_derived (manager, config);
meta_monitor_manager_notify_monitors_changed (manager);
g_list_free_full (old_logical_monitors, g_object_unref);
}
void
meta_output_parse_edid (MetaOutput *output,
GBytes *edid)
{
MonitorInfo *parsed_edid;
gsize len;
if (!edid)
goto out;
parsed_edid = decode_edid (g_bytes_get_data (edid, &len));
if (parsed_edid)
{
output->vendor = g_strndup (parsed_edid->manufacturer_code, 4);
if (!g_utf8_validate (output->vendor, -1, NULL))
g_clear_pointer (&output->vendor, g_free);
output->product = g_strndup (parsed_edid->dsc_product_name, 14);
if (!g_utf8_validate (output->product, -1, NULL) ||
output->product[0] == '\0')
{
g_clear_pointer (&output->product, g_free);
output->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code);
}
output->serial = g_strndup (parsed_edid->dsc_serial_number, 14);
if (!g_utf8_validate (output->serial, -1, NULL) ||
output->serial[0] == '\0')
{
g_clear_pointer (&output->serial, g_free);
output->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
}
g_free (parsed_edid);
}
out:
if (!output->vendor)
output->vendor = g_strdup ("unknown");
if (!output->product)
output->product = g_strdup ("unknown");
if (!output->serial)
output->serial = g_strdup ("unknown");
}
gboolean
meta_output_is_laptop (MetaOutput *output)
{
/* FIXME: extend with better heuristics */
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_manager_on_hotplug (MetaMonitorManager *manager)
{
meta_monitor_manager_ensure_configured (manager);
}
static gboolean
calculate_viewport_matrix (MetaMonitorManager *manager,
MetaLogicalMonitor *logical_monitor,
gfloat viewport[6])
{
gfloat x, y, width, height;
x = (float) logical_monitor->rect.x / manager->screen_width;
y = (float) logical_monitor->rect.y / manager->screen_height;
width = (float) logical_monitor->rect.width / manager->screen_width;
height = (float) logical_monitor->rect.height / manager->screen_height;
viewport[0] = width;
viewport[1] = 0.0f;
viewport[2] = x;
viewport[3] = 0.0f;
viewport[4] = height;
viewport[5] = y;
return TRUE;
}
static inline void
multiply_matrix (float a[6],
float b[6],
float res[6])
{
res[0] = a[0] * b[0] + a[1] * b[3];
res[1] = a[0] * b[1] + a[1] * b[4];
res[2] = a[0] * b[2] + a[1] * b[5] + a[2];
res[3] = a[3] * b[0] + a[4] * b[3];
res[4] = a[3] * b[1] + a[4] * b[4];
res[5] = a[3] * b[2] + a[4] * b[5] + a[5];
}
gboolean
meta_monitor_manager_get_monitor_matrix (MetaMonitorManager *manager,
MetaLogicalMonitor *logical_monitor,
gfloat matrix[6])
{
MetaMonitorTransform transform;
gfloat viewport[9];
if (!calculate_viewport_matrix (manager, logical_monitor, viewport))
return FALSE;
transform = logical_monitor->transform;
multiply_matrix (viewport, transform_matrices[transform],
matrix);
return TRUE;
}
/**
* meta_monitor_manager_get_monitor_for_connector:
* @manager: A #MetaMonitorManager
* @connector: A valid connector name
*
* Returns: The monitor index or -1 if @id isn't valid or the connector
* isn't associated with a logical monitor.
*/
gint
meta_monitor_manager_get_monitor_for_connector (MetaMonitorManager *manager,
const char *connector)
{
GList *l;
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (meta_monitor_is_active (monitor) &&
g_str_equal (connector, meta_monitor_get_connector (monitor)))
{
MetaOutput *main_output = meta_monitor_get_main_output (monitor);
return main_output->crtc->logical_monitor->number;
}
}
return -1;
}
gboolean
meta_monitor_manager_get_is_builtin_display_on (MetaMonitorManager *manager)
{
MetaMonitor *laptop_panel;
g_return_val_if_fail (META_IS_MONITOR_MANAGER (manager), FALSE);
laptop_panel = meta_monitor_manager_get_laptop_panel (manager);
if (!laptop_panel)
return FALSE;
return meta_monitor_is_active (laptop_panel);
}
void
meta_monitor_manager_rotate_monitor (MetaMonitorManager *manager)
{
GError *error = NULL;
MetaMonitorsConfig *config =
meta_monitor_config_manager_create_for_rotate_monitor (manager->config_manager);
if (!config)
return;
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
META_MONITORS_CONFIG_METHOD_TEMPORARY,
&error))
{
g_warning ("Failed to use rotate monitor configuration: %s",
error->message);
g_error_free (error);
}
g_object_unref (config);
}
void
meta_monitor_manager_switch_config (MetaMonitorManager *manager,
MetaMonitorSwitchConfigType config_type)
{
GError *error = NULL;
MetaMonitorsConfig *config;
g_return_if_fail (config_type != META_MONITOR_SWITCH_CONFIG_UNKNOWN);
config =
meta_monitor_config_manager_create_for_switch_config (manager->config_manager,
config_type);
if (!config)
return;
if (!meta_monitor_manager_apply_monitors_config (manager,
config,
META_MONITORS_CONFIG_METHOD_TEMPORARY,
&error))
{
g_warning ("Failed to use switch monitor configuration: %s",
error->message);
g_error_free (error);
}
else
{
manager->current_switch_config = config_type;
}
g_object_unref (config);
}
gboolean
meta_monitor_manager_can_switch_config (MetaMonitorManager *manager)
{
return (!meta_monitor_manager_is_lid_closed (manager) &&
g_list_length (manager->monitors) > 1);
}
MetaMonitorSwitchConfigType
meta_monitor_manager_get_switch_config (MetaMonitorManager *manager)
{
return manager->current_switch_config;
}
MetaMonitorConfigManager *
meta_monitor_manager_get_config_manager (MetaMonitorManager *manager)
{
return manager->config_manager;
}