mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 18:40:40 -05:00
357 lines
9.4 KiB
C
357 lines
9.4 KiB
C
|
/* -*- 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, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
|
* 02111-1307, USA.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <clutter/clutter.h>
|
||
|
|
||
|
#ifdef HAVE_RANDR
|
||
|
#include <X11/extensions/Xrandr.h>
|
||
|
#endif
|
||
|
|
||
|
#include "monitor-private.h"
|
||
|
|
||
|
struct _MetaMonitorManager
|
||
|
{
|
||
|
GObject parent_instance;
|
||
|
|
||
|
/* Outputs refers to physical screens,
|
||
|
while monitor_infos refer to logical ones (aka CRTC)
|
||
|
They can be different if two outputs are
|
||
|
in clone mode
|
||
|
*/
|
||
|
MetaOutput *outputs;
|
||
|
int primary_monitor_index;
|
||
|
int n_outputs;
|
||
|
|
||
|
MetaMonitorInfo *monitor_infos;
|
||
|
int n_monitor_infos;
|
||
|
|
||
|
#ifdef HAVE_RANDR
|
||
|
Display *xdisplay;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
struct _MetaMonitorManagerClass
|
||
|
{
|
||
|
GObjectClass parent_class;
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
MONITORS_CHANGED,
|
||
|
SIGNALS_LAST
|
||
|
};
|
||
|
|
||
|
static int signals[SIGNALS_LAST];
|
||
|
|
||
|
G_DEFINE_TYPE (MetaMonitorManager, meta_monitor_manager, G_TYPE_OBJECT);
|
||
|
|
||
|
static void
|
||
|
make_dummy_monitor_config (MetaMonitorManager *manager)
|
||
|
{
|
||
|
manager->monitor_infos = g_new0 (MetaMonitorInfo, 1);
|
||
|
manager->n_monitor_infos = 1;
|
||
|
|
||
|
manager->monitor_infos[0].number = 0;
|
||
|
manager->monitor_infos[0].xinerama_index = 0;
|
||
|
manager->monitor_infos[0].rect.x = 0;
|
||
|
manager->monitor_infos[0].rect.y = 0;
|
||
|
if (manager->xdisplay)
|
||
|
{
|
||
|
Screen *screen = ScreenOfDisplay (manager->xdisplay,
|
||
|
DefaultScreen (manager->xdisplay));
|
||
|
|
||
|
manager->monitor_infos[0].rect.width = WidthOfScreen (screen);
|
||
|
manager->monitor_infos[0].rect.height = HeightOfScreen (screen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
manager->monitor_infos[0].rect.width = 1024;
|
||
|
manager->monitor_infos[0].rect.height = 768;
|
||
|
}
|
||
|
manager->monitor_infos[0].refresh_rate = 60.0f;
|
||
|
manager->monitor_infos[0].is_primary = TRUE;
|
||
|
manager->monitor_infos[0].in_fullscreen = -1;
|
||
|
manager->monitor_infos[0].output_id = 1;
|
||
|
|
||
|
manager->outputs = g_new0 (MetaOutput, 1);
|
||
|
manager->n_outputs = 1;
|
||
|
|
||
|
manager->outputs[0].monitor = &manager->monitor_infos[0];
|
||
|
manager->outputs[0].name = g_strdup ("LVDS");
|
||
|
manager->outputs[0].width_mm = 222;
|
||
|
manager->outputs[0].height_mm = 125;
|
||
|
manager->outputs[0].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_RANDR
|
||
|
|
||
|
/* In the case of multiple outputs of a single crtc (mirroring), we consider one of the
|
||
|
* outputs the "main". This is the one we consider "owning" the windows, so if
|
||
|
* the mirroring is changed to a dual monitor setup then the windows are moved to the
|
||
|
* crtc that now has that main output. If one of the outputs is the primary that is
|
||
|
* always the main, otherwise we just use the first.
|
||
|
*/
|
||
|
static void
|
||
|
find_main_output_for_crtc (MetaMonitorManager *manager,
|
||
|
XRRScreenResources *resources,
|
||
|
XRRCrtcInfo *crtc,
|
||
|
MetaMonitorInfo *info,
|
||
|
GArray *outputs)
|
||
|
{
|
||
|
XRROutputInfo *output;
|
||
|
RROutput primary_output;
|
||
|
int i;
|
||
|
|
||
|
primary_output = XRRGetOutputPrimary (manager->xdisplay,
|
||
|
DefaultRootWindow (manager->xdisplay));
|
||
|
|
||
|
for (i = 0; i < crtc->noutput; i++)
|
||
|
{
|
||
|
output = XRRGetOutputInfo (manager->xdisplay, resources, crtc->outputs[i]);
|
||
|
|
||
|
if (output->connection != RR_Disconnected)
|
||
|
{
|
||
|
MetaOutput meta_output;
|
||
|
|
||
|
meta_output.name = g_strdup (output->name);
|
||
|
meta_output.width_mm = output->mm_width;
|
||
|
meta_output.height_mm = output->mm_height;
|
||
|
meta_output.subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
||
|
|
||
|
g_array_append_val (outputs, meta_output);
|
||
|
|
||
|
if (crtc->outputs[i] == primary_output)
|
||
|
{
|
||
|
info->output_id = crtc->outputs[i];
|
||
|
info->is_primary = TRUE;
|
||
|
manager->primary_monitor_index = info->number;
|
||
|
}
|
||
|
else if (info->output_id == 0)
|
||
|
{
|
||
|
info->output_id = crtc->outputs[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XRRFreeOutputInfo (output);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
|
||
|
{
|
||
|
XRRScreenResources *resources;
|
||
|
GArray *outputs;
|
||
|
int i, j;
|
||
|
|
||
|
resources = XRRGetScreenResourcesCurrent (manager->xdisplay,
|
||
|
DefaultRootWindow (manager->xdisplay));
|
||
|
if (!resources)
|
||
|
return make_dummy_monitor_config (manager);
|
||
|
|
||
|
outputs = g_array_new (FALSE, TRUE, sizeof (MetaOutput));
|
||
|
|
||
|
manager->n_outputs = 0;
|
||
|
manager->n_monitor_infos = resources->ncrtc;
|
||
|
|
||
|
manager->monitor_infos = g_new0 (MetaMonitorInfo, manager->n_monitor_infos);
|
||
|
|
||
|
for (i = 0; i < resources->ncrtc; i++)
|
||
|
{
|
||
|
XRRCrtcInfo *crtc;
|
||
|
MetaMonitorInfo *info;
|
||
|
|
||
|
crtc = XRRGetCrtcInfo (manager->xdisplay, resources, resources->crtcs[i]);
|
||
|
|
||
|
info = &manager->monitor_infos[i];
|
||
|
|
||
|
info->number = i;
|
||
|
info->rect.x = crtc->x;
|
||
|
info->rect.y = crtc->y;
|
||
|
info->rect.width = crtc->width;
|
||
|
info->rect.height = crtc->height;
|
||
|
info->in_fullscreen = -1;
|
||
|
|
||
|
for (j = 0; j < resources->nmode; j++)
|
||
|
{
|
||
|
if (resources->modes[j].id == crtc->mode)
|
||
|
info->refresh_rate = (resources->modes[j].dotClock /
|
||
|
((float)resources->modes[j].hTotal *
|
||
|
resources->modes[j].vTotal));
|
||
|
}
|
||
|
|
||
|
find_main_output_for_crtc (manager, resources, crtc, info, outputs);
|
||
|
|
||
|
XRRFreeCrtcInfo (crtc);
|
||
|
}
|
||
|
|
||
|
manager->n_outputs = outputs->len;
|
||
|
manager->outputs = (void*)g_array_free (outputs, FALSE);
|
||
|
|
||
|
XRRFreeScreenResources (resources);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* meta_has_dummy_output:
|
||
|
*
|
||
|
* Returns TRUE if the only available monitor is the dummy one
|
||
|
* backing the ClutterStage window.
|
||
|
*/
|
||
|
static gboolean
|
||
|
has_dummy_output (void)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
meta_monitor_manager_init (MetaMonitorManager *manager)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_current_config (MetaMonitorManager *manager)
|
||
|
{
|
||
|
if (has_dummy_output ())
|
||
|
return make_dummy_monitor_config (manager);
|
||
|
|
||
|
#ifdef HAVE_RANDR
|
||
|
return read_monitor_infos_from_xrandr (manager);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static MetaMonitorManager *
|
||
|
meta_monitor_manager_new (Display *display)
|
||
|
{
|
||
|
MetaMonitorManager *manager;
|
||
|
|
||
|
manager = g_object_new (META_TYPE_MONITOR_MANAGER, NULL);
|
||
|
|
||
|
manager->xdisplay = display;
|
||
|
|
||
|
read_current_config (manager);
|
||
|
return manager;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
free_output_array (MetaOutput *old_outputs,
|
||
|
int n_old_outputs)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < n_old_outputs; i++)
|
||
|
g_free (old_outputs[i].name);
|
||
|
g_free (old_outputs);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
meta_monitor_manager_finalize (GObject *object)
|
||
|
{
|
||
|
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
|
||
|
|
||
|
free_output_array (manager->outputs, manager->n_outputs);
|
||
|
g_free (manager->monitor_infos);
|
||
|
|
||
|
G_OBJECT_CLASS (meta_monitor_manager_parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
|
||
|
object_class->finalize = meta_monitor_manager_finalize;
|
||
|
|
||
|
signals[MONITORS_CHANGED] =
|
||
|
g_signal_new ("monitors-changed",
|
||
|
G_TYPE_FROM_CLASS (object_class),
|
||
|
G_SIGNAL_RUN_LAST,
|
||
|
0,
|
||
|
NULL, NULL, NULL,
|
||
|
G_TYPE_NONE, 0);
|
||
|
}
|
||
|
|
||
|
static MetaMonitorManager *global_manager;
|
||
|
|
||
|
void
|
||
|
meta_monitor_manager_initialize (Display *display)
|
||
|
{
|
||
|
global_manager = meta_monitor_manager_new (display);
|
||
|
}
|
||
|
|
||
|
MetaMonitorManager *
|
||
|
meta_monitor_manager_get (void)
|
||
|
{
|
||
|
g_assert (global_manager != NULL);
|
||
|
|
||
|
return global_manager;
|
||
|
}
|
||
|
|
||
|
MetaMonitorInfo *
|
||
|
meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
|
||
|
int *n_infos)
|
||
|
{
|
||
|
*n_infos = manager->n_monitor_infos;
|
||
|
return manager->monitor_infos;
|
||
|
}
|
||
|
|
||
|
MetaOutput *
|
||
|
meta_monitor_manager_get_outputs (MetaMonitorManager *manager,
|
||
|
int *n_outputs)
|
||
|
{
|
||
|
*n_outputs = manager->n_outputs;
|
||
|
return manager->outputs;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
meta_monitor_manager_get_primary_index (MetaMonitorManager *manager)
|
||
|
{
|
||
|
return manager->primary_monitor_index;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
meta_monitor_manager_invalidate (MetaMonitorManager *manager)
|
||
|
{
|
||
|
MetaOutput *old_outputs;
|
||
|
MetaMonitorInfo *old_monitor_infos;
|
||
|
int n_old_outputs;
|
||
|
|
||
|
/* Save the old monitor infos, so they stay valid during the update */
|
||
|
old_outputs = manager->outputs;
|
||
|
n_old_outputs = manager->n_outputs;
|
||
|
old_monitor_infos = manager->monitor_infos;
|
||
|
|
||
|
read_current_config (manager);
|
||
|
|
||
|
g_signal_emit (manager, signals[MONITORS_CHANGED], 0);
|
||
|
|
||
|
g_free (old_monitor_infos);
|
||
|
free_output_array (old_outputs, n_old_outputs);
|
||
|
}
|
||
|
|