[mutter/wip/wayland-display: 1/5] Introduce a new DBus interface for display configuration



commit d38eeee9c74160d3ea9f05b7bf7ac6c464e7420b
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Fri Jul 19 14:39:28 2013 +0200

    Introduce a new DBus interface for display configuration
    
    This new interface will be used by the control center and possibly
    the settings daemon to configure the screens. It is designed to
    resemble a simplified XRandR, while still exposing all the quirks
    of the hardware, so that the panel can limit the user choices
    appropriately.
    
    To do so, MetaMonitorMode needs to track CRTCs, outputs and modes,
    so the low level objects have been decoupled from the high-level
    MetaMonitorInfo, which is used by core and API and offers a simplified
    view of HW, that hides away the details of what is cloned and how.
    This is still not efficient as it should be, because on every
    HW change we drop all data structures and rebuild them from scratch
    (which is not expensive because there aren't many of them, but
    at least in the XRandR path it involves a few sync X calls)

 src/Makefile.am                  |   10 +
 src/core/monitor-private.h       |   76 ++++-
 src/core/monitor.c               |  719 ++++++++++++++++++++++++++++++--------
 src/core/util.c                  |    2 +
 src/meta/util.h                  |    1 +
 src/wayland/meta-wayland-stage.c |    2 +-
 src/wayland/meta-wayland.c       |   39 ++-
 src/xrandr.xml                   |  126 +++++++
 8 files changed, 820 insertions(+), 155 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 2302c0d..087f3b1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -36,6 +36,7 @@ INCLUDES += \
 endif
 
 mutter_built_sources = \
+       $(dbus_xrandr_built_sources)    \
        mutter-enum-types.h \
        mutter-enum-types.c
 
@@ -378,6 +379,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                                      \
+               xrandr.xml
+
 if HAVE_WAYLAND
 wayland/%-protocol.c : @WAYLAND_EXTENSION_PROTOCOLS_DIR@/%.xml
        $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
diff --git a/src/core/monitor-private.h b/src/core/monitor-private.h
index 0150dc2..30526f6 100644
--- a/src/core/monitor-private.h
+++ b/src/core/monitor-private.h
@@ -37,36 +37,100 @@
 #ifndef META_MONITOR_PRIVATE_H
 #define META_MONITOR_PRIVATE_H
 
+#include <cogl/cogl.h>
+
 #include "display-private.h"
 #include <meta/screen.h>
 #include "stack-tracker.h"
 #include "ui.h"
 
-#include <cogl/cogl.h>
-
 typedef struct _MetaOutput MetaOutput;
+typedef struct _MetaCRTC MetaCRTC;
+typedef struct _MetaMonitorMode MetaMonitorMode;
 typedef struct _MetaMonitorInfo MetaMonitorInfo;
 
 struct _MetaOutput
 {
-  MetaMonitorInfo *monitor;
+  /* 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;
+
+  /* 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;
+
+  /* Only used to build the logical configuration
+     from the HW one
+  */
+  MetaMonitorInfo *logical_monitor;
+};
+
+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;
-  float refresh_rate;
 
-  /* The primary or first output for this crtc, 0 if we can't figure out.
-     This is a XID when using XRandR, otherwise a KMS id (not implemented) */
+  /* The primary or first output for this monitor, 0 if we can't figure out.
+     This is a XID when using XRandR, otherwise a KMS id (not implemented).
+     In any case, 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;
 };
 
diff --git a/src/core/monitor.c b/src/core/monitor.c
index 850ae30..6eae86a 100644
--- a/src/core/monitor.c
+++ b/src/core/monitor.c
@@ -27,36 +27,57 @@
 
 #include "config.h"
 
+#include <string.h>
 #include <clutter/clutter.h>
 
 #ifdef HAVE_RANDR
 #include <X11/extensions/Xrandr.h>
 #endif
 
+#include <meta/main.h>
 #include "monitor-private.h"
 #ifdef HAVE_WAYLAND
 #include "meta-wayland-private.h"
 #endif
 
+#include "meta-dbus-xrandr.h"
+
 struct _MetaMonitorManager
 {
   GObject parent_instance;
 
-  /* Outputs refers to physical screens,
-     while monitor_infos refer to logical ones (aka CRTC)
-     They can be different if two outputs are
-     in clone mode
+  /* XXX: this structure is very badly
+     packed, but I like the logical organization
+     of fields */
+
+  unsigned int serial;
+
+  /* Outputs refer to physical screens,
+     CRTCs refer to stuff that can drive outputs
+     (like encoders, but less tied to the HW),
+     while monitor_infos refer to logical ones.
+
+     See also the comment in monitor-private.h
   */
   MetaOutput *outputs;
-  int primary_monitor_index;
-  int n_outputs;
+  unsigned int n_outputs;
+
+  MetaMonitorMode *modes;
+  unsigned int n_modes;
+
+  MetaCRTC *crtcs;
+  unsigned int n_crtcs;
 
   MetaMonitorInfo *monitor_infos;
