mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 16:40:41 -05:00
d804ecbd95
Only the first output of the first monitor of the primary logical monitor should be made primary. This fixes an issue where the wrong logical monitor ended up as primary when the logical state was derived. https://bugzilla.gnome.org/show_bug.cgi?id=777732
1082 lines
32 KiB
C
1082 lines
32 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-config-manager.h"
|
|
|
|
#include "backends/meta-monitor-config-store.h"
|
|
#include "backends/meta-monitor-manager-private.h"
|
|
#include "core/boxes-private.h"
|
|
|
|
struct _MetaMonitorConfigManager
|
|
{
|
|
GObject parent;
|
|
|
|
MetaMonitorManager *monitor_manager;
|
|
|
|
MetaMonitorConfigStore *config_store;
|
|
|
|
MetaMonitorsConfig *current_config;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaMonitorConfigManager, meta_monitor_config_manager,
|
|
G_TYPE_OBJECT)
|
|
|
|
G_DEFINE_TYPE (MetaMonitorsConfig, meta_monitors_config,
|
|
G_TYPE_OBJECT)
|
|
|
|
MetaMonitorConfigManager *
|
|
meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager)
|
|
{
|
|
MetaMonitorConfigManager *config_manager;
|
|
|
|
config_manager = g_object_new (META_TYPE_MONITOR_CONFIG_MANAGER, NULL);
|
|
config_manager->monitor_manager = monitor_manager;
|
|
config_manager->config_store =
|
|
meta_monitor_config_store_new (monitor_manager);
|
|
|
|
return config_manager;
|
|
}
|
|
|
|
MetaMonitorConfigStore *
|
|
meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
return config_manager->config_store;
|
|
}
|
|
|
|
static gboolean
|
|
is_crtc_assigned (MetaCrtc *crtc,
|
|
GPtrArray *crtc_infos)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < crtc_infos->len; i++)
|
|
{
|
|
MetaCrtcInfo *assigned_crtc_info = g_ptr_array_index (crtc_infos, i);
|
|
|
|
if (assigned_crtc_info->crtc == crtc)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static MetaCrtc *
|
|
find_unassigned_crtc (MetaOutput *output,
|
|
GPtrArray *crtc_infos)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < output->n_possible_crtcs; i++)
|
|
{
|
|
MetaCrtc *crtc = output->possible_crtcs[i];
|
|
|
|
if (is_crtc_assigned (crtc, crtc_infos))
|
|
continue;
|
|
|
|
return crtc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MetaMonitorManager *monitor_manager;
|
|
MetaLogicalMonitorConfig *logical_monitor_config;
|
|
MetaMonitorConfig *monitor_config;
|
|
GPtrArray *crtc_infos;
|
|
GPtrArray *output_infos;
|
|
} MonitorAssignmentData;
|
|
|
|
static gboolean
|
|
assign_monitor_crtc (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode,
|
|
MetaMonitorCrtcMode *monitor_crtc_mode,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
MonitorAssignmentData *data = user_data;
|
|
MetaOutput *output;
|
|
MetaCrtc *crtc;
|
|
MetaMonitorTransform transform;
|
|
MetaMonitorTransform crtc_transform;
|
|
int crtc_x, crtc_y;
|
|
MetaCrtcInfo *crtc_info;
|
|
MetaOutputInfo *output_info;
|
|
MetaMonitorConfig *first_monitor_config;
|
|
gboolean assign_output_as_primary;
|
|
gboolean assign_output_as_presentation;
|
|
|
|
output = monitor_crtc_mode->output;
|
|
|
|
crtc = find_unassigned_crtc (output, data->crtc_infos);
|
|
if (!crtc)
|
|
{
|
|
MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor);
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"No available CRTC for monitor '%s %s' not found",
|
|
monitor_spec->vendor, monitor_spec->product);
|
|
return FALSE;
|
|
}
|
|
|
|
transform = data->logical_monitor_config->transform;
|
|
if (meta_monitor_manager_is_transform_handled (data->monitor_manager,
|
|
crtc,
|
|
transform))
|
|
crtc_transform = transform;
|
|
else
|
|
crtc_transform = META_MONITOR_TRANSFORM_NORMAL;
|
|
|
|
meta_monitor_calculate_crtc_pos (monitor, mode, output, crtc_transform,
|
|
&crtc_x, &crtc_y);
|
|
|
|
crtc_info = g_slice_new0 (MetaCrtcInfo);
|
|
*crtc_info = (MetaCrtcInfo) {
|
|
.crtc = crtc,
|
|
.mode = monitor_crtc_mode->crtc_mode,
|
|
.x = crtc_x,
|
|
.y = crtc_y,
|
|
.transform = crtc_transform,
|
|
.outputs = g_ptr_array_new ()
|
|
};
|
|
g_ptr_array_add (crtc_info->outputs, output);
|
|
|
|
/*
|
|
* Currently, MetaCrtcInfo are deliberately offset incorrectly to carry over
|
|
* logical monitor location inside the MetaCrtc struct, when in fact this
|
|
* depends on the framebuffer configuration. This will eventually be negated
|
|
* when setting the actual KMS mode.
|
|
*
|
|
* TODO: Remove this hack when we don't need to rely on MetaCrtc to pass
|
|
* logical monitor state.
|
|
*/
|
|
crtc_info->x += data->logical_monitor_config->layout.x;
|
|
crtc_info->y += data->logical_monitor_config->layout.y;
|
|
|
|
/*
|
|
* Only one output can be marked as primary (due to Xrandr limitation),
|
|
* so only mark the main output of the first monitor in the logical monitor
|
|
* as such.
|
|
*/
|
|
first_monitor_config = data->logical_monitor_config->monitor_configs->data;
|
|
if (data->logical_monitor_config->is_primary &&
|
|
data->monitor_config == first_monitor_config &&
|
|
meta_monitor_get_main_output (monitor) == output)
|
|
assign_output_as_primary = TRUE;
|
|
else
|
|
assign_output_as_primary = FALSE;
|
|
|
|
if (data->logical_monitor_config->is_presentation)
|
|
assign_output_as_presentation = TRUE;
|
|
else
|
|
assign_output_as_presentation = FALSE;
|
|
|
|
output_info = g_slice_new0 (MetaOutputInfo);
|
|
*output_info = (MetaOutputInfo) {
|
|
.output = output,
|
|
.is_primary = assign_output_as_primary,
|
|
.is_presentation = assign_output_as_presentation,
|
|
.is_underscanning = data->monitor_config->enable_underscanning
|
|
};
|
|
|
|
g_ptr_array_add (data->crtc_infos, crtc_info);
|
|
g_ptr_array_add (data->output_infos, output_info);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
assign_monitor_crtcs (MetaMonitorManager *manager,
|
|
MetaLogicalMonitorConfig *logical_monitor_config,
|
|
MetaMonitorConfig *monitor_config,
|
|
GPtrArray *crtc_infos,
|
|
GPtrArray *output_infos,
|
|
GError **error)
|
|
{
|
|
MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec;
|
|
MetaMonitorModeSpec *monitor_mode_spec = monitor_config->mode_spec;
|
|
MetaMonitor *monitor;
|
|
MetaMonitorMode *monitor_mode;
|
|
MonitorAssignmentData data;
|
|
|
|
monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec);
|
|
if (!monitor)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Configured monitor '%s %s' not found",
|
|
monitor_spec->vendor, monitor_spec->product);
|
|
return FALSE;
|
|
}
|
|
|
|
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,
|
|
"Invalid mode %dx%d (%f) for monitor '%s %s'",
|
|
monitor_mode_spec->width, monitor_mode_spec->height,
|
|
monitor_mode_spec->refresh_rate,
|
|
monitor_spec->vendor, monitor_spec->product);
|
|
return FALSE;
|
|
}
|
|
|
|
data = (MonitorAssignmentData) {
|
|
.monitor_manager = manager,
|
|
.logical_monitor_config = logical_monitor_config,
|
|
.monitor_config = monitor_config,
|
|
.crtc_infos = crtc_infos,
|
|
.output_infos = output_infos
|
|
};
|
|
if (!meta_monitor_mode_foreach_crtc (monitor, monitor_mode,
|
|
assign_monitor_crtc,
|
|
&data,
|
|
error))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
assign_logical_monitor_crtcs (MetaMonitorManager *manager,
|
|
MetaLogicalMonitorConfig *logical_monitor_config,
|
|
GPtrArray *crtc_infos,
|
|
GPtrArray *output_infos,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = logical_monitor_config->monitor_configs; l; l = l->next)
|
|
{
|
|
MetaMonitorConfig *monitor_config = l->data;
|
|
|
|
if (!assign_monitor_crtcs (manager,
|
|
logical_monitor_config,
|
|
monitor_config,
|
|
crtc_infos, output_infos,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitor_config_manager_assign (MetaMonitorManager *manager,
|
|
MetaMonitorsConfig *config,
|
|
GPtrArray **out_crtc_infos,
|
|
GPtrArray **out_output_infos,
|
|
GError **error)
|
|
{
|
|
GPtrArray *crtc_infos;
|
|
GPtrArray *output_infos;
|
|
GList *l;
|
|
|
|
crtc_infos =
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) meta_crtc_info_free);
|
|
output_infos =
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) meta_output_info_free);
|
|
|
|
for (l = config->logical_monitor_configs; l; l = l->next)
|
|
{
|
|
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
|
|
|
|
if (!assign_logical_monitor_crtcs (manager, logical_monitor_config,
|
|
crtc_infos, output_infos,
|
|
error))
|
|
{
|
|
g_ptr_array_free (crtc_infos, TRUE);
|
|
g_ptr_array_free (output_infos, TRUE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*out_crtc_infos = crtc_infos;
|
|
*out_output_infos = output_infos;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaMonitorsConfigKey *
|
|
create_key_for_current_state (MetaMonitorManager *monitor_manager)
|
|
{
|
|
MetaMonitorsConfigKey *config_key;
|
|
GList *l;
|
|
GList *monitor_specs;
|
|
|
|
monitor_specs = NULL;
|
|
for (l = monitor_manager->monitors; l; l = l->next)
|
|
{
|
|
MetaMonitor *monitor = l->data;
|
|
MetaMonitorSpec *monitor_spec;
|
|
|
|
if (meta_monitor_is_laptop_panel (monitor) &&
|
|
meta_monitor_manager_is_lid_closed (monitor_manager))
|
|
continue;
|
|
|
|
monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor));
|
|
monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
|
|
}
|
|
|
|
if (!monitor_specs)
|
|
return NULL;
|
|
|
|
monitor_specs = g_list_sort (monitor_specs,
|
|
(GCompareFunc) meta_monitor_spec_compare);
|
|
|
|
config_key = g_new0 (MetaMonitorsConfigKey, 1);
|
|
*config_key = (MetaMonitorsConfigKey) {
|
|
.monitor_specs = monitor_specs
|
|
};
|
|
|
|
return config_key;
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
MetaMonitorsConfigKey *config_key;
|
|
MetaMonitorsConfig *config;
|
|
|
|
config_key = create_key_for_current_state (config_manager->monitor_manager);
|
|
if (!config_key)
|
|
return NULL;
|
|
|
|
config = meta_monitor_config_store_lookup (config_manager->config_store,
|
|
config_key);
|
|
meta_monitors_config_key_free (config_key);
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef enum _MonitorMatchRule
|
|
{
|
|
MONITOR_MATCH_ALL = 0,
|
|
MONITOR_MATCH_EXTERNAL = (1 << 0)
|
|
} MonitorMatchRule;
|
|
|
|
static MetaMonitor *
|
|
find_monitor_with_highest_preferred_resolution (MetaMonitorManager *monitor_manager,
|
|
MonitorMatchRule match_rule)
|
|
{
|
|
GList *monitors;
|
|
GList *l;
|
|
int largest_area = 0;
|
|
MetaMonitor *largest_monitor = NULL;
|
|
|
|
monitors = meta_monitor_manager_get_monitors (monitor_manager);
|
|
for (l = monitors; l; l = l->next)
|
|
{
|
|
MetaMonitor *monitor = l->data;
|
|
MetaMonitorMode *mode;
|
|
int width, height;
|
|
int area;
|
|
|
|
if (match_rule & MONITOR_MATCH_EXTERNAL)
|
|
{
|
|
if (meta_monitor_is_laptop_panel (monitor))
|
|
continue;
|
|
}
|
|
|
|
mode = meta_monitor_get_preferred_mode (monitor);
|
|
meta_monitor_mode_get_resolution (mode, &width, &height);
|
|
area = width * height;
|
|
|
|
if (area > largest_area)
|
|
{
|
|
largest_area = area;
|
|
largest_monitor = monitor;
|
|
}
|
|
}
|
|
|
|
return largest_monitor;
|
|
}
|
|
|
|
/*
|
|
* Try to find the primary monitor. The priority of classification is:
|
|
*
|
|
* 1. Find the primary monitor as reported by the underlying system,
|
|
* 2. Find the laptop panel
|
|
* 3. Find the external monitor with highest resolution
|
|
*
|
|
* If the laptop lid is closed, exclude the laptop panel from possible
|
|
* alternatives, except if no other alternatives exist.
|
|
*/
|
|
static MetaMonitor *
|
|
find_primary_monitor (MetaMonitorManager *monitor_manager)
|
|
{
|
|
MetaMonitor *monitor;
|
|
|
|
if (meta_monitor_manager_is_lid_closed (monitor_manager))
|
|
{
|
|
monitor = meta_monitor_manager_get_primary_monitor (monitor_manager);
|
|
if (monitor && !meta_monitor_is_laptop_panel (monitor))
|
|
return monitor;
|
|
|
|
monitor =
|
|
find_monitor_with_highest_preferred_resolution (monitor_manager,
|
|
MONITOR_MATCH_EXTERNAL);
|
|
if (monitor)
|
|
return monitor;
|
|
|
|
return find_monitor_with_highest_preferred_resolution (monitor_manager,
|
|
MONITOR_MATCH_ALL);
|
|
}
|
|
else
|
|
{
|
|
monitor = meta_monitor_manager_get_primary_monitor (monitor_manager);
|
|
if (monitor)
|
|
return monitor;
|
|
|
|
monitor = meta_monitor_manager_get_laptop_panel (monitor_manager);
|
|
if (monitor)
|
|
return monitor;
|
|
|
|
return find_monitor_with_highest_preferred_resolution (monitor_manager,
|
|
MONITOR_MATCH_ALL);
|
|
}
|
|
}
|
|
|
|
static MetaMonitorConfig *
|
|
create_monitor_config (MetaMonitor *monitor,
|
|
MetaMonitorMode *mode)
|
|
{
|
|
MetaMonitorSpec *monitor_spec;
|
|
MetaMonitorModeSpec *mode_spec;
|
|
MetaMonitorConfig *monitor_config;
|
|
|
|
monitor_spec = meta_monitor_get_spec (monitor);
|
|
mode_spec = meta_monitor_mode_get_spec (mode);
|
|
|
|
monitor_config = g_new0 (MetaMonitorConfig, 1);
|
|
*monitor_config = (MetaMonitorConfig) {
|
|
.monitor_spec = meta_monitor_spec_clone (monitor_spec),
|
|
.mode_spec = g_memdup (mode_spec, sizeof (MetaMonitorModeSpec)),
|
|
.enable_underscanning = meta_monitor_is_underscanning (monitor)
|
|
};
|
|
|
|
return monitor_config;
|
|
}
|
|
|
|
static MetaLogicalMonitorConfig *
|
|
create_preferred_logical_monitor_config (MetaMonitorManager *monitor_manager,
|
|
MetaMonitor *monitor,
|
|
int x,
|
|
int y,
|
|
MetaLogicalMonitorLayoutMode layout_mode)
|
|
{
|
|
MetaMonitorMode *mode;
|
|
int width, height;
|
|
int scale;
|
|
MetaMonitorConfig *monitor_config;
|
|
MetaLogicalMonitorConfig *logical_monitor_config;
|
|
|
|
mode = meta_monitor_get_preferred_mode (monitor);
|
|
meta_monitor_mode_get_resolution (mode, &width, &height);
|
|
scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager,
|
|
monitor,
|
|
mode);
|
|
|
|
switch (layout_mode)
|
|
{
|
|
case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
|
|
width /= scale;
|
|
height /= scale;
|
|
break;
|
|
case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
|
|
break;
|
|
}
|
|
|
|
monitor_config = create_monitor_config (monitor, mode);
|
|
|
|
logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1);
|
|
*logical_monitor_config = (MetaLogicalMonitorConfig) {
|
|
.layout = (MetaRectangle) {
|
|
.x = x,
|
|
.y = y,
|
|
.width = width,
|
|
.height = height
|
|
},
|
|
.scale = scale,
|
|
.monitor_configs = g_list_append (NULL, monitor_config)
|
|
};
|
|
|
|
return logical_monitor_config;
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
|
|
GList *logical_monitor_configs;
|
|
MetaMonitor *primary_monitor;
|
|
MetaLogicalMonitorLayoutMode layout_mode;
|
|
MetaLogicalMonitorConfig *primary_logical_monitor_config;
|
|
int x;
|
|
GList *monitors;
|
|
GList *l;
|
|
|
|
primary_monitor = find_primary_monitor (monitor_manager);
|
|
if (!primary_monitor)
|
|
return NULL;
|
|
|
|
layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
|
|
|
|
primary_logical_monitor_config =
|
|
create_preferred_logical_monitor_config (monitor_manager,
|
|
primary_monitor,
|
|
0, 0,
|
|
layout_mode);
|
|
primary_logical_monitor_config->is_primary = TRUE;
|
|
logical_monitor_configs = g_list_append (NULL,
|
|
primary_logical_monitor_config);
|
|
|
|
x = primary_logical_monitor_config->layout.width;
|
|
monitors = meta_monitor_manager_get_monitors (monitor_manager);
|
|
for (l = monitors; l; l = l->next)
|
|
{
|
|
MetaMonitor *monitor = l->data;
|
|
MetaLogicalMonitorConfig *logical_monitor_config;
|
|
|
|
if (monitor == primary_monitor)
|
|
continue;
|
|
|
|
if (meta_monitor_is_laptop_panel (monitor) &&
|
|
meta_monitor_manager_is_lid_closed (monitor_manager))
|
|
continue;
|
|
|
|
logical_monitor_config =
|
|
create_preferred_logical_monitor_config (monitor_manager,
|
|
monitor,
|
|
x, 0,
|
|
layout_mode);
|
|
logical_monitor_configs = g_list_append (logical_monitor_configs,
|
|
logical_monitor_config);
|
|
|
|
x += logical_monitor_config->layout.width;
|
|
}
|
|
|
|
return meta_monitors_config_new (logical_monitor_configs, layout_mode);
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
|
|
MetaMonitor *primary_monitor;
|
|
GList *logical_monitor_configs;
|
|
MetaLogicalMonitorLayoutMode layout_mode;
|
|
MetaLogicalMonitorConfig *primary_logical_monitor_config;
|
|
|
|
primary_monitor = find_primary_monitor (monitor_manager);
|
|
if (!primary_monitor)
|
|
return NULL;
|
|
|
|
layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
|
|
|
|
primary_logical_monitor_config =
|
|
create_preferred_logical_monitor_config (monitor_manager,
|
|
primary_monitor,
|
|
0, 0,
|
|
layout_mode);
|
|
primary_logical_monitor_config->is_primary = TRUE;
|
|
logical_monitor_configs = g_list_append (NULL,
|
|
primary_logical_monitor_config);
|
|
|
|
return meta_monitors_config_new (logical_monitor_configs, layout_mode);
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
MetaMonitorManager *monitor_manager = config_manager->monitor_manager;
|
|
MetaLogicalMonitorConfig *primary_logical_monitor_config = NULL;
|
|
MetaMonitor *primary_monitor;
|
|
MetaLogicalMonitorLayoutMode layout_mode;
|
|
GList *logical_monitor_configs;
|
|
GList *region;
|
|
GList *monitors;
|
|
GList *l;
|
|
|
|
primary_monitor = find_primary_monitor (monitor_manager);
|
|
if (!primary_monitor)
|
|
return NULL;
|
|
|
|
layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager);
|
|
|
|
logical_monitor_configs = NULL;
|
|
region = NULL;
|
|
monitors = meta_monitor_manager_get_monitors (monitor_manager);
|
|
for (l = monitors; l; l = l->next)
|
|
{
|
|
MetaMonitor *monitor = l->data;
|
|
MetaLogicalMonitorConfig *logical_monitor_config;
|
|
int x, y;
|
|
|
|
if (!meta_monitor_get_suggested_position (monitor, &x, &y))
|
|
continue;
|
|
|
|
logical_monitor_config =
|
|
create_preferred_logical_monitor_config (monitor_manager,
|
|
monitor,
|
|
x, y,
|
|
layout_mode);
|
|
logical_monitor_configs = g_list_append (logical_monitor_configs,
|
|
logical_monitor_config);
|
|
|
|
if (meta_rectangle_overlaps_with_region (region,
|
|
&logical_monitor_config->layout))
|
|
{
|
|
g_warning ("Suggested monitor config has overlapping region, rejecting");
|
|
g_list_free (region);
|
|
g_list_free_full (logical_monitor_configs,
|
|
(GDestroyNotify) meta_logical_monitor_config_free);
|
|
return NULL;
|
|
}
|
|
|
|
region = g_list_prepend (region, &logical_monitor_config->layout);
|
|
|
|
if (monitor == primary_monitor)
|
|
primary_logical_monitor_config = logical_monitor_config;
|
|
}
|
|
|
|
g_list_free (region);
|
|
|
|
if (!logical_monitor_configs)
|
|
return NULL;
|
|
|
|
if (!primary_logical_monitor_config)
|
|
primary_logical_monitor_config =
|
|
g_list_first (logical_monitor_configs)->data;
|
|
|
|
primary_logical_monitor_config->is_primary = TRUE;
|
|
|
|
return meta_monitors_config_new (logical_monitor_configs, layout_mode);
|
|
}
|
|
|
|
void
|
|
meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager,
|
|
MetaMonitorsConfig *config)
|
|
{
|
|
g_set_object (&config_manager->current_config, config);
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitor_config_manager_get_current (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
return config_manager->current_config;
|
|
}
|
|
|
|
static void
|
|
meta_monitor_config_manager_dispose (GObject *object)
|
|
{
|
|
MetaMonitorConfigManager *config_manager =
|
|
META_MONITOR_CONFIG_MANAGER (object);
|
|
|
|
g_clear_object (&config_manager->current_config);
|
|
|
|
G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
meta_monitor_config_manager_init (MetaMonitorConfigManager *config_manager)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_monitor_config_manager_class_init (MetaMonitorConfigManagerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = meta_monitor_config_manager_dispose;
|
|
}
|
|
|
|
void
|
|
meta_monitor_config_free (MetaMonitorConfig *monitor_config)
|
|
{
|
|
meta_monitor_spec_free (monitor_config->monitor_spec);
|
|
g_free (monitor_config->mode_spec);
|
|
g_free (monitor_config);
|
|
}
|
|
|
|
void
|
|
meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config)
|
|
{
|
|
g_list_free_full (logical_monitor_config->monitor_configs,
|
|
(GDestroyNotify) meta_monitor_config_free);
|
|
g_free (logical_monitor_config);
|
|
}
|
|
|
|
static MetaMonitorsConfigKey *
|
|
meta_monitors_config_key_new (GList *logical_monitor_configs)
|
|
{
|
|
MetaMonitorsConfigKey *config_key;
|
|
GList *monitor_specs;
|
|
GList *l;
|
|
|
|
monitor_specs = NULL;
|
|
for (l = 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)
|
|
{
|
|
MetaMonitorConfig *monitor_config = k->data;
|
|
MetaMonitorSpec *monitor_spec;
|
|
|
|
monitor_spec = meta_monitor_spec_clone (monitor_config->monitor_spec);
|
|
monitor_specs = g_list_prepend (monitor_specs, monitor_spec);
|
|
}
|
|
}
|
|
|
|
monitor_specs = g_list_sort (monitor_specs,
|
|
(GCompareFunc) meta_monitor_spec_compare);
|
|
|
|
config_key = g_new0 (MetaMonitorsConfigKey, 1);
|
|
*config_key = (MetaMonitorsConfigKey) {
|
|
.monitor_specs = monitor_specs
|
|
};
|
|
|
|
return config_key;
|
|
}
|
|
|
|
void
|
|
meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key)
|
|
{
|
|
g_list_free_full (config_key->monitor_specs,
|
|
(GDestroyNotify) meta_monitor_spec_free);
|
|
g_free (config_key);
|
|
}
|
|
|
|
unsigned int
|
|
meta_monitors_config_key_hash (gconstpointer data)
|
|
{
|
|
const MetaMonitorsConfigKey *config_key = data;
|
|
GList *l;
|
|
unsigned long hash;
|
|
|
|
hash = 0;
|
|
for (l = config_key->monitor_specs; l; l = l->next)
|
|
{
|
|
MetaMonitorSpec *monitor_spec = l->data;
|
|
|
|
hash ^= (g_str_hash (monitor_spec->connector) ^
|
|
g_str_hash (monitor_spec->vendor) ^
|
|
g_str_hash (monitor_spec->product) ^
|
|
g_str_hash (monitor_spec->serial));
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
gboolean
|
|
meta_monitors_config_key_equal (gconstpointer data_a,
|
|
gconstpointer data_b)
|
|
{
|
|
const MetaMonitorsConfigKey *config_key_a = data_a;
|
|
const MetaMonitorsConfigKey *config_key_b = data_b;
|
|
GList *l_a, *l_b;
|
|
|
|
for (l_a = config_key_a->monitor_specs, l_b = config_key_b->monitor_specs;
|
|
l_a && l_b;
|
|
l_a = l_a->next, l_b = l_b->next)
|
|
{
|
|
MetaMonitorSpec *monitor_spec_a = l_a->data;
|
|
MetaMonitorSpec *monitor_spec_b = l_b->data;
|
|
|
|
if (!meta_monitor_spec_equals (monitor_spec_a, monitor_spec_b))
|
|
return FALSE;
|
|
}
|
|
|
|
if (l_b || l_b)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
MetaMonitorsConfig *
|
|
meta_monitors_config_new (GList *logical_monitor_configs,
|
|
MetaLogicalMonitorLayoutMode layout_mode)
|
|
{
|
|
MetaMonitorsConfig *config;
|
|
|
|
config = g_object_new (META_TYPE_MONITORS_CONFIG, NULL);
|
|
config->logical_monitor_configs = logical_monitor_configs;
|
|
config->key = meta_monitors_config_key_new (logical_monitor_configs);
|
|
config->layout_mode = layout_mode;
|
|
|
|
return config;
|
|
}
|
|
|
|
static void
|
|
meta_monitors_config_finalize (GObject *object)
|
|
{
|
|
MetaMonitorsConfig *config = META_MONITORS_CONFIG (object);
|
|
|
|
meta_monitors_config_key_free (config->key);
|
|
g_list_free_full (config->logical_monitor_configs,
|
|
(GDestroyNotify) meta_logical_monitor_config_free);
|
|
}
|
|
|
|
static void
|
|
meta_monitors_config_init (MetaMonitorsConfig *config)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_monitors_config_class_init (MetaMonitorsConfigClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_monitors_config_finalize;
|
|
}
|
|
|
|
gboolean
|
|
meta_verify_monitor_mode_spec (MetaMonitorModeSpec *monitor_mode_spec,
|
|
GError **error)
|
|
{
|
|
if (monitor_mode_spec->width > 0 &&
|
|
monitor_mode_spec->height > 0 &&
|
|
monitor_mode_spec->refresh_rate > 0.0f)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Monitor mode invalid");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_verify_monitor_spec (MetaMonitorSpec *monitor_spec,
|
|
GError **error)
|
|
{
|
|
if (monitor_spec->connector &&
|
|
monitor_spec->vendor &&
|
|
monitor_spec->product &&
|
|
monitor_spec->serial)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Monitor spec incomplete");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_verify_monitor_config (MetaMonitorConfig *monitor_config,
|
|
GError **error)
|
|
{
|
|
if (monitor_config->monitor_spec && monitor_config->mode_spec)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Monitor config incomplete");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config,
|
|
MetaLogicalMonitorLayoutMode layout_mode,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
int expected_mode_width = 0;
|
|
int expected_mode_height = 0;
|
|
|
|
if (logical_monitor_config->scale < 1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Invalid logical monitor config scale %d",
|
|
logical_monitor_config->scale);
|
|
return FALSE;
|
|
}
|
|
|
|
if (logical_monitor_config->layout.x < 0 ||
|
|
logical_monitor_config->layout.y < 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Invalid logical monitor position (%d, %d)",
|
|
logical_monitor_config->layout.x,
|
|
logical_monitor_config->layout.y);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!logical_monitor_config->monitor_configs)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Logical monitor is empty");
|
|
return FALSE;
|
|
}
|
|
|
|
if (meta_monitor_transform_is_rotated (logical_monitor_config->transform))
|
|
{
|
|
expected_mode_width = logical_monitor_config->layout.height;
|
|
expected_mode_height = logical_monitor_config->layout.width;
|
|
}
|
|
else
|
|
{
|
|
expected_mode_width = logical_monitor_config->layout.width;
|
|
expected_mode_height = logical_monitor_config->layout.height;
|
|
}
|
|
|
|
switch (layout_mode)
|
|
{
|
|
case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL:
|
|
expected_mode_width *= logical_monitor_config->scale;
|
|
expected_mode_height *= logical_monitor_config->scale;
|
|
break;
|
|
case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL:
|
|
break;
|
|
}
|
|
|
|
for (l = logical_monitor_config->monitor_configs; l; l = l->next)
|
|
{
|
|
MetaMonitorConfig *monitor_config = l->data;
|
|
|
|
if (monitor_config->mode_spec->width != expected_mode_width ||
|
|
monitor_config->mode_spec->height != expected_mode_height)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Monitor modes in logical monitor conflict");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
has_adjecent_neighbour (MetaMonitorsConfig *config,
|
|
MetaLogicalMonitorConfig *logical_monitor_config)
|
|
{
|
|
GList *l;
|
|
|
|
if (!config->logical_monitor_configs->next)
|
|
{
|
|
g_assert (config->logical_monitor_configs->data ==
|
|
logical_monitor_config);
|
|
return TRUE;
|
|
}
|
|
|
|
for (l = config->logical_monitor_configs; l; l = l->next)
|
|
{
|
|
MetaLogicalMonitorConfig *other_logical_monitor_config = l->data;
|
|
|
|
if (logical_monitor_config == other_logical_monitor_config)
|
|
continue;
|
|
|
|
if (meta_rectangle_is_adjecent_to (&logical_monitor_config->layout,
|
|
&other_logical_monitor_config->layout))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
meta_verify_monitors_config (MetaMonitorsConfig *config,
|
|
GError **error)
|
|
{
|
|
int min_x, min_y;
|
|
gboolean has_primary;
|
|
GList *region;
|
|
GList *l;
|
|
|
|
if (!config->logical_monitor_configs)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Monitors config incomplete");
|
|
return FALSE;
|
|
}
|
|
|
|
min_x = INT_MAX;
|
|
min_y = INT_MAX;
|
|
region = NULL;
|
|
has_primary = FALSE;
|
|
for (l = config->logical_monitor_configs; l; l = l->next)
|
|
{
|
|
MetaLogicalMonitorConfig *logical_monitor_config = l->data;
|
|
|
|
if (meta_rectangle_overlaps_with_region (region,
|
|
&logical_monitor_config->layout))
|
|
{
|
|
g_list_free (region);
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Logical monitors overlap");
|
|
return FALSE;
|
|
}
|
|
|
|
if (has_primary && logical_monitor_config->is_primary)
|
|
{
|
|
g_list_free (region);
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Config contains multiple primary logical monitors");
|
|
return FALSE;
|
|
}
|
|
else if (logical_monitor_config->is_primary)
|
|
{
|
|
has_primary = TRUE;
|
|
}
|
|
|
|
if (!has_adjecent_neighbour (config, logical_monitor_config))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Logical monitors not adjecent");
|
|
return FALSE;
|
|
}
|
|
|
|
min_x = MIN (logical_monitor_config->layout.x, min_x);
|
|
min_y = MIN (logical_monitor_config->layout.y, min_y);
|
|
|
|
region = g_list_prepend (region, &logical_monitor_config->layout);
|
|
}
|
|
|
|
g_list_free (region);
|
|
|
|
if (min_x != 0 || min_y != 0)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Logical monitors positions are offset");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!has_primary)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Config is missing primary logical");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|