diff --git a/src/core/monitor-private.h b/src/core/monitor-private.h
index b28123d07..8c24c8daa 100644
--- a/src/core/monitor-private.h
+++ b/src/core/monitor-private.h
@@ -94,6 +94,9 @@ struct _MetaCRTC
from the HW one
*/
MetaMonitorInfo *logical_monitor;
+
+ /* Used when changing configuration */
+ gboolean dirty;
};
struct _MetaMonitorMode
diff --git a/src/core/monitor.c b/src/core/monitor.c
index 5f3cfb121..2af3ca8a6 100644
--- a/src/core/monitor.c
+++ b/src/core/monitor.c
@@ -35,6 +35,7 @@
#endif
#include
+#include
#include "monitor-private.h"
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
@@ -42,16 +43,40 @@
#include "meta-dbus-xrandr.h"
+#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 enum {
+ META_BACKEND_DUMMY,
+ META_BACKEND_XRANDR,
+ META_BACKEND_COGL
+} MetaBackend;
+
struct _MetaMonitorManager
{
GObject parent_instance;
+ MetaBackend backend;
+
/* XXX: this structure is very badly
packed, but I like the logical organization
of fields */
unsigned int serial;
+ int max_screen_width;
+ int max_screen_height;
+
/* Outputs refer to physical screens,
CRTCs refer to stuff that can drive outputs
(like encoders, but less tied to the HW),
@@ -74,6 +99,8 @@ struct _MetaMonitorManager
#ifdef HAVE_RANDR
Display *xdisplay;
+ XRRScreenResources *resources;
+ int time;
#endif
int dbus_name_id;
@@ -97,6 +124,8 @@ G_DEFINE_TYPE (MetaMonitorManager, meta_monitor_manager, G_TYPE_OBJECT);
static void
make_dummy_monitor_config (MetaMonitorManager *manager)
{
+ manager->backend = META_BACKEND_DUMMY;
+
manager->modes = g_new0 (MetaMonitorMode, 1);
manager->n_modes = 1;
@@ -125,6 +154,8 @@ make_dummy_monitor_config (MetaMonitorManager *manager)
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].dirty = FALSE;
+ manager->crtcs[0].logical_monitor = NULL;
manager->outputs = g_new0 (MetaOutput, 1);
manager->n_outputs = 1;
@@ -147,6 +178,9 @@ make_dummy_monitor_config (MetaMonitorManager *manager)
manager->outputs[0].possible_crtcs[0] = &manager->crtcs[0];
manager->outputs[0].n_possible_clones = 0;
manager->outputs[0].possible_clones = g_new0 (MetaOutput *, 0);
+
+ manager->max_screen_width = manager->modes[0].width;
+ manager->max_screen_height = manager->modes[0].height;
}
#ifdef HAVE_RANDR
@@ -158,12 +192,26 @@ read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
RROutput primary_output;
unsigned int i, j, k;
unsigned int n_actual_outputs;
+ int min_width, min_height;
+
+ if (manager->resources)
+ XRRFreeScreenResources (manager->resources);
+ manager->resources = NULL;
+
+ XRRGetScreenSizeRange (manager->xdisplay, DefaultRootWindow (manager->xdisplay),
+ &min_width,
+ &min_height,
+ &manager->max_screen_width,
+ &manager->max_screen_height);
resources = XRRGetScreenResourcesCurrent (manager->xdisplay,
DefaultRootWindow (manager->xdisplay));
if (!resources)
return make_dummy_monitor_config (manager);
+ manager->backend = META_BACKEND_XRANDR;
+ manager->resources = resources;
+ manager->time = resources->configTimestamp;
manager->n_outputs = resources->noutput;
manager->n_crtcs = resources->ncrtc;
manager->n_modes = resources->nmode;
@@ -199,6 +247,7 @@ read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
meta_crtc->rect.y = crtc->y;
meta_crtc->rect.width = crtc->width;
meta_crtc->rect.height = crtc->height;
+ meta_crtc->dirty = FALSE;
for (j = 0; j < (unsigned)resources->nmode; j++)
{
@@ -318,8 +367,6 @@ read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
}
}
}
-
- XRRFreeScreenResources (resources);
}
#endif
@@ -385,6 +432,8 @@ read_monitor_infos_from_cogl (MetaMonitorManager *manager)
if (output_list == NULL)
return make_dummy_monitor_config (manager);
+ manager->backend = META_BACKEND_COGL;
+
n = g_list_length (output_list);
modes = g_array_new (FALSE, TRUE, sizeof (MetaMonitorMode));
crtcs = g_array_sized_new (FALSE, TRUE, sizeof (MetaCRTC), n);
@@ -419,6 +468,7 @@ read_monitor_infos_from_cogl (MetaMonitorManager *manager)
cogl_output_get_height (output),
cogl_output_get_refresh_rate (output));
meta_crtc.logical_monitor = NULL;
+ meta_crtc.dirty = FALSE;
/* This will never resize the array because we preallocated with g_array_sized_new() */
g_array_append_val (crtcs, meta_crtc);
@@ -840,9 +890,334 @@ handle_get_resources (MetaDBusDisplayConfig *skeleton,
manager->serial,
g_variant_builder_end (&crtc_builder),
g_variant_builder_end (&output_builder),
- g_variant_builder_end (&mode_builder));
- return FALSE;
-}
+ 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;
+}
+
+#ifdef HAVE_RANDR
+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
+apply_config_xrandr (MetaMonitorManager *manager,
+ GVariantIter *crtcs,
+ GVariantIter *outputs)
+{
+ GVariant *nested_outputs, *properties;
+ guint crtc_id, output_id, transform;
+ int new_mode, x, y;
+ unsigned i;
+
+ while (g_variant_iter_loop (crtcs, "(uiiiu@aua{sv})",
+ &crtc_id, &new_mode, &x, &y,
+ &transform, &nested_outputs, NULL))
+ {
+ MetaCRTC *crtc = &manager->crtcs[crtc_id];
+ crtc->dirty = TRUE;
+
+ if (new_mode == -1)
+ {
+ XRRSetCrtcConfig (manager->xdisplay,
+ manager->resources,
+ (XID)crtc->crtc_id,
+ manager->time,
+ 0, 0,
+ None,
+ RR_Rotate_0,
+ NULL, 0);
+ }
+ else
+ {
+ MetaMonitorMode *mode;
+ XID *outputs;
+ int i, n_outputs;
+ guint output_id;
+ Status ok;
+
+ mode = &manager->modes[new_mode];
+
+ n_outputs = g_variant_n_children (nested_outputs);
+ outputs = g_new (XID, n_outputs);
+
+ for (i = 0; i < n_outputs; i++)
+ {
+ g_variant_get_child (nested_outputs, i, "u", &output_id);
+
+ outputs[i] = manager->outputs[output_id].output_id;
+ }
+
+ meta_error_trap_push (meta_get_display ());
+ ok = XRRSetCrtcConfig (manager->xdisplay,
+ manager->resources,
+ (XID)crtc->crtc_id,
+ manager->time,
+ x, y,
+ (XID)mode->mode_id,
+ wl_transform_to_xrandr (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 - manager->crtcs), (unsigned)(mode - manager->modes),
+ mode->width, mode->height, (float)mode->refresh_rate, x, y, transform);
+
+ g_free (outputs);
+ }
+ }
+
+ while (g_variant_iter_loop (outputs, "(u@a{sv})",
+ &output_id, &properties))
+ {
+ gboolean primary;
+
+ if (g_variant_lookup (properties, "primary", "b", &primary) && primary)
+ {
+ MetaOutput *output = &manager->outputs[output_id];
+
+ XRRSetOutputPrimary (manager->xdisplay,
+ DefaultRootWindow (manager->xdisplay),
+ (XID)output->output_id);
+ }
+ }
+
+ /* Disable CRTCs not mentioned in the list */
+ for (i = 0; i < manager->n_crtcs; i++)
+ {
+ MetaCRTC *crtc = &manager->crtcs[i];
+
+ if (crtc->dirty)
+ continue;
+ if (crtc->current_mode == NULL)
+ continue;
+
+ XRRSetCrtcConfig (manager->xdisplay,
+ manager->resources,
+ (XID)crtc->crtc_id,
+ manager->time,
+ 0, 0,
+ None,
+ RR_Rotate_0,
+ NULL, 0);
+ }
+}
+#endif
+
+static gboolean
+handle_apply_configuration (MetaDBusDisplayConfig *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint serial,
+ gboolean persistent,
+ GVariant *crtcs,
+ GVariant *outputs,
+ MetaMonitorManager *manager)
+{
+ GVariantIter crtc_iter, output_iter, *nested_outputs;
+ guint crtc_id;
+ int new_mode, x, y;
+ guint transform;
+ guint output_id;
+
+ 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 (persistent)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Persistent configuration is not yet implemented");
+ return TRUE;
+ }
+
+ if (manager->backend != META_BACKEND_XRANDR)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "Changing configuration is not supported by the backend");
+ return TRUE;
+ }
+
+ /* Validate all arguments */
+ 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))
+ {
+ MetaOutput *first_output;
+ MetaCRTC *crtc;
+ MetaMonitorMode *mode;
+ guint output_id;
+
+ if (crtc_id < 0 || 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];
+
+ 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;
+
+ if (mode &&
+ (x < 0 ||
+ x + mode->width > manager->max_screen_width ||
+ y < 0 ||
+ y + mode->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;
+ }
+
+ if (transform < WL_OUTPUT_TRANSFORM_NORMAL ||
+ transform > WL_OUTPUT_TRANSFORM_FLIPPED_270)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Invalid transform");
+ return TRUE;
+ }
+
+ first_output = NULL;
+ while (g_variant_iter_loop (nested_outputs, "u", &output_id))
+ {
+ MetaOutput *output;
+
+ if (output_id < 0 || 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;
+ }
+
+ 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_variant_iter_init (&output_iter, outputs);
+ while (g_variant_iter_loop (&output_iter, "(ua{sv})", &output_id, NULL))
+ {
+ if (output_id < 0 || 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;
+ }
+ }
+
+ g_variant_iter_init (&crtc_iter, crtcs);
+ g_variant_iter_init (&output_iter, outputs);
+ apply_config_xrandr (manager, &crtc_iter, &output_iter);
+
+ meta_dbus_display_config_complete_apply_configuration (skeleton, invocation);
+ return TRUE;
+}
static void
on_bus_acquired (GDBusConnection *connection,
@@ -855,6 +1230,8 @@ on_bus_acquired (GDBusConnection *connection,
g_signal_connect_object (manager->skeleton, "handle-get-resources",
G_CALLBACK (handle_get_resources), manager, 0);
+ g_signal_connect_object (manager->skeleton, "handle-apply-configuration",
+ G_CALLBACK (handle_apply_configuration), manager, 0);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager->skeleton),
connection,
diff --git a/src/xrandr.xml b/src/xrandr.xml
index 4f293938a..10a7ee3be 100644
--- a/src/xrandr.xml
+++ b/src/xrandr.xml
@@ -18,6 +18,8 @@
@crtcs: available CRTCs
@outputs: available outputs
@modes: available modes
+ @max_screen_width:
+ @max_screen_height:
Retrieves the current layout of the hardware.
@@ -130,6 +132,8 @@
+
+