-  int n_monitor_infos;
+  unsigned int n_monitor_infos;
+  int primary_monitor_index;
 
 #ifdef HAVE_RANDR
   Display *xdisplay;
 #endif
+
+  int dbus_name_id;
+  MetaDBusDisplayConfig *skeleton;
 };
 
 struct _MetaMonitorManagerClass
@@ -76,210 +97,334 @@ G_DEFINE_TYPE (MetaMonitorManager, meta_monitor_manager, G_TYPE_OBJECT);
 static void
 make_dummy_monitor_config (MetaMonitorManager *manager)
 {
-  manager->monitor_infos = g_new0 (MetaMonitorInfo, 1);
-  manager->n_monitor_infos = 1;
+  manager->modes = g_new0 (MetaMonitorMode, 1);
+  manager->n_modes = 1;
 
-  manager->monitor_infos[0].number = 0;
-  manager->monitor_infos[0].xinerama_index = 0;
-  manager->monitor_infos[0].rect.x = 0;
-  manager->monitor_infos[0].rect.y = 0;
+  manager->modes[0].mode_id = 1;
   if (manager->xdisplay)
     {
       Screen *screen = ScreenOfDisplay (manager->xdisplay,
                                         DefaultScreen (manager->xdisplay));
 
-      manager->monitor_infos[0].rect.width = WidthOfScreen (screen);
-      manager->monitor_infos[0].rect.height = HeightOfScreen (screen);
+      manager->modes[0].width = WidthOfScreen (screen);
+      manager->modes[0].height = HeightOfScreen (screen);
     }
   else
     {
-      manager->monitor_infos[0].rect.width = 1024;
-      manager->monitor_infos[0].rect.height = 768;
+      manager->modes[0].width = 1024;
+      manager->modes[0].height = 768;
     }
-  manager->monitor_infos[0].refresh_rate = 60.0f;
-  manager->monitor_infos[0].is_primary = TRUE;
-  manager->monitor_infos[0].in_fullscreen = -1;
-  manager->monitor_infos[0].output_id = 1;
+  manager->modes[0].refresh_rate = 60.0;
+
+  manager->crtcs = g_new0 (MetaCRTC, 1);
+  manager->n_crtcs = 1;
+
+  manager->crtcs[0].crtc_id = 2;
+  manager->crtcs[0].rect.x = 0;
+  manager->crtcs[0].rect.y = 0;
+  manager->crtcs[0].rect.width = manager->modes[0].width;
+  manager->crtcs[0].rect.height = manager->modes[0].height;
+  manager->crtcs[0].current_mode = &manager->modes[0];
 
   manager->outputs = g_new0 (MetaOutput, 1);
   manager->n_outputs = 1;
 
-  manager->outputs[0].monitor = &manager->monitor_infos[0];
+  manager->outputs[0].crtc = &manager->crtcs[0];
+  manager->outputs[0].output_id = 3;
   manager->outputs[0].name = g_strdup ("LVDS");
+  manager->outputs[0].vendor = g_strdup ("unknown");
+  manager->outputs[0].product = g_strdup ("unknown");
+  manager->outputs[0].serial = g_strdup ("");
   manager->outputs[0].width_mm = 222;
   manager->outputs[0].height_mm = 125;
   manager->outputs[0].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+  manager->outputs[0].preferred_mode = &manager->modes[0];
+  manager->outputs[0].n_modes = 1;
+  manager->outputs[0].modes = g_new0 (MetaMonitorMode *, 1);
+  manager->outputs[0].modes[0] = &manager->modes[0];
+  manager->outputs[0].n_possible_crtcs = 1;
+  manager->outputs[0].possible_crtcs = g_new0 (MetaCRTC *, 1);
+  manager->outputs[0].possible_crtcs[0] = &manager->crtcs[0];
 }
 
 #ifdef HAVE_RANDR
 
