summaryrefslogtreecommitdiff
path: root/src/core/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/monitor.c')
-rw-r--r--src/core/monitor.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/core/monitor.c b/src/core/monitor.c
new file mode 100644
index 000000000..05d4f2b41
--- /dev/null
+++ b/src/core/monitor.c
@@ -0,0 +1,356 @@
+/* -*- 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);
+}
+