2013-07-18 07:09:16 -04:00
|
|
|
/* -*- 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"
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
2013-07-18 07:09:16 -04:00
|
|
|
#include <clutter/clutter.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_RANDR
|
|
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
#endif
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
#include <meta/main.h>
|
|
|
|
#include <meta/util.h>
|
2013-07-18 07:09:16 -04:00
|
|
|
#include "monitor-private.h"
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
#include "meta-dbus-xrandr.h"
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
struct _MetaMonitorManager
|
|
|
|
{
|
|
|
|
GObject parent_instance;
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
/* XXX: this structure is very badly
|
|
|
|
packed, but I like the logical organization
|
|
|
|
of fields */
|
|
|
|
|
|
|
|
unsigned int serial;
|
|
|
|
|
|
|
|
/* Outputs refer to physical screens,
|
|
|
|
CRTCs refer to stuff that can drive outputs
|
|
|
|
(like encoders, but less tied to the HW),
|
|
|
|
while monitor_infos refer to logical ones.
|
|
|
|
|
|
|
|
See also the comment in monitor-private.h
|
2013-07-18 07:09:16 -04:00
|
|
|
*/
|
|
|
|
MetaOutput *outputs;
|
2013-07-19 08:39:28 -04:00
|
|
|
unsigned int n_outputs;
|
|
|
|
|
|
|
|
MetaMonitorMode *modes;
|
|
|
|
unsigned int n_modes;
|
|
|
|
|
|
|
|
MetaCRTC *crtcs;
|
|
|
|
unsigned int n_crtcs;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
MetaMonitorInfo *monitor_infos;
|
2013-07-19 08:39:28 -04:00
|
|
|
unsigned int n_monitor_infos;
|
|
|
|
int primary_monitor_index;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
#ifdef HAVE_RANDR
|
|
|
|
Display *xdisplay;
|
|
|
|
#endif
|
2013-07-19 08:39:28 -04:00
|
|
|
|
|
|
|
int dbus_name_id;
|
|
|
|
MetaDBusDisplayConfig *skeleton;
|
2013-07-18 07:09:16 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->modes = g_new0 (MetaMonitorMode, 1);
|
|
|
|
manager->n_modes = 1;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->modes[0].mode_id = 1;
|
2013-07-18 07:09:16 -04:00
|
|
|
if (manager->xdisplay)
|
|
|
|
{
|
|
|
|
Screen *screen = ScreenOfDisplay (manager->xdisplay,
|
|
|
|
DefaultScreen (manager->xdisplay));
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->modes[0].width = WidthOfScreen (screen);
|
|
|
|
manager->modes[0].height = HeightOfScreen (screen);
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->modes[0].width = 1024;
|
|
|
|
manager->modes[0].height = 768;
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->modes[0].refresh_rate = 60.0;
|
|
|
|
|
|
|
|
manager->crtcs = g_new0 (MetaCRTC, 1);
|
|
|
|
manager->n_crtcs = 1;
|
|
|
|
|
|
|
|
manager->crtcs[0].crtc_id = 2;
|
|
|
|
manager->crtcs[0].rect.x = 0;
|
|
|
|
manager->crtcs[0].rect.y = 0;
|
|
|
|
manager->crtcs[0].rect.width = manager->modes[0].width;
|
|
|
|
manager->crtcs[0].rect.height = manager->modes[0].height;
|
|
|
|
manager->crtcs[0].current_mode = &manager->modes[0];
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
manager->outputs = g_new0 (MetaOutput, 1);
|
|
|
|
manager->n_outputs = 1;
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->outputs[0].crtc = &manager->crtcs[0];
|
|
|
|
manager->outputs[0].output_id = 3;
|
2013-07-18 07:09:16 -04:00
|
|
|
manager->outputs[0].name = g_strdup ("LVDS");
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->outputs[0].vendor = g_strdup ("unknown");
|
|
|
|
manager->outputs[0].product = g_strdup ("unknown");
|
|
|
|
manager->outputs[0].serial = g_strdup ("");
|
2013-07-18 07:09:16 -04:00
|
|
|
manager->outputs[0].width_mm = 222;
|
|
|
|
manager->outputs[0].height_mm = 125;
|
|
|
|
manager->outputs[0].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->outputs[0].preferred_mode = &manager->modes[0];
|
|
|
|
manager->outputs[0].n_modes = 1;
|
|
|
|
manager->outputs[0].modes = g_new0 (MetaMonitorMode *, 1);
|
|
|
|
manager->outputs[0].modes[0] = &manager->modes[0];
|
|
|
|
manager->outputs[0].n_possible_crtcs = 1;
|
|
|
|
manager->outputs[0].possible_crtcs = g_new0 (MetaCRTC *, 1);
|
|
|
|
manager->outputs[0].possible_crtcs[0] = &manager->crtcs[0];
|
2013-07-19 12:47:01 -04:00
|
|
|
manager->outputs[0].n_possible_clones = 0;
|
|
|
|
manager->outputs[0].possible_clones = g_new0 (MetaOutput *, 0);
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_RANDR
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
|
|
|
|
{
|
|
|
|
XRRScreenResources *resources;
|
2013-07-19 08:39:28 -04:00
|
|
|
RROutput primary_output;
|
|
|
|
unsigned int i, j, k;
|
|
|
|
unsigned int n_actual_outputs;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
resources = XRRGetScreenResourcesCurrent (manager->xdisplay,
|
|
|
|
DefaultRootWindow (manager->xdisplay));
|
|
|
|
if (!resources)
|
|
|
|
return make_dummy_monitor_config (manager);
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->n_outputs = resources->noutput;
|
|
|
|
manager->n_crtcs = resources->ncrtc;
|
|
|
|
manager->n_modes = resources->nmode;
|
|
|
|
manager->outputs = g_new0 (MetaOutput, manager->n_outputs);
|
|
|
|
manager->modes = g_new0 (MetaMonitorMode, manager->n_modes);
|
|
|
|
manager->crtcs = g_new0 (MetaCRTC, manager->n_crtcs);
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
for (i = 0; i < (unsigned)resources->nmode; i++)
|
|
|
|
{
|
|
|
|
XRRModeInfo *xmode = &resources->modes[i];
|
|
|
|
MetaMonitorMode *mode;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
mode = &manager->modes[i];
|
|
|
|
|
|
|
|
mode->mode_id = xmode->id;
|
|
|
|
mode->width = xmode->width;
|
|
|
|
mode->height = xmode->height;
|
|
|
|
mode->refresh_rate = (xmode->dotClock /
|
|
|
|
((float)xmode->hTotal * xmode->vTotal));
|
|
|
|
}
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
for (i = 0; i < (unsigned)resources->ncrtc; i++)
|
2013-07-18 07:09:16 -04:00
|
|
|
{
|
|
|
|
XRRCrtcInfo *crtc;
|
2013-07-19 08:39:28 -04:00
|
|
|
MetaCRTC *meta_crtc;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
crtc = XRRGetCrtcInfo (manager->xdisplay, resources, resources->crtcs[i]);
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
meta_crtc = &manager->crtcs[i];
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
meta_crtc->crtc_id = resources->crtcs[i];
|
|
|
|
meta_crtc->rect.x = crtc->x;
|
|
|
|
meta_crtc->rect.y = crtc->y;
|
|
|
|
meta_crtc->rect.width = crtc->width;
|
|
|
|
meta_crtc->rect.height = crtc->height;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
for (j = 0; j < (unsigned)resources->nmode; j++)
|
2013-07-18 07:09:16 -04:00
|
|
|
{
|
|
|
|
if (resources->modes[j].id == crtc->mode)
|
2013-07-19 08:39:28 -04:00
|
|
|
{
|
|
|
|
meta_crtc->current_mode = &manager->modes[j];
|
|
|
|
break;
|
|
|
|
}
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
XRRFreeCrtcInfo (crtc);
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
primary_output = XRRGetOutputPrimary (manager->xdisplay,
|
|
|
|
DefaultRootWindow (manager->xdisplay));
|
|
|
|
|
|
|
|
n_actual_outputs = 0;
|
|
|
|
for (i = 0; i < (unsigned)resources->noutput; i++)
|
|
|
|
{
|
|
|
|
XRROutputInfo *output;
|
|
|
|
MetaOutput *meta_output;
|
|
|
|
|
|
|
|
output = XRRGetOutputInfo (manager->xdisplay, resources, resources->outputs[i]);
|
|
|
|
|
|
|
|
meta_output = &manager->outputs[n_actual_outputs];
|
|
|
|
|
|
|
|
if (output->connection != RR_Disconnected)
|
|
|
|
{
|
|
|
|
meta_output->output_id = resources->outputs[i];
|
|
|
|
meta_output->name = g_strdup (output->name);
|
|
|
|
/* FIXME: to fill useful values for these we need an EDID parser */
|
|
|
|
meta_output->vendor = g_strdup ("unknown");
|
|
|
|
meta_output->product = g_strdup ("unknown");
|
|
|
|
meta_output->serial = g_strdup ("");
|
|
|
|
meta_output->width_mm = output->mm_width;
|
|
|
|
meta_output->height_mm = output->mm_height;
|
|
|
|
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
|
|
|
|
|
|
|
|
meta_output->n_modes = output->nmode;
|
|
|
|
meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
|
|
|
|
for (j = 0; j < meta_output->n_modes; j++)
|
|
|
|
{
|
|
|
|
for (k = 0; k < manager->n_modes; k++)
|
|
|
|
{
|
|
|
|
if (output->modes[j] == (XID)manager->modes[k].mode_id)
|
|
|
|
{
|
|
|
|
meta_output->modes[j] = &manager->modes[k];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
meta_output->preferred_mode = meta_output->modes[0];
|
|
|
|
|
|
|
|
meta_output->n_possible_crtcs = output->ncrtc;
|
|
|
|
meta_output->possible_crtcs = g_new0 (MetaCRTC *, meta_output->n_possible_crtcs);
|
|
|
|
for (j = 0; j < (unsigned)output->ncrtc; j++)
|
|
|
|
{
|
|
|
|
for (k = 0; k < manager->n_crtcs; k++)
|
|
|
|
{
|
|
|
|
if ((XID)manager->crtcs[k].crtc_id == output->crtcs[j])
|
|
|
|
{
|
|
|
|
meta_output->possible_crtcs[j] = &manager->crtcs[k];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
meta_output->crtc = NULL;
|
|
|
|
for (j = 0; j < manager->n_crtcs; j++)
|
|
|
|
{
|
|
|
|
if ((XID)manager->crtcs[j].crtc_id == output->crtc)
|
|
|
|
{
|
|
|
|
meta_output->crtc = &manager->crtcs[j];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-19 12:47:01 -04:00
|
|
|
meta_output->n_possible_clones = output->nclone;
|
|
|
|
meta_output->possible_clones = g_new0 (MetaOutput *, meta_output->n_possible_clones);
|
|
|
|
/* We can build the list of clones now, because we don't have the list of outputs
|
|
|
|
yet, so temporarily set the pointers to the bare XIDs, and then we'll fix them
|
|
|
|
in a second pass
|
|
|
|
*/
|
|
|
|
for (j = 0; j < (unsigned)output->nclone; j++)
|
|
|
|
{
|
|
|
|
meta_output->possible_clones = GINT_TO_POINTER (output->clones[j]);
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
meta_output->is_primary = ((XID)meta_output->output_id == primary_output);
|
|
|
|
meta_output->is_presentation = FALSE;
|
|
|
|
|
|
|
|
n_actual_outputs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
XRRFreeOutputInfo (output);
|
|
|
|
}
|
|
|
|
|
|
|
|
manager->n_outputs = n_actual_outputs;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 12:47:01 -04:00
|
|
|
/* Now fix the clones */
|
|
|
|
for (i = 0; i < manager->n_outputs; i++)
|
|
|
|
{
|
|
|
|
MetaOutput *meta_output;
|
|
|
|
|
|
|
|
meta_output = &manager->outputs[i];
|
|
|
|
|
|
|
|
for (j = 0; j < meta_output->n_possible_clones; j++)
|
|
|
|
{
|
|
|
|
RROutput clone = GPOINTER_TO_INT (meta_output->possible_clones[j]);
|
|
|
|
|
|
|
|
for (k = 0; k < manager->n_outputs; k++)
|
|
|
|
{
|
|
|
|
if (clone == (XID)manager->outputs[k].output_id)
|
|
|
|
{
|
|
|
|
meta_output->possible_clones[j] = &manager->outputs[k];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
static gboolean
|
|
|
|
make_debug_config (MetaMonitorManager *manager)
|
|
|
|
{
|
|
|
|
const char *env;
|
|
|
|
|
|
|
|
env = g_getenv ("META_DEBUG_MULTIMONITOR");
|
|
|
|
|
|
|
|
if (env == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#ifdef HAVE_RANDR
|
|
|
|
if (strcmp (env, "xrandr") == 0)
|
|
|
|
read_monitor_infos_from_xrandr (manager);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
make_dummy_monitor_config (manager);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
static void
|
|
|
|
read_current_config (MetaMonitorManager *manager)
|
|
|
|
{
|
2013-07-19 08:39:28 -04:00
|
|
|
if (make_debug_config (manager))
|
|
|
|
return;
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
if (has_dummy_output ())
|
|
|
|
return make_dummy_monitor_config (manager);
|
|
|
|
|
|
|
|
#ifdef HAVE_RANDR
|
|
|
|
return read_monitor_infos_from_xrandr (manager);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
/*
|
|
|
|
* make_logical_config:
|
|
|
|
*
|
|
|
|
* Turn outputs and CRTCs into logical MetaMonitorInfo,
|
|
|
|
* that will be used by the core and API layer (MetaScreen
|
|
|
|
* and friends)
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
make_logical_config (MetaMonitorManager *manager)
|
|
|
|
{
|
|
|
|
GArray *monitor_infos;
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
monitor_infos = g_array_sized_new (FALSE, TRUE, sizeof (MetaMonitorInfo),
|
|
|
|
manager->n_outputs);
|
|
|
|
|
|
|
|
/* Walk the list of MetaCRTCs, and build a MetaMonitorInfo
|
|
|
|
for each of them, unless they reference a rectangle that
|
|
|
|
is already there.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < manager->n_crtcs; i++)
|
|
|
|
{
|
|
|
|
MetaCRTC *crtc = &manager->crtcs[i];
|
|
|
|
|
|
|
|
/* Ignore CRTCs not in use */
|
|
|
|
if (crtc->current_mode == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < monitor_infos->len; j++)
|
|
|
|
{
|
|
|
|
MetaMonitorInfo *info = &g_array_index (monitor_infos, MetaMonitorInfo, i);
|
|
|
|
if (meta_rectangle_equal (&crtc->rect,
|
|
|
|
&info->rect))
|
|
|
|
{
|
|
|
|
crtc->logical_monitor = info;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (crtc->logical_monitor == NULL)
|
|
|
|
{
|
|
|
|
MetaMonitorInfo info;
|
|
|
|
|
|
|
|
info.number = monitor_infos->len;
|
|
|
|
info.rect = crtc->rect;
|
|
|
|
info.is_primary = FALSE;
|
|
|
|
/* This starts true because we want
|
|
|
|
is_presentation only if all outputs are
|
|
|
|
marked as such (while for primary it's enough
|
|
|
|
that any is marked)
|
|
|
|
*/
|
|
|
|
info.is_presentation = TRUE;
|
|
|
|
info.in_fullscreen = -1;
|
|
|
|
info.output_id = 0;
|
|
|
|
|
|
|
|
g_array_append_val (monitor_infos, info);
|
|
|
|
|
|
|
|
crtc->logical_monitor = &g_array_index (monitor_infos, MetaMonitorInfo,
|
|
|
|
info.number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now walk the list of outputs applying extended properties (primary
|
|
|
|
and presentation)
|
|
|
|
*/
|
|
|
|
for (i = 0; i < manager->n_outputs; i++)
|
|
|
|
{
|
|
|
|
MetaOutput *output;
|
|
|
|
MetaMonitorInfo *info;
|
|
|
|
|
|
|
|
output = &manager->outputs[i];
|
|
|
|
|
|
|
|
/* Ignore outputs that are not active */
|
|
|
|
if (output->crtc == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We must have a logical monitor on every CRTC at this point */
|
|
|
|
g_assert (output->crtc->logical_monitor != NULL);
|
|
|
|
|
|
|
|
info = output->crtc->logical_monitor;
|
|
|
|
|
|
|
|
info->is_primary = info->is_primary || output->is_primary;
|
|
|
|
info->is_presentation = info->is_presentation && output->is_presentation;
|
|
|
|
|
|
|
|
if (output->is_primary || info->output_id == 0)
|
|
|
|
info->output_id = output->output_id;
|
|
|
|
|
|
|
|
if (info->is_primary)
|
|
|
|
manager->primary_monitor_index = info->number;
|
|
|
|
}
|
|
|
|
|
|
|
|
manager->n_monitor_infos = monitor_infos->len;
|
|
|
|
manager->monitor_infos = (void*)g_array_free (monitor_infos, FALSE);
|
|
|
|
}
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
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);
|
2013-07-19 08:39:28 -04:00
|
|
|
make_logical_config (manager);
|
2013-07-18 07:09:16 -04:00
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_output_array (MetaOutput *old_outputs,
|
|
|
|
int n_old_outputs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < n_old_outputs; i++)
|
2013-07-19 08:39:28 -04:00
|
|
|
{
|
|
|
|
g_free (old_outputs[i].name);
|
|
|
|
g_free (old_outputs[i].vendor);
|
|
|
|
g_free (old_outputs[i].product);
|
|
|
|
g_free (old_outputs[i].serial);
|
|
|
|
g_free (old_outputs[i].modes);
|
|
|
|
g_free (old_outputs[i].possible_crtcs);
|
2013-07-19 12:47:01 -04:00
|
|
|
g_free (old_outputs[i].possible_clones);
|
2013-07-19 08:39:28 -04:00
|
|
|
}
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
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);
|
2013-07-19 08:39:28 -04:00
|
|
|
g_free (manager->modes);
|
|
|
|
g_free (manager->crtcs);
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (meta_monitor_manager_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
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->skeleton);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (meta_monitor_manager_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
2013-07-18 07:09:16 -04:00
|
|
|
static void
|
|
|
|
meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
object_class->dispose = meta_monitor_manager_dispose;
|
2013-07-18 07:09:16 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
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 (MetaOutput *output)
|
|
|
|
{
|
|
|
|
if (output->width_mm != -1 && output->height_mm != -1)
|
|
|
|
{
|
|
|
|
double d = sqrt (output->width_mm * output->width_mm +
|
|
|
|
output->height_mm * output->height_mm);
|
|
|
|
char *inches = diagonal_to_str (d / 25.4);
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
ret = g_strdup_printf ("%s %s", output->vendor, inches);
|
|
|
|
|
|
|
|
g_free (inches);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return g_strdup (output->vendor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
handle_get_resources (MetaDBusDisplayConfig *skeleton,
|
|
|
|
GDBusMethodInvocation *invocation,
|
|
|
|
MetaMonitorManager *manager)
|
|
|
|
{
|
|
|
|
GVariantBuilder crtc_builder, output_builder, mode_builder;
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
g_variant_builder_init (&crtc_builder, G_VARIANT_TYPE ("a(uxiiiiiuaua{sv})"));
|
2013-07-19 12:47:01 -04:00
|
|
|
g_variant_builder_init (&output_builder, G_VARIANT_TYPE ("a(uxiausauaua{sv})"));
|
2013-07-19 08:39:28 -04:00
|
|
|
g_variant_builder_init (&mode_builder, G_VARIANT_TYPE ("a(uxuud)"));
|
|
|
|
|
|
|
|
for (i = 0; i < manager->n_crtcs; i++)
|
|
|
|
{
|
|
|
|
MetaCRTC *crtc = &manager->crtcs[i];
|
|
|
|
GVariantBuilder transforms;
|
|
|
|
|
|
|
|
g_variant_builder_init (&transforms, G_VARIANT_TYPE ("au"));
|
|
|
|
g_variant_builder_add (&transforms, "u", 0); /* 0 = WL_OUTPUT_TRANSFORM_NORMAL */
|
|
|
|
|
|
|
|
g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})",
|
|
|
|
i, /* ID */
|
|
|
|
crtc->crtc_id,
|
|
|
|
(int)crtc->rect.x,
|
|
|
|
(int)crtc->rect.y,
|
|
|
|
(int)crtc->rect.width,
|
|
|
|
(int)crtc->rect.height,
|
|
|
|
(int)(crtc->current_mode ? crtc->current_mode - manager->modes : -1),
|
|
|
|
0, /* 0 = WL_OUTPUT_TRANSFORM_NORMAL */
|
|
|
|
&transforms,
|
|
|
|
NULL /* properties */);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < manager->n_outputs; i++)
|
|
|
|
{
|
|
|
|
MetaOutput *output = &manager->outputs[i];
|
2013-07-19 12:47:01 -04:00
|
|
|
GVariantBuilder crtcs, modes, clones, properties;
|
2013-07-19 08:39:28 -04:00
|
|
|
|
|
|
|
g_variant_builder_init (&crtcs, G_VARIANT_TYPE ("au"));
|
|
|
|
for (j = 0; j < output->n_possible_crtcs; j++)
|
|
|
|
g_variant_builder_add (&crtcs, "u",
|
|
|
|
(unsigned)(output->possible_crtcs[j] - manager->crtcs));
|
|
|
|
|
|
|
|
g_variant_builder_init (&modes, G_VARIANT_TYPE ("au"));
|
|
|
|
for (j = 0; j < output->n_modes; j++)
|
|
|
|
g_variant_builder_add (&modes, "u",
|
|
|
|
(unsigned)(output->modes[j] - manager->modes));
|
|
|
|
|
2013-07-19 12:47:01 -04:00
|
|
|
g_variant_builder_init (&clones, G_VARIANT_TYPE ("au"));
|
|
|
|
for (j = 0; j < output->n_possible_clones; j++)
|
|
|
|
g_variant_builder_add (&clones, "u",
|
|
|
|
(unsigned)(output->possible_clones[j] - manager->outputs));
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
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}", "display-name",
|
|
|
|
g_variant_new_take_string (make_display_name (output)));
|
|
|
|
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));
|
|
|
|
|
2013-07-19 12:47:01 -04:00
|
|
|
g_variant_builder_add (&output_builder, "(uxiausauaua{sv})",
|
2013-07-19 08:39:28 -04:00
|
|
|
i, /* ID */
|
|
|
|
output->output_id,
|
|
|
|
(int)(output->crtc ? output->crtc - manager->crtcs : -1),
|
|
|
|
&crtcs,
|
|
|
|
output->name,
|
|
|
|
&modes,
|
2013-07-19 12:47:01 -04:00
|
|
|
&clones,
|
2013-07-19 08:39:28 -04:00
|
|
|
&properties);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < manager->n_modes; i++)
|
|
|
|
{
|
|
|
|
MetaMonitorMode *mode = &manager->modes[i];
|
|
|
|
|
|
|
|
g_variant_builder_add (&mode_builder, "(uxuud)",
|
|
|
|
i, /* ID */
|
|
|
|
mode->mode_id,
|
|
|
|
mode->width,
|
|
|
|
mode->height,
|
|
|
|
(double)mode->refresh_rate);
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_bus_acquired (GDBusConnection *connection,
|
|
|
|
const char *name,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
MetaMonitorManager *manager = user_data;
|
|
|
|
|
|
|
|
manager->skeleton = META_DBUS_DISPLAY_CONFIG (meta_dbus_display_config_skeleton_new ());
|
|
|
|
|
|
|
|
g_signal_connect_object (manager->skeleton, "handle-get-resources",
|
|
|
|
G_CALLBACK (handle_get_resources), manager, 0);
|
|
|
|
|
|
|
|
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager->skeleton),
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MetaMonitorManager *global_monitor_manager;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
meta_monitor_manager_initialize (Display *display)
|
|
|
|
{
|
2013-07-19 08:39:28 -04:00
|
|
|
global_monitor_manager = meta_monitor_manager_new (display);
|
|
|
|
|
|
|
|
initialize_dbus_interface (global_monitor_manager);
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
MetaMonitorManager *
|
|
|
|
meta_monitor_manager_get (void)
|
|
|
|
{
|
2013-07-19 08:39:28 -04:00
|
|
|
g_assert (global_monitor_manager != NULL);
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
return global_monitor_manager;
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2013-07-19 08:39:28 -04:00
|
|
|
MetaCRTC *old_crtcs;
|
2013-07-18 07:09:16 -04:00
|
|
|
MetaMonitorInfo *old_monitor_infos;
|
2013-07-19 08:39:28 -04:00
|
|
|
MetaMonitorMode *old_modes;
|
2013-07-18 07:09:16 -04:00
|
|
|
int n_old_outputs;
|
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
/* Save the old structures, so they stay valid during the update */
|
2013-07-18 07:09:16 -04:00
|
|
|
old_outputs = manager->outputs;
|
|
|
|
n_old_outputs = manager->n_outputs;
|
|
|
|
old_monitor_infos = manager->monitor_infos;
|
2013-07-19 08:39:28 -04:00
|
|
|
old_modes = manager->modes;
|
|
|
|
old_crtcs = manager->crtcs;
|
2013-07-18 07:09:16 -04:00
|
|
|
|
2013-07-19 08:39:28 -04:00
|
|
|
manager->serial ++;
|
2013-07-18 07:09:16 -04:00
|
|
|
read_current_config (manager);
|
2013-07-19 08:39:28 -04:00
|
|
|
make_logical_config (manager);
|
2013-07-18 07:09:16 -04:00
|
|
|
|
|
|
|
g_signal_emit (manager, signals[MONITORS_CHANGED], 0);
|
|
|
|
|
|
|
|
g_free (old_monitor_infos);
|
|
|
|
free_output_array (old_outputs, n_old_outputs);
|
2013-07-19 08:39:28 -04:00
|
|
|
g_free (old_modes);
|
|
|
|
g_free (old_crtcs);
|
2013-07-18 07:09:16 -04:00
|
|
|
}
|
|
|
|
|