-/* In the case of multiple outputs of a single crtc (mirroring), we consider one of the
- * outputs the "main". This is the one we consider "owning" the windows, so if
- * the mirroring is changed to a dual monitor setup then the windows are moved to the
- * crtc that now has that main output. If one of the outputs is the primary that is
- * always the main, otherwise we just use the first.
- */
-static void
-find_main_output_for_crtc (MetaMonitorManager         *manager,
-                           XRRScreenResources *resources,
-                           XRRCrtcInfo        *crtc,
-                           MetaMonitorInfo    *info,
-                           GArray             *outputs)
-{
-  XRROutputInfo *output;
-  RROutput primary_output;
-  int i;
-
-  primary_output = XRRGetOutputPrimary (manager->xdisplay,
-                                        DefaultRootWindow (manager->xdisplay));
-
-  for (i = 0; i < crtc->noutput; i++)
-    {
-      output = XRRGetOutputInfo (manager->xdisplay, resources, crtc->outputs[i]);
-
-      if (output->connection != RR_Disconnected)
-        {
-          MetaOutput meta_output;
-
-          meta_output.name = g_strdup (output->name);
-          meta_output.width_mm = output->mm_width;
-          meta_output.height_mm = output->mm_height;
-          meta_output.subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
-
-          g_array_append_val (outputs, meta_output);
-
-          if (crtc->outputs[i] == primary_output)
-            {
-              info->output_id = crtc->outputs[i];
-              info->is_primary = TRUE;
-              manager->primary_monitor_index = info->number;
-            }
-          else if (info->output_id == 0)
-            {
-              info->output_id = crtc->outputs[i];
-            }
-        }
-
-      XRRFreeOutputInfo (output);
-    }
-}
-
 static void
 read_monitor_infos_from_xrandr (MetaMonitorManager *manager)
 {
     XRRScreenResources *resources;
-    GArray *outputs;
-    int i, j;
+    RROutput primary_output;
+    unsigned int i, j, k;
+    unsigned int n_actual_outputs;
 
     resources = XRRGetScreenResourcesCurrent (manager->xdisplay,
                                               DefaultRootWindow (manager->xdisplay));
     if (!resources)
       return make_dummy_monitor_config (manager);
 
-    outputs = g_array_new (FALSE, TRUE, sizeof (MetaOutput));
+    manager->n_outputs = 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);
 
-    manager->n_outputs = 0;
-    manager->n_monitor_infos = resources->ncrtc;
+    for (i = 0; i < (unsigned)resources->nmode; i++)
+      {
+        XRRModeInfo *xmode = &resources->modes[i];
+        MetaMonitorMode *mode;
 
-    manager->monitor_infos = g_new0 (MetaMonitorInfo, manager->n_monitor_infos);
+        mode = &manager->modes[i];
 
-    for (i = 0; i < resources->ncrtc; 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;
-        MetaMonitorInfo *info;
+        MetaCRTC *meta_crtc;
 
         crtc = XRRGetCrtcInfo (manager->xdisplay, resources, resources->crtcs[i]);
 
-        info = &manager->monitor_infos[i];
+        meta_crtc = &manager->crtcs[i];
 
-        info->number = i;
-        info->rect.x = crtc->x;
-        info->rect.y = crtc->y;
-        info->rect.width = crtc->width;
-        info->rect.height = crtc->height;
-        info->in_fullscreen = -1;
+        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;
 
-        for (j = 0; j < resources->nmode; j++)
+        for (j = 0; j < (unsigned)resources->nmode; j++)
           {
             if (resources->modes[j].id == crtc->mode)
-              info->refresh_rate = (resources->modes[j].dotClock /
-                                    ((float)resources->modes[j].hTotal *
-                                     resources->modes[j].vTotal));
+              {
+                meta_crtc->current_mode = &manager->modes[j];
+                break;
+              }
           }
 
-        find_main_output_for_crtc (manager, resources, crtc, info, outputs);
-
         XRRFreeCrtcInfo (crtc);
       }
 
-    manager->n_outputs = outputs->len;
-    manager->outputs = (void*)g_array_free (outputs, FALSE);
+    primary_output = XRRGetOutputPrimary (manager->xdisplay,
+                                          DefaultRootWindow (manager->xdisplay));
+
+    n_actual_outputs = 0;
+    for (i = 0; i < (unsigned)resources->noutput; i++)
+      {
+        XRROutputInfo *output;
+        MetaOutput *meta_output;
+
+        output = XRRGetOutputInfo (manager->xdisplay, resources, resources->outputs[i]);
+
+        meta_output = &manager->outputs[n_actual_outputs];
+
+        if (output->connection != RR_Disconnected)
+          {
+            meta_output->output_id = resources->outputs[i];
+            meta_output->name = g_strdup (output->name);
+            meta_output->vendor = g_strdup ("unknown");
+            meta_output->product = g_strdup ("unknown");
+            meta_output->serial = g_strdup ("");
+            meta_output->width_mm = output->mm_width;
+            meta_output->height_mm = output->mm_height;
+            meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+
+            meta_output->n_modes = output->nmode;
+            meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
+            for (j = 0; j < meta_output->n_modes; j++)
+              {
+                for (k = 0; k < manager->n_modes; k++)
+                  {
+                    if (output->modes[j] == (XID)manager->modes[k].mode_id)
+                      {
+                        meta_output->modes[j] = &manager->modes[k];
+                        break;
+                      }
+                  }
+              }
+            for (k = 0; k < manager->n_modes; k++)
+              {
+                if (output->npreferred == manager->modes[k].mode_id)
+                  {
+                    meta_output->preferred_mode = &manager->modes[k];
+                    break;
+                  }
+              }
+
+            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->is_primary = ((XID)meta_output->output_id == primary_output);
+            meta_output->is_presentation = FALSE;
+
+            n_actual_outputs++;
+          }
+
+        XRRFreeOutputInfo (output);
+      }
+
+    manager->n_outputs = n_actual_outputs;
 
     XRRFreeScreenResources (resources);
 }
 
 #endif
 
