diff --git a/configure.ac b/configure.ac
index 18a24b080..427cf721c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,8 @@ MUTTER_PC_MODULES="
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
$CLUTTER_PACKAGE >= 1.14.3
cogl-1.0 >= 1.13.3
+ upower-glib > 0.9.11
+ gnome-desktop-3.0
"
GLIB_GSETTINGS
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1e1b5a247..fb893350e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -12,6 +12,7 @@ src/core/display.c
src/core/errors.c
src/core/keybindings.c
src/core/main.c
+src/core/monitor.c
src/core/mutter.c
src/core/prefs.c
src/core/screen.c
@@ -23,12 +24,9 @@ src/core/xprops.c
src/mutter.desktop.in
src/mutter-wm.desktop.in
src/org.gnome.mutter.gschema.xml.in
-src/tools/mutter-message.c
src/ui/frames.c
src/ui/menu.c
src/ui/metaaccellabel.c
src/ui/resizepopup.c
src/ui/theme.c
src/ui/theme-parser.c
-src/ui/theme-viewer.c
-
diff --git a/src/Makefile.am b/src/Makefile.am
index 0dbd77887..7fca4f23d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,6 +37,7 @@ INCLUDES += \
mutter_built_sources = \
$(dbus_idle_built_sources) \
+ $(dbus_xrandr_built_sources) \
mutter-enum-types.h \
mutter-enum-types.c \
wayland/xserver-protocol.c \
@@ -105,6 +106,8 @@ libmutter_wayland_la_SOURCES = \
ui/draw-workspace.h \
core/edge-resistance.c \
core/edge-resistance.h \
+ core/edid-parse.c \
+ core/edid.h \
core/errors.c \
meta/errors.h \
core/frame.c \
@@ -125,6 +128,11 @@ libmutter_wayland_la_SOURCES = \
core/meta-cursor-tracker-private.h \
core/meta-idle-monitor.c \
core/meta-idle-monitor-private.h \
+ core/meta-xrandr-shared.h \
+ core/monitor.c \
+ core/monitor-config.c \
+ core/monitor-private.h \
+ core/monitor-xrandr.c \
core/mutter-Xatomtype.h \
core/place.c \
core/place.h \
@@ -353,6 +361,15 @@ mutter-enum-types.c: stamp-mutter-enum-types.h mutter-enum-types.c.in
cp xgen-tetc mutter-enum-types.c && \
rm -f xgen-tetc
+dbus_xrandr_built_sources = meta-dbus-xrandr.c meta-dbus-xrandr.h
+
+$(dbus_xrandr_built_sources) : Makefile.am xrandr.xml
+ $(AM_V_GEN)gdbus-codegen \
+ --interface-prefix org.gnome.Mutter \
+ --c-namespace MetaDBus \
+ --generate-c-code meta-dbus-xrandr \
+ $(srcdir)/xrandr.xml
+
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
$(dbus_idle_built_sources) : Makefile.am idle-monitor.xml
diff --git a/src/compositor/meta-plugin-manager.c b/src/compositor/meta-plugin-manager.c
index 2ae100e84..fa9fe96e3 100644
--- a/src/compositor/meta-plugin-manager.c
+++ b/src/compositor/meta-plugin-manager.c
@@ -85,12 +85,20 @@ meta_plugin_manager_load (const gchar *plugin_name)
g_free (path);
}
+static void
+on_confirm_display_change (MetaMonitorManager *monitors,
+ MetaPluginManager *plugin_mgr)
+{
+ meta_plugin_manager_confirm_display_change (plugin_mgr);
+}
+
MetaPluginManager *
meta_plugin_manager_new (MetaScreen *screen)
{
MetaPluginManager *plugin_mgr;
MetaPluginClass *klass;
MetaPlugin *plugin;
+ MetaMonitorManager *monitors;
plugin_mgr = g_new0 (MetaPluginManager, 1);
plugin_mgr->screen = screen;
@@ -101,6 +109,10 @@ meta_plugin_manager_new (MetaScreen *screen)
if (klass->start)
klass->start (plugin);
+ monitors = meta_monitor_manager_get ();
+ g_signal_connect (monitors, "confirm-display-change",
+ G_CALLBACK (on_confirm_display_change), plugin_mgr);
+
return plugin_mgr;
}
@@ -330,3 +342,15 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
}
+
+void
+meta_plugin_manager_confirm_display_change (MetaPluginManager *plugin_mgr)
+{
+ MetaPlugin *plugin = plugin_mgr->plugin;
+ MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin);
+
+ if (klass->confirm_display_change)
+ return klass->confirm_display_change (plugin);
+ else
+ return meta_plugin_complete_display_change (plugin, TRUE);
+}
diff --git a/src/compositor/meta-plugin-manager.h b/src/compositor/meta-plugin-manager.h
index 9df24c17a..215d450a5 100644
--- a/src/compositor/meta-plugin-manager.h
+++ b/src/compositor/meta-plugin-manager.h
@@ -73,4 +73,6 @@ gboolean meta_plugin_manager_filter_keybinding (MetaPluginManager *mgr,
gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *mgr,
XEvent *xev);
+void meta_plugin_manager_confirm_display_change (MetaPluginManager *mgr);
+
#endif
diff --git a/src/compositor/meta-plugin.c b/src/compositor/meta-plugin.c
index af50cab47..ba8b9cbd9 100644
--- a/src/compositor/meta-plugin.c
+++ b/src/compositor/meta-plugin.c
@@ -41,6 +41,7 @@
#include "compositor-private.h"
#include "meta-window-actor-private.h"
+#include "monitor-private.h"
G_DEFINE_ABSTRACT_TYPE (MetaPlugin, meta_plugin, G_TYPE_OBJECT);
@@ -332,3 +333,13 @@ meta_plugin_get_screen (MetaPlugin *plugin)
return priv->screen;
}
+
+void
+meta_plugin_complete_display_change (MetaPlugin *plugin,
+ gboolean ok)
+{
+ MetaMonitorManager *manager;
+
+ manager = meta_monitor_manager_get ();
+ meta_monitor_manager_confirm_configuration (manager, ok);
+}
diff --git a/src/compositor/plugins/default.c b/src/compositor/plugins/default.c
index 228fb19e0..404302c8b 100644
--- a/src/compositor/plugins/default.c
+++ b/src/compositor/plugins/default.c
@@ -24,6 +24,8 @@
#include
#include
#include
+#include
+#include
#include
#define _(x) dgettext (GETTEXT_PACKAGE, x)
@@ -98,6 +100,8 @@ static void kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *actor);
static void kill_switch_workspace (MetaPlugin *plugin);
+static void confirm_display_change (MetaPlugin *plugin);
+
static const MetaPluginInfo * plugin_info (MetaPlugin *plugin);
META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_plugin);
@@ -113,6 +117,8 @@ struct _MetaDefaultPluginPrivate
ClutterActor *desktop1;
ClutterActor *desktop2;
+ ClutterActor *background_group;
+
MetaPluginInfo info;
};
@@ -203,6 +209,7 @@ meta_default_plugin_class_init (MetaDefaultPluginClass *klass)
plugin_class->plugin_info = plugin_info;
plugin_class->kill_window_effects = kill_window_effects;
plugin_class->kill_switch_workspace = kill_switch_workspace;
+ plugin_class->confirm_display_change = confirm_display_change;
g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate));
}
@@ -299,9 +306,58 @@ show_stage (MetaPlugin *plugin)
return FALSE;
}
+static void
+on_monitors_changed (MetaScreen *screen,
+ MetaPlugin *plugin)
+{
+ MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
+ int i, n;
+
+ clutter_actor_destroy_all_children (self->priv->background_group);
+
+ n = meta_screen_get_n_monitors (screen);
+ for (i = 0; i < n; i++)
+ {
+ MetaRectangle rect;
+ ClutterActor *background;
+ ClutterColor color;
+
+ meta_screen_get_monitor_geometry (screen, i, &rect);
+
+ background = meta_background_actor_new ();
+
+ clutter_actor_set_position (background, rect.x, rect.y);
+ clutter_actor_set_size (background, rect.width, rect.height);
+
+ /* Don't use rand() here, mesa calls srand() internally when
+ parsing the driconf XML, but it's nice if the colors are
+ reproducible.
+ */
+ clutter_color_init (&color,
+ g_random_int () % 255,
+ g_random_int () % 255,
+ g_random_int () % 255,
+ 255);
+ clutter_actor_set_background_color (background, &color);
+
+ clutter_actor_add_child (self->priv->background_group, background);
+ }
+}
+
static void
start (MetaPlugin *plugin)
{
+ MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
+ MetaScreen *screen = meta_plugin_get_screen (plugin);
+
+ self->priv->background_group = meta_background_group_new ();
+ clutter_actor_insert_child_below (meta_get_window_group_for_screen (screen),
+ self->priv->background_group, NULL);
+
+ g_signal_connect (screen, "monitors-changed",
+ G_CALLBACK (on_monitors_changed), plugin);
+ on_monitors_changed (screen, plugin);
+
meta_later_add (META_LATER_BEFORE_REDRAW,
(GSourceFunc) show_stage,
plugin,
@@ -782,3 +838,33 @@ plugin_info (MetaPlugin *plugin)
return &priv->info;
}
+
+static void
+on_dialog_closed (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+ MetaPlugin *plugin = user_data;
+ gboolean ok;
+
+ ok = g_spawn_check_exit_status (status, NULL);
+ meta_plugin_complete_display_change (plugin, ok);
+}
+
+static void
+confirm_display_change (MetaPlugin *plugin)
+{
+ GPid pid;
+
+ pid = meta_show_dialog ("--question",
+ "Does the display look OK?",
+ "20",
+ NULL,
+ "_Keep This Configuration",
+ "_Restore Previous Configuration",
+ "preferences-desktop-display",
+ 0,
+ NULL, NULL);
+
+ g_child_watch_add (pid, on_dialog_closed, plugin);
+}
diff --git a/src/core/display.c b/src/core/display.c
index 118e60ebf..c0d42d85e 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -2198,6 +2198,7 @@ meta_display_handle_event (MetaDisplay *display,
gboolean bypass_compositor;
gboolean filter_out_event;
XIEvent *input_event;
+ MetaMonitorManager *monitor;
MetaScreen *screen;
#ifdef WITH_VERBOSE_MODE
@@ -2208,6 +2209,14 @@ meta_display_handle_event (MetaDisplay *display,
#ifdef HAVE_STARTUP_NOTIFICATION
sn_display_process_event (display->sn_display, event);
#endif
+
+ /* Intercept XRandR events early and don't attempt any
+ processing for them. We still let them through to Gdk though,
+ so it can update its own internal state.
+ */
+ monitor = meta_monitor_manager_get ();
+ if (meta_monitor_manager_handle_xevent (monitor, event))
+ return FALSE;
bypass_compositor = FALSE;
filter_out_event = FALSE;
@@ -2910,32 +2919,10 @@ meta_display_handle_event (MetaDisplay *display,
meta_stack_tracker_configure_event (screen->stack_tracker,
&event->xconfigure);
}
+
if (window && window->override_redirect)
meta_window_configure_notify (window, &event->xconfigure);
- else
- /* Handle screen resize */
- {
- MetaScreen *screen;
- screen = meta_display_screen_for_root (display,
- event->xconfigure.window);
-
- if (screen != NULL)
- {
-#ifdef HAVE_RANDR
- /* do the resize the official way */
- XRRUpdateConfiguration (event);
-#else
- /* poke around in Xlib */
- screen->xscreen->width = event->xconfigure.width;
- screen->xscreen->height = event->xconfigure.height;
-#endif
-
- meta_screen_resize (screen,
- event->xconfigure.width,
- event->xconfigure.height);
- }
- }
break;
case ConfigureRequest:
/* This comment and code is found in both twm and fvwm */
diff --git a/src/core/edid-parse.c b/src/core/edid-parse.c
new file mode 100644
index 000000000..5b3283ae5
--- /dev/null
+++ b/src/core/edid-parse.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Author: Soren Sandmann */
+
+#include "edid.h"
+#include
+#include
+#include
+#include
+
+static int
+get_bit (int in, int bit)
+{
+ return (in & (1 << bit)) >> bit;
+}
+
+static int
+get_bits (int in, int begin, int end)
+{
+ int mask = (1 << (end - begin + 1)) - 1;
+
+ return (in >> begin) & mask;
+}
+
+static int
+decode_header (const uchar *edid)
+{
+ if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static int
+decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
+{
+ int is_model_year;
+
+ /* Manufacturer Code */
+ info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6);
+ info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3;
+ info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
+ info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4);
+ info->manufacturer_code[3] = '\0';
+
+ info->manufacturer_code[0] += 'A' - 1;
+ info->manufacturer_code[1] += 'A' - 1;
+ info->manufacturer_code[2] += 'A' - 1;
+
+ /* Product Code */
+ info->product_code = edid[0x0b] << 8 | edid[0x0a];
+
+ /* Serial Number */
+ info->serial_number =
+ edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
+
+ /* Week and Year */
+ is_model_year = FALSE;
+ switch (edid[0x10])
+ {
+ case 0x00:
+ info->production_week = -1;
+ break;
+
+ case 0xff:
+ info->production_week = -1;
+ is_model_year = TRUE;
+ break;
+
+ default:
+ info->production_week = edid[0x10];
+ break;
+ }
+
+ if (is_model_year)
+ {
+ info->production_year = -1;
+ info->model_year = 1990 + edid[0x11];
+ }
+ else
+ {
+ info->production_year = 1990 + edid[0x11];
+ info->model_year = -1;
+ }
+
+ return TRUE;
+}
+
+static int
+decode_edid_version (const uchar *edid, MonitorInfo *info)
+{
+ info->major_version = edid[0x12];
+ info->minor_version = edid[0x13];
+
+ return TRUE;
+}
+
+static int
+decode_display_parameters (const uchar *edid, MonitorInfo *info)
+{
+ /* Digital vs Analog */
+ info->is_digital = get_bit (edid[0x14], 7);
+
+ if (info->is_digital)
+ {
+ int bits;
+
+ static const int bit_depth[8] =
+ {
+ -1, 6, 8, 10, 12, 14, 16, -1
+ };
+
+ static const Interface interfaces[6] =
+ {
+ UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
+ };
+
+ bits = get_bits (edid[0x14], 4, 6);
+ info->connector.digital.bits_per_primary = bit_depth[bits];
+
+ bits = get_bits (edid[0x14], 0, 3);
+
+ if (bits <= 5)
+ info->connector.digital.interface = interfaces[bits];
+ else
+ info->connector.digital.interface = UNDEFINED;
+ }
+ else
+ {
+ int bits = get_bits (edid[0x14], 5, 6);
+
+ static const double levels[][3] =
+ {
+ { 0.7, 0.3, 1.0 },
+ { 0.714, 0.286, 1.0 },
+ { 1.0, 0.4, 1.4 },
+ { 0.7, 0.0, 0.7 },
+ };
+
+ info->connector.analog.video_signal_level = levels[bits][0];
+ info->connector.analog.sync_signal_level = levels[bits][1];
+ info->connector.analog.total_signal_level = levels[bits][2];
+
+ info->connector.analog.blank_to_black = get_bit (edid[0x14], 4);
+
+ info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3);
+ info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
+ info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
+
+ info->connector.analog.serration_on_vsync = get_bit (edid[0x14], 0);
+ }
+
+ /* Screen Size / Aspect Ratio */
+ if (edid[0x15] == 0 && edid[0x16] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = -1.0;
+ }
+ else if (edid[0x16] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x15] + 99);
+ }
+ else if (edid[0x15] == 0)
+ {
+ info->width_mm = -1;
+ info->height_mm = -1;
+ info->aspect_ratio = 100.0 / (edid[0x16] + 99);
+ info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
+ }
+ else
+ {
+ info->width_mm = 10 * edid[0x15];
+ info->height_mm = 10 * edid[0x16];
+ }
+
+ /* Gamma */
+ if (edid[0x17] == 0xFF)
+ info->gamma = -1.0;
+ else
+ info->gamma = (edid[0x17] + 100.0) / 100.0;
+
+ /* Features */
+ info->standby = get_bit (edid[0x18], 7);
+ info->suspend = get_bit (edid[0x18], 6);
+ info->active_off = get_bit (edid[0x18], 5);
+
+ if (info->is_digital)
+ {
+ info->connector.digital.rgb444 = TRUE;
+ if (get_bit (edid[0x18], 3))
+ info->connector.digital.ycrcb444 = 1;
+ if (get_bit (edid[0x18], 4))
+ info->connector.digital.ycrcb422 = 1;
+ }
+ else
+ {
+ int bits = get_bits (edid[0x18], 3, 4);
+ ColorType color_type[4] =
+ {
+ MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
+ };
+
+ info->connector.analog.color_type = color_type[bits];
+ }
+
+ info->srgb_is_standard = get_bit (edid[0x18], 2);
+
+ /* In 1.3 this is called "has preferred timing" */
+ info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
+
+ /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
+ info->continuous_frequency = get_bit (edid[0x18], 0);
+ return TRUE;
+}
+
+static double
+decode_fraction (int high, int low)
+{
+ double result = 0.0;
+ int i;
+
+ high = (high << 2) | low;
+
+ for (i = 0; i < 10; ++i)
+ result += get_bit (high, i) * pow (2, i - 10);
+
+ return result;
+}
+
+static int
+decode_color_characteristics (const uchar *edid, MonitorInfo *info)
+{
+ info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
+ info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
+ info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
+ info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
+ info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
+ info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
+ info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
+ info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
+
+ return TRUE;
+}
+
+static int
+decode_established_timings (const uchar *edid, MonitorInfo *info)
+{
+ static const Timing established[][8] =
+ {
+ {
+ { 800, 600, 60 },
+ { 800, 600, 56 },
+ { 640, 480, 75 },
+ { 640, 480, 72 },
+ { 640, 480, 67 },
+ { 640, 480, 60 },
+ { 720, 400, 88 },
+ { 720, 400, 70 }
+ },
+ {
+ { 1280, 1024, 75 },
+ { 1024, 768, 75 },
+ { 1024, 768, 70 },
+ { 1024, 768, 60 },
+ { 1024, 768, 87 },
+ { 832, 624, 75 },
+ { 800, 600, 75 },
+ { 800, 600, 72 }
+ },
+ {
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 1152, 870, 75 }
+ },
+ };
+
+ int i, j, idx;
+
+ idx = 0;
+ for (i = 0; i < 3; ++i)
+ {
+ for (j = 0; j < 8; ++j)
+ {
+ int byte = edid[0x23 + i];
+
+ if (get_bit (byte, j) && established[i][j].frequency != 0)
+ info->established[idx++] = established[i][j];
+ }
+ }
+ return TRUE;
+}
+
+static int
+decode_standard_timings (const uchar *edid, MonitorInfo *info)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ int first = edid[0x26 + 2 * i];
+ int second = edid[0x27 + 2 * i];
+
+ if (first != 0x01 && second != 0x01)
+ {
+ int w = 8 * (first + 31);
+ int h = 0;
+
+ switch (get_bits (second, 6, 7))
+ {
+ case 0x00: h = (w / 16) * 10; break;
+ case 0x01: h = (w / 4) * 3; break;
+ case 0x02: h = (w / 5) * 4; break;
+ case 0x03: h = (w / 16) * 9; break;
+ }
+
+ info->standard[i].width = w;
+ info->standard[i].height = h;
+ info->standard[i].frequency = get_bits (second, 0, 5) + 60;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+decode_lf_string (const uchar *s, int n_chars, char *result)
+{
+ int i;
+ for (i = 0; i < n_chars; ++i)
+ {
+ if (s[i] == 0x0a)
+ {
+ *result++ = '\0';
+ break;
+ }
+ else if (s[i] == 0x00)
+ {
+ /* Convert embedded 0's to spaces */
+ *result++ = ' ';
+ }
+ else
+ {
+ *result++ = s[i];
+ }
+ }
+}
+
+static void
+decode_display_descriptor (const uchar *desc,
+ MonitorInfo *info)
+{
+ switch (desc[0x03])
+ {
+ case 0xFC:
+ decode_lf_string (desc + 5, 13, info->dsc_product_name);
+ break;
+ case 0xFF:
+ decode_lf_string (desc + 5, 13, info->dsc_serial_number);
+ break;
+ case 0xFE:
+ decode_lf_string (desc + 5, 13, info->dsc_string);
+ break;
+ case 0xFD:
+ /* Range Limits */
+ break;
+ case 0xFB:
+ /* Color Point */
+ break;
+ case 0xFA:
+ /* Timing Identifications */
+ break;
+ case 0xF9:
+ /* Color Management */
+ break;
+ case 0xF8:
+ /* Timing Codes */
+ break;
+ case 0xF7:
+ /* Established Timings */
+ break;
+ case 0x10:
+ break;
+ }
+}
+
+static void
+decode_detailed_timing (const uchar *timing,
+ DetailedTiming *detailed)
+{
+ int bits;
+ StereoType stereo[] =
+ {
+ NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
+ TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
+ FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
+ };
+
+ detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
+ detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
+ detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
+ detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
+ detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
+ detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
+ detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
+ detailed->v_front_porch =
+ get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
+ detailed->v_sync =
+ get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
+ detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
+ detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
+ detailed->right_border = timing[0x0f];
+ detailed->top_border = timing[0x10];
+
+ detailed->interlaced = get_bit (timing[0x11], 7);
+
+ /* Stereo */
+ bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
+ detailed->stereo = stereo[bits];
+
+ /* Sync */
+ bits = timing[0x11];
+
+ detailed->digital_sync = get_bit (bits, 4);
+ if (detailed->digital_sync)
+ {
+ detailed->connector.digital.composite = !get_bit (bits, 3);
+
+ if (detailed->connector.digital.composite)
+ {
+ detailed->connector.digital.serrations = get_bit (bits, 2);
+ detailed->connector.digital.negative_vsync = FALSE;
+ }
+ else
+ {
+ detailed->connector.digital.serrations = FALSE;
+ detailed->connector.digital.negative_vsync = !get_bit (bits, 2);
+ }
+
+ detailed->connector.digital.negative_hsync = !get_bit (bits, 0);
+ }
+ else
+ {
+ detailed->connector.analog.bipolar = get_bit (bits, 3);
+ detailed->connector.analog.serrations = get_bit (bits, 2);
+ detailed->connector.analog.sync_on_green = !get_bit (bits, 1);
+ }
+}
+
+static int
+decode_descriptors (const uchar *edid, MonitorInfo *info)
+{
+ int i;
+ int timing_idx;
+
+ timing_idx = 0;
+
+ for (i = 0; i < 4; ++i)
+ {
+ int index = 0x36 + i * 18;
+
+ if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
+ {
+ decode_display_descriptor (edid + index, info);
+ }
+ else
+ {
+ decode_detailed_timing (edid + index, &(info->detailed_timings[timing_idx++]));
+ }
+ }
+
+ info->n_detailed_timings = timing_idx;
+
+ return TRUE;
+}
+
+static void
+decode_check_sum (const uchar *edid,
+ MonitorInfo *info)
+{
+ int i;
+ uchar check = 0;
+
+ for (i = 0; i < 128; ++i)
+ check += edid[i];
+
+ info->checksum = check;
+}
+
+MonitorInfo *
+decode_edid (const uchar *edid)
+{
+ MonitorInfo *info = g_new0 (MonitorInfo, 1);
+
+ decode_check_sum (edid, info);
+
+ if (decode_header (edid)
+ && decode_vendor_and_product_identification (edid, info)
+ && decode_edid_version (edid, info)
+ && decode_display_parameters (edid, info)
+ && decode_color_characteristics (edid, info)
+ && decode_established_timings (edid, info)
+ && decode_standard_timings (edid, info)
+ && decode_descriptors (edid, info))
+ {
+ return info;
+ }
+ else
+ {
+ g_free (info);
+ return NULL;
+ }
+}
diff --git a/src/core/edid.h b/src/core/edid.h
new file mode 100644
index 000000000..703c639ee
--- /dev/null
+++ b/src/core/edid.h
@@ -0,0 +1,195 @@
+/* edid.h
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Soren Sandmann
+ */
+
+#ifndef EDID_H
+#define EDID_H
+
+typedef unsigned char uchar;
+typedef struct MonitorInfo MonitorInfo;
+typedef struct Timing Timing;
+typedef struct DetailedTiming DetailedTiming;
+
+typedef enum
+{
+ UNDEFINED,
+ DVI,
+ HDMI_A,
+ HDMI_B,
+ MDDI,
+ DISPLAY_PORT
+} Interface;
+
+typedef enum
+{
+ UNDEFINED_COLOR,
+ MONOCHROME,
+ RGB,
+ OTHER_COLOR
+} ColorType;
+
+typedef enum
+{
+ NO_STEREO,
+ FIELD_RIGHT,
+ FIELD_LEFT,
+ TWO_WAY_RIGHT_ON_EVEN,
+ TWO_WAY_LEFT_ON_EVEN,
+ FOUR_WAY_INTERLEAVED,
+ SIDE_BY_SIDE
+} StereoType;
+
+struct Timing
+{
+ int width;
+ int height;
+ int frequency;
+};
+
+struct DetailedTiming
+{
+ int pixel_clock;
+ int h_addr;
+ int h_blank;
+ int h_sync;
+ int h_front_porch;
+ int v_addr;
+ int v_blank;
+ int v_sync;
+ int v_front_porch;
+ int width_mm;
+ int height_mm;
+ int right_border;
+ int top_border;
+ int interlaced;
+ StereoType stereo;
+
+ int digital_sync;
+ union
+ {
+ struct
+ {
+ int bipolar;
+ int serrations;
+ int sync_on_green;
+ } analog;
+
+ struct
+ {
+ int composite;
+ int serrations;
+ int negative_vsync;
+ int negative_hsync;
+ } digital;
+ } connector;
+};
+
+struct MonitorInfo
+{
+ int checksum;
+ char manufacturer_code[4];
+ int product_code;
+ unsigned int serial_number;
+
+ int production_week; /* -1 if not specified */
+ int production_year; /* -1 if not specified */
+ int model_year; /* -1 if not specified */
+
+ int major_version;
+ int minor_version;
+
+ int is_digital;
+
+ union
+ {
+ struct
+ {
+ int bits_per_primary;
+ Interface interface;
+ int rgb444;
+ int ycrcb444;
+ int ycrcb422;
+ } digital;
+
+ struct
+ {
+ double video_signal_level;
+ double sync_signal_level;
+ double total_signal_level;
+
+ int blank_to_black;
+
+ int separate_hv_sync;
+ int composite_sync_on_h;
+ int composite_sync_on_green;
+ int serration_on_vsync;
+ ColorType color_type;
+ } analog;
+ } connector;
+
+ int width_mm; /* -1 if not specified */
+ int height_mm; /* -1 if not specified */
+ double aspect_ratio; /* -1.0 if not specififed */
+
+ double gamma; /* -1.0 if not specified */
+
+ int standby;
+ int suspend;
+ int active_off;
+
+ int srgb_is_standard;
+ int preferred_timing_includes_native;
+ int continuous_frequency;
+
+ double red_x;
+ double red_y;
+ double green_x;
+ double green_y;
+ double blue_x;
+ double blue_y;
+ double white_x;
+ double white_y;
+
+ Timing established[24]; /* Terminated by 0x0x0 */
+ Timing standard[8];
+
+ int n_detailed_timings;
+ DetailedTiming detailed_timings[4]; /* If monitor has a preferred
+ * mode, it is the first one
+ * (whether it has, is
+ * determined by the
+ * preferred_timing_includes
+ * bit.
+ */
+
+ /* Optional product description */
+ char dsc_serial_number[14];
+ char dsc_product_name[14];
+ char dsc_string[14]; /* Unspecified ASCII data */
+};
+
+MonitorInfo *decode_edid (const uchar *data);
+char *make_display_name (const MonitorInfo *info);
+char *make_display_size_string (int width_mm, int height_mm);
+
+#endif
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 8439ea830..7f58334f8 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -1965,6 +1965,23 @@ process_overlay_key (MetaDisplay *display,
return TRUE;
meta_display_overlay_key_activate (display);
}
+ else
+ {
+ /* In some rare race condition, mutter might not receive the Super_L
+ * KeyRelease event because:
+ * - the compositor might end the modal mode and call XIUngrabDevice
+ * while the key is still down
+ * - passive grabs are only activated on KeyPress and not KeyRelease.
+ *
+ * In this case, display->overlay_key_only_pressed might be wrong.
+ * Mutter still ought to acknowledge events, otherwise the X server
+ * will not send the next events.
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=666101
+ */
+ XIAllowEvents (display->xdisplay, event->deviceid,
+ XIAsyncDevice, event->time);
+ }
return TRUE;
}
diff --git a/src/core/meta-xrandr-shared.h b/src/core/meta-xrandr-shared.h
new file mode 100644
index 000000000..c98f58103
--- /dev/null
+++ b/src/core/meta-xrandr-shared.h
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * 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.
+ */
+
+/* This file is shared between mutter (src/core/meta-xrandr-shared.h)
+ and gnome-desktop (libgnome-desktop/meta-xrandr-shared.h).
+
+ The canonical place for all changes is mutter.
+
+ There should be no includes in this file.
+*/
+
+#ifndef META_XRANDR_SHARED_H
+#define META_XRANDR_SHARED_H
+
+typedef enum {
+ META_POWER_SAVE_UNKNOWN = -1,
+ META_POWER_SAVE_ON = 0,
+ META_POWER_SAVE_STANDBY,
+ META_POWER_SAVE_SUSPEND,
+ META_POWER_SAVE_OFF,
+} MetaPowerSave;
+
+#endif
diff --git a/src/core/monitor-config.c b/src/core/monitor-config.c
new file mode 100644
index 000000000..420c670b6
--- /dev/null
+++ b/src/core/monitor-config.c
@@ -0,0 +1,1769 @@
+/* -*- 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.
+ */
+
+/*
+ * Portions of this file are derived from gnome-desktop/libgnome-desktop/gnome-rr-config.c
+ *
+ * Copyright 2007, 2008, Red Hat, Inc.
+ * Copyright 2010 Giovanni Campagna
+ *
+ * Author: Soren Sandmann
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include "monitor-private.h"
+
+/* These structures represent the intended/persistent configuration,
+ as stored in the monitors.xml file.
+*/
+
+typedef struct {
+ char *connector;
+ char *vendor;
+ char *product;
+ char *serial;
+} MetaOutputKey;
+
+typedef struct {
+ gboolean enabled;
+ MetaRectangle rect;
+ float refresh_rate;
+ enum wl_output_transform transform;
+
+ gboolean is_primary;
+ gboolean is_presentation;
+} MetaOutputConfig;
+
+typedef struct {
+ MetaOutputKey *keys;
+ MetaOutputConfig *outputs;
+ unsigned int n_outputs;
+} MetaConfiguration;
+
+struct _MetaMonitorConfig {
+ GObject parent_instance;
+
+ GHashTable *configs;
+ MetaConfiguration *current;
+ gboolean current_is_stored;
+ MetaConfiguration *previous;
+
+ GFile *file;
+ GCancellable *save_cancellable;
+
+ UpClient *up_client;
+ gboolean lid_is_closed;
+};
+
+struct _MetaMonitorConfigClass {
+ GObjectClass parent;
+};
+
+G_DEFINE_TYPE (MetaMonitorConfig, meta_monitor_config, G_TYPE_OBJECT);
+
+static gboolean meta_monitor_config_assign_crtcs (MetaConfiguration *config,
+ MetaMonitorManager *manager,
+ GPtrArray *crtcs,
+ GPtrArray *outputs);
+
+static void power_client_changed_cb (UpClient *client,
+ gpointer user_data);
+
+static void
+free_output_key (MetaOutputKey *key)
+{
+ g_free (key->connector);
+ g_free (key->vendor);
+ g_free (key->product);
+ g_free (key->serial);
+}
+
+static void
+config_clear (MetaConfiguration *config)
+{
+ unsigned int i;
+
+ for (i = 0; i < config->n_outputs; i++)
+ free_output_key (&config->keys[i]);
+
+ g_free (config->keys);
+ g_free (config->outputs);
+}
+
+static void
+config_free (gpointer config)
+{
+ config_clear (config);
+ g_slice_free (MetaConfiguration, config);
+}
+
+static unsigned long
+output_key_hash (const MetaOutputKey *key)
+{
+ return g_str_hash (key->connector) ^
+ g_str_hash (key->vendor) ^
+ g_str_hash (key->product) ^
+ g_str_hash (key->serial);
+}
+
+static gboolean
+output_key_equal (const MetaOutputKey *one,
+ const MetaOutputKey *two)
+{
+ return strcmp (one->connector, two->connector) == 0 &&
+ strcmp (one->vendor, two->vendor) == 0 &&
+ strcmp (one->product, two->product) == 0 &&
+ strcmp (one->serial, two->serial) == 0;
+}
+
+static unsigned int
+config_hash (gconstpointer data)
+{
+ const MetaConfiguration *config = data;
+ unsigned int i, hash;
+
+ hash = 0;
+ for (i = 0; i < config->n_outputs; i++)
+ hash ^= output_key_hash (&config->keys[i]);
+
+ return hash;
+}
+
+static gboolean
+config_equal (gconstpointer one,
+ gconstpointer two)
+{
+ const MetaConfiguration *c_one = one;
+ const MetaConfiguration *c_two = two;
+ unsigned int i;
+ gboolean ok;
+
+ if (c_one->n_outputs != c_two->n_outputs)
+ return FALSE;
+
+ ok = TRUE;
+ for (i = 0; i < c_one->n_outputs && ok; i++)
+ ok = output_key_equal (&c_one->keys[i],
+ &c_two->keys[i]);
+
+ return ok;
+}
+
+static void
+meta_monitor_config_init (MetaMonitorConfig *self)
+{
+ const char *filename;
+ char *path;
+
+ self->configs = g_hash_table_new_full (config_hash, config_equal, NULL, config_free);
+
+ filename = g_getenv ("MUTTER_MONITOR_FILENAME");
+ if (filename == NULL)
+ filename = "monitors.xml";
+
+ path = g_build_filename (g_get_user_config_dir (), filename, NULL);
+ self->file = g_file_new_for_path (path);
+ g_free (path);
+
+ self->up_client = up_client_new ();
+ self->lid_is_closed = up_client_get_lid_is_closed (self->up_client);
+
+ g_signal_connect_object (self->up_client, "changed",
+ G_CALLBACK (power_client_changed_cb), self, 0);
+}
+
+static void
+meta_monitor_config_finalize (GObject *object)
+{
+ MetaMonitorConfig *self = META_MONITOR_CONFIG (object);
+
+ g_hash_table_destroy (self->configs);
+}
+
+static void
+meta_monitor_config_class_init (MetaMonitorConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_monitor_config_finalize;
+}
+
+typedef enum {
+ STATE_INITIAL,
+ STATE_MONITORS,
+ STATE_CONFIGURATION,
+ STATE_OUTPUT,
+ STATE_OUTPUT_FIELD,
+ STATE_CLONE
+} ParserState;
+
+typedef struct {
+ MetaMonitorConfig *config;
+ ParserState state;
+ int unknown_count;
+
+ GArray *key_array;
+ GArray *output_array;
+ MetaOutputKey key;
+ MetaOutputConfig output;
+
+ char *output_field;
+} ConfigParser;
+
+static void
+handle_start_element (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_INITIAL:
+ {
+ char *version;
+
+ if (strcmp (element_name, "monitors") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Invalid document element %s", element_name);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING, "version", &version,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ if (strcmp (version, "1") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid or unsupported version %s", version);
+ return;
+ }
+
+ parser->state = STATE_MONITORS;
+ return;
+ }
+
+ case STATE_MONITORS:
+ {
+ if (strcmp (element_name, "configuration") != 0)
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Invalid toplevel element %s", element_name);
+ return;
+ }
+
+ parser->key_array = g_array_new (FALSE, FALSE, sizeof (MetaOutputKey));
+ parser->output_array = g_array_new (FALSE, FALSE, sizeof (MetaOutputConfig));
+ parser->state = STATE_CONFIGURATION;
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (strcmp (element_name, "clone") == 0 && parser->unknown_count == 0)
+ {
+ parser->state = STATE_CLONE;
+ }
+ else if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0)
+ {
+ char *name;
+
+ if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_INVALID))
+ return;
+
+ memset (&parser->key, 0, sizeof (MetaOutputKey));
+ memset (&parser->output, 0, sizeof (MetaOutputConfig));
+
+ parser->key.connector = g_strdup (name);
+ parser->state = STATE_OUTPUT;
+ }
+ else
+ {
+ parser->unknown_count++;
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if ((strcmp (element_name, "vendor") == 0 ||
+ strcmp (element_name, "product") == 0 ||
+ strcmp (element_name, "serial") == 0 ||
+ strcmp (element_name, "width") == 0 ||
+ strcmp (element_name, "height") == 0 ||
+ strcmp (element_name, "rate") == 0 ||
+ strcmp (element_name, "x") == 0 ||
+ strcmp (element_name, "y") == 0 ||
+ strcmp (element_name, "rotation") == 0 ||
+ strcmp (element_name, "reflect_x") == 0 ||
+ strcmp (element_name, "reflect_y") == 0 ||
+ strcmp (element_name, "primary") == 0 ||
+ strcmp (element_name, "presentation") == 0) && parser->unknown_count == 0)
+ {
+ parser->state = STATE_OUTPUT_FIELD;
+
+ parser->output_field = g_strdup (element_name);
+ }
+ else
+ {
+ parser->unknown_count++;
+ }
+
+ return;
+ }
+
+ case STATE_CLONE:
+ case STATE_OUTPUT_FIELD:
+ {
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected element %s", element_name);
+ return;
+ }
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+handle_end_element (GMarkupParseContext *context,
+ const char *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_MONITORS:
+ {
+ parser->state = STATE_INITIAL;
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (strcmp (element_name, "configuration") == 0 && parser->unknown_count == 0)
+ {
+ MetaConfiguration *config = g_slice_new (MetaConfiguration);
+
+ g_assert (parser->key_array->len == parser->output_array->len);
+
+ config->n_outputs = parser->key_array->len;
+ config->keys = (void*)g_array_free (parser->key_array, FALSE);
+ config->outputs = (void*)g_array_free (parser->output_array, FALSE);
+
+ g_hash_table_replace (parser->config->configs, config, config);
+
+ parser->key_array = NULL;
+ parser->output_array = NULL;
+ parser->state = STATE_MONITORS;
+ }
+ else
+ {
+ parser->unknown_count--;
+
+ g_assert (parser->unknown_count >= 0);
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0)
+ {
+ if (parser->key.vendor == NULL ||
+ parser->key.product == NULL ||
+ parser->key.serial == NULL)
+ {
+ /* Disconnected output, ignore */
+ free_output_key (&parser->key);
+ }
+ else
+ {
+ if (parser->output.rect.width == 0 &&
+ parser->output.rect.width == 0)
+ parser->output.enabled = FALSE;
+ else
+ parser->output.enabled = TRUE;
+
+ g_array_append_val (parser->key_array, parser->key);
+ g_array_append_val (parser->output_array, parser->output);
+ }
+
+ memset (&parser->key, 0, sizeof (MetaOutputKey));
+ memset (&parser->output, 0, sizeof (MetaOutputConfig));
+
+ parser->state = STATE_CONFIGURATION;
+ }
+ else
+ {
+ parser->unknown_count--;
+
+ g_assert (parser->unknown_count >= 0);
+ }
+
+ return;
+ }
+
+ case STATE_CLONE:
+ {
+ parser->state = STATE_CONFIGURATION;
+ return;
+ }
+
+ case STATE_OUTPUT_FIELD:
+ {
+ g_free (parser->output_field);
+ parser->output_field = NULL;
+
+ parser->state = STATE_OUTPUT;
+ return;
+ }
+
+ case STATE_INITIAL:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+read_int (const char *text,
+ gsize text_len,
+ gint *field,
+ GError **error)
+{
+ char buf[64];
+ gint64 v;
+ char *end;
+
+ strncpy (buf, text, text_len);
+ buf[MIN (63, text_len)] = 0;
+
+ v = g_ascii_strtoll (buf, &end, 10);
+
+ /* Limit reasonable values (actual limits are a lot smaller that these) */
+ if (*end || v < 0 || v > G_MAXINT16)
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Expected a number, got %s", buf);
+ else
+ *field = v;
+}
+
+static void
+read_float (const char *text,
+ gsize text_len,
+ gfloat *field,
+ GError **error)
+{
+ char buf[64];
+ gfloat v;
+ char *end;
+
+ strncpy (buf, text, text_len);
+ buf[MIN (63, text_len)] = 0;
+
+ v = g_ascii_strtod (buf, &end);
+
+ /* Limit reasonable values (actual limits are a lot smaller that these) */
+ if (*end)
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Expected a number, got %s", buf);
+ else
+ *field = v;
+}
+
+static gboolean
+read_bool (const char *text,
+ gsize text_len,
+ GError **error)
+{
+ if (strncmp (text, "no", text_len) == 0)
+ return FALSE;
+ else if (strncmp (text, "yes", text_len) == 0)
+ return TRUE;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid boolean value %.*s", (int)text_len, text);
+
+ return FALSE;
+}
+
+static gboolean
+is_all_whitespace (const char *text,
+ gsize text_len)
+{
+ gsize i;
+
+ for (i = 0; i < text_len; i++)
+ if (!g_ascii_isspace (text[i]))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+handle_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ConfigParser *parser = user_data;
+
+ switch (parser->state)
+ {
+ case STATE_MONITORS:
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ return;
+ }
+
+ case STATE_CONFIGURATION:
+ {
+ if (parser->unknown_count == 0)
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ }
+ else
+ {
+ /* Handling unknown element, ignore */
+ }
+
+ return;
+ }
+
+ case STATE_OUTPUT:
+ {
+ if (parser->unknown_count == 0)
+ {
+ if (!is_all_whitespace (text, text_len))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected content at this point");
+ }
+ else
+ {
+ /* Handling unknown element, ignore */
+ }
+ return;
+ }
+
+ case STATE_CLONE:
+ {
+ /* Ignore the clone flag */
+ return;
+ }
+
+ case STATE_OUTPUT_FIELD:
+ {
+ if (strcmp (parser->output_field, "vendor") == 0)
+ parser->key.vendor = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "product") == 0)
+ parser->key.product = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "serial") == 0)
+ parser->key.serial = g_strndup (text, text_len);
+ else if (strcmp (parser->output_field, "width") == 0)
+ read_int (text, text_len, &parser->output.rect.width, error);
+ else if (strcmp (parser->output_field, "height") == 0)
+ read_int (text, text_len, &parser->output.rect.height, error);
+ else if (strcmp (parser->output_field, "rate") == 0)
+ read_float (text, text_len, &parser->output.refresh_rate, error);
+ else if (strcmp (parser->output_field, "x") == 0)
+ read_int (text, text_len, &parser->output.rect.x, error);
+ else if (strcmp (parser->output_field, "y") == 0)
+ read_int (text, text_len, &parser->output.rect.y, error);
+ else if (strcmp (parser->output_field, "rotation") == 0)
+ {
+ if (strncmp (text, "normal", text_len) == 0)
+ parser->output.transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ else if (strncmp (text, "left", text_len) == 0)
+ parser->output.transform = WL_OUTPUT_TRANSFORM_90;
+ else if (strncmp (text, "upside_down", text_len) == 0)
+ parser->output.transform = WL_OUTPUT_TRANSFORM_180;
+ else if (strncmp (text, "right", text_len) == 0)
+ parser->output.transform = WL_OUTPUT_TRANSFORM_270;
+ else
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid rotation type %.*s", (int)text_len, text);
+ }
+ else if (strcmp (parser->output_field, "reflect_x") == 0)
+ parser->output.transform += read_bool (text, text_len, error) ?
+ WL_OUTPUT_TRANSFORM_FLIPPED : 0;
+ else if (strcmp (parser->output_field, "reflect_y") == 0)
+ {
+ /* FIXME (look at the rotation map in monitor.c) */
+ if (read_bool (text, text_len, error))
+ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Y reflection is not supported");
+ }
+ else if (strcmp (parser->output_field, "primary") == 0)
+ parser->output.is_primary = read_bool (text, text_len, error);
+ else if (strcmp (parser->output_field, "presentation") == 0)
+ parser->output.is_presentation = read_bool (text, text_len, error);
+ else
+ g_assert_not_reached ();
+ return;
+ }
+
+ case STATE_INITIAL:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static const GMarkupParser config_parser = {
+ .start_element = handle_start_element,
+ .end_element = handle_end_element,
+ .text = handle_text,
+};
+
+static void
+meta_monitor_config_load (MetaMonitorConfig *self)
+{
+ char *contents;
+ gsize size;
+ gboolean ok;
+ GError *error;
+ GMarkupParseContext *context;
+ ConfigParser parser;
+
+ /* Note: we're explicitly loading this file synchronously because
+ we don't want to leave the default configuration on for even a frame, ie we
+ want atomic modeset as much as possible.
+
+ This function is called only at early initialization anyway, before
+ we connect to X or create the wayland socket.
+ */
+
+ error = NULL;
+ ok = g_file_load_contents (self->file, NULL, &contents, &size, NULL, &error);
+ if (!ok)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ meta_warning ("Failed to load stored monitor configuration: %s\n", error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ memset (&parser, 0, sizeof (ConfigParser));
+ parser.config = self;
+ parser.state = STATE_INITIAL;
+
+ context = g_markup_parse_context_new (&config_parser,
+ G_MARKUP_TREAT_CDATA_AS_TEXT |
+ G_MARKUP_PREFIX_ERROR_POSITION,
+ &parser, NULL);
+ ok = g_markup_parse_context_parse (context, contents, size, &error);
+
+ if (!ok)
+ {
+ meta_warning ("Failed to parse stored monitor configuration: %s\n", error->message);
+
+ g_error_free (error);
+
+ if (parser.key_array)
+ g_array_free (parser.key_array, TRUE);
+ if (parser.output_array)
+ g_array_free (parser.output_array, TRUE);
+
+ free_output_key (&parser.key);
+ }
+}
+
+MetaMonitorConfig *
+meta_monitor_config_new (void)
+{
+ MetaMonitorConfig *self;
+
+ self = g_object_new (META_TYPE_MONITOR_CONFIG, NULL);
+ meta_monitor_config_load (self);
+
+ return self;
+}
+
+static void
+init_key_from_output (MetaOutputKey *key,
+ MetaOutput *output)
+{
+ key->connector = g_strdup (output->name);
+ key->product = g_strdup (output->product);
+ key->vendor = g_strdup (output->vendor);
+ key->serial = g_strdup (output->serial);
+}
+
+static void
+make_config_key (MetaConfiguration *key,
+ MetaOutput *outputs,
+ unsigned n_outputs,
+ unsigned skip)
+{
+ unsigned int o, i;
+
+ key->outputs = NULL;
+ key->keys = g_new0 (MetaOutputKey, n_outputs);
+
+ for (o = 0, i = 0; i < n_outputs; o++, i++)
+ if (i == skip)
+ o--;
+ else
+ init_key_from_output (&key->keys[o], &outputs[i]);
+
+ key->n_outputs = o;
+}
+
+gboolean
+meta_monitor_config_match_current (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ MetaOutput *outputs;
+ unsigned n_outputs;
+ MetaConfiguration key;
+ gboolean ok;
+
+ if (self->current == NULL)
+ return FALSE;
+
+ outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
+
+ make_config_key (&key, outputs, n_outputs, -1);
+ ok = config_equal (&key, self->current);
+
+ config_clear (&key);
+ return ok;
+}
+
+static MetaConfiguration *
+meta_monitor_config_get_stored (MetaMonitorConfig *self,
+ MetaOutput *outputs,
+ unsigned n_outputs)
+{
+ MetaConfiguration key;
+ MetaConfiguration *stored;
+
+ make_config_key (&key, outputs, n_outputs, -1);
+ stored = g_hash_table_lookup (self->configs, &key);
+
+ config_clear (&key);
+ return stored;
+}
+
+static gboolean
+apply_configuration (MetaMonitorConfig *self,
+ MetaConfiguration *config,
+ MetaMonitorManager *manager,
+ gboolean stored)
+{
+ GPtrArray *crtcs, *outputs;
+
+ crtcs = g_ptr_array_new_full (config->n_outputs, (GDestroyNotify)meta_crtc_info_free);
+ outputs = g_ptr_array_new_full (config->n_outputs, (GDestroyNotify)meta_output_info_free);
+
+ if (!meta_monitor_config_assign_crtcs (config, manager, crtcs, outputs))
+ {
+ g_ptr_array_unref (crtcs);
+ g_ptr_array_unref (outputs);
+ if (!stored)
+ config_free (config);
+
+ return FALSE;
+ }
+
+ meta_monitor_manager_apply_configuration (manager,
+ (MetaCRTCInfo**)crtcs->pdata, crtcs->len,
+ (MetaOutputInfo**)outputs->pdata, outputs->len);
+
+ /* Stored (persistent) configurations override the previous one always.
+ Also, we clear the previous configuration if the current one (which is
+ about to become previous) is stored.
+ */
+ if (stored ||
+ (self->current && self->current_is_stored))
+ {
+ if (self->previous)
+ config_free (self->previous);
+ self->previous = NULL;
+ }
+ else
+ {
+ self->previous = self->current;
+ }
+
+ self->current = config;
+ self->current_is_stored = stored;
+
+ if (self->current == self->previous)
+ self->previous = NULL;
+
+ g_ptr_array_unref (crtcs);
+ g_ptr_array_unref (outputs);
+ return TRUE;
+}
+
+static gboolean
+key_is_laptop (MetaOutputKey *key)
+{
+ /* FIXME: extend with better heuristics */
+ return g_str_has_prefix (key->connector, "LVDS") ||
+ g_str_has_prefix (key->connector, "eDP");
+}
+
+static gboolean
+laptop_display_is_on (MetaConfiguration *config)
+{
+ unsigned int i;
+
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ MetaOutputKey *key = &config->keys[i];
+ MetaOutputConfig *output = &config->outputs[i];
+
+ if (key_is_laptop (key) && output->enabled)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static MetaConfiguration *
+make_laptop_lid_config (MetaConfiguration *reference)
+{
+ MetaConfiguration *new;
+ unsigned int i;
+ gboolean has_primary;
+
+ g_assert (reference->n_outputs > 1);
+
+ new = g_slice_new0 (MetaConfiguration);
+ new->n_outputs = reference->n_outputs;
+ new->keys = g_new0 (MetaOutputKey, reference->n_outputs);
+ new->outputs = g_new0 (MetaOutputConfig, reference->n_outputs);
+
+ for (i = 0; i < new->n_outputs; i++)
+ {
+ MetaOutputKey *current_key = &reference->keys[i];
+ MetaOutputConfig *current_output = &reference->outputs[i];
+
+ new->keys[i].connector = g_strdup (current_key->connector);
+ new->keys[i].vendor = g_strdup (current_key->vendor);
+ new->keys[i].product = g_strdup (current_key->product);
+ new->keys[i].serial = g_strdup (current_key->serial);
+
+ if (g_str_has_prefix (current_key->connector, "LVDS") ||
+ g_str_has_prefix (current_key->connector, "eDP"))
+ new->outputs[i].enabled = FALSE;
+ else
+ /* This can potentially leave a "hole" in the screen,
+ but this is actually a good thing, as it means windows
+ don't move around.
+ */
+ new->outputs[i] = *current_output;
+ }
+
+ has_primary = FALSE;
+ for (i = 0; i < new->n_outputs; i++)
+ {
+ if (new->outputs[i].is_primary)
+ {
+ has_primary = TRUE;
+ break;
+ }
+ }
+ if (!has_primary)
+ new->outputs[0].is_primary = TRUE;
+
+ return new;
+}
+
+gboolean
+meta_monitor_config_apply_stored (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ MetaOutput *outputs;
+ MetaConfiguration *stored;
+ unsigned n_outputs;
+
+ outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
+ stored = meta_monitor_config_get_stored (self, outputs, n_outputs);
+
+ if (stored)
+ {
+ if (self->lid_is_closed &&
+ stored->n_outputs > 1 &&
+ laptop_display_is_on (stored))
+ return apply_configuration (self, make_laptop_lid_config (stored),
+ manager, FALSE);
+ else
+ return apply_configuration (self, stored, manager, TRUE);
+ }
+ else
+ return FALSE;
+}
+
+/*
+ * Tries to find the primary output according to the current layout,
+ * or failing that, an output that is good to be a primary (LVDS or eDP,
+ * which are internal monitors), or failing that, the one with the
+ * best resolution
+ */
+static MetaOutput *
+find_primary_output (MetaOutput *outputs,
+ unsigned n_outputs)
+{
+ unsigned i;
+ MetaOutput *best;
+ int best_width, best_height;
+
+ g_assert (n_outputs >= 1);
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ if (outputs[i].is_primary)
+ return &outputs[i];
+ }
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ if (g_str_has_prefix (outputs[i].name, "LVDS") ||
+ g_str_has_prefix (outputs[i].name, "eDP"))
+ return &outputs[i];
+ }
+
+ best = NULL;
+ best_width = 0; best_height = 0;
+ for (i = 0; i < n_outputs; i++)
+ {
+ if (outputs[i].preferred_mode->width * outputs[i].preferred_mode->height >
+ best_width * best_height)
+ {
+ best = &outputs[i];
+ best_width = outputs[i].preferred_mode->width;
+ best_height = outputs[i].preferred_mode->height;
+ }
+ }
+
+ return best;
+}
+
+static MetaConfiguration *
+make_default_config (MetaMonitorConfig *self,
+ MetaOutput *outputs,
+ unsigned n_outputs,
+ int max_width,
+ int max_height)
+{
+ unsigned i, j;
+ int x, y;
+ MetaConfiguration *ret;
+ MetaOutput *primary;
+
+ ret = g_slice_new (MetaConfiguration);
+ make_config_key (ret, outputs, n_outputs, -1);
+ ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+ /* Special case the simple case: one output, primary at preferred mode,
+ nothing else to do */
+ if (n_outputs == 1)
+ {
+ ret->outputs[0].enabled = TRUE;
+ ret->outputs[0].rect.x = 0;
+ ret->outputs[0].rect.y = 0;
+ ret->outputs[0].rect.width = outputs[0].preferred_mode->width;
+ ret->outputs[0].rect.height = outputs[0].preferred_mode->height;
+ ret->outputs[0].refresh_rate = outputs[0].preferred_mode->refresh_rate;
+ ret->outputs[0].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ ret->outputs[0].is_primary = TRUE;
+
+ return ret;
+ }
+
+ /* If we reach this point, this is either the first time mutter runs
+ on this system ever, or we just hotplugged a new screen.
+ In the latter case, search for a configuration that includes one
+ less screen, then add the new one as a presentation screen
+ in preferred mode.
+
+ XXX: but presentation mode is not implemented in the control-center
+ or in mutter core, so let's do extended for now.
+ */
+ x = 0;
+ y = 0;
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaConfiguration key;
+ MetaConfiguration *ref;
+
+ make_config_key (&key, outputs, n_outputs, i);
+ ref = g_hash_table_lookup (self->configs, &key);
+ config_clear (&key);
+
+ if (ref)
+ {
+ for (j = 0; j < n_outputs; j++)
+ {
+ if (j < i)
+ {
+ g_assert (output_key_equal (&ret->keys[j], &ref->keys[j]));
+ ret->outputs[j] = ref->outputs[j];
+ x = MAX (x, ref->outputs[j].rect.x + ref->outputs[j].rect.width);
+ y = MAX (y, ref->outputs[j].rect.y + ref->outputs[j].rect.height);
+ }
+ else if (j > i)
+ {
+ g_assert (output_key_equal (&ret->keys[j], &ref->keys[j - 1]));
+ ret->outputs[j] = ref->outputs[j - 1];
+ x = MAX (x, ref->outputs[j - 1].rect.x + ref->outputs[j - 1].rect.width);
+ y = MAX (y, ref->outputs[j - 1].rect.y + ref->outputs[j - 1].rect.height);
+ }
+ else
+ {
+ ret->outputs[j].enabled = TRUE;
+ ret->outputs[j].rect.x = 0;
+ ret->outputs[j].rect.y = 0;
+ ret->outputs[j].rect.width = outputs[0].preferred_mode->width;
+ ret->outputs[j].rect.height = outputs[0].preferred_mode->height;
+ ret->outputs[j].refresh_rate = outputs[0].preferred_mode->refresh_rate;
+ ret->outputs[j].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ ret->outputs[j].is_primary = FALSE;
+ ret->outputs[j].is_presentation = FALSE;
+ }
+ }
+
+ /* Place the new output at the right end of the screen, if it fits,
+ otherwise below it, otherwise disable it (or apply_configuration will fail) */
+ if (x + ret->outputs[i].rect.width <= max_width)
+ ret->outputs[i].rect.x = x;
+ else if (y + ret->outputs[i].rect.height <= max_height)
+ ret->outputs[i].rect.y = y;
+ else
+ ret->outputs[i].enabled = FALSE;
+
+ return ret;
+ }
+ }
+
+ /* No previous configuration found, try with a really default one, which
+ is one primary that goes first and the rest to the right of it, extended.
+ */
+ primary = find_primary_output (outputs, n_outputs);
+
+ x = primary->preferred_mode->width;
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaOutput *output = &outputs[i];
+
+ ret->outputs[i].enabled = TRUE;
+ ret->outputs[i].rect.x = (output == primary) ? 0 : x;
+ ret->outputs[i].rect.y = 0;
+ ret->outputs[i].rect.width = output->preferred_mode->width;
+ ret->outputs[i].rect.height = output->preferred_mode->height;
+ ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate;
+ ret->outputs[i].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ ret->outputs[i].is_primary = (output == primary);
+
+ /* Disable outputs that would go beyond framebuffer limits */
+ if (ret->outputs[i].rect.x + ret->outputs[i].rect.width > max_width)
+ ret->outputs[i].enabled = FALSE;
+ else if (output != primary)
+ x += output->preferred_mode->width;
+ }
+
+ return ret;
+}
+
+static gboolean
+ensure_at_least_one_output (MetaMonitorConfig *self,
+ MetaMonitorManager *manager,
+ MetaOutput *outputs,
+ unsigned n_outputs)
+{
+ MetaConfiguration *ret;
+ MetaOutput *primary;
+ unsigned i;
+
+ /* Check that we have at least one active output */
+ for (i = 0; i < n_outputs; i++)
+ if (outputs[i].crtc != NULL)
+ return TRUE;
+
+ /* Oh no, we don't! Activate the primary one and disable everything else */
+
+ ret = g_slice_new (MetaConfiguration);
+ make_config_key (ret, outputs, n_outputs, -1);
+ ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
+
+ primary = find_primary_output (outputs, n_outputs);
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaOutput *output = &outputs[i];
+
+ if (output == primary)
+ {
+ ret->outputs[i].enabled = TRUE;
+ ret->outputs[i].rect.x = 0;
+ ret->outputs[i].rect.y = 0;
+ ret->outputs[i].rect.width = output->preferred_mode->width;
+ ret->outputs[i].rect.height = output->preferred_mode->height;
+ ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate;
+ ret->outputs[i].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ ret->outputs[i].is_primary = TRUE;
+ }
+ else
+ {
+ ret->outputs[i].enabled = FALSE;
+ }
+ }
+
+ apply_configuration (self, ret, manager, FALSE);
+ return FALSE;
+}
+
+void
+meta_monitor_config_make_default (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ MetaOutput *outputs;
+ MetaConfiguration *default_config;
+ unsigned n_outputs;
+ gboolean ok;
+ int max_width, max_height;
+
+ outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
+ meta_monitor_manager_get_screen_limits (manager, &max_width, &max_height);
+
+ default_config = make_default_config (self, outputs, n_outputs, max_width, max_height);
+
+ if (default_config != NULL)
+ {
+ if (self->lid_is_closed &&
+ default_config->n_outputs > 1 &&
+ laptop_display_is_on (default_config))
+ {
+ ok = apply_configuration (self, make_laptop_lid_config (default_config),
+ manager, FALSE);
+ config_free (default_config);
+ }
+ else
+ ok = apply_configuration (self, default_config, manager, FALSE);
+ }
+ else
+ ok = FALSE;
+
+ if (!ok)
+ {
+ meta_warning ("Could not make default configuration for current output layout, leaving unconfigured\n");
+ if (ensure_at_least_one_output (self, manager, outputs, n_outputs))
+ meta_monitor_config_update_current (self, manager);
+ }
+}
+
+static void
+init_config_from_output (MetaOutputConfig *config,
+ MetaOutput *output)
+{
+ config->enabled = (output->crtc != NULL);
+
+ if (!config->enabled)
+ return;
+
+ config->rect = output->crtc->rect;
+ config->refresh_rate = output->crtc->current_mode->refresh_rate;
+ config->transform = output->crtc->transform;
+ config->is_primary = output->is_primary;
+ config->is_presentation = output->is_presentation;
+}
+
+void
+meta_monitor_config_update_current (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ MetaOutput *outputs;
+ unsigned n_outputs;
+ MetaConfiguration *current;
+ unsigned int i;
+
+ outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
+
+ current = g_slice_new (MetaConfiguration);
+ current->n_outputs = n_outputs;
+ current->outputs = g_new0 (MetaOutputConfig, n_outputs);
+ current->keys = g_new0 (MetaOutputKey, n_outputs);
+
+ for (i = 0; i < current->n_outputs; i++)
+ {
+ init_key_from_output (¤t->keys[i], &outputs[i]);
+ init_config_from_output (¤t->outputs[i], &outputs[i]);
+ }
+
+ if (self->current && !self->current_is_stored)
+ config_free (self->current);
+
+ self->current = current;
+ self->current_is_stored = FALSE;
+}
+
+void
+meta_monitor_config_restore_previous (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ if (self->previous)
+ apply_configuration (self, self->previous, manager, FALSE);
+ else
+ {
+ if (!meta_monitor_config_apply_stored (self, manager))
+ meta_monitor_config_make_default (self, manager);
+ }
+}
+
+static void
+turn_off_laptop_display (MetaMonitorConfig *self,
+ MetaMonitorManager *manager)
+{
+ MetaConfiguration *new;
+
+ if (self->current->n_outputs == 1)
+ return;
+
+ new = make_laptop_lid_config (self->current);
+ apply_configuration (self, new, manager, FALSE);
+}
+
+static void
+power_client_changed_cb (UpClient *client,
+ gpointer user_data)
+{
+ MetaMonitorManager *manager = meta_monitor_manager_get ();
+ MetaMonitorConfig *self = user_data;
+ gboolean is_closed;
+
+ is_closed = up_client_get_lid_is_closed (self->up_client);
+
+ if (is_closed != self->lid_is_closed)
+ {
+ self->lid_is_closed = is_closed;
+
+ if (is_closed)
+ turn_off_laptop_display (self, manager);
+ else
+ meta_monitor_config_restore_previous (self, manager);
+ }
+}
+
+typedef struct {
+ MetaMonitorConfig *config;
+ GString *buffer;
+} SaveClosure;
+
+static void
+saved_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SaveClosure *closure = user_data;
+ GError *error;
+ gboolean ok;
+
+ error = NULL;
+ ok = g_file_replace_contents_finish (G_FILE (object), result, NULL, &error);
+ if (!ok)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ meta_warning ("Saving monitor configuration failed: %s\n", error->message);
+
+ g_error_free (error);
+ }
+
+ g_clear_object (&closure->config->save_cancellable);
+ g_object_unref (closure->config);
+ g_string_free (closure->buffer, TRUE);
+
+ g_slice_free (SaveClosure, closure);
+}
+
+static void
+meta_monitor_config_save (MetaMonitorConfig *self)
+{
+ static const char * const rotation_map[4] = {
+ "normal",
+ "left",
+ "upside_down",
+ "right"
+ };
+ SaveClosure *closure;
+ GString *buffer;
+ GHashTableIter iter;
+ MetaConfiguration *config;
+ unsigned int i;
+
+ if (self->save_cancellable)
+ {
+ g_cancellable_cancel (self->save_cancellable);
+ g_object_unref (self->save_cancellable);
+ self->save_cancellable = NULL;
+ }
+
+ self->save_cancellable = g_cancellable_new ();
+
+ buffer = g_string_new ("\n");
+
+ g_hash_table_iter_init (&iter, self->configs);
+ while (g_hash_table_iter_next (&iter, (gpointer*) &config, NULL))
+ {
+ /* Note: we don't distinguish clone vs non-clone here, that's
+ something for the UI (ie gnome-control-center) to handle,
+ and our configurations are more complex anyway.
+ */
+
+ g_string_append (buffer,
+ " \n"
+ " no\n");
+
+ for (i = 0; i < config->n_outputs; i++)
+ {
+ MetaOutputKey *key = &config->keys[i];
+ MetaOutputConfig *output = &config->outputs[i];
+
+ g_string_append_printf (buffer,
+ " \n");
+ }
+
+ g_string_append (buffer, " \n");
+ }
+
+ g_string_append (buffer, "\n");
+
+ closure = g_slice_new (SaveClosure);
+ closure->config = g_object_ref (self);
+ closure->buffer = buffer;
+
+ g_file_replace_contents_async (self->file,
+ buffer->str, buffer->len,
+ NULL, /* etag */
+ TRUE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ self->save_cancellable,
+ saved_cb, closure);
+}
+
+void
+meta_monitor_config_make_persistent (MetaMonitorConfig *self)
+{
+ if (self->current_is_stored)
+ return;
+
+ self->current_is_stored = TRUE;
+ g_hash_table_replace (self->configs, self->current, self->current);
+
+ if (self->previous)
+ config_free (self->previous);
+ self->previous = NULL;
+
+ meta_monitor_config_save (self);
+}
+
+/*
+ * CRTC assignment
+ */
+typedef struct
+{
+ MetaConfiguration *config;
+ MetaMonitorManager *manager;
+ GHashTable *info;
+} CrtcAssignment;
+
+static gboolean
+output_can_clone (MetaOutput *output,
+ MetaOutput *clone)
+{
+ unsigned int i;
+
+ for (i = 0; i < output->n_possible_clones; i++)
+ if (output->possible_clones[i] == clone)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+can_clone (MetaCRTCInfo *info,
+ MetaOutput *output)
+{
+ unsigned int i;
+
+ for (i = 0; i < info->outputs->len; ++i)
+ {
+ MetaOutput *clone = info->outputs->pdata[i];
+
+ if (!output_can_clone (clone, output))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+crtc_can_drive_output (MetaCRTC *crtc,
+ MetaOutput *output)
+{
+ unsigned int i;
+
+ for (i = 0; i < output->n_possible_crtcs; i++)
+ if (output->possible_crtcs[i] == crtc)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+output_supports_mode (MetaOutput *output,
+ MetaMonitorMode *mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < output->n_modes; i++)
+ if (output->modes[i] == mode)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+crtc_assignment_assign (CrtcAssignment *assign,
+ MetaCRTC *crtc,
+ MetaMonitorMode *mode,
+ int x,
+ int y,
+ enum wl_output_transform transform,
+ MetaOutput *output)
+{
+ MetaCRTCInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+ if (!crtc_can_drive_output (crtc, output))
+ return FALSE;
+
+ if (!output_supports_mode (output, mode))
+ return FALSE;
+
+ if ((crtc->all_transforms & (1 << transform)) == 0)
+ return FALSE;
+
+ if (info)
+ {
+ if (!(info->mode == mode &&
+ info->x == x &&
+ info->y == y &&
+ info->transform == transform))
+ return FALSE;
+
+ if (!can_clone (info, output))
+ return FALSE;
+
+ g_ptr_array_add (info->outputs, output);
+ return TRUE;
+ }
+ else
+ {
+ MetaCRTCInfo *info = g_slice_new0 (MetaCRTCInfo);
+
+ info->crtc = crtc;
+ info->mode = mode;
+ info->x = x;
+ info->y = y;
+ info->transform = transform;
+ info->outputs = g_ptr_array_new ();
+
+ g_ptr_array_add (info->outputs, output);
+ g_hash_table_insert (assign->info, crtc, info);
+
+ return TRUE;
+ }
+}
+
+static void
+crtc_assignment_unassign (CrtcAssignment *assign,
+ MetaCRTC *crtc,
+ MetaOutput *output)
+{
+ MetaCRTCInfo *info = g_hash_table_lookup (assign->info, crtc);
+
+ if (info)
+ {
+ g_ptr_array_remove (info->outputs, output);
+
+ if (info->outputs->len == 0)
+ g_hash_table_remove (assign->info, crtc);
+ }
+}
+
+static MetaOutput *
+find_output_by_key (MetaOutput *outputs,
+ unsigned int n_outputs,
+ MetaOutputKey *key)
+{
+ unsigned int i;
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ if (strcmp (outputs[i].name, key->connector) == 0)
+ {
+ /* This should be checked a lot earlier! */
+
+ g_warn_if_fail (strcmp (outputs[i].vendor, key->vendor) == 0 &&
+ strcmp (outputs[i].product, key->product) == 0 &&
+ strcmp (outputs[i].serial, key->serial) == 0);
+ return &outputs[i];
+ }
+ }
+
+ /* Just to satisfy GCC - this is a fatal error if occurs */
+ return NULL;
+}
+
+/* Check whether the given set of settings can be used
+ * at the same time -- ie. whether there is an assignment
+ * of CRTC's to outputs.
+ *
+ * Brute force - the number of objects involved is small
+ * enough that it doesn't matter.
+ */
+static gboolean
+real_assign_crtcs (CrtcAssignment *assignment,
+ unsigned int output_num)
+{
+ MetaMonitorMode *modes;
+ MetaCRTC *crtcs;
+ MetaOutput *outputs;
+ unsigned int n_crtcs, n_modes, n_outputs;
+ MetaOutputKey *output_key;
+ MetaOutputConfig *output_config;
+ unsigned int i;
+ gboolean success;
+
+ if (output_num == assignment->config->n_outputs)
+ return TRUE;
+
+ output_key = &assignment->config->keys[output_num];
+ output_config = &assignment->config->outputs[output_num];
+
+ /* It is always allowed for an output to be turned off */
+ if (!output_config->enabled)
+ return real_assign_crtcs (assignment, output_num + 1);
+
+ meta_monitor_manager_get_resources (assignment->manager,
+ &modes, &n_modes,
+ &crtcs, &n_crtcs,
+ &outputs, &n_outputs);
+
+ success = FALSE;
+
+ for (i = 0; i < n_crtcs; i++)
+ {
+ MetaCRTC *crtc = &crtcs[i];
+ unsigned int pass;
+
+ /* Make two passes, one where frequencies must match, then
+ * one where they don't have to
+ */
+ for (pass = 0; pass < 2; pass++)
+ {
+ MetaOutput *output = find_output_by_key (outputs, n_outputs, output_key);
+ unsigned int j;
+
+ for (j = 0; j < n_modes; j++)
+ {
+ MetaMonitorMode *mode = &modes[j];
+ int width, height;
+
+ if (meta_monitor_transform_is_rotated (output_config->transform))
+ {
+ width = mode->height;
+ height = mode->width;
+ }
+ else
+ {
+ width = mode->width;
+ height = mode->height;
+ }
+
+ if (width == output_config->rect.width &&
+ height == output_config->rect.height &&
+ (pass == 1 || mode->refresh_rate == output_config->refresh_rate))
+ {
+ meta_verbose ("CRTC %ld: trying mode %dx%d@%fHz with output at %dx%d@%fHz (transform %d) (pass %d)\n",
+ crtc->crtc_id,
+ mode->width, mode->height, mode->refresh_rate,
+ output_config->rect.width, output_config->rect.height, output_config->refresh_rate,
+ output_config->transform,
+ pass);
+
+
+ if (crtc_assignment_assign (assignment, crtc, &modes[j],
+ output_config->rect.x, output_config->rect.y,
+ output_config->transform,
+ output))
+ {
+ if (real_assign_crtcs (assignment, output_num + 1))
+ {
+ success = TRUE;
+ goto out;
+ }
+
+ crtc_assignment_unassign (assignment, crtc, output);
+ }
+ }
+ }
+ }
+ }
+
+out:
+ return success;
+}
+
+static gboolean
+meta_monitor_config_assign_crtcs (MetaConfiguration *config,
+ MetaMonitorManager *manager,
+ GPtrArray *crtcs,
+ GPtrArray *outputs)
+{
+ CrtcAssignment assignment;
+ GHashTableIter iter;
+ MetaCRTC *crtc;
+ MetaCRTCInfo *info;
+ unsigned int i;
+ MetaOutput *all_outputs;
+ unsigned int n_outputs;
+
+ assignment.config = config;
+ assignment.manager = manager;
+ assignment.info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)meta_crtc_info_free);
+
+ if (!real_assign_crtcs (&assignment, 0))
+ {
+ meta_warning ("Could not assign CRTC to outputs, ignoring configuration\n");
+
+ g_hash_table_destroy (assignment.info);
+ return FALSE;
+ }
+
+ g_hash_table_iter_init (&iter, assignment.info);
+ while (g_hash_table_iter_next (&iter, (void**)&crtc, (void**)&info))
+ {
+ g_hash_table_iter_steal (&iter);
+ g_ptr_array_add (crtcs, info);
+ }
+
+ all_outputs = meta_monitor_manager_get_outputs (manager,
+ &n_outputs);
+ g_assert (n_outputs == config->n_outputs);
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaOutputInfo *output_info = g_slice_new (MetaOutputInfo);
+ MetaOutputConfig *output_config = &config->outputs[i];
+
+ output_info->output = find_output_by_key (all_outputs, n_outputs,
+ &config->keys[i]);
+ output_info->is_primary = output_config->is_primary;
+ output_info->is_presentation = output_config->is_presentation;
+
+ g_ptr_array_add (outputs, output_info);
+ }
+
+ g_hash_table_destroy (assignment.info);
+ return TRUE;
+}
+
+void
+meta_crtc_info_free (MetaCRTCInfo *info)
+{
+ g_ptr_array_free (info->outputs, TRUE);
+ g_slice_free (MetaCRTCInfo, info);
+}
+
+void
+meta_output_info_free (MetaOutputInfo *info)
+{
+ g_slice_free (MetaOutputInfo, info);
+}
diff --git a/src/core/monitor-private.h b/src/core/monitor-private.h
new file mode 100644
index 000000000..b0fd70c4c
--- /dev/null
+++ b/src/core/monitor-private.h
@@ -0,0 +1,390 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * \file screen-private.h Handling of monitor configuration
+ *
+ * Managing multiple monitors
+ * This file contains structures and functions that handle
+ * multiple monitors, including reading the current configuration
+ * and available hardware, and applying it.
+ *
+ * This interface is private to mutter, API users should look
+ * at MetaScreen instead.
+ */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * 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.
+ */
+
+#ifndef META_MONITOR_PRIVATE_H
+#define META_MONITOR_PRIVATE_H
+
+#include
+#include
+
+#include "display-private.h"
+#include
+#include "stack-tracker.h"
+#include "ui.h"
+#ifdef HAVE_WAYLAND
+#include
+#endif
+#include "meta-xrandr-shared.h"
+
+#include "meta-dbus-xrandr.h"
+
+typedef struct _MetaMonitorManagerClass MetaMonitorManagerClass;
+typedef struct _MetaMonitorManager MetaMonitorManager;
+typedef struct _MetaMonitorConfigClass MetaMonitorConfigClass;
+typedef struct _MetaMonitorConfig MetaMonitorConfig;
+
+#ifndef HAVE_WAYLAND
+enum wl_output_transform {
+ WL_OUTPUT_TRANSFORM_NORMAL,
+ WL_OUTPUT_TRANSFORM_90,
+ WL_OUTPUT_TRANSFORM_180,
+ WL_OUTPUT_TRANSFORM_270,
+ WL_OUTPUT_TRANSFORM_FLIPPED,
+ WL_OUTPUT_TRANSFORM_FLIPPED_90,
+ WL_OUTPUT_TRANSFORM_FLIPPED_180,
+ WL_OUTPUT_TRANSFORM_FLIPPED_270
+};
+#endif
+
+typedef struct _MetaOutput MetaOutput;
+typedef struct _MetaCRTC MetaCRTC;
+typedef struct _MetaMonitorMode MetaMonitorMode;
+typedef struct _MetaMonitorInfo MetaMonitorInfo;
+typedef struct _MetaCRTCInfo MetaCRTCInfo;
+typedef struct _MetaOutputInfo MetaOutputInfo;
+
+struct _MetaOutput
+{
+ /* The CRTC driving this output, NULL if the output is not enabled */
+ MetaCRTC *crtc;
+ /* The low-level ID of this output, used to apply back configuration */
+ glong output_id;
+ char *name;
+ char *vendor;
+ char *product;
+ char *serial;
+ int width_mm;
+ int height_mm;
+ CoglSubpixelOrder subpixel_order;
+
+ MetaMonitorMode *preferred_mode;
+ MetaMonitorMode **modes;
+ unsigned int n_modes;
+
+ MetaCRTC **possible_crtcs;
+ unsigned int n_possible_crtcs;
+
+ MetaOutput **possible_clones;
+ unsigned int n_possible_clones;
+
+ int backlight;
+ int backlight_min;
+ int backlight_max;
+
+ /* Used when changing configuration */
+ gboolean is_dirty;
+
+ /* The low-level bits used to build the high-level info
+ in MetaMonitorInfo
+
+ XXX: flags maybe?
+ There is a lot of code that uses MonitorInfo->is_primary,
+ but nobody uses MetaOutput yet
+ */
+ gboolean is_primary;
+ gboolean is_presentation;
+};
+
+struct _MetaCRTC
+{
+ glong crtc_id;
+ MetaRectangle rect;
+ MetaMonitorMode *current_mode;
+ enum wl_output_transform transform;
+ unsigned int all_transforms;
+
+ /* Only used to build the logical configuration
+ from the HW one
+ */
+ MetaMonitorInfo *logical_monitor;
+
+ /* Used when changing configuration */
+ gboolean is_dirty;
+};
+
+struct _MetaMonitorMode
+{
+ /* The low-level ID of this mode, used to apply back configuration */
+ glong mode_id;
+
+ int width;
+ int height;
+ float refresh_rate;
+};
+
+/**
+ * MetaMonitorInfo:
+ *
+ * A structure with high-level information about monitors.
+ * This corresponds to a subset of the compositor coordinate space.
+ * Clones are only reported once, irrespective of the way
+ * they're implemented (two CRTCs configured for the same
+ * coordinates or one CRTCs driving two outputs). Inactive CRTCs
+ * are ignored, and so are disabled outputs.
+ */
+struct _MetaMonitorInfo
+{
+ int number;
+ int xinerama_index;
+ MetaRectangle rect;
+ gboolean is_primary;
+ gboolean is_presentation; /* XXX: not yet used */
+ gboolean in_fullscreen;
+
+ /* The primary or first output for this monitor, 0 if we can't figure out.
+ It can be matched to an output_id of a MetaOutput.
+
+ This is used as an opaque token on reconfiguration when switching from
+ clone to extened, to decide on what output the windows should go next
+ (it's an attempt to keep windows on the same monitor, and preferably on
+ the primary one).
+ */
+ glong output_id;
+};
+
+/*
+ * MetaCRTCInfo:
+ * This represents the writable part of a CRTC, as deserialized from DBus
+ * or built by MetaMonitorConfig
+ *
+ * Note: differently from the other structures in this file, MetaCRTCInfo
+ * is handled by pointer. This is to accomodate the usage in MetaMonitorConfig
+ */
+struct _MetaCRTCInfo {
+ MetaCRTC *crtc;
+ MetaMonitorMode *mode;
+ int x;
+ int y;
+ enum wl_output_transform transform;
+ GPtrArray *outputs;
+};
+
+/*
+ * MetaOutputInfo:
+ * this is the same as MetaOutputInfo, but for CRTCs
+ */
+struct _MetaOutputInfo {
+ MetaOutput *output;
+ gboolean is_primary;
+ gboolean is_presentation;
+};
+
+#define META_TYPE_MONITOR_MANAGER (meta_monitor_manager_get_type ())
+#define META_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManager))
+#define META_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
+#define META_IS_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER))
+#define META_IS_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER))
+#define META_MONITOR_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
+
+struct _MetaMonitorManager
+{
+ MetaDBusDisplayConfigSkeleton parent_instance;
+
+ /* XXX: this structure is very badly
+ packed, but I like the logical organization
+ of fields */
+
+ gboolean in_init;
+ unsigned int serial;
+
+ MetaPowerSave power_save_mode;
+
+ int max_screen_width;
+ int max_screen_height;
+ int screen_width;
+ int screen_height;
+
+ /* 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
+ */
+ MetaOutput *outputs;
+ unsigned int n_outputs;
+
+ MetaMonitorMode *modes;
+ unsigned int n_modes;
+
+ MetaCRTC *crtcs;
+ unsigned int n_crtcs;
+
+ MetaMonitorInfo *monitor_infos;
+ unsigned int n_monitor_infos;
+ int primary_monitor_index;
+
+ int dbus_name_id;
+
+ int persistent_timeout_id;
+ MetaMonitorConfig *config;
+
+ GnomePnpIds *pnp_ids;
+};
+
+struct _MetaMonitorManagerClass
+{
+ MetaDBusDisplayConfigSkeletonClass parent_class;
+
+ void (*read_current) (MetaMonitorManager *);
+
+ char* (*get_edid_file) (MetaMonitorManager *,
+ MetaOutput *);
+ GBytes* (*read_edid) (MetaMonitorManager *,
+ MetaOutput *);
+
+ void (*apply_configuration) (MetaMonitorManager *,
+ MetaCRTCInfo **,
+ unsigned int ,
+ MetaOutputInfo **,
+ unsigned int);
+
+ void (*set_power_save_mode) (MetaMonitorManager *,
+ MetaPowerSave);
+
+ void (*change_backlight) (MetaMonitorManager *,
+ MetaOutput *,
+ int);
+
+ void (*get_crtc_gamma) (MetaMonitorManager *,
+ MetaCRTC *,
+ gsize *,
+ unsigned short **,
+ unsigned short **,
+ unsigned short **);
+ void (*set_crtc_gamma) (MetaMonitorManager *,
+ MetaCRTC *,
+ gsize ,
+ unsigned short *,
+ unsigned short *,
+ unsigned short *);
+
+ gboolean (*handle_xevent) (MetaMonitorManager *,
+ XEvent *);
+};
+
+GType meta_monitor_manager_get_type (void);
+
+void meta_monitor_manager_initialize (void);
+MetaMonitorManager *meta_monitor_manager_get (void);
+
+MetaMonitorInfo *meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
+ unsigned int *n_infos);
+
+MetaOutput *meta_monitor_manager_get_outputs (MetaMonitorManager *manager,
+ unsigned int *n_outputs);
+
+void meta_monitor_manager_get_resources (MetaMonitorManager *manager,
+ MetaMonitorMode **modes,
+ unsigned int *n_modes,
+ MetaCRTC **crtcs,
+ unsigned int *n_crtcs,
+ MetaOutput **outputs,
+ unsigned int *n_outputs);
+
+int meta_monitor_manager_get_primary_index (MetaMonitorManager *manager);
+
+gboolean meta_monitor_manager_handle_xevent (MetaMonitorManager *manager,
+ XEvent *event);
+
+void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager,
+ int *width,
+ int *height);
+
+void meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
+ int *width,
+ int *height);
+
+void meta_monitor_manager_apply_configuration (MetaMonitorManager *manager,
+ MetaCRTCInfo **crtcs,
+ unsigned int n_crtcs,
+ MetaOutputInfo **outputs,
+ unsigned int n_outputs);
+
+void meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
+ gboolean ok);
+
+#define META_TYPE_MONITOR_MANAGER_XRANDR (meta_monitor_manager_xrandr_get_type ())
+#define META_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandr))
+#define META_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
+#define META_IS_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_XRANDR))
+#define META_IS_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_XRANDR))
+#define META_MONITOR_MANAGER_XRANDR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
+
+typedef struct _MetaMonitorManagerXrandrClass MetaMonitorManagerXrandrClass;
+typedef struct _MetaMonitorManagerXrandr MetaMonitorManagerXrandr;
+
+GType meta_monitor_manager_xrandr_get_type (void);
+
+#define META_TYPE_MONITOR_CONFIG (meta_monitor_config_get_type ())
+#define META_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfig))
+#define META_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
+#define META_IS_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_CONFIG))
+#define META_IS_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_CONFIG))
+#define META_MONITOR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
+
+GType meta_monitor_config_get_type (void) G_GNUC_CONST;
+
+MetaMonitorConfig *meta_monitor_config_new (void);
+
+gboolean meta_monitor_config_match_current (MetaMonitorConfig *config,
+ MetaMonitorManager *manager);
+
+gboolean meta_monitor_config_apply_stored (MetaMonitorConfig *config,
+ MetaMonitorManager *manager);
+
+void meta_monitor_config_make_default (MetaMonitorConfig *config,
+ MetaMonitorManager *manager);
+
+void meta_monitor_config_update_current (MetaMonitorConfig *config,
+ MetaMonitorManager *manager);
+void meta_monitor_config_make_persistent (MetaMonitorConfig *config);
+
+void meta_monitor_config_restore_previous (MetaMonitorConfig *config,
+ MetaMonitorManager *manager);
+
+void meta_crtc_info_free (MetaCRTCInfo *info);
+void meta_output_info_free (MetaOutputInfo *info);
+
+/* Returns true if transform causes width and height to be inverted
+ This is true for the odd transforms in the enum */
+static inline gboolean
+meta_monitor_transform_is_rotated (enum wl_output_transform transform)
+{
+ return (transform % 2);
+}
+
+#endif
diff --git a/src/core/monitor-xrandr.c b/src/core/monitor-xrandr.c
new file mode 100644
index 000000000..bcceff80f
--- /dev/null
+++ b/src/core/monitor-xrandr.c
@@ -0,0 +1,1039 @@
+/* -*- 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
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include "monitor-private.h"
+
+#include "edid.h"
+
+#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
+
+/* Look for DPI_FALLBACK in:
+ * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
+ * for the reasoning */
+#define DPI_FALLBACK 96.0
+
+struct _MetaMonitorManagerXrandr
+{
+ MetaMonitorManager parent_instance;
+
+ Display *xdisplay;
+ XRRScreenResources *resources;
+ int time;
+ int rr_event_base;
+ int rr_error_base;
+};
+
+struct _MetaMonitorManagerXrandrClass
+{
+ MetaMonitorManagerClass parent_class;
+};
+
+G_DEFINE_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META_TYPE_MONITOR_MANAGER);
+
+static enum wl_output_transform
+wl_transform_from_xrandr (Rotation rotation)
+{
+ static const enum wl_output_transform y_reflected_map[4] = {
+ WL_OUTPUT_TRANSFORM_FLIPPED_180,
+ WL_OUTPUT_TRANSFORM_FLIPPED_90,
+ WL_OUTPUT_TRANSFORM_FLIPPED,
+ WL_OUTPUT_TRANSFORM_FLIPPED_270
+ };
+ enum wl_output_transform ret;
+
+ switch (rotation & 0x7F)
+ {
+ default:
+ case RR_Rotate_0:
+ ret = WL_OUTPUT_TRANSFORM_NORMAL;
+ break;
+ case RR_Rotate_90:
+ ret = WL_OUTPUT_TRANSFORM_90;
+ break;
+ case RR_Rotate_180:
+ ret = WL_OUTPUT_TRANSFORM_180;
+ break;
+ case RR_Rotate_270:
+ ret = WL_OUTPUT_TRANSFORM_270;
+ break;
+ }
+
+ if (rotation & RR_Reflect_X)
+ return ret + 4;
+ else if (rotation & RR_Reflect_Y)
+ return y_reflected_map[ret];
+ else
+ return ret;
+}
+
+#define ALL_ROTATIONS (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)
+
+static unsigned int
+wl_transform_from_xrandr_all (Rotation rotation)
+{
+ unsigned ret;
+
+ /* Handle the common cases first (none or all) */
+ if (rotation == 0 || rotation == RR_Rotate_0)
+ return (1 << WL_OUTPUT_TRANSFORM_NORMAL);
+
+ /* All rotations and one reflection -> all of them by composition */
+ if ((rotation & ALL_ROTATIONS) &&
+ ((rotation & RR_Reflect_X) || (rotation & RR_Reflect_Y)))
+ return ALL_WL_TRANSFORMS;
+
+ ret = 1 << WL_OUTPUT_TRANSFORM_NORMAL;
+ if (rotation & RR_Rotate_90)
+ ret |= 1 << WL_OUTPUT_TRANSFORM_90;
+ if (rotation & RR_Rotate_180)
+ ret |= 1 << WL_OUTPUT_TRANSFORM_180;
+ if (rotation & RR_Rotate_270)
+ ret |= 1 << WL_OUTPUT_TRANSFORM_270;
+ if (rotation & (RR_Rotate_0 | RR_Reflect_X))
+ ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED;
+ if (rotation & (RR_Rotate_90 | RR_Reflect_X))
+ ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_90;
+ if (rotation & (RR_Rotate_180 | RR_Reflect_X))
+ ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_180;
+ if (rotation & (RR_Rotate_270 | RR_Reflect_X))
+ ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_270;
+
+ return ret;
+}
+
+static gboolean
+output_get_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
+ MetaOutput *output)
+{
+ MetaDisplay *display = meta_get_display ();
+ gboolean value;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *buffer;
+
+ XRRGetOutputProperty (manager_xrandr->xdisplay,
+ (XID)output->output_id,
+ display->atom__MUTTER_PRESENTATION_OUTPUT,
+ 0, G_MAXLONG, False, False, XA_CARDINAL,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &buffer);
+
+ if (actual_type != XA_CARDINAL || actual_format != 32 ||
+ nitems < 1)
+ return FALSE;
+
+ value = ((int*)buffer)[0];
+
+ XFree (buffer);
+ return value;
+}
+
+static int
+normalize_backlight (MetaOutput *output,
+ int hw_value)
+{
+ return round ((double)(hw_value - output->backlight_min) /
+ (output->backlight_max - output->backlight_min) * 100.0);
+}
+
+static int
+output_get_backlight_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
+ MetaOutput *output)
+{
+ MetaDisplay *display = meta_get_display ();
+ gboolean value;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *buffer;
+
+ XRRGetOutputProperty (manager_xrandr->xdisplay,
+ (XID)output->output_id,
+ display->atom_BACKLIGHT,
+ 0, G_MAXLONG, False, False, XA_INTEGER,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &buffer);
+
+ if (actual_type != XA_INTEGER || actual_format != 32 ||
+ nitems < 1)
+ return -1;
+
+ value = ((int*)buffer)[0];
+
+ XFree (buffer);
+ return normalize_backlight (output, value);
+}
+
+static void
+output_get_backlight_limits_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
+ MetaOutput *output)
+{
+ MetaDisplay *display = meta_get_display ();
+ XRRPropertyInfo *info;
+
+ meta_error_trap_push (display);
+ info = XRRQueryOutputProperty (manager_xrandr->xdisplay,
+ (XID)output->output_id,
+ display->atom_BACKLIGHT);
+ meta_error_trap_pop (display);
+
+ if (info == NULL)
+ {
+ meta_verbose ("could not get output property for %s\n", output->name);
+ return;
+ }
+
+ if (!info->range || info->num_values != 2)
+ {
+ meta_verbose ("backlight %s was not range\n", output->name);
+ goto out;
+ }
+
+ output->backlight_min = info->values[0];
+ output->backlight_max = info->values[1];
+
+out:
+ XFree (info);
+}
+
+static int
+compare_outputs (const void *one,
+ const void *two)
+{
+ const MetaOutput *o_one = one, *o_two = two;
+
+ return strcmp (o_one->name, o_two->name);
+}
+
+static guint8 *
+get_edid_property (Display *dpy,
+ RROutput output,
+ Atom atom,
+ gsize *len)
+{
+ unsigned char *prop;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ guint8 *result;
+
+ XRRGetOutputProperty (dpy, output, atom,
+ 0, 100, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop);
+
+ if (actual_type == XA_INTEGER && actual_format == 8)
+ {
+ result = g_memdup (prop, nitems);
+ if (len)
+ *len = nitems;
+ }
+ else
+ {
+ result = NULL;
+ }
+
+ XFree (prop);
+
+ return result;
+}
+
+static GBytes *
+read_output_edid (MetaMonitorManagerXrandr *manager_xrandr,
+ XID output_id)
+{
+ Atom edid_atom;
+ guint8 *result;
+ gsize len;
+
+ edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID", FALSE);
+ result = get_edid_property (manager_xrandr->xdisplay, output_id, edid_atom, &len);
+
+ if (!result)
+ {
+ edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID_DATA", FALSE);
+ result = get_edid_property (manager_xrandr->xdisplay, output_id, edid_atom, &len);
+ }
+
+ if (!result)
+ {
+ edid_atom = XInternAtom (manager_xrandr->xdisplay, "XFree86_DDC_EDID1_RAWDATA", FALSE);
+ result = get_edid_property (manager_xrandr->xdisplay, output_id, edid_atom, &len);
+ }
+
+ if (result)
+ {
+ if (len > 0 && len % 128 == 0)
+ return g_bytes_new_take (result, len);
+ else
+ g_free (result);
+ }
+
+ return NULL;
+}
+
+static void
+meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ XRRScreenResources *resources;
+ RROutput primary_output;
+ unsigned int i, j, k;
+ unsigned int n_actual_outputs;
+ int min_width, min_height;
+ Screen *screen;
+ BOOL dpms_capable, dpms_enabled;
+ CARD16 dpms_state;
+
+ if (manager_xrandr->resources)
+ XRRFreeScreenResources (manager_xrandr->resources);
+ manager_xrandr->resources = NULL;
+
+ meta_error_trap_push (meta_get_display ());
+ dpms_capable = DPMSCapable (manager_xrandr->xdisplay);
+ meta_error_trap_pop (meta_get_display ());
+
+ if (dpms_capable &&
+ DPMSInfo (manager_xrandr->xdisplay, &dpms_state, &dpms_enabled) &&
+ dpms_enabled)
+ {
+ switch (dpms_state)
+ {
+ case DPMSModeOn:
+ manager->power_save_mode = META_POWER_SAVE_ON;
+ break;
+ case DPMSModeStandby:
+ manager->power_save_mode = META_POWER_SAVE_STANDBY;
+ break;
+ case DPMSModeSuspend:
+ manager->power_save_mode = META_POWER_SAVE_SUSPEND;
+ break;
+ case DPMSModeOff:
+ manager->power_save_mode = META_POWER_SAVE_OFF;
+ break;
+ default:
+ manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
+ break;
+ }
+ }
+ else
+ {
+ manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
+ }
+
+ XRRGetScreenSizeRange (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay),
+ &min_width,
+ &min_height,
+ &manager->max_screen_width,
+ &manager->max_screen_height);
+
+ screen = ScreenOfDisplay (manager_xrandr->xdisplay,
+ DefaultScreen (manager_xrandr->xdisplay));
+ /* This is updated because we called RRUpdateConfiguration below */
+ manager->screen_width = WidthOfScreen (screen);
+ manager->screen_height = HeightOfScreen (screen);
+
+ resources = XRRGetScreenResourcesCurrent (manager_xrandr->xdisplay,
+ DefaultRootWindow (manager_xrandr->xdisplay));
+ if (!resources)
+ return;
+
+ manager_xrandr->resources = resources;
+ manager_xrandr->time = resources->configTimestamp;
+ 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);
+
+ for (i = 0; i < (unsigned)resources->nmode; i++)
+ {
+ XRRModeInfo *xmode = &resources->modes[i];
+ MetaMonitorMode *mode;
+
+ 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));
+ }
+
+ for (i = 0; i < (unsigned)resources->ncrtc; i++)
+ {
+ XRRCrtcInfo *crtc;
+ MetaCRTC *meta_crtc;
+
+ crtc = XRRGetCrtcInfo (manager_xrandr->xdisplay, resources, resources->crtcs[i]);
+
+ meta_crtc = &manager->crtcs[i];
+
+ 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;
+ meta_crtc->is_dirty = FALSE;
+ meta_crtc->transform = wl_transform_from_xrandr (crtc->rotation);
+ meta_crtc->all_transforms = wl_transform_from_xrandr_all (crtc->rotations);
+
+ for (j = 0; j < (unsigned)resources->nmode; j++)
+ {
+ if (resources->modes[j].id == crtc->mode)
+ {
+ meta_crtc->current_mode = &manager->modes[j];
+ break;
+ }
+ }
+
+ XRRFreeCrtcInfo (crtc);
+ }
+
+ primary_output = XRRGetOutputPrimary (manager_xrandr->xdisplay,
+ DefaultRootWindow (manager_xrandr->xdisplay));
+
+ n_actual_outputs = 0;
+ for (i = 0; i < (unsigned)resources->noutput; i++)
+ {
+ XRROutputInfo *output;
+ MetaOutput *meta_output;
+
+ output = XRRGetOutputInfo (manager_xrandr->xdisplay, resources, resources->outputs[i]);
+
+ meta_output = &manager->outputs[n_actual_outputs];
+
+ if (output->connection != RR_Disconnected)
+ {
+ GBytes *edid;
+ MonitorInfo *parsed_edid;
+
+ meta_output->output_id = resources->outputs[i];
+ meta_output->name = g_strdup (output->name);
+
+ edid = read_output_edid (manager_xrandr, meta_output->output_id);
+ if (edid)
+ {
+ gsize len;
+
+ parsed_edid = decode_edid (g_bytes_get_data (edid, &len));
+ if (parsed_edid)
+ {
+ meta_output->vendor = g_strndup (parsed_edid->manufacturer_code, 4);
+ if (parsed_edid->dsc_product_name[0])
+ meta_output->product = g_strndup (parsed_edid->dsc_product_name, 14);
+ else
+ meta_output->product = g_strdup_printf ("0x%04x", (unsigned)parsed_edid->product_code);
+ if (parsed_edid->dsc_serial_number[0])
+ meta_output->serial = g_strndup (parsed_edid->dsc_serial_number, 14);
+ else
+ meta_output->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
+
+ g_free (parsed_edid);
+ }
+
+ g_bytes_unref (edid);
+ }
+
+ if (!meta_output->vendor)
+ {
+ meta_output->vendor = g_strdup ("unknown");
+ meta_output->product = g_strdup ("unknown");
+ meta_output->serial = g_strdup ("unknown");
+ }
+ 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;
+ }
+ }
+
+ 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[j] = GINT_TO_POINTER (output->clones[j]);
+ }
+
+ meta_output->is_primary = ((XID)meta_output->output_id == primary_output);
+ meta_output->is_presentation = output_get_presentation_xrandr (manager_xrandr, meta_output);
+ output_get_backlight_limits_xrandr (manager_xrandr, meta_output);
+
+ if (!(meta_output->backlight_min == 0 && meta_output->backlight_max == 0))
+ meta_output->backlight = output_get_backlight_xrandr (manager_xrandr, meta_output);
+ else
+ meta_output->backlight = -1;
+
+ n_actual_outputs++;
+ }
+
+ XRRFreeOutputInfo (output);
+ }
+
+ manager->n_outputs = n_actual_outputs;
+
+ /* Sort the outputs for easier handling in MetaMonitorConfig */
+ qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs);
+
+ /* 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;
+ }
+ }
+ }
+ }
+}
+
+static GBytes *
+meta_monitor_manager_xrandr_read_edid (MetaMonitorManager *manager,
+ MetaOutput *output)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+
+ return read_output_edid (manager_xrandr, output->output_id);
+}
+
+static void
+meta_monitor_manager_xrandr_set_power_save_mode (MetaMonitorManager *manager,
+ MetaPowerSave mode)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ CARD16 state;
+
+ switch (mode) {
+ case META_POWER_SAVE_ON:
+ state = DPMSModeOn;
+ break;
+ case META_POWER_SAVE_STANDBY:
+ state = DPMSModeStandby;
+ break;
+ case META_POWER_SAVE_SUSPEND:
+ state = DPMSModeSuspend;
+ break;
+ case META_POWER_SAVE_OFF:
+ state = DPMSModeOff;
+ break;
+ default:
+ return;
+ }
+
+ meta_error_trap_push (meta_get_display ());
+ DPMSForceLevel (manager_xrandr->xdisplay, state);
+ DPMSSetTimeouts (manager_xrandr->xdisplay, 0, 0, 0);
+ meta_error_trap_pop (meta_get_display ());
+}
+
+static Rotation
+wl_transform_to_xrandr (enum wl_output_transform transform)
+{
+ switch (transform)
+ {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ return RR_Rotate_0;
+ case WL_OUTPUT_TRANSFORM_90:
+ return RR_Rotate_90;
+ case WL_OUTPUT_TRANSFORM_180:
+ return RR_Rotate_180;
+ case WL_OUTPUT_TRANSFORM_270:
+ return RR_Rotate_270;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ return RR_Reflect_X | RR_Rotate_0;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ return RR_Reflect_X | RR_Rotate_90;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ return RR_Reflect_X | RR_Rotate_180;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ return RR_Reflect_X | RR_Rotate_270;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+output_set_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
+ MetaOutput *output,
+ gboolean presentation)
+{
+ MetaDisplay *display = meta_get_display ();
+ int value = presentation;
+
+ XRRChangeOutputProperty (manager_xrandr->xdisplay,
+ (XID)output->output_id,
+ display->atom__MUTTER_PRESENTATION_OUTPUT,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*) &value, 1);
+}
+
+static void
+meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
+ MetaCRTCInfo **crtcs,
+ unsigned int n_crtcs,
+ MetaOutputInfo **outputs,
+ unsigned int n_outputs)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ unsigned i;
+ int width, height, width_mm, height_mm;
+
+ meta_display_grab (meta_get_display ());
+
+ /* First compute the new size of the screen (framebuffer) */
+ width = 0; height = 0;
+ for (i = 0; i < n_crtcs; i++)
+ {
+ MetaCRTCInfo *crtc_info = crtcs[i];
+ MetaCRTC *crtc = crtc_info->crtc;
+ crtc->is_dirty = TRUE;
+
+ if (crtc_info->mode == NULL)
+ continue;
+
+ if (meta_monitor_transform_is_rotated (crtc_info->transform))
+ {
+ width = MAX (width, crtc_info->x + crtc_info->mode->height);
+ height = MAX (height, crtc_info->y + crtc_info->mode->width);
+ }
+ else
+ {
+ width = MAX (width, crtc_info->x + crtc_info->mode->width);
+ height = MAX (height, crtc_info->y + crtc_info->mode->height);
+ }
+ }
+
+ /* Second disable all newly disabled CRTCs, or CRTCs that in the previous
+ configuration would be outside the new framebuffer (otherwise X complains
+ loudly when resizing)
+ CRTC will be enabled again after resizing the FB
+ */
+ for (i = 0; i < n_crtcs; i++)
+ {
+ MetaCRTCInfo *crtc_info = crtcs[i];
+ MetaCRTC *crtc = crtc_info->crtc;
+
+ if (crtc_info->mode == NULL ||
+ crtc->rect.x + crtc->rect.width > width ||
+ crtc->rect.y + crtc->rect.height > height)
+ {
+ XRRSetCrtcConfig (manager_xrandr->xdisplay,
+ manager_xrandr->resources,
+ (XID)crtc->crtc_id,
+ manager_xrandr->time,
+ 0, 0,
+ None,
+ RR_Rotate_0,
+ NULL, 0);
+
+ crtc->rect.x = 0;
+ crtc->rect.y = 0;
+ crtc->rect.width = 0;
+ crtc->rect.height = 0;
+ crtc->current_mode = NULL;
+ }
+ }
+
+ /* Disable CRTCs not mentioned in the list */
+ for (i = 0; i < manager->n_crtcs; i++)
+ {
+ MetaCRTC *crtc = &manager->crtcs[i];
+
+ if (crtc->is_dirty)
+ {
+ crtc->is_dirty = FALSE;
+ continue;
+ }
+ if (crtc->current_mode == NULL)
+ continue;
+
+ XRRSetCrtcConfig (manager_xrandr->xdisplay,
+ manager_xrandr->resources,
+ (XID)crtc->crtc_id,
+ manager_xrandr->time,
+ 0, 0,
+ None,
+ RR_Rotate_0,
+ NULL, 0);
+
+ crtc->rect.x = 0;
+ crtc->rect.y = 0;
+ crtc->rect.width = 0;
+ crtc->rect.height = 0;
+ crtc->current_mode = NULL;
+ }
+
+ g_assert (width > 0 && height > 0);
+ /* The 'physical size' of an X screen is meaningless if that screen
+ * can consist of many monitors. So just pick a size that make the
+ * dpi 96.
+ *
+ * Firefox and Evince apparently believe what X tells them.
+ */
+ width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
+ height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
+ meta_error_trap_push (meta_get_display ());
+ XRRSetScreenSize (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay),
+ width, height, width_mm, height_mm);
+ meta_error_trap_pop (meta_get_display ());
+
+ for (i = 0; i < n_crtcs; i++)
+ {
+ MetaCRTCInfo *crtc_info = crtcs[i];
+ MetaCRTC *crtc = crtc_info->crtc;
+
+ if (crtc_info->mode != NULL)
+ {
+ MetaMonitorMode *mode;
+ XID *outputs;
+ unsigned int j, n_outputs;
+ int width, height;
+ Status ok;
+ unsigned long old_controlled_mask;
+ unsigned long new_controlled_mask;
+
+ mode = crtc_info->mode;
+
+ n_outputs = crtc_info->outputs->len;
+ outputs = g_new (XID, n_outputs);
+
+ old_controlled_mask = 0;
+ for (j = 0; j < manager->n_outputs; j++)
+ {
+ MetaOutput *output;
+
+ output = &manager->outputs[j];
+
+ if (output->crtc == crtc)
+ old_controlled_mask |= 1UL << j;
+ }
+
+ new_controlled_mask = 0;
+ for (j = 0; j < n_outputs; j++)
+ {
+ MetaOutput *output;
+
+ output = ((MetaOutput**)crtc_info->outputs->pdata)[j];
+
+ output->is_dirty = TRUE;
+ output->crtc = crtc;
+ new_controlled_mask |= 1UL << j;
+
+ outputs[j] = output->output_id;
+ }
+
+ if (crtc->current_mode == mode &&
+ crtc->rect.x == crtc_info->x &&
+ crtc->rect.y == crtc_info->y &&
+ crtc->transform == crtc_info->transform &&
+ old_controlled_mask == new_controlled_mask)
+ {
+ /* No change */
+ goto next;
+ }
+
+ meta_error_trap_push (meta_get_display ());
+ ok = XRRSetCrtcConfig (manager_xrandr->xdisplay,
+ manager_xrandr->resources,
+ (XID)crtc->crtc_id,
+ manager_xrandr->time,
+ crtc_info->x, crtc_info->y,
+ (XID)mode->mode_id,
+ wl_transform_to_xrandr (crtc_info->transform),
+ outputs, n_outputs);
+ meta_error_trap_pop (meta_get_display ());
+
+ if (ok != Success)
+ {
+ meta_warning ("Configuring CRTC %d with mode %d (%d x %d @ %f) at position %d, %d and transfrom %u failed\n",
+ (unsigned)(crtc->crtc_id), (unsigned)(mode->mode_id),
+ mode->width, mode->height, (float)mode->refresh_rate,
+ crtc_info->x, crtc_info->y, crtc_info->transform);
+ goto next;
+ }
+
+ if (meta_monitor_transform_is_rotated (crtc_info->transform))
+ {
+ width = mode->height;
+ height = mode->width;
+ }
+ else
+ {
+ width = mode->width;
+ height = mode->height;
+ }
+
+ crtc->rect.x = crtc_info->x;
+ crtc->rect.y = crtc_info->y;
+ crtc->rect.width = width;
+ crtc->rect.height = height;
+ crtc->current_mode = mode;
+ crtc->transform = crtc_info->transform;
+
+ next:
+ g_free (outputs);
+ }
+ }
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaOutputInfo *output_info = outputs[i];
+ MetaOutput *output = output_info->output;
+
+ if (output_info->is_primary)
+ {
+ XRRSetOutputPrimary (manager_xrandr->xdisplay,
+ DefaultRootWindow (manager_xrandr->xdisplay),
+ (XID)output_info->output->output_id);
+ }
+
+ output_set_presentation_xrandr (manager_xrandr,
+ output_info->output,
+ output_info->is_presentation);
+
+ output->is_primary = output_info->is_primary;
+ output->is_presentation = output_info->is_presentation;
+ }
+
+ /* Disable outputs not mentioned in the list */
+ for (i = 0; i < manager->n_outputs; i++)
+ {
+ MetaOutput *output = &manager->outputs[i];
+
+ if (output->is_dirty)
+ {
+ output->is_dirty = FALSE;
+ continue;
+ }
+
+ output->crtc = NULL;
+ output->is_primary = FALSE;
+ }
+
+ meta_display_ungrab (meta_get_display ());
+}
+
+static void
+meta_monitor_manager_xrandr_change_backlight (MetaMonitorManager *manager,
+ MetaOutput *output,
+ gint value)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ MetaDisplay *display = meta_get_display ();
+ int hw_value;
+
+ hw_value = round ((double)value / 100.0 * output->backlight_max + output->backlight_min);
+
+ meta_error_trap_push (display);
+ XRRChangeOutputProperty (manager_xrandr->xdisplay,
+ (XID)output->output_id,
+ display->atom_BACKLIGHT,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *) &hw_value, 1);
+ meta_error_trap_pop (display);
+
+ /* We're not selecting for property notifies, so update the value immediately */
+ output->backlight = normalize_backlight (output, hw_value);
+}
+
+static void
+meta_monitor_manager_xrandr_get_crtc_gamma (MetaMonitorManager *manager,
+ MetaCRTC *crtc,
+ gsize *size,
+ unsigned short **red,
+ unsigned short **green,
+ unsigned short **blue)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ XRRCrtcGamma *gamma;
+
+ gamma = XRRGetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id);
+
+ *size = gamma->size;
+ *red = g_memdup (gamma->red, sizeof (unsigned short) * gamma->size);
+ *green = g_memdup (gamma->green, sizeof (unsigned short) * gamma->size);
+ *blue = g_memdup (gamma->blue, sizeof (unsigned short) * gamma->size);
+
+ XRRFreeGamma (gamma);
+}
+
+static void
+meta_monitor_manager_xrandr_set_crtc_gamma (MetaMonitorManager *manager,
+ MetaCRTC *crtc,
+ gsize size,
+ unsigned short *red,
+ unsigned short *green,
+ unsigned short *blue)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+ XRRCrtcGamma *gamma;
+
+ gamma = XRRAllocGamma (size);
+ memcpy (gamma->red, red, sizeof (unsigned short) * size);
+ memcpy (gamma->green, green, sizeof (unsigned short) * size);
+ memcpy (gamma->blue, blue, sizeof (unsigned short) * size);
+
+ XRRSetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id, gamma);
+
+ XRRFreeGamma (gamma);
+}
+
+static gboolean
+meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManager *manager,
+ XEvent *event)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
+
+ if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
+ return FALSE;
+
+ XRRUpdateConfiguration (event);
+ return TRUE;
+}
+
+static void
+meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr)
+{
+ MetaDisplay *display = meta_get_display ();
+
+ manager_xrandr->xdisplay = display->xdisplay;
+
+ if (!XRRQueryExtension (manager_xrandr->xdisplay,
+ &manager_xrandr->rr_event_base,
+ &manager_xrandr->rr_error_base))
+ {
+ return;
+ }
+ else
+ {
+ /* We only use ScreenChangeNotify, but GDK uses the others,
+ and we don't want to step on its toes */
+ XRRSelectInput (manager_xrandr->xdisplay,
+ DefaultRootWindow (manager_xrandr->xdisplay),
+ RRScreenChangeNotifyMask
+ | RRCrtcChangeNotifyMask
+ | RROutputPropertyNotifyMask);
+ }
+}
+
+static void
+meta_monitor_manager_xrandr_finalize (GObject *object)
+{
+ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object);
+
+ if (manager_xrandr->resources)
+ XRRFreeScreenResources (manager_xrandr->resources);
+ manager_xrandr->resources = NULL;
+
+ G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object);
+}
+
+static void
+meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass)
+{
+ MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_monitor_manager_xrandr_finalize;
+
+ manager_class->read_current = meta_monitor_manager_xrandr_read_current;
+ manager_class->read_edid = meta_monitor_manager_xrandr_read_edid;
+ manager_class->apply_configuration = meta_monitor_manager_xrandr_apply_configuration;
+ manager_class->set_power_save_mode = meta_monitor_manager_xrandr_set_power_save_mode;
+ manager_class->change_backlight = meta_monitor_manager_xrandr_change_backlight;
+ manager_class->get_crtc_gamma = meta_monitor_manager_xrandr_get_crtc_gamma;
+ manager_class->set_crtc_gamma = meta_monitor_manager_xrandr_set_crtc_gamma;
+ manager_class->handle_xevent = meta_monitor_manager_xrandr_handle_xevent;
+}
+
diff --git a/src/core/monitor.c b/src/core/monitor.c
new file mode 100644
index 000000000..c6b46496c
--- /dev/null
+++ b/src/core/monitor.c
@@ -0,0 +1,1548 @@
+/* -*- 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
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include "monitor-private.h"
+
+#include "meta-dbus-xrandr.h"
+
+#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
+
+enum {
+ CONFIRM_DISPLAY_CHANGE,
+ SIGNALS_LAST
+};
+
+enum {
+ PROP_0,
+ PROP_POWER_SAVE_MODE,
+ PROP_LAST
+};
+
+static int signals[SIGNALS_LAST];
+
+static void meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface);
+
+G_DEFINE_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 free_output_array (MetaOutput *old_outputs,
+ int n_old_outputs);
+static void invalidate_logical_config (MetaMonitorManager *manager);
+static void initialize_dbus_interface (MetaMonitorManager *manager);
+
+static void
+read_current_dummy (MetaMonitorManager *manager)
+{
+ /* The dummy monitor config has:
+ - one enabled output, LVDS, primary, at 0x0 and 1024x768
+ - one free CRTC
+ - two disabled outputs
+ - three modes, 1024x768, 800x600 and 640x480
+ - no clones are possible (use different CRTCs)
+
+ Low-level IDs should be assigned sequentially, to
+ mimick what XRandR and KMS do
+ */
+
+ manager->max_screen_width = 65535;
+ manager->max_screen_height = 65535;
+ manager->screen_width = 1024;
+ manager->screen_height = 768;
+
+ manager->modes = g_new0 (MetaMonitorMode, 6);
+ manager->n_modes = 6;
+
+ manager->modes[0].mode_id = 1;
+ manager->modes[0].width = 1024;
+ manager->modes[0].height = 768;
+ manager->modes[0].refresh_rate = 60.0;
+
+ manager->modes[1].mode_id = 2;
+ manager->modes[1].width = 800;
+ manager->modes[1].height = 600;
+ manager->modes[1].refresh_rate = 60.0;
+
+ manager->modes[2].mode_id = 3;
+ manager->modes[2].width = 640;
+ manager->modes[2].height = 480;
+ manager->modes[2].refresh_rate = 60.0;
+
+ manager->modes[3].mode_id = 4;
+ manager->modes[3].width = 1920;
+ manager->modes[3].height = 1080;
+ manager->modes[3].refresh_rate = 60.0;
+
+ manager->modes[4].mode_id = 5;
+ manager->modes[4].width = 1920;
+ manager->modes[4].height = 1080;
+ manager->modes[4].refresh_rate = 55.0;
+
+ manager->modes[5].mode_id = 6;
+ manager->modes[5].width = 1600;
+ manager->modes[5].height = 900;
+ manager->modes[5].refresh_rate = 60.0;
+
+ manager->crtcs = g_new0 (MetaCRTC, 3);
+ manager->n_crtcs = 3;
+
+ manager->crtcs[0].crtc_id = 4;
+ 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];
+ manager->crtcs[0].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ manager->crtcs[0].all_transforms = ALL_WL_TRANSFORMS;
+ manager->crtcs[0].is_dirty = FALSE;
+ manager->crtcs[0].logical_monitor = NULL;
+
+ manager->crtcs[1].crtc_id = 5;
+ manager->crtcs[1].rect.x = 0;
+ manager->crtcs[1].rect.y = 0;
+ manager->crtcs[1].rect.width = 0;
+ manager->crtcs[1].rect.height = 0;
+ manager->crtcs[1].current_mode = NULL;
+ manager->crtcs[1].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ manager->crtcs[1].all_transforms = ALL_WL_TRANSFORMS;
+ manager->crtcs[1].is_dirty = FALSE;
+ manager->crtcs[1].logical_monitor = NULL;
+
+ manager->crtcs[2].crtc_id = 5;
+ manager->crtcs[2].rect.x = 0;
+ manager->crtcs[2].rect.y = 0;
+ manager->crtcs[2].rect.width = 0;
+ manager->crtcs[2].rect.height = 0;
+ manager->crtcs[2].current_mode = NULL;
+ manager->crtcs[2].transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ manager->crtcs[2].all_transforms = ALL_WL_TRANSFORMS;
+ manager->crtcs[2].is_dirty = FALSE;
+ manager->crtcs[2].logical_monitor = NULL;
+
+ manager->outputs = g_new0 (MetaOutput, 3);
+ manager->n_outputs = 3;
+
+ manager->outputs[0].crtc = NULL;
+ manager->outputs[0].output_id = 6;
+ manager->outputs[0].name = g_strdup ("HDMI");
+ manager->outputs[0].vendor = g_strdup ("MetaProducts Inc.");
+ manager->outputs[0].product = g_strdup ("unknown");
+ manager->outputs[0].serial = g_strdup ("0xC0F01A");
+ manager->outputs[0].width_mm = 510;
+ manager->outputs[0].height_mm = 287;
+ manager->outputs[0].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+ manager->outputs[0].preferred_mode = &manager->modes[3];
+ manager->outputs[0].n_modes = 5;
+ manager->outputs[0].modes = g_new0 (MetaMonitorMode *, 5);
+ manager->outputs[0].modes[0] = &manager->modes[0];
+ manager->outputs[0].modes[1] = &manager->modes[1];
+ manager->outputs[0].modes[2] = &manager->modes[2];
+ manager->outputs[0].modes[3] = &manager->modes[3];
+ manager->outputs[0].modes[4] = &manager->modes[4];
+ manager->outputs[0].n_possible_crtcs = 3;
+ manager->outputs[0].possible_crtcs = g_new0 (MetaCRTC *, 3);
+ manager->outputs[0].possible_crtcs[0] = &manager->crtcs[0];
+ manager->outputs[0].possible_crtcs[1] = &manager->crtcs[1];
+ manager->outputs[0].possible_crtcs[2] = &manager->crtcs[2];
+ manager->outputs[0].n_possible_clones = 0;
+ manager->outputs[0].possible_clones = g_new0 (MetaOutput *, 0);
+ manager->outputs[0].backlight = -1;
+ manager->outputs[0].backlight_min = 0;
+ manager->outputs[0].backlight_max = 0;
+
+ manager->outputs[1].crtc = &manager->crtcs[0];
+ manager->outputs[1].output_id = 7;
+ manager->outputs[1].name = g_strdup ("LVDS");
+ manager->outputs[1].vendor = g_strdup ("MetaProducts Inc.");
+ manager->outputs[1].product = g_strdup ("unknown");
+ manager->outputs[1].serial = g_strdup ("0xC0FFEE");
+ manager->outputs[1].width_mm = 222;
+ manager->outputs[1].height_mm = 125;
+ manager->outputs[1].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+ manager->outputs[1].preferred_mode = &manager->modes[5];
+ manager->outputs[1].n_modes = 4;
+ manager->outputs[1].modes = g_new0 (MetaMonitorMode *, 4);
+ manager->outputs[1].modes[0] = &manager->modes[0];
+ manager->outputs[1].modes[1] = &manager->modes[1];
+ manager->outputs[1].modes[2] = &manager->modes[2];
+ manager->outputs[1].modes[3] = &manager->modes[5];
+ manager->outputs[1].n_possible_crtcs = 3;
+ manager->outputs[1].possible_crtcs = g_new0 (MetaCRTC *, 3);
+ manager->outputs[1].possible_crtcs[0] = &manager->crtcs[0];
+ manager->outputs[1].possible_crtcs[1] = &manager->crtcs[1];
+ manager->outputs[1].possible_crtcs[2] = &manager->crtcs[2];
+ manager->outputs[1].n_possible_clones = 0;
+ manager->outputs[1].possible_clones = g_new0 (MetaOutput *, 0);
+ manager->outputs[1].backlight = -1;
+ manager->outputs[1].backlight_min = 0;
+ manager->outputs[1].backlight_max = 0;
+
+ manager->outputs[2].crtc = NULL;
+ manager->outputs[2].output_id = 8;
+ manager->outputs[2].name = g_strdup ("VGA");
+ manager->outputs[2].vendor = g_strdup ("MetaProducts Inc.");
+ manager->outputs[2].product = g_strdup ("unknown");
+ manager->outputs[2].serial = g_strdup ("0xC4FE");
+ manager->outputs[2].width_mm = 309;
+ manager->outputs[2].height_mm = 174;
+ manager->outputs[2].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+ manager->outputs[2].preferred_mode = &manager->modes[0];
+ manager->outputs[2].n_modes = 3;
+ manager->outputs[2].modes = g_new0 (MetaMonitorMode *, 3);
+ manager->outputs[2].modes[0] = &manager->modes[0];
+ manager->outputs[2].modes[1] = &manager->modes[1];
+ manager->outputs[2].modes[2] = &manager->modes[2];
+ manager->outputs[2].n_possible_crtcs = 3;
+ manager->outputs[2].possible_crtcs = g_new0 (MetaCRTC *, 3);
+ manager->outputs[2].possible_crtcs[0] = &manager->crtcs[0];
+ manager->outputs[2].possible_crtcs[1] = &manager->crtcs[1];
+ manager->outputs[2].possible_crtcs[2] = &manager->crtcs[2];
+ manager->outputs[2].n_possible_clones = 0;
+ manager->outputs[2].possible_clones = g_new0 (MetaOutput *, 0);
+ manager->outputs[2].backlight = -1;
+ manager->outputs[2].backlight_min = 0;
+ manager->outputs[2].backlight_max = 0;
+}
+
+static void
+apply_config_dummy (MetaMonitorManager *manager,
+ MetaCRTCInfo **crtcs,
+ unsigned int n_crtcs,
+ MetaOutputInfo **outputs,
+ unsigned int n_outputs)
+{
+ unsigned i;
+ int screen_width = 0, screen_height = 0;
+
+ for (i = 0; i < n_crtcs; i++)
+ {
+ MetaCRTCInfo *crtc_info = crtcs[i];
+ MetaCRTC *crtc = crtc_info->crtc;
+ crtc->is_dirty = TRUE;
+
+ if (crtc_info->mode == NULL)
+ {
+ crtc->rect.x = 0;
+ crtc->rect.y = 0;
+ crtc->rect.width = 0;
+ crtc->rect.height = 0;
+ crtc->current_mode = NULL;
+ }
+ else
+ {
+ MetaMonitorMode *mode;
+ MetaOutput *output;
+ int i, n_outputs;
+ int width, height;
+
+ mode = crtc_info->mode;
+
+ if (meta_monitor_transform_is_rotated (crtc_info->transform))
+ {
+ width = mode->height;
+ height = mode->width;
+ }
+ else
+ {
+ width = mode->width;
+ height = mode->height;
+ }
+
+ crtc->rect.x = crtc_info->x;
+ crtc->rect.y = crtc_info->y;
+ crtc->rect.width = width;
+ crtc->rect.height = height;
+ crtc->current_mode = mode;
+ crtc->transform = crtc_info->transform;
+
+ screen_width = MAX (screen_width, crtc_info->x + width);
+ screen_height = MAX (screen_height, crtc_info->y + height);
+
+ n_outputs = crtc_info->outputs->len;
+ for (i = 0; i < n_outputs; i++)
+ {
+ output = ((MetaOutput**)crtc_info->outputs->pdata)[i];
+
+ output->is_dirty = TRUE;
+ output->crtc = crtc;
+ }
+ }
+ }
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ MetaOutputInfo *output_info = outputs[i];
+ MetaOutput *output = output_info->output;
+
+ output->is_primary = output_info->is_primary;
+ output->is_presentation = output_info->is_presentation;
+ }
+
+ /* Disable CRTCs not mentioned in the list */
+ for (i = 0; i < manager->n_crtcs; i++)
+ {
+ MetaCRTC *crtc = &manager->crtcs[i];
+
+ crtc->logical_monitor = NULL;
+
+ if (crtc->is_dirty)
+ {
+ crtc->is_dirty = FALSE;
+ continue;
+ }
+
+ crtc->rect.x = 0;
+ crtc->rect.y = 0;
+ crtc->rect.width = 0;
+ crtc->rect.height = 0;
+ crtc->current_mode = NULL;
+ }
+
+ /* Disable outputs not mentioned in the list */
+ for (i = 0; i < manager->n_outputs; i++)
+ {
+ MetaOutput *output = &manager->outputs[i];
+
+ if (output->is_dirty)
+ {
+ output->is_dirty = FALSE;
+ continue;
+ }
+
+ output->crtc = NULL;
+ output->is_primary = FALSE;
+ }
+
+ manager->screen_width = screen_width;
+ manager->screen_height = screen_height;
+
+ invalidate_logical_config (manager);
+}
+
+static GBytes *
+read_edid_dummy (MetaMonitorManager *manager,
+ MetaOutput *output)
+{
+ return NULL;
+}
+
+static char *
+get_edid_file_dummy (MetaMonitorManager *manager,
+ MetaOutput *output)
+{
+ return NULL;
+}
+
+static void
+meta_monitor_manager_init (MetaMonitorManager *manager)
+{
+}
+
+static void
+read_current_config (MetaMonitorManager *manager)
+{
+ manager->serial++;
+
+ META_MONITOR_MANAGER_GET_CLASS (manager)->read_current (manager);
+}
+
+/*
+ * 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);
+}
+
+static MetaMonitorManager *
+meta_monitor_manager_new (void)
+{
+ const char *env;
+ GType type;
+
+ env = g_getenv ("META_DEBUG_MULTIMONITOR");
+
+ if (env == NULL)
+ type = META_TYPE_MONITOR_MANAGER_XRANDR;
+ else if (strcmp (env, "xrandr") == 0)
+ type = META_TYPE_MONITOR_MANAGER_XRANDR;
+ else
+ type = META_TYPE_MONITOR_MANAGER;
+
+ return g_object_new (type, NULL);
+}
+
+static void
+meta_monitor_manager_constructed (GObject *object)
+{
+ MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
+
+ manager->in_init = TRUE;
+
+ manager->config = meta_monitor_config_new ();
+
+ read_current_config (manager);
+
+ if (!meta_monitor_config_apply_stored (manager->config, manager))
+ meta_monitor_config_make_default (manager->config, manager);
+
+ /* Under XRandR, we don't rebuild our data structures until we see
+ the RRScreenNotify event, but at least at startup we want to have
+ the right configuration immediately.
+
+ The other backends keep the data structures always updated,
+ so this is not needed.
+ */
+ if (META_IS_MONITOR_MANAGER_XRANDR (manager))
+ {
+ MetaOutput *old_outputs;
+ MetaCRTC *old_crtcs;
+ MetaMonitorMode *old_modes;
+ int n_old_outputs;
+
+ old_outputs = manager->outputs;
+ n_old_outputs = manager->n_outputs;
+ old_modes = manager->modes;
+ old_crtcs = manager->crtcs;
+
+ read_current_config (manager);
+
+ free_output_array (old_outputs, n_old_outputs);
+ g_free (old_modes);
+ g_free (old_crtcs);
+ }
+
+ make_logical_config (manager);
+ initialize_dbus_interface (manager);
+
+ manager->in_init = FALSE;
+}
+
+static void
+meta_monitor_manager_set_power_save_mode (MetaMonitorManager *manager,
+ MetaPowerSave mode)
+{
+ MetaMonitorManagerClass *klass;
+
+ if (mode == manager->power_save_mode)
+ return;
+
+ if (manager->power_save_mode == META_POWER_SAVE_UNKNOWN ||
+ mode == META_POWER_SAVE_UNKNOWN)
+ 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;
+}
+
+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[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);
+ g_free (old_outputs[i].possible_clones);
+ }
+
+ 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_free (manager->modes);
+ g_free (manager->crtcs);
+
+ 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_OBJECT_CLASS (meta_monitor_manager_parent_class)->dispose (object);
+}
+
+static void
+meta_monitor_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaMonitorManager *self = META_MONITOR_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_POWER_SAVE_MODE:
+ meta_monitor_manager_set_power_save_mode (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_monitor_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaMonitorManager *self = META_MONITOR_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_POWER_SAVE_MODE:
+ g_value_set_int (value, self->power_save_mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+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->get_property = meta_monitor_manager_get_property;
+ object_class->set_property = meta_monitor_manager_set_property;
+ object_class->dispose = meta_monitor_manager_dispose;
+ object_class->finalize = meta_monitor_manager_finalize;
+
+ klass->read_current = read_current_dummy;
+ klass->apply_configuration = apply_config_dummy;
+ klass->get_edid_file = get_edid_file_dummy;
+ klass->read_edid = read_edid_dummy;
+
+ 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);
+
+ g_object_class_override_property (object_class, PROP_POWER_SAVE_MODE, "power-save-mode");
+}
+
+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)
+{
+ if (g_str_has_prefix (output->name, "LVDS") ||
+ g_str_has_prefix (output->name, "eDP"))
+ return g_strdup (_("Built-in display"));
+
+ 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 *vendor_name;
+ char *ret;
+
+ 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);
+
+ ret = g_strdup_printf ("%s %s", vendor_name, inches);
+
+ g_free (vendor_name);
+ }
+ else
+ {
+ /* TRANSLATORS: this is a monitor name (in case we don't know
+ the vendor), it's Unknown followed by a size in inches,
+ like 'Unknown 15"'
+ */
+ ret = g_strdup_printf (_("Unknown %s"), inches);
+ }
+
+ g_free (inches);
+ return ret;
+ }
+ else
+ {
+ return g_strdup (output->vendor);
+ }
+}
+
+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);
+ GVariantBuilder crtc_builder, output_builder, mode_builder;
+ unsigned int i, j;
+
+ 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(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"));
+ for (j = 0; j <= WL_OUTPUT_TRANSFORM_FLIPPED_270; j++)
+ if (crtc->all_transforms & (1 << j))
+ g_variant_builder_add (&transforms, "u", j);
+
+ 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),
+ crtc->transform,
+ &transforms,
+ NULL /* properties */);
+ }
+
+ for (i = 0; i < manager->n_outputs; i++)
+ {
+ MetaOutput *output = &manager->outputs[i];
+ GVariantBuilder crtcs, modes, clones, properties;
+ GBytes *edid;
+ char *edid_file;
+
+ 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));
+
+ 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));
+
+ 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}", "primary",
+ g_variant_new_boolean (output->is_primary));
+ g_variant_builder_add (&properties, "{sv}", "presentation",
+ g_variant_new_boolean (output->is_presentation));
+
+ 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);
+ }
+ }
+
+ g_variant_builder_add (&output_builder, "(uxiausauaua{sv})",
+ i, /* ID */
+ output->output_id,
+ (int)(output->crtc ? output->crtc - manager->crtcs : -1),
+ &crtcs,
+ output->name,
+ &modes,
+ &clones,
+ &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),
+ manager->max_screen_width,
+ manager->max_screen_height);
+ return TRUE;
+}
+
+static gboolean
+output_can_config (MetaOutput *output,
+ MetaCRTC *crtc,
+ MetaMonitorMode *mode)
+{
+ unsigned int i;
+ gboolean ok = FALSE;
+
+ for (i = 0; i < output->n_possible_crtcs && !ok; i++)
+ ok = output->possible_crtcs[i] == crtc;
+
+ if (!ok)
+ return FALSE;
+
+ if (mode == NULL)
+ return TRUE;
+
+ ok = FALSE;
+ for (i = 0; i < output->n_modes && !ok; i++)
+ ok = output->modes[i] == mode;
+
+ return ok;
+}
+
+static gboolean
+output_can_clone (MetaOutput *output,
+ MetaOutput *clone)
+{
+ unsigned int i;
+ gboolean ok = FALSE;
+
+ for (i = 0; i < output->n_possible_clones && !ok; i++)
+ ok = output->possible_clones[i] == clone;
+
+ return ok;
+}
+
+void
+meta_monitor_manager_apply_configuration (MetaMonitorManager *manager,
+ MetaCRTCInfo **crtcs,
+ unsigned int n_crtcs,
+ MetaOutputInfo **outputs,
+ unsigned int n_outputs)
+{
+ META_MONITOR_MANAGER_GET_CLASS (manager)->apply_configuration (manager,
+ crtcs, n_crtcs,
+ outputs, n_outputs);
+}
+
+static gboolean
+save_config_timeout (gpointer user_data)
+{
+ MetaMonitorManager *manager = user_data;
+
+ meta_monitor_config_restore_previous (manager->config, manager);
+
+ manager->persistent_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+meta_monitor_manager_handle_apply_configuration (MetaDBusDisplayConfig *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint serial,
+ gboolean persistent,
+ GVariant *crtcs,
+ GVariant *outputs)
+{
+ MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
+ GVariantIter crtc_iter, output_iter, *nested_outputs;
+ GVariant *properties;
+ guint crtc_id;
+ int new_mode, x, y;
+ int new_screen_width, new_screen_height;
+ guint transform;
+ guint output_id;
+ GPtrArray *crtc_infos, *output_infos;
+
+ 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;
+ }
+
+ crtc_infos = g_ptr_array_new_full (g_variant_n_children (crtcs),
+ (GDestroyNotify) meta_crtc_info_free);
+ output_infos = g_ptr_array_new_full (g_variant_n_children (outputs),
+ (GDestroyNotify) meta_output_info_free);
+
+ /* Validate all arguments */
+ new_screen_width = 0; new_screen_height = 0;
+ g_variant_iter_init (&crtc_iter, crtcs);
+ while (g_variant_iter_loop (&crtc_iter, "(uiiiuaua{sv})",
+ &crtc_id, &new_mode, &x, &y, &transform,
+ &nested_outputs, NULL))
+ {
+ MetaCRTCInfo *crtc_info;
+ MetaOutput *first_output;
+ MetaCRTC *crtc;
+ MetaMonitorMode *mode;
+ guint output_id;
+
+ crtc_info = g_slice_new (MetaCRTCInfo);
+ crtc_info->outputs = g_ptr_array_new ();
+
+ if (crtc_id >= manager->n_crtcs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid CRTC id");
+ return TRUE;
+ }
+ crtc = &manager->crtcs[crtc_id];
+ crtc_info->crtc = crtc;
+
+ if (new_mode != -1 && (new_mode < 0 || (unsigned)new_mode >= manager->n_modes))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid mode id");
+ return TRUE;
+ }
+ mode = new_mode != -1 ? &manager->modes[new_mode] : NULL;
+ crtc_info->mode = mode;
+
+ if (mode)
+ {
+ int width, height;
+
+ if (meta_monitor_transform_is_rotated (transform))
+ {
+ width = mode->height;
+ height = mode->width;
+ }
+ else
+ {
+ width = mode->width;
+ height = mode->height;
+ }
+
+ if (x < 0 ||
+ x + width > manager->max_screen_width ||
+ y < 0 ||
+ y + height > manager->max_screen_height)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid CRTC geometry");
+ return TRUE;
+ }
+
+ new_screen_width = MAX (new_screen_width, x + width);
+ new_screen_height = MAX (new_screen_height, y + height);
+ crtc_info->x = x;
+ crtc_info->y = y;
+ }
+ else
+ {
+ crtc_info->x = 0;
+ crtc_info->y = 0;
+ }
+
+ if (transform < WL_OUTPUT_TRANSFORM_NORMAL ||
+ transform > WL_OUTPUT_TRANSFORM_FLIPPED_270 ||
+ ((crtc->all_transforms & (1 << transform)) == 0))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid transform");
+ return TRUE;
+ }
+ crtc_info->transform = transform;
+
+ first_output = NULL;
+ while (g_variant_iter_loop (nested_outputs, "u", &output_id))
+ {
+ MetaOutput *output;
+
+ if (output_id >= manager->n_outputs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid output id");
+ return TRUE;
+ }
+ output = &manager->outputs[output_id];
+
+ if (!output_can_config (output, crtc, mode))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Output cannot be assigned to this CRTC or mode");
+ return TRUE;
+ }
+ g_ptr_array_add (crtc_info->outputs, output);
+
+ if (first_output)
+ {
+ if (!output_can_clone (output, first_output))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Outputs cannot be cloned");
+ return TRUE;
+ }
+ }
+ else
+ first_output = output;
+ }
+
+ if (!first_output && mode)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Mode specified without outputs?");
+ return TRUE;
+ }
+
+ g_ptr_array_add (crtc_infos, crtc_info);
+ }
+
+ if (new_screen_width == 0 || new_screen_height == 0)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Refusing to disable all outputs");
+ return TRUE;
+ }
+
+ g_variant_iter_init (&output_iter, outputs);
+ while (g_variant_iter_loop (&output_iter, "(u@a{sv})", &output_id, &properties))
+ {
+ MetaOutputInfo *output_info;
+ gboolean primary, presentation;
+
+ if (output_id >= manager->n_outputs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid output id");
+ return TRUE;
+ }
+
+ output_info = g_slice_new0 (MetaOutputInfo);
+ output_info->output = &manager->outputs[output_id];
+
+ if (g_variant_lookup (properties, "primary", "b", &primary))
+ output_info->is_primary = primary;
+
+ if (g_variant_lookup (properties, "presentation", "b", &presentation))
+ output_info->is_presentation = presentation;
+
+ g_ptr_array_add (output_infos, output_info);
+ }
+
+ /* If we were in progress of making a persistent change and we see a
+ new request, it's likely that the old one failed in some way, so
+ don't save it, but also don't queue for restoring it.
+ */
+ if (manager->persistent_timeout_id && persistent)
+ {
+ g_source_remove (manager->persistent_timeout_id);
+ manager->persistent_timeout_id = 0;
+ }
+
+ meta_monitor_manager_apply_configuration (manager,
+ (MetaCRTCInfo**)crtc_infos->pdata,
+ crtc_infos->len,
+ (MetaOutputInfo**)output_infos->pdata,
+ output_infos->len);
+
+ g_ptr_array_unref (crtc_infos);
+ g_ptr_array_unref (output_infos);
+
+ /* Update MetaMonitorConfig data structures immediately so that we
+ don't revert the change at the next XRandR event, then ask the plugin
+ manager (through MetaScreen) to confirm the display change with the
+ appropriate UI. Then wait 20 seconds and if not confirmed, revert the
+ configuration.
+ */
+ meta_monitor_config_update_current (manager->config, manager);
+ if (persistent)
+ {
+ manager->persistent_timeout_id = g_timeout_add_seconds (20, save_config_timeout, manager);
+ g_signal_emit (manager, signals[CONFIRM_DISPLAY_CHANGE], 0);
+ }
+
+ meta_dbus_display_config_complete_apply_configuration (skeleton, invocation);
+ return TRUE;
+}
+
+void
+meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
+ gboolean ok)
+{
+ if (!manager->persistent_timeout_id)
+ {
+ /* too late */
+ return;
+ }
+
+ g_source_remove (manager->persistent_timeout_id);
+ manager->persistent_timeout_id = 0;
+
+ if (ok)
+ meta_monitor_config_make_persistent (manager->config);
+ else
+ meta_monitor_config_restore_previous (manager->config, manager);
+}
+
+static gboolean
+meta_monitor_manager_handle_change_backlight (MetaDBusDisplayConfig *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint serial,
+ guint output_id,
+ gint value)
+{
+ MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton);
+ 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;
+ }
+
+ if (output_id >= manager->n_outputs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid output id");
+ return TRUE;
+ }
+ output = &manager->outputs[output_id];
+
+ 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;
+ 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;
+ }
+
+ if (crtc_id >= manager->n_crtcs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid crtc id");
+ return TRUE;
+ }
+ crtc = &manager->crtcs[crtc_id];
+
+ 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;
+ 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;
+ }
+
+ if (crtc_id >= manager->n_crtcs)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid crtc id");
+ return TRUE;
+ }
+ crtc = &manager->crtcs[crtc_id];
+
+ 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_apply_configuration = meta_monitor_manager_handle_apply_configuration;
+ 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;
+}
+
+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);
+}
+
+static MetaMonitorManager *global_monitor_manager;
+
+void
+meta_monitor_manager_initialize (void)
+{
+ global_monitor_manager = meta_monitor_manager_new ();
+}
+
+MetaMonitorManager *
+meta_monitor_manager_get (void)
+{
+ g_assert (global_monitor_manager != NULL);
+
+ return global_monitor_manager;
+}
+
+MetaMonitorInfo *
+meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
+ unsigned int *n_infos)
+{
+ *n_infos = manager->n_monitor_infos;
+ return manager->monitor_infos;
+}
+
+MetaOutput *
+meta_monitor_manager_get_outputs (MetaMonitorManager *manager,
+ unsigned int *n_outputs)
+{
+ *n_outputs = manager->n_outputs;
+ return manager->outputs;
+}
+
+void
+meta_monitor_manager_get_resources (MetaMonitorManager *manager,
+ MetaMonitorMode **modes,
+ unsigned int *n_modes,
+ MetaCRTC **crtcs,
+ unsigned int *n_crtcs,
+ MetaOutput **outputs,
+ unsigned int *n_outputs)
+{
+ *modes = manager->modes;
+ *n_modes = manager->n_modes;
+ *crtcs = manager->crtcs;
+ *n_crtcs = manager->n_crtcs;
+ *outputs = manager->outputs;
+ *n_outputs = manager->n_outputs;
+}
+
+int
+meta_monitor_manager_get_primary_index (MetaMonitorManager *manager)
+{
+ return manager->primary_monitor_index;
+}
+
+void
+meta_monitor_manager_get_screen_size (MetaMonitorManager *manager,
+ int *width,
+ int *height)
+{
+ *width = manager->screen_width;
+ *height = manager->screen_height;
+}
+
+void
+meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
+ int *width,
+ int *height)
+{
+ *width = manager->max_screen_width;
+ *height = manager->max_screen_height;
+}
+
+static void
+invalidate_logical_config (MetaMonitorManager *manager)
+{
+ MetaMonitorInfo *old_monitor_infos;
+
+ old_monitor_infos = manager->monitor_infos;
+
+ if (manager->in_init)
+ return;
+
+ make_logical_config (manager);
+
+ g_signal_emit_by_name (manager, "monitors-changed");
+
+ g_free (old_monitor_infos);
+}
+
+gboolean
+meta_monitor_manager_handle_xevent (MetaMonitorManager *manager,
+ XEvent *event)
+{
+ MetaMonitorManagerClass *klass;
+ MetaOutput *old_outputs;
+ MetaCRTC *old_crtcs;
+ MetaMonitorMode *old_modes;
+ int n_old_outputs;
+ gboolean changed;
+
+ klass = META_MONITOR_MANAGER_GET_CLASS (manager);
+ if (klass->handle_xevent)
+ changed = klass->handle_xevent (manager, event);
+ else
+ changed = FALSE;
+
+ if (!changed)
+ return FALSE;
+
+ /* Save the old structures, so they stay valid during the update */
+ old_outputs = manager->outputs;
+ n_old_outputs = manager->n_outputs;
+ old_modes = manager->modes;
+ old_crtcs = manager->crtcs;
+
+ read_current_config (manager);
+
+ /* Check if the current intended configuration has the same outputs
+ as the new real one. If so, this was a result of an ApplyConfiguration
+ call (or a change from ourselves), and we can go straight to rebuild
+ the logical config and tell the outside world.
+
+ Otherwise, this event was caused by hotplug, so give a chance to
+ MetaMonitorConfig.
+ */
+ if (meta_monitor_config_match_current (manager->config, manager))
+ {
+ invalidate_logical_config (manager);
+ }
+ else
+ {
+ if (!meta_monitor_config_apply_stored (manager->config, manager))
+ meta_monitor_config_make_default (manager->config, manager);
+ }
+
+ free_output_array (old_outputs, n_old_outputs);
+ g_free (old_modes);
+ g_free (old_crtcs);
+
+ return TRUE;
+}
+
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 6bb32f16d..8ddf049b5 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -38,17 +38,7 @@
#include
#include "stack-tracker.h"
#include "ui.h"
-
-typedef struct _MetaMonitorInfo MetaMonitorInfo;
-
-struct _MetaMonitorInfo
-{
- int number;
- MetaRectangle rect;
- gboolean is_primary;
- gboolean in_fullscreen;
- XID output; /* The primary or first output for this crtc, None if no xrandr */
-};
+#include "monitor-private.h"
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
gpointer user_data);
@@ -101,10 +91,11 @@ struct _MetaScreen
Window wm_sn_selection_window;
Atom wm_sn_atom;
guint32 wm_sn_timestamp;
-
+
MetaMonitorInfo *monitor_infos;
- int primary_monitor_index;
int n_monitor_infos;
+ int primary_monitor_index;
+ gboolean has_xinerama_indices;
/* Cache the current monitor */
int last_monitor_index;
@@ -232,10 +223,6 @@ void meta_screen_calc_workspace_layout (MetaScreen *screen,
MetaWorkspaceLayout *layout);
void meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout);
-void meta_screen_resize (MetaScreen *screen,
- int width,
- int height);
-
void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen,
MetaWindow *keep);
@@ -263,4 +250,9 @@ Window meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
gboolean meta_screen_handle_xevent (MetaScreen *screen,
XEvent *xevent);
+int meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
+ int index);
+int meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
+ int index);
+
#endif
diff --git a/src/core/screen.c b/src/core/screen.c
index eb3d5264f..b8870da20 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -50,10 +50,6 @@
#include
-#ifdef HAVE_RANDR
-#include
-#endif
-
#include
#include
#include
@@ -78,6 +74,9 @@ static void meta_screen_sn_event (SnMonitorEvent *event,
void *user_data);
#endif
+static void on_monitors_changed (MetaMonitorManager *manager,
+ MetaScreen *screen);
+
enum
{
PROP_N_WORKSPACES = 1,
@@ -352,250 +351,93 @@ set_wm_icon_size_hint (MetaScreen *screen)
#undef N_VALS
}
-/* The list of monitors reported by the windowing system might include
- * mirrored monitors with identical bounds. Since mirrored monitors
- * shouldn't be treated as separate monitors for most purposes, we
- * filter them out here. (We ignore the possibility of partially
- * overlapping monitors because they are rare and it's hard to come
- * up with any sensible interpretation.)
- */
static void
-filter_mirrored_monitors (MetaScreen *screen)
+meta_screen_ensure_xinerama_indices (MetaScreen *screen)
{
- int i, j;
+ XineramaScreenInfo *infos;
+ int n_infos, i, j;
- /* Currently always true and simplifies things */
- g_assert (screen->primary_monitor_index == 0);
+ if (screen->has_xinerama_indices)
+ return;
- for (i = 1; i < screen->n_monitor_infos; i++)
+ screen->has_xinerama_indices = TRUE;
+
+ if (!XineramaIsActive (screen->display->xdisplay))
+ return;
+
+ infos = XineramaQueryScreens (screen->display->xdisplay, &n_infos);
+ if (n_infos <= 0 || infos == NULL)
{
- /* In case we've filtered previous monitors */
- screen->monitor_infos[i].number = i;
+ meta_XFree (infos);
+ return;
+ }
- for (j = 0; j < i; j++)
+ for (i = 0; i < screen->n_monitor_infos; ++i)
+ {
+ for (j = 0; j < n_infos; ++j)
{
- if (meta_rectangle_equal (&screen->monitor_infos[i].rect,
- &screen->monitor_infos[j].rect))
- {
- memmove (&screen->monitor_infos[i],
- &screen->monitor_infos[i + 1],
- (screen->n_monitor_infos - i - 1) * sizeof (MetaMonitorInfo));
- screen->n_monitor_infos--;
- i--;
-
- continue;
- }
+ if (screen->monitor_infos[i].rect.x == infos[j].x_org &&
+ screen->monitor_infos[i].rect.y == infos[j].y_org &&
+ screen->monitor_infos[i].rect.width == infos[j].width &&
+ screen->monitor_infos[i].rect.height == infos[j].height)
+ screen->monitor_infos[i].xinerama_index = j;
}
}
+
+ meta_XFree (infos);
}
-#ifdef HAVE_RANDR
-static MetaMonitorInfo *
-find_monitor_with_rect (MetaScreen *screen, int x, int y, int w, int h)
+int
+meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
+ int index)
+{
+ meta_screen_ensure_xinerama_indices (screen);
+
+ return screen->monitor_infos[index].xinerama_index;
+}
+
+int
+meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
+ int index)
{
- MetaMonitorInfo *info;
int i;
+ meta_screen_ensure_xinerama_indices (screen);
+
for (i = 0; i < screen->n_monitor_infos; i++)
- {
- info = &screen->monitor_infos[i];
- if (x == info->rect.x &&
- y == info->rect.y &&
- w == info->rect.width &&
- h == info->rect.height)
- return info;
- }
- return NULL;
+ if (screen->monitor_infos[i].xinerama_index == index)
+ return i;
+
+ return -1;
}
-/* 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 XID
-find_main_output_for_crtc (MetaScreen *screen, XRRScreenResources *resources, XRRCrtcInfo *crtc)
-{
- XRROutputInfo *output;
- RROutput primary_output;
- int i;
- XID res;
-
- primary_output = XRRGetOutputPrimary (screen->display->xdisplay, screen->xroot);
-
- res = None;
- for (i = 0; i < crtc->noutput; i++)
- {
- output = XRRGetOutputInfo (screen->display->xdisplay, resources, crtc->outputs[i]);
- if (output->connection != RR_Disconnected &&
- (res == None || crtc->outputs[i] == primary_output))
- res = crtc->outputs[i];
- XRRFreeOutputInfo (output);
- }
-
- return res;
-}
-
-#endif
-
static void
reload_monitor_infos (MetaScreen *screen)
{
- MetaDisplay *display;
+ GList *tmp;
+ MetaMonitorManager *manager;
- {
- GList *tmp;
+ tmp = screen->workspaces;
+ while (tmp != NULL)
+ {
+ MetaWorkspace *space = tmp->data;
- tmp = screen->workspaces;
- while (tmp != NULL)
- {
- MetaWorkspace *space = tmp->data;
+ meta_workspace_invalidate_work_area (space);
+
+ tmp = tmp->next;
+ }
- meta_workspace_invalidate_work_area (space);
-
- tmp = tmp->next;
- }
- }
+ /* Any previous screen->monitor_infos or screen->outputs is freed by the caller */
- display = screen->display;
-
- /* Any previous screen->monitor_infos is freed by the caller */
-
- screen->monitor_infos = NULL;
- screen->n_monitor_infos = 0;
screen->last_monitor_index = 0;
-
- /* Xinerama doesn't have a concept of primary monitor, however XRandR
- * does. However, the XRandR xinerama compat code always sorts the
- * primary output first, so we rely on that here. We could use the
- * native XRandR calls instead of xinerama, but that would be
- * slightly problematic for _NET_WM_FULLSCREEN_MONITORS support, as
- * that is defined in terms of xinerama monitor indexes.
- * So, since we don't need anything in xrandr except the primary
- * we can keep using xinerama and use the first monitor as the
- * primary.
- */
- screen->primary_monitor_index = 0;
-
+ screen->has_xinerama_indices = FALSE;
screen->display->monitor_cache_invalidated = TRUE;
- if (g_getenv ("MUTTER_DEBUG_XINERAMA"))
- {
- meta_topic (META_DEBUG_XINERAMA,
- "Pretending a single monitor has two Xinerama screens\n");
+ manager = meta_monitor_manager_get ();
- screen->monitor_infos = g_new0 (MetaMonitorInfo, 2);
- screen->n_monitor_infos = 2;
-
- screen->monitor_infos[0].number = 0;
- screen->monitor_infos[0].rect = screen->rect;
- screen->monitor_infos[0].rect.width = screen->rect.width / 2;
- screen->monitor_infos[0].in_fullscreen = -1;
-
- screen->monitor_infos[1].number = 1;
- screen->monitor_infos[1].rect = screen->rect;
- screen->monitor_infos[1].rect.x = screen->rect.width / 2;
- screen->monitor_infos[1].rect.width = screen->rect.width / 2;
- screen->monitor_infos[0].in_fullscreen = -1;
- }
-
- if (screen->n_monitor_infos == 0 &&
- XineramaIsActive (display->xdisplay))
- {
- XineramaScreenInfo *infos;
- int n_infos;
- int i;
-
- n_infos = 0;
- infos = XineramaQueryScreens (display->xdisplay, &n_infos);
-
- meta_topic (META_DEBUG_XINERAMA,
- "Found %d Xinerama screens on display %s\n",
- n_infos, display->name);
-
- if (n_infos > 0)
- {
- screen->monitor_infos = g_new0 (MetaMonitorInfo, n_infos);
- screen->n_monitor_infos = n_infos;
-
- i = 0;
- while (i < n_infos)
- {
- screen->monitor_infos[i].number = infos[i].screen_number;
- screen->monitor_infos[i].rect.x = infos[i].x_org;
- screen->monitor_infos[i].rect.y = infos[i].y_org;
- screen->monitor_infos[i].rect.width = infos[i].width;
- screen->monitor_infos[i].rect.height = infos[i].height;
- screen->monitor_infos[i].in_fullscreen = -1;
-
- meta_topic (META_DEBUG_XINERAMA,
- "Monitor %d is %d,%d %d x %d\n",
- screen->monitor_infos[i].number,
- screen->monitor_infos[i].rect.x,
- screen->monitor_infos[i].rect.y,
- screen->monitor_infos[i].rect.width,
- screen->monitor_infos[i].rect.height);
-
- ++i;
- }
- }
-
- meta_XFree (infos);
-
-#ifdef HAVE_RANDR
- {
- XRRScreenResources *resources;
-
- resources = XRRGetScreenResourcesCurrent (display->xdisplay, screen->xroot);
- if (resources)
- {
- for (i = 0; i < resources->ncrtc; i++)
- {
- XRRCrtcInfo *crtc;
- MetaMonitorInfo *info;
-
- crtc = XRRGetCrtcInfo (display->xdisplay, resources, resources->crtcs[i]);
- info = find_monitor_with_rect (screen, crtc->x, crtc->y, (int)crtc->width, (int)crtc->height);
- if (info)
- info->output = find_main_output_for_crtc (screen, resources, crtc);
-
- XRRFreeCrtcInfo (crtc);
- }
- XRRFreeScreenResources (resources);
- }
- }
-#endif
- }
- else if (screen->n_monitor_infos > 0)
- {
- meta_topic (META_DEBUG_XINERAMA,
- "No Xinerama extension or Xinerama inactive on display %s\n",
- display->name);
- }
-
- /* If no Xinerama, fill in the single screen info so
- * we can use the field unconditionally
- */
- if (screen->n_monitor_infos == 0)
- {
- meta_topic (META_DEBUG_XINERAMA,
- "No Xinerama screens, using default screen info\n");
-
- screen->monitor_infos = g_new0 (MetaMonitorInfo, 1);
- screen->n_monitor_infos = 1;
-
- screen->monitor_infos[0].number = 0;
- screen->monitor_infos[0].rect = screen->rect;
- screen->monitor_infos[0].in_fullscreen = -1;
- }
-
- filter_mirrored_monitors (screen);
-
- screen->monitor_infos[screen->primary_monitor_index].is_primary = TRUE;
-
- g_assert (screen->n_monitor_infos > 0);
- g_assert (screen->monitor_infos != NULL);
+ screen->monitor_infos = meta_monitor_manager_get_monitor_infos (manager,
+ (unsigned*)&screen->n_monitor_infos);
+ screen->primary_monitor_index = meta_monitor_manager_get_primary_index (manager);
}
/* The guard window allows us to leave minimized windows mapped so
@@ -674,6 +516,7 @@ meta_screen_new (MetaDisplay *display,
guint32 manager_timestamp;
gulong current_workspace;
MetaWaylandCompositor *compositor;
+ MetaMonitorManager *manager;
replace_current_wm = meta_get_replace_current_wm ();
@@ -833,17 +676,15 @@ meta_screen_new (MetaDisplay *display,
screen->xroot = xroot;
screen->rect.x = screen->rect.y = 0;
- if (meta_is_wayland_compositor ())
- {
- compositor = meta_wayland_compositor_get_default ();
- screen->rect.width = clutter_actor_get_width (compositor->stage);
- screen->rect.height = clutter_actor_get_height (compositor->stage);
- }
- else
- {
- screen->rect.width = WidthOfScreen (screen->xscreen);
- screen->rect.height = HeightOfScreen (screen->xscreen);
- }
+ meta_monitor_manager_initialize ();
+
+ manager = meta_monitor_manager_get ();
+ g_signal_connect (manager, "monitors-changed",
+ G_CALLBACK (on_monitors_changed), screen);
+
+ meta_monitor_manager_get_screen_size (manager,
+ &screen->rect.width,
+ &screen->rect.height);
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
@@ -869,10 +710,6 @@ meta_screen_new (MetaDisplay *display,
screen->compositor_data = NULL;
screen->guard_window = None;
- screen->monitor_infos = NULL;
- screen->n_monitor_infos = 0;
- screen->last_monitor_index = 0;
-
reload_monitor_infos (screen);
meta_cursor_tracker_get_for_screen (screen);
@@ -959,7 +796,7 @@ meta_screen_new (MetaDisplay *display,
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
screen->number, screen->screen_name, screen->xroot);
-
+
return screen;
}
@@ -3007,19 +2844,15 @@ meta_screen_resize_func (MetaScreen *screen,
meta_window_recalc_features (window);
}
-void
-meta_screen_resize (MetaScreen *screen,
- int width,
- int height)
+static void
+on_monitors_changed (MetaMonitorManager *manager,
+ MetaScreen *screen)
{
- GSList *windows, *tmp;
- MetaMonitorInfo *old_monitor_infos;
+ GSList *tmp, *windows;
- screen->rect.width = width;
- screen->rect.height = height;
-
- /* Save the old monitor infos, so they stay valid during the update */
- old_monitor_infos = screen->monitor_infos;
+ meta_monitor_manager_get_screen_size (manager,
+ &screen->rect.width,
+ &screen->rect.height);
reload_monitor_infos (screen);
set_desktop_geometry_hint (screen);
@@ -3031,8 +2864,8 @@ meta_screen_resize (MetaScreen *screen,
changes.x = 0;
changes.y = 0;
- changes.width = width;
- changes.height = height;
+ changes.width = screen->rect.width;
+ changes.height = screen->rect.height;
XConfigureWindow(screen->display->xdisplay,
screen->guard_window,
@@ -3042,7 +2875,8 @@ meta_screen_resize (MetaScreen *screen,
if (screen->display->compositor)
meta_compositor_sync_screen_size (screen->display->compositor,
- screen, width, height);
+ screen,
+ screen->rect.width, screen->rect.height);
/* Queue a resize on all the windows */
meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
@@ -3058,7 +2892,6 @@ meta_screen_resize (MetaScreen *screen,
meta_window_update_for_monitors_changed (window);
}
- g_free (old_monitor_infos);
g_slist_free (windows);
meta_screen_queue_check_fullscreen (screen);
diff --git a/src/core/util.c b/src/core/util.c
index c14fad736..d5014607b 100644
--- a/src/core/util.c
+++ b/src/core/util.c
@@ -345,6 +345,8 @@ topic_name (MetaDebugTopic topic)
return "COMPOSITOR";
case META_DEBUG_EDGE_RESISTANCE:
return "EDGE_RESISTANCE";
+ case META_DEBUG_DBUS:
+ return "DBUS";
case META_DEBUG_VERBOSE:
return "VERBOSE";
}
@@ -650,8 +652,13 @@ meta_show_dialog (const char *type,
append_argument (args, "zenity");
append_argument (args, type);
- append_argument (args, "--display");
- append_argument (args, display);
+
+ if (display)
+ {
+ append_argument (args, "--display");
+ append_argument (args, display);
+ }
+
append_argument (args, "--class");
append_argument (args, "mutter-dialog");
append_argument (args, "--title");
diff --git a/src/core/window-private.h b/src/core/window-private.h
index c498aa417..c068c35af 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -179,7 +179,7 @@ struct _MetaWindow
* been overridden (via a client message), the window will cover the union of
* these monitors. If not, this is the single monitor which the window's
* origin is on. */
- long fullscreen_monitors[4];
+ gint fullscreen_monitors[4];
/* Whether we're trying to constrain the window to be fully onscreen */
guint require_fully_onscreen : 1;
diff --git a/src/core/window.c b/src/core/window.c
index 1cafc5ba5..faf4bb2d7 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -2281,10 +2281,14 @@ set_net_wm_state (MetaWindow *window)
if (window->fullscreen)
{
- data[0] = window->fullscreen_monitors[0];
- data[1] = window->fullscreen_monitors[1];
- data[2] = window->fullscreen_monitors[2];
- data[3] = window->fullscreen_monitors[3];
+ data[0] = meta_screen_monitor_index_to_xinerama_index (window->screen,
+ window->fullscreen_monitors[0]);
+ data[1] = meta_screen_monitor_index_to_xinerama_index (window->screen,
+ window->fullscreen_monitors[1]);
+ data[2] = meta_screen_monitor_index_to_xinerama_index (window->screen,
+ window->fullscreen_monitors[2]);
+ data[3] = meta_screen_monitor_index_to_xinerama_index (window->screen,
+ window->fullscreen_monitors[3]);
meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
meta_error_trap_push (window->display);
@@ -4958,7 +4962,8 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
{
MetaMonitorInfo *info = &window->screen->monitor_infos[i];
- if (info->output == old->output)
+ if (info->output_id != 0 &&
+ info->output_id == old->output_id)
{
new = info;
break;
@@ -7303,10 +7308,14 @@ meta_window_client_message (MetaWindow *window,
meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
window->desc);
- top = event->xclient.data.l[0];
- bottom = event->xclient.data.l[1];
- left = event->xclient.data.l[2];
- right = event->xclient.data.l[3];
+ top = meta_screen_xinerama_index_to_monitor_index (window->screen,
+ event->xclient.data.l[0]);
+ bottom = meta_screen_xinerama_index_to_monitor_index (window->screen,
+ event->xclient.data.l[1]);
+ left = meta_screen_xinerama_index_to_monitor_index (window->screen,
+ event->xclient.data.l[2]);
+ right = meta_screen_xinerama_index_to_monitor_index (window->screen,
+ event->xclient.data.l[3]);
/* source_indication = event->xclient.data.l[4]; */
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
diff --git a/src/meta/atomnames.h b/src/meta/atomnames.h
index 064679914..d7a6e7e35 100644
--- a/src/meta/atomnames.h
+++ b/src/meta/atomnames.h
@@ -72,6 +72,7 @@ item(_MUTTER_TIMESTAMP_PING)
item(_MUTTER_FOCUS_SET)
item(_MUTTER_SENTINEL)
item(_MUTTER_VERSION)
+item(_MUTTER_PRESENTATION_OUTPUT)
item(WM_CLIENT_MACHINE)
item(MANAGER)
item(TARGETS)
@@ -79,6 +80,7 @@ item(MULTIPLE)
item(TIMESTAMP)
item(VERSION)
item(ATOM_PAIR)
+item(BACKLIGHT)
/* Oddities: These are used, and we need atoms for them,
* but when we need all _NET_WM hints (i.e. when we're making
diff --git a/src/meta/meta-plugin.h b/src/meta/meta-plugin.h
index 8999b133e..0587551c8 100644
--- a/src/meta/meta-plugin.h
+++ b/src/meta/meta-plugin.h
@@ -205,6 +205,21 @@ struct _MetaPluginClass
gboolean (*keybinding_filter) (MetaPlugin *plugin,
MetaKeyBinding *binding);
+ /**
+ * MetaPluginClass::confirm_display_config:
+ * @plugin: a #MetaPlugin
+ *
+ * Virtual function called when the display configuration changes.
+ * The common way to implement this function is to show some form
+ * of modal dialog that should ask the user if everything was ok.
+ *
+ * When confirmed by the user, the plugin must call meta_plugin_complete_display_change()
+ * to make the configuration permanent. If that function is not
+ * called within the timeout, the previous configuration will be
+ * reapplied.
+ */
+ void (*confirm_display_change) (MetaPlugin *plugin);
+
/**
* MetaPluginClass::plugin_info:
* @plugin: a #MetaPlugin
@@ -214,6 +229,7 @@ struct _MetaPluginClass
* Returns: a #MetaPluginInfo.
*/
const MetaPluginInfo * (*plugin_info) (MetaPlugin *plugin);
+
};
/**
@@ -360,6 +376,10 @@ void
meta_plugin_destroy_completed (MetaPlugin *plugin,
MetaWindowActor *actor);
+void
+meta_plugin_complete_display_change (MetaPlugin *plugin,
+ gboolean ok);
+
/**
* MetaModalOptions:
* @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already
diff --git a/src/meta/util.h b/src/meta/util.h
index 91f968ba0..cc60fa277 100644
--- a/src/meta/util.h
+++ b/src/meta/util.h
@@ -102,7 +102,8 @@ typedef enum
META_DEBUG_RESIZING = 1 << 18,
META_DEBUG_SHAPES = 1 << 19,
META_DEBUG_COMPOSITOR = 1 << 20,
- META_DEBUG_EDGE_RESISTANCE = 1 << 21
+ META_DEBUG_EDGE_RESISTANCE = 1 << 21,
+ META_DEBUG_DBUS = 1 << 22
} MetaDebugTopic;
void meta_topic_real (MetaDebugTopic topic,
diff --git a/src/xrandr.xml b/src/xrandr.xml
new file mode 100644
index 000000000..06449c36a
--- /dev/null
+++ b/src/xrandr.xml
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+