-typedef struct {
-  GArray *monitor_infos;
-  GArray *outputs;
-} ReadMonitorCoglClosure;
+static MetaMonitorMode *
+find_or_create_monitor_mode (GArray          *modes,
+                             int              width,
+                             int              height,
+                             float            refresh)
+{
+  unsigned int i;
+  MetaMonitorMode new;
+
+  for (i = 0; i < modes->len; i++)
+    {
+      MetaMonitorMode *existing;
 
+      existing = &g_array_index (modes, MetaMonitorMode, i);
+      if (existing->width == width &&
+          existing->height == height &&
+          existing->refresh_rate == refresh)
+        return existing;
+    }
+
+  new.mode_id = 1 + modes->len;
+  new.width = width;
+  new.height = height;
+  new.refresh_rate = refresh;
+
+  g_array_append_val (modes, new);
+  return &g_array_index (modes, MetaMonitorMode, modes->len - 1);
+}
+    
 static void
 read_monitor_from_cogl_helper (CoglOutput *output,
                                void       *user_data)
 {
-  ReadMonitorCoglClosure *closure = user_data;
-  MetaMonitorInfo info;
-  MetaOutput meta_output;
-
-  info.number = closure->monitor_infos->len;
-  info.rect.x = cogl_output_get_x (output);
-  info.rect.y = cogl_output_get_y (output);
-  info.rect.width = cogl_output_get_width (output);
-  info.rect.height = cogl_output_get_height (output);
-  info.refresh_rate = cogl_output_get_refresh_rate (output);
-  info.is_primary = (info.number == 0);
-  info.in_fullscreen = -1;
-  info.output_id = 0; /* unknown */
-
-  g_array_append_val (closure->monitor_infos, info);
-
-  meta_output.monitor = &g_array_index (closure->monitor_infos,
-                                        MetaMonitorInfo,
-                                        closure->monitor_infos->len - 1);
-  meta_output.name = NULL;
-  meta_output.width_mm = cogl_output_get_mm_width (output);
-  meta_output.height_mm = cogl_output_get_mm_height (output);
-  meta_output.subpixel_order = cogl_output_get_subpixel_order (output);
-
-  g_array_append_val (closure->outputs, meta_output);
+  GList **closure = user_data;
+
+  *closure = g_list_prepend (*closure, cogl_object_ref (output));
 }
 
 static void
 read_monitor_infos_from_cogl (MetaMonitorManager *manager)
 {
-  ReadMonitorCoglClosure closure;
+  GList *iter, *output_list;
+  GArray *modes;
+  GArray *crtcs;
+  GArray *outputs;
   ClutterBackend *backend;
   CoglContext *cogl_context;
   CoglRenderer *cogl_renderer;
+  int n;
 
   backend = clutter_get_default_backend ();
   cogl_context = clutter_backend_get_cogl_context (backend);
   cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
 
-  closure.monitor_infos = g_array_new (FALSE, TRUE, sizeof (MetaMonitorInfo));
-  closure.outputs = g_array_new (FALSE, TRUE, sizeof (MetaOutput));
-
+  output_list = NULL;
   cogl_renderer_foreach_output (cogl_renderer,
-                                read_monitor_from_cogl_helper, &closure);
+                                read_monitor_from_cogl_helper, &output_list);
+
+  if (output_list == NULL)
+    return make_dummy_monitor_config (manager);
+
+  n = g_list_length (output_list);
+  modes = g_array_new (FALSE, TRUE, sizeof (MetaMonitorMode));
+  crtcs = g_array_sized_new (FALSE, TRUE, sizeof (MetaCRTC), n);
+  outputs = g_array_sized_new (FALSE, TRUE, sizeof (MetaOutput), n);
+  
+  for (iter = output_list; iter; iter = iter->next)
+    {
+      /* First create all modes, so that we can reference them later
+         without the risk to resize the array
+      */
+      find_or_create_monitor_mode (modes,
+                                   cogl_output_get_width (iter->data),
+                                   cogl_output_get_height (iter->data),
+                                   cogl_output_get_refresh_rate (iter->data));
+    }
+
+  for (iter = output_list; iter; iter = iter->next)
+    {
+      CoglOutput *output;
+      MetaCRTC meta_crtc;
+      MetaOutput meta_output;
+
+      output = iter->data;
+
+      meta_crtc.crtc_id = 1 + crtcs->len;
+      meta_crtc.rect.x = cogl_output_get_x (output);
+      meta_crtc.rect.y = cogl_output_get_y (output);
+      meta_crtc.rect.width = cogl_output_get_width (output);
+      meta_crtc.rect.height = cogl_output_get_height (output);
+      meta_crtc.current_mode = find_or_create_monitor_mode (modes,
+                                                            cogl_output_get_width (output),
+                                                            cogl_output_get_height (output),
+                                                            cogl_output_get_refresh_rate (output));
+      meta_crtc.logical_monitor = NULL;
+
+      /* This will never resize the array because we preallocated with g_array_sized_new() */
+      g_array_append_val (crtcs, meta_crtc);
+
+      meta_output.output_id = 1 + n + crtcs->len;
+      meta_output.crtc = &g_array_index (crtcs, MetaCRTC, crtcs->len - 1);
+      meta_output.name = g_strdup ("unknown");
+      meta_output.vendor = g_strdup ("unknown");
+      meta_output.product = g_strdup ("unknown");
+      meta_output.serial = g_strdup ("");
+      meta_output.width_mm = cogl_output_get_mm_width (output);
+      meta_output.height_mm = cogl_output_get_mm_height (output);
+      meta_output.subpixel_order = cogl_output_get_subpixel_order (output);
+      meta_output.preferred_mode = meta_crtc.current_mode;
+      meta_output.n_modes = 1;
+      meta_output.modes = g_new (MetaMonitorMode *, 1);
+      meta_output.modes[0] = meta_output.preferred_mode;
+      meta_output.n_possible_crtcs = 1;
+      meta_output.possible_crtcs = g_new (MetaCRTC *, 1);
+      meta_output.possible_crtcs[0] = meta_output.crtc;
+      meta_output.is_primary = (iter == output_list);
+      meta_output.is_presentation = FALSE;
+
+      g_array_append_val (outputs, meta_output);
+    }
 
-  manager->n_monitor_infos = closure.monitor_infos->len;
-  manager->monitor_infos = (void*)g_array_free (closure.monitor_infos, FALSE);
-  manager->n_outputs = closure.outputs->len;
-  manager->outputs = (void*)g_array_free (closure.outputs, FALSE);
+  manager->n_modes = modes->len;
+  manager->modes = (void*)g_array_free (modes, FALSE);
+  manager->n_crtcs = crtcs->len;
+  manager->crtcs = (void*)g_array_free (crtcs, FALSE);
+  manager->n_outputs = outputs->len;
+  manager->outputs = (void*)g_array_free (outputs, FALSE);
 
-  manager->primary_monitor_index = 0;
+  g_list_free_full (output_list, cogl_object_unref);
 }
 
 /*
@@ -291,6 +436,7 @@ read_monitor_infos_from_cogl (MetaMonitorManager *manager)
 static gboolean
 has_dummy_output (void)
 {
+#ifdef HAVE_WAYLAND
   MetaWaylandCompositor *compositor;
 
   if (!meta_is_display_server ())
@@ -300,6 +446,9 @@ has_dummy_output (void)
      expose the outputs through CoglOutput - yet */
   compositor = meta_wayland_compositor_get_default ();
   return !meta_wayland_compositor_is_native (compositor);
+#else
+  return FALSE
+#endif
 }
 
 static void
@@ -307,9 +456,35 @@ meta_monitor_manager_init (MetaMonitorManager *manager)
 {
 }
 
+static gboolean
+make_debug_config (MetaMonitorManager *manager)
+{
+  const char *env;
+
+  env = g_getenv ("META_DEBUG_MULTIMONITOR");
+
+  if (env == NULL)
+    return FALSE;
+
+#ifdef HAVE_RANDR
+  if (strcmp (env, "xrandr") == 0)
+    read_monitor_infos_from_xrandr (manager);
+  else
+#endif
+    if (strcmp (env, "cogl") == 0)
+      read_monitor_infos_from_cogl (manager);
+    else
+      make_dummy_monitor_config (manager);
+
+  return TRUE;
+}
+
 static void
 read_current_config (MetaMonitorManager *manager)
 {
+  if (make_debug_config (manager))
+    return;
+
   if (has_dummy_output ())
     return make_dummy_monitor_config (manager);
 
@@ -321,6 +496,101 @@ read_current_config (MetaMonitorManager *manager)
   return read_monitor_infos_from_cogl (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 (Display *display)
 {
@@ -331,6 +601,7 @@ meta_monitor_manager_new (Display *display)
   manager->xdisplay = display;
 
   read_current_config (manager);
+  make_logical_config (manager);
   return manager;
 }
 
@@ -341,7 +612,15 @@ free_output_array (MetaOutput *old_outputs,
   int i;
 
   for (i = 0; i < n_old_outputs; i++)
-    g_free (old_outputs[i].name);
+    {
+      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);
 }
 
@@ -352,15 +631,34 @@ meta_monitor_manager_finalize (GObject *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_clear_object (&manager->skeleton);
+
+  G_OBJECT_CLASS (meta_monitor_manager_parent_class)->dispose (object);
+}
+
+static void
 meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+  object_class->dispose = meta_monitor_manager_dispose;
   object_class->finalize = meta_monitor_manager_finalize;
 
   signals[MONITORS_CHANGED] =
@@ -372,12 +670,151 @@ meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
                  G_TYPE_NONE, 0);
 }
 
+static gboolean
+handle_get_resources (MetaDBusDisplayConfig *skeleton,
+                      GDBusMethodInvocation *invocation,
+                      MetaMonitorManager    *manager)
+{
+  GVariantBuilder crtc_builder, output_builder, mode_builder;
+  unsigned int i, j;
+
+  g_variant_builder_init (&crtc_builder, G_VARIANT_TYPE ("a(uxiiiiiuaua{sv})"));
+  g_variant_builder_init (&output_builder, G_VARIANT_TYPE ("a(uxiaussssaua{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"));
+      g_variant_builder_add (&transforms, "u", 0); /* 0 = WL_OUTPUT_TRANSFORM_NORMAL */
+
+      g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})",
+                             i, /* ID */
+                             crtc->crtc_id,
+                             (int)crtc->rect.x,
+                             (int)crtc->rect.y,
+                             (int)crtc->rect.width,
+                             (int)crtc->rect.height,
+                             (int)(crtc->current_mode ? crtc->current_mode - manager->modes : -1),
+                             0, /* 0 = WL_OUTPUT_TRANSFORM_NORMAL */
+                             &transforms,
+                             NULL /* properties */);
+    }
+
+  for (i = 0; i < manager->n_outputs; i++)
+    {
+      MetaOutput *output = &manager->outputs[i];
+      GVariantBuilder crtcs, modes, properties;
+
+      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 (&properties, G_VARIANT_TYPE ("a{sv}"));
+      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));
+
+      g_variant_builder_add (&output_builder, "(uxiaussssaua{sv})",
+                             i, /* ID */
+                             output->output_id,
+                             (int)(output->crtc ? output->crtc - manager->crtcs : -1),
+                             &crtcs,
+                             output->name,
+                             output->vendor,
+                             output->product,
+                             output->serial,
+                             &modes,
+                             &properties);
+    }
+      
+  for (i = 0; i < manager->n_modes; i++)
+    {
+      MetaMonitorMode *mode = &manager->modes[i];
+
+      g_variant_builder_add (&mode_builder, "(uxuud)",
+                             i, /* ID */
+                             mode->mode_id,
+                             mode->width,
+                             mode->height,
+                             (double)mode->refresh_rate);
+    }
+
+  meta_dbus_display_config_complete_get_resources (skeleton,
+                                                   invocation,
+                                                   manager->serial,
+                                                   g_variant_builder_end (&crtc_builder),
+                                                   g_variant_builder_end (&output_builder),
+                                                   g_variant_builder_end (&mode_builder));
+  return FALSE;
+}                     
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const char      *name,
+                 gpointer         user_data)
+{
+  MetaMonitorManager *manager = user_data;
+
+  manager->skeleton = META_DBUS_DISPLAY_CONFIG (meta_dbus_display_config_skeleton_new ());
+
+  g_signal_connect_object (manager->skeleton, "handle-get-resources",
+                           G_CALLBACK (handle_get_resources), manager, 0);
+
+  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager->skeleton),
+                                    connection,
+                                    "/org/gnome/Mutter/DisplayConfig",
+                                    NULL);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const char      *name,
+                  gpointer         user_data)
+{
+  meta_topic (META_DEBUG_DBUS, "Acquired name %s\n", name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const char      *name,
+              gpointer         user_data)
+{
+  meta_topic (META_DEBUG_DBUS, "Lost or failed to acquire name %s\n", name);
+}
+
+static void
+initialize_dbus_interface (MetaMonitorManager *manager)
+{
+  manager->dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                          "org.gnome.Mutter.DisplayConfig",
+                                          G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+                                          (meta_get_replace_current_wm () ?
+                                           G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
+                                          on_bus_acquired,
+                                          on_name_acquired,
+                                          on_name_lost,
+                                          g_object_ref (manager),
+                                          g_object_unref);
+}
+
 static MetaMonitorManager *global_manager;
 
 void
 meta_monitor_manager_initialize (Display *display)
 {
   global_manager = meta_monitor_manager_new (display);
+
+  initialize_dbus_interface (global_manager);
 }
 
 MetaMonitorManager *
@@ -414,19 +851,27 @@ void
 meta_monitor_manager_invalidate (MetaMonitorManager *manager)
 {
   MetaOutput *old_outputs;
+  MetaCRTC *old_crtcs;
   MetaMonitorInfo *old_monitor_infos;
+  MetaMonitorMode *old_modes;
   int n_old_outputs;
 
-  /* Save the old monitor infos, so they stay valid during the update */
+  /* Save the old structures, so they stay valid during the update */
   old_outputs = manager->outputs;
   n_old_outputs = manager->n_outputs;
   old_monitor_infos = manager->monitor_infos;
+  old_modes = manager->modes;
+  old_crtcs = manager->crtcs;
 
+  manager->serial ++;
   read_current_config (manager);
+  make_logical_config (manager);
 
   g_signal_emit (manager, signals[MONITORS_CHANGED], 0);
 
   g_free (old_monitor_infos);
   free_output_array (old_outputs, n_old_outputs);
+  g_free (old_modes);
+  g_free (old_crtcs);
 }
 
diff --git a/src/core/util.c b/src/core/util.c
index 2426fb8..80e963e 100644
--- a/src/core/util.c
+++ b/src/core/util.c
@@ -347,6 +347,8 @@ topic_name (MetaDebugTopic topic)
       return "EDGE_RESISTANCE";
     case META_DEBUG_SYNTH_EVENTS:
       return "SYNTH_EVENTS";
+    case META_DEBUG_DBUS:
+      return "DBUS";
     case META_DEBUG_VERBOSE:
       return "VERBOSE";
     }
diff --git a/src/meta/util.h b/src/meta/util.h
index d2eb290..597d97a 100644
--- a/src/meta/util.h
+++ b/src/meta/util.h
@@ -104,6 +104,7 @@ typedef enum
   META_DEBUG_COMPOSITOR      = 1 << 20,
   META_DEBUG_EDGE_RESISTANCE = 1 << 21,
   META_DEBUG_SYNTH_EVENTS    = 1 << 22,
+  META_DEBUG_DBUS            = 1 << 23
 } MetaDebugTopic;
 
 void meta_topic_real      (MetaDebugTopic topic,
diff --git a/src/wayland/meta-wayland-stage.c b/src/wayland/meta-wayland-stage.c
index 8a464ae..cbcfccb 100644
--- a/src/wayland/meta-wayland-stage.c
+++ b/src/wayland/meta-wayland-stage.c
@@ -254,7 +254,7 @@ meta_wayland_stage_apply_monitor_config (MetaWaylandStage *stage)
   manager = meta_monitor_manager_get ();
   infos = meta_monitor_manager_get_monitor_infos (manager, &n_infos);
 
-  g_assert (n_infos == 1);
+  g_assert (n_infos == 1 || g_getenv ("META_DEBUG_MULTIMONITOR"));
 
   /* FIXME: when we support a sliced stage, this is the place to do it
      But! This is not the place to apply KMS config, here we only
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 830209e..fb50a69 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -613,25 +613,31 @@ bind_output (struct wl_client *client,
 
   resource = wl_resource_create (client, &wl_output_interface, version, id);
 
+  meta_verbose ("Binding output %p/%s (%u, %u, %u, %u) x %f\n",
+               output, output->name,
+               output->crtc->rect.x, output->crtc->rect.y,
+               output->crtc->rect.width, output->crtc->rect.height,
+               output->crtc->current_mode->refresh_rate);
+
   wl_resource_post_event (resource,
                           WL_OUTPUT_GEOMETRY,
-                          (int)output->monitor->rect.x,
-                         (int)output->monitor->rect.y,
+                          (int)output->crtc->rect.x,
+                         (int)output->crtc->rect.y,
                          output->width_mm,
                           output->height_mm,
                          /* Cogl values reflect XRandR values,
                             and so does wayland */
                          output->subpixel_order,
-                          "unknown", /* make */
-                          "unknown", /* model */
+                         output->vendor,
+                         output->product,
                          WL_OUTPUT_TRANSFORM_NORMAL);
 
   wl_resource_post_event (resource,
                          WL_OUTPUT_MODE,
                          WL_OUTPUT_MODE_PREFERRED | WL_OUTPUT_MODE_CURRENT,
-                         (int)output->monitor->rect.width,
-                         (int)output->monitor->rect.height,
-                         (int)output->monitor->refresh_rate);
+                         (int)output->crtc->rect.width,
+                         (int)output->crtc->rect.height,
+                         (int)output->crtc->current_mode->refresh_rate);
 
   if (version >= 2)
     wl_resource_post_event (resource,
@@ -648,10 +654,20 @@ meta_wayland_compositor_create_outputs (MetaWaylandCompositor *compositor,
   outputs = meta_monitor_manager_get_outputs (monitors, &n_outputs);
 
   for (i = 0; i < n_outputs; i++)
-    compositor->outputs = g_list_prepend (compositor->outputs,
-                                         wl_global_create (compositor->wayland_display,
-                                                           &wl_output_interface, 2,
-                                                           &outputs[i], bind_output));
+    {
+      MetaOutput *output = &outputs[i];
+      struct wl_global *global;
+
+      /* wayland does not expose disabled outputs */
+      if (output->crtc == NULL)
+       continue;
+
+      global = wl_global_create (compositor->wayland_display,
+                                &wl_output_interface, 2,
+                                output, bind_output);
+      compositor->outputs = g_list_prepend (compositor->outputs, global);
+    }
+
 }
 
 const static struct wl_compositor_interface meta_wayland_compositor_interface = {
@@ -1917,6 +1933,7 @@ on_monitors_changed (MetaMonitorManager    *monitors,
                     MetaWaylandCompositor *compositor)
 {
   g_list_free_full (compositor->outputs, (GDestroyNotify) wl_global_destroy);
+  compositor->outputs = NULL;
   meta_wayland_compositor_create_outputs (compositor, monitors);
   meta_wayland_stage_apply_monitor_config (META_WAYLAND_STAGE (compositor->stage));
 }
diff --git a/src/xrandr.xml b/src/xrandr.xml
new file mode 100644
index 0000000..1942727
--- /dev/null
+++ b/src/xrandr.xml
@@ -0,0 +1,126 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+  <!--
+      org.gnome.Mutter.DisplayConfig:
+      @short_description: display configuration interface
+
+      This interface is used by mutter and gnome-settings-daemon
+      to apply multiple monitor configuration.
+  -->
+
+  <interface name="org.gnome.Mutter.DisplayConfig">
+
+    <!--
+        GetResources:
+       @serial: configuration serial
+       @crtcs: available CRTCs
+       @outputs: available outputs
+       @modes: available modes
+
+        Retrieves the current layout of the hardware.
+
+        @serial is an unique identifier representing the current state
+        of the screen. It must be passed back to ApplyConfiguration()
+       and will be increased for every configuration change (so that
+       mutter can detect that the new configuration is based on old
+       state).
+
+       A CRTC (CRT controller) is a logical monitor, ie a portion
+       of the compositor coordinate space. It might correspond
+       to multiple monitors, when in clone mode, but not that
+       it is possible to implement clone mode also by setting different
+       CRTCs to the same coordinates.
+
+       The number of CRTCs represent the maximum number of monitors
+       that can be set to expand and it is a HW constraint; if more
+       monitors are connected, then necessarily some will clone. This
+       is complementary to the concept of the encoder (not exposed in
+       the API), which groups outputs that necessarily will show the
+       same image (again a HW constraint).
+
+       A CRTC is represented by a DBus structure with the following
+       layout:
+       * u ID: the ID in the API of this CRTC
+       * x winsys_id: the low-level ID of this CRTC (which might
+                      be a XID, a KMS handle or something entirely
+                      different)
+       * i x, y, width, height: the geometry of this CRTC
+                                (might be invalid if the CRTC is not in
+                                use)
+       * i current_mode: the current mode of the CRTC, or -1 if this
+                         CRTC is not used
+                         Note: the size of the mode will always correspond
+                         to the width and height of the CRTC
+       * u current_transform: the current transform (espressed according
+                              to the wayland protocol)
+       * au transforms: all possible transforms
+       * a{sv} properties: other high-level properties that affect this
+                           CRTC; they are not necessarily reflected in
+                           the hardware.
+                           No property is specified in this version of the API.
+        FIXME: gamma?
+
+        Note: all geometry information refers to the untransformed
+       display.
+
+       An output represents a physical screen, connected somewhere to
+       the computer. Floating connectors are not exposed in the API.
+       An output is a DBus struct with the following fields:
+       * u ID: the ID in the API
+       * x winsys_id: the low-level ID of this output (XID or KMS handle)
+       * i current_crtc: the CRTC that is currently driving this output,
+                         or -1 if the output is disabled
+       * au possible_crtcs: all CRTCs that can control this output
+       * s name: the name of the connector to which the output is attached
+                 (like VGA1 or HDMI)
+       * s vendor: the human readable name of the manufacturer
+       * s product: the human readable name of the display model
+       * s serial: the serial number of this particular hardward part
+       * au modes: valid modes for this output
+       * a{sv} properties: other high-level properties that affect this
+                           output; they are not necessarily reflected in
+                           the hardware.
+                           Known properties:
+                           - "primary" (b): whether this output is primary
+                                            or not
+                           - "presentation" (b): whether this output is
+                                                 for presentation only
+                           Note: properties might be ignored if not consistenly
+                           applied to all outputs in the same clone group. In
+                           general, it's expected that presentation or primary
+                           outputs will not be cloned.
+
+        A mode represents a set of parameters that are applied to
+       each output, such as resolution and refresh rate. It is a separate
+       object so that it can be referenced by CRTCs and outputs.
+       Multiple outputs in the same CRTCs must all have the same mode.
+       A mode is exposed as:
+       * u ID: the ID in the API
+       * x winsys_id: the low-level ID of this mode
+       * u width, height: the resolution
+       * d frequency: refresh rate
+
+        Output and modes are read-only objects (except for output properties),
+       they can change only in accordance to HW changes (such as hotplugging
+       a monitor), while CRTCs can be changed with ApplyConfiguration().
+
+        XXX: actually, if you insist enough, you can add new modes
+       through xrandr command line or the KMS API, overriding what the
+       kernel driver and the EDID say.
+       Usually, it only matters with old cards with broken drivers, or
+       old monitors with broken EDIDs, but it happens more often with
+       projectors (if for example the kernel driver doesn't add the
+       640x480 - 800x600 - 1024x768 default modes). Probably something
+       that we need to handle in mutter anyway.
+    -->
+
+    <method name="GetResources">
+      <arg name="serial" direction="out" type="u" />
+      <arg name="crtcs" direction="out" type="a(uxiiiiiuaua{sv})" />
+      <arg name="outputs" direction="out" type="a(uxiaussssaua{sv})" />
+      <arg name="modes" direction="out" type="a(uxuud)" />
+    </method>
+  </interface>
+</node>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]