[cogl] Add CoglOutput and track for the GLX backend



commit d7ef9d8d71488d0e6874f1ffc6e48700d5c82a31
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Mon Nov 12 11:31:16 2012 -0500

    Add CoglOutput and track for the GLX backend
    
    The CoglOutput object represents one output such as a monitor or
    laptop panel, with information about attributes of the output such as
    the position of the output within the global coordinate space, and
    the refresh rate.
    
    We don't yet publically export the ability to get output information but
    we track it for the GLX backend, where we'll use it to track the refresh
    rate.
    
    Reviewed-by: Robert Bragg <robert linux intel com>

 cogl/Makefile.am                  |    3 +
 cogl/cogl-output-private.h        |   50 ++++++
 cogl/cogl-output.c                |  110 ++++++++++++
 cogl/cogl-output.h                |  242 +++++++++++++++++++++++++++
 cogl/cogl-renderer-private.h      |    2 +
 cogl/cogl-xlib-renderer-private.h |   10 +
 cogl/cogl-xlib-renderer.c         |  334 +++++++++++++++++++++++++++++++++++++
 cogl/winsys/cogl-winsys-glx.c     |  110 +++++++++++-
 cogl/winsys/cogl-winsys-private.h |    3 +
 configure.ac                      |    3 +-
 10 files changed, 858 insertions(+), 9 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 1389aed..f1d1025 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -91,6 +91,7 @@ cogl_public_h = \
 	$(srcdir)/cogl-poll.h			\
 	$(srcdir)/cogl-primitive-texture.h	\
 	$(srcdir)/cogl-primitive.h 		\
+	$(srcdir)/cogl-output.h			\
 	$(srcdir)/cogl-quaternion.h 		\
 	$(srcdir)/cogl-matrix-stack.h		\
 	$(srcdir)/cogl-renderer.h 		\
@@ -356,6 +357,8 @@ cogl_sources_c = \
 	$(srcdir)/cogl-framebuffer.c 			\
 	$(srcdir)/cogl-onscreen-private.h		\
 	$(srcdir)/cogl-onscreen.c 			\
+	$(srcdir)/cogl-output-private.h			\
+	$(srcdir)/cogl-output.c				\
 	$(srcdir)/cogl-profile.h 			\
 	$(srcdir)/cogl-profile.c 			\
 	$(srcdir)/cogl-flags.h				\
diff --git a/cogl/cogl-output-private.h b/cogl/cogl-output-private.h
new file mode 100644
index 0000000..a4910e9
--- /dev/null
+++ b/cogl/cogl-output-private.h
@@ -0,0 +1,50 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef __COGL_OUTPUT_PRIVATE_H
+#define __COGL_OUTPUT_PRIVATE_H
+
+#include "cogl-output.h"
+#include "cogl-object-private.h"
+
+struct _CoglOutput
+{
+  CoglObject _parent;
+
+  char *name;
+
+  int x; /* Must be first field for _cogl_output_values_equal() */
+  int y;
+  int width;
+  int height;
+  int mm_width;
+  int mm_height;
+  float refresh_rate;
+  CoglSubpixelOrder subpixel_order;
+};
+
+CoglOutput *_cogl_output_new (const char *name);
+CoglBool _cogl_output_values_equal (CoglOutput *output,
+                                    CoglOutput *other);
+
+#endif /* __COGL_OUTPUT_PRIVATE_H */
diff --git a/cogl/cogl-output.c b/cogl/cogl-output.c
new file mode 100644
index 0000000..1f5e78f
--- /dev/null
+++ b/cogl/cogl-output.c
@@ -0,0 +1,110 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-output-private.h"
+
+#include <string.h>
+
+static void _cogl_output_free (CoglOutput *output);
+
+COGL_OBJECT_DEFINE (Output, output);
+
+CoglOutput *
+_cogl_output_new (const char *name)
+{
+  CoglOutput *output;
+
+  output = g_slice_new0 (CoglOutput);
+  output->name = g_strdup (name);
+
+  return _cogl_output_object_new (output);
+}
+
+static void
+_cogl_output_free (CoglOutput *output)
+{
+  g_free (output->name);
+
+  g_slice_free (CoglOutput, output);
+}
+
+gboolean
+_cogl_output_values_equal (CoglOutput *output,
+                           CoglOutput *other)
+{
+  return memcmp ((const char *)output + G_STRUCT_OFFSET (CoglOutput, x),
+                 (const char *)other + G_STRUCT_OFFSET (CoglOutput, x),
+                 sizeof (CoglOutput) - G_STRUCT_OFFSET (CoglOutput, x)) == 0;
+}
+
+int
+cogl_output_get_x (CoglOutput *output)
+{
+  return output->x;
+}
+
+int
+cogl_output_get_y (CoglOutput *output)
+{
+  return output->y;
+}
+
+int
+cogl_output_get_width (CoglOutput *output)
+{
+  return output->width;
+}
+
+int
+cogl_output_get_height (CoglOutput *output)
+{
+  return output->height;
+}
+
+int
+cogl_output_get_mm_width (CoglOutput *output)
+{
+  return output->mm_width;
+}
+
+int
+cogl_output_get_mm_height (CoglOutput *output)
+{
+  return output->mm_height;
+}
+
+CoglSubpixelOrder
+cogl_output_get_subpixel_order (CoglOutput *output)
+{
+  return output->subpixel_order;
+}
+
+float
+cogl_output_get_refresh_rate (CoglOutput *output)
+{
+  return output->refresh_rate;
+}
diff --git a/cogl/cogl-output.h b/cogl/cogl-output.h
new file mode 100644
index 0000000..473a5cc
--- /dev/null
+++ b/cogl/cogl-output.h
@@ -0,0 +1,242 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Owen Taylor <otaylor redhat com>
+ */
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_OUTPUT_H
+#define __COGL_OUTPUT_H
+
+#include <cogl/cogl-types.h>
+
+COGL_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-output
+ * @short_description: information about an output device
+ *
+ * The #CoglOutput object holds information about an output device
+ * such as a monitor or laptop display. It can be queried to find
+ * out the position of the output with respect to the screen
+ * coordinate system and other information such as the resolution
+ * and refresh rate of the device.
+ *
+ * There can be any number of outputs which may overlap: the
+ * same area of the screen may be displayed by multiple output
+ * devices.
+ *
+ * XXX: though it's possible to query the position of the output
+ * with respect to screen coordinates, there is currently no way
+ * of finding out the position of a #CoglOnscreen in screen
+ * coordinates, at least without using windowing-system specific
+ * API's, so it's not easy to get the output positions relative
+ * to the #CoglOnscreen.
+ */
+
+typedef struct _CoglOutput CoglOutput;
+#define COGL_OUTPUT(X) ((CoglOutput *)(X))
+
+/**
+ * CoglSubpixelOrder
+ * @COGL_SUBPIXEL_ORDER_UNKNOWN: the layout of subpixel
+ *   components for the device is unknown.
+ * @COGL_SUBPIXEL_ORDER_NONE: the device displays colors
+ *   without geometrically-separated subpixel components,
+ *   or the positioning or colors of the components do not
+ *   match any of the values in the enumeration.
+ * @COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: the device has
+ *   horizontally arranged components in the order
+ *   red-green-blue from left to right.
+ * @COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: the device has
+ *   horizontally arranged  components in the order
+ *   blue-green-red from left to right.
+ * @COGL_SUBPIXEL_ORDER_VERTICAL_RGB: the device has
+ *   vertically arranged components in the order
+ *   red-green-blue from top to bottom.
+ * @COGL_SUBPIXEL_ORDER_VERTICAL_BGR: the device has
+ *   vertically arranged components in the order
+ *   blue-green-red from top to bottom.
+ *
+ * Some output devices (such as LCD panels) display colors
+ * by making each pixel consist of smaller "subpixels"
+ * that each have a particular color. By using knowledge
+ * of the layout of this subpixel components, it is possible
+ * to create image content with higher resolution than the
+ * pixel grid.
+ *
+ * Since: 1.14
+ * Stability: unstable
+ */
+typedef enum {
+  COGL_SUBPIXEL_ORDER_UNKNOWN,
+  COGL_SUBPIXEL_ORDER_NONE,
+  COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB,
+  COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR,
+  COGL_SUBPIXEL_ORDER_VERTICAL_RGB,
+  COGL_SUBPIXEL_ORDER_VERTICAL_BGR
+} CoglSubpixelOrder;
+
+/**
+ * cogl_is_output:
+ * @object: A #CoglObject pointer
+ *
+ * Gets whether the given object references a #CoglOutput.
+ *
+ * Return value: %TRUE if the object references a #CoglOutput
+ *   and %FALSE otherwise.
+ * Since: 1.14
+ * Stability: unstable
+ */
+CoglBool
+cogl_is_output (void *object);
+
+/**
+ * cogl_output_get_x:
+ * @output: a #CoglOutput
+ *
+ * Gets the X position of the output with respect to the coordinate
+ * system of the screen.
+ *
+ * Return value: the X position of the output as a pixel offset
+ *  from the left side of the screen coordinate space
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_x (CoglOutput *output);
+
+/**
+ * cogl_output_get_y:
+ * @output: a #CoglOutput
+ *
+ * Gets the Y position of the output with respect to the coordinate
+ * system of the screen.
+ *
+ * Return value: the Y position of the output as a pixel offset
+ *  from the top side of the screen coordinate space
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_y (CoglOutput *output);
+
+/**
+ * cogl_output_get_width:
+ * @output: a #CoglOutput
+ *
+ * Gets the width of the output in pixels.
+ *
+ * Return value: the width of the output in pixels
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_width (CoglOutput *output);
+
+/**
+ * cogl_output_get_height:
+ * @output: a #CoglOutput
+ *
+ * Gets the height of the output in pixels.
+ *
+ * Return value: the height of the output in pixels
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_height (CoglOutput *output);
+
+/**
+ * cogl_output_get_mm_width:
+ * @output: a #CoglOutput
+ *
+ * Gets the physical width of the output. In some cases (such as
+ * as a projector), the value returned here might correspond to
+ * nominal resolution rather than the actual physical size of the
+ * output device.
+ *
+ * Return value: the height of the output in millimeters. A value
+ *  of 0 indicates the width is unknown
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_mm_width (CoglOutput *output);
+
+/**
+ * cogl_output_get_mm_height:
+ * @output: a #CoglOutput
+ *
+ * Gets the physical height of the output. In some cases (such as
+ * as a projector), the value returned here might correspond to
+ * nominal resolution rather than the actual physical size of the
+ * output device.
+ *
+ * Return value: the height of the output in millimeters. A value
+ *  of 0 indicates that the height is unknown
+ * Since: 1.14
+ * Stability: unstable
+ */
+int
+cogl_output_get_mm_height (CoglOutput *output);
+
+/**
+ * cogl_output_get_subpixel_order:
+ * @output: a #CoglOutput
+ *
+ * For an output device where each pixel is made up of smaller components
+ * with different colors, returns the layout of the subpixel
+ * components.
+ *
+ * Return value: the order of subpixel components for the output device
+ * Since: 1.14
+ * Stability: unstable
+ */
+CoglSubpixelOrder
+cogl_output_get_subpixel_order (CoglOutput *output);
+
+/**
+ * cogl_output_get_refresh_rate:
+ * @output: a #CoglOutput
+ *
+ * Gets the number of times per second that the output device refreshes
+ * the display contents.
+ *
+ * Return value: the refresh rate of the output device. A value of zero
+ *  indicates that the refresh rate is unknown.
+ * Since: 1.14
+ * Stability: unstable
+ */
+float
+cogl_output_get_refresh_rate (CoglOutput *output);
+
+COGL_END_DECLS
+
+#endif /* __COGL_OUTPUT_H */
+
+
+
diff --git a/cogl/cogl-renderer-private.h b/cogl/cogl-renderer-private.h
index ac6effd..2b5c836 100644
--- a/cogl/cogl-renderer-private.h
+++ b/cogl/cogl-renderer-private.h
@@ -50,6 +50,8 @@ struct _CoglRenderer
   CoglWinsysID winsys_id_override;
   GList *constraints;
 
+  GList *outputs;
+
 #ifdef COGL_HAS_XLIB_SUPPORT
   Display *foreign_xdpy;
   CoglBool xlib_enable_event_retrieval;
diff --git a/cogl/cogl-xlib-renderer-private.h b/cogl/cogl-xlib-renderer-private.h
index 4970f20..7990e6c 100644
--- a/cogl/cogl-xlib-renderer-private.h
+++ b/cogl/cogl-xlib-renderer-private.h
@@ -28,6 +28,7 @@
 #include "cogl-xlib-private.h"
 #include "cogl-x11-renderer-private.h"
 #include "cogl-context.h"
+#include "cogl-output.h"
 
 typedef struct _CoglXlibRenderer
 {
@@ -41,6 +42,8 @@ typedef struct _CoglXlibRenderer
 
   /* A poll FD for handling event retrieval within Cogl */
   CoglPollFD poll_fd;
+
+  unsigned long outputs_update_serial;
 } CoglXlibRenderer;
 
 CoglBool
@@ -92,4 +95,11 @@ _cogl_xlib_renderer_poll_dispatch (CoglRenderer *renderer,
                                    const CoglPollFD *poll_fds,
                                    int n_poll_fds);
 
+CoglOutput *
+_cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer,
+                                          int x,
+                                          int y,
+                                          int width,
+                                          int height);
+
 #endif /* __COGL_RENDERER_XLIB_PRIVATE_H */
diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c
index 1116332..18c0fe6 100644
--- a/cogl/cogl-xlib-renderer.c
+++ b/cogl/cogl-xlib-renderer.c
@@ -32,6 +32,7 @@
 #include "cogl-util.h"
 #include "cogl-object.h"
 
+#include "cogl-output-private.h"
 #include "cogl-renderer-private.h"
 #include "cogl-xlib-renderer-private.h"
 #include "cogl-x11-renderer-private.h"
@@ -40,8 +41,10 @@
 
 #include <X11/Xlib.h>
 #include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xrandr.h>
 
 #include <stdlib.h>
+#include <string.h>
 
 static char *_cogl_x11_display_name = NULL;
 static GList *_cogl_xlib_renderers = NULL;
@@ -190,6 +193,280 @@ assert_xlib_display (CoglRenderer *renderer, CoglError **error)
   return xdpy;
 }
 
+static int
+compare_outputs (CoglOutput *a,
+                 CoglOutput *b)
+{
+  return strcmp (a->name, b->name);
+}
+
+#define CSO(X) COGL_SUBPIXEL_ORDER_ ## X
+static CoglSubpixelOrder subpixel_map[6][6] = {
+  { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR),
+    CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR) },   /* 0 */
+  { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR),
+    CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB) }, /* 90 */
+  { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB),
+    CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB) },   /* 180 */
+  { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB),
+    CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR) }, /* 270 */
+  { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB),
+    CSO(VERTICAL_RGB),   CSO(VERTICAL_BGR) },   /* Reflect_X */
+  { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR),
+    CSO(VERTICAL_BGR),   CSO(VERTICAL_RGB) },   /* Reflect_Y */
+};
+#undef CSO
+
+static void
+update_outputs (CoglRenderer *renderer,
+                CoglBool notify)
+{
+  CoglXlibRenderer *xlib_renderer =
+    _cogl_xlib_renderer_get_data (renderer);
+  XRRScreenResources *resources;
+  CoglXlibTrapState state;
+  CoglBool error = FALSE;
+  GList *new_outputs = NULL;
+  GList *l, *m;
+  CoglBool changed = FALSE;
+  int i;
+
+  xlib_renderer->outputs_update_serial = XNextRequest (xlib_renderer->xdpy);
+
+  resources = XRRGetScreenResources (xlib_renderer->xdpy,
+                                     DefaultRootWindow (xlib_renderer->xdpy));
+
+  _cogl_xlib_renderer_trap_errors (renderer, &state);
+
+  for (i = 0; i < resources->ncrtc && !error; i++)
+    {
+      XRRCrtcInfo *crtc_info = NULL;
+      XRROutputInfo *output_info = NULL;
+      CoglOutput *output;
+      float refresh_rate = 0;
+      int j;
+
+      crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy,
+                                  resources, resources->crtcs[i]);
+      if (crtc_info == NULL)
+        {
+          error = TRUE;
+          goto next;
+        }
+
+      if (crtc_info->mode == None)
+        goto next;
+
+      for (j = 0; j < resources->nmode; j++)
+        {
+          if (resources->modes[j].id == crtc_info->mode)
+            refresh_rate = (resources->modes[j].dotClock /
+                            ((float)resources->modes[j].hTotal *
+                             resources->modes[j].vTotal));
+        }
+
+      output_info = XRRGetOutputInfo (xlib_renderer->xdpy,
+                                      resources,
+                                      crtc_info->outputs[0]);
+      if (output_info == NULL)
+        {
+          error = TRUE;
+          goto next;
+        }
+
+      output = _cogl_output_new (output_info->name);
+      output->x = crtc_info->x;
+      output->y = crtc_info->y;
+      output->width = crtc_info->width;
+      output->height = crtc_info->height;
+      if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0)
+        {
+          output->mm_width = output_info->mm_height;
+          output->mm_height = output_info->mm_width;
+        }
+      else
+        {
+          output->mm_width = output_info->mm_width;
+          output->mm_height = output_info->mm_height;
+        }
+
+      output->refresh_rate = refresh_rate;
+
+      switch (output_info->subpixel_order)
+        {
+        case SubPixelUnknown:
+        default:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
+          break;
+        case SubPixelNone:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
+          break;
+        case SubPixelHorizontalRGB:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+          break;
+        case SubPixelHorizontalBGR:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
+          break;
+        case SubPixelVerticalRGB:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
+          break;
+        case SubPixelVerticalBGR:
+          output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
+          break;
+        }
+
+      output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
+
+      /* Handle the effect of rotation and reflection on subpixel order (ugh) */
+      for (j = 0; j < 6; j++)
+        {
+          if ((crtc_info->rotation & (1 << j)) != 0)
+            output->subpixel_order = subpixel_map[j][output->subpixel_order];
+        }
+
+      new_outputs = g_list_prepend (new_outputs, output);
+
+    next:
+      if (crtc_info != NULL)
+        XFree (crtc_info);
+
+      if (output_info != NULL)
+        XFree (output_info);
+    }
+
+  XFree (resources);
+
+  if (!error)
+    {
+      new_outputs = g_list_sort (new_outputs, (GCompareFunc)compare_outputs);
+
+      l = new_outputs;
+      m = renderer->outputs;
+
+      while (l || m)
+        {
+          int cmp;
+          CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL;
+          CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL;
+
+          if (l && m)
+            cmp = compare_outputs (output_l, output_m);
+          else if (l)
+            cmp = -1;
+          else
+            cmp = 1;
+
+          if (cmp == 0)
+            {
+              GList *m_next = m->next;
+
+              if (!_cogl_output_values_equal (output_l, output_m))
+                {
+                  renderer->outputs = g_list_remove_link (renderer->outputs, m);
+                  renderer->outputs = g_list_insert_before (renderer->outputs,
+                                                            m_next, output_l);
+                  cogl_object_ref (output_l);
+
+                  changed = TRUE;
+                }
+
+              l = l->next;
+              m = m_next;
+            }
+          else if (cmp < 0)
+            {
+              renderer->outputs =
+                g_list_insert_before (renderer->outputs, m, output_l);
+              cogl_object_ref (output_l);
+              changed = TRUE;
+              l = l->next;
+            }
+          else
+            {
+              GList *m_next = m->next;
+              renderer->outputs = g_list_remove_link (renderer->outputs, m);
+              changed = TRUE;
+              m = m_next;
+            }
+        }
+    }
+
+  g_list_free_full (new_outputs, (GDestroyNotify)cogl_object_unref);
+  _cogl_xlib_renderer_untrap_errors (renderer, &state);
+
+  if (changed)
+    {
+      const CoglWinsysVtable *winsys = renderer->winsys_vtable;
+
+      if (notify)
+        COGL_NOTE (WINSYS, "Outputs changed:");
+      else
+        COGL_NOTE (WINSYS, "Outputs:");
+
+      for (l = renderer->outputs; l; l = l->next)
+        {
+          CoglOutput *output = l->data;
+          const char *subpixel_string;
+
+          switch (output->subpixel_order)
+            {
+            case COGL_SUBPIXEL_ORDER_UNKNOWN:
+            default:
+              subpixel_string = "unknown";
+              break;
+            case COGL_SUBPIXEL_ORDER_NONE:
+              subpixel_string = "none";
+              break;
+            case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB:
+              subpixel_string = "horizontal_rgb";
+              break;
+            case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR:
+              subpixel_string = "horizontal_bgr";
+              break;
+            case COGL_SUBPIXEL_ORDER_VERTICAL_RGB:
+              subpixel_string = "vertical_rgb";
+              break;
+            case COGL_SUBPIXEL_ORDER_VERTICAL_BGR:
+              subpixel_string = "vertical_bgr";
+              break;
+            }
+
+          COGL_NOTE (WINSYS,
+                     " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f "
+                     "subpixel_order=%s refresh_rate=%.3f",
+                     output->name,
+                     output->x, output->y, output->width, output->height,
+                     output->mm_width, output->mm_height,
+                     output->width / (output->mm_width / 25.4),
+                     output->height / (output->mm_height / 25.4),
+                     subpixel_string,
+                     output->refresh_rate);
+        }
+
+      if (notify && winsys->renderer_outputs_changed != NULL)
+        winsys->renderer_outputs_changed (renderer);
+    }
+}
+
+static CoglFilterReturn
+randr_filter (XEvent *event,
+              void   *data)
+{
+  CoglRenderer *renderer = data;
+  CoglXlibRenderer *xlib_renderer =
+    _cogl_xlib_renderer_get_data (renderer);
+  CoglX11Renderer *x11_renderer =
+    (CoglX11Renderer *) xlib_renderer;
+
+  if (x11_renderer->randr_base != -1 &&
+      (event->xany.type == x11_renderer->randr_base + RRScreenChangeNotify ||
+       event->xany.type == x11_renderer->randr_base + RRNotify) &&
+      event->xany.serial >= xlib_renderer->outputs_update_serial)
+    update_outputs (renderer, TRUE);
+
+  return COGL_FILTER_CONTINUE;
+}
+
 CoglBool
 _cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error)
 {
@@ -198,6 +475,7 @@ _cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error)
   CoglX11Renderer *x11_renderer =
     (CoglX11Renderer *) xlib_renderer;
   int damage_error;
+  int randr_error;
 
   if (!assert_xlib_display (renderer, error))
     return FALSE;
@@ -211,13 +489,30 @@ _cogl_xlib_renderer_connect (CoglRenderer *renderer, CoglError **error)
                               &damage_error))
     x11_renderer->damage_base = -1;
 
+  /* Check whether randr is supported on this display */
+  if (!XRRQueryExtension (xlib_renderer->xdpy,
+                          &x11_renderer->randr_base,
+                          &randr_error))
+    x11_renderer->randr_base = -1;
+
   xlib_renderer->trap_state = NULL;
 
   xlib_renderer->poll_fd.fd = ConnectionNumber (xlib_renderer->xdpy);
   xlib_renderer->poll_fd.events = COGL_POLL_FD_EVENT_IN;
 
+  XRRSelectInput(xlib_renderer->xdpy,
+                 DefaultRootWindow (xlib_renderer->xdpy),
+                 RRScreenChangeNotifyMask
+                 | RRCrtcChangeNotifyMask
+                 | RROutputPropertyNotifyMask);
+  update_outputs (renderer, FALSE);
+
   register_xlib_renderer (renderer);
 
+  cogl_xlib_renderer_add_filter (renderer,
+                                 randr_filter,
+                                 renderer);
+
   return TRUE;
 }
 
@@ -227,6 +522,9 @@ _cogl_xlib_renderer_disconnect (CoglRenderer *renderer)
   CoglXlibRenderer *xlib_renderer =
     _cogl_xlib_renderer_get_data (renderer);
 
+  g_list_free_full (renderer->outputs, (GDestroyNotify)cogl_object_unref);
+  renderer->outputs = NULL;
+
   if (!renderer->foreign_xdpy && xlib_renderer->xdpy)
     XCloseDisplay (xlib_renderer->xdpy);
 
@@ -312,3 +610,39 @@ _cogl_xlib_renderer_poll_dispatch (CoglRenderer *renderer,
         cogl_xlib_renderer_handle_event (renderer, &xevent);
       }
 }
+
+CoglOutput *
+_cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer,
+                                          int x,
+                                          int y,
+                                          int width,
+                                          int height)
+{
+  int max_overlap = 0;
+  CoglOutput *max_overlapped = NULL;
+  GList *l;
+  int xa1 = x, xa2 = x + width;
+  int ya1 = y, ya2 = y + height;
+
+  for (l = renderer->outputs; l; l = l->next)
+    {
+      CoglOutput *output = l->data;
+      int xb1 = output->x, xb2 = output->x + output->width;
+      int yb1 = output->y, yb2 = output->y + output->height;
+
+      int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1);
+      int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1);
+
+      if (overlap_x > 0 && overlap_y > 0)
+        {
+          int overlap = overlap_x * overlap_y;
+          if (overlap > max_overlap)
+            {
+              max_overlap = overlap;
+              max_overlapped = output;
+            }
+        }
+    }
+
+  return max_overlapped;
+}
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 2baf47b..ddc1092 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -71,7 +71,9 @@ typedef struct _CoglContextGLX
 typedef struct _CoglOnscreenXlib
 {
   Window xwin;
+  int x, y;
   CoglBool is_foreign_xwin;
+  CoglOutput *output;
 } CoglOnscreenXlib;
 
 typedef struct _CoglOnscreenGLX
@@ -186,29 +188,84 @@ notify_swap_buffers (CoglContext *context, GLXDrawable drawable)
 }
 
 static void
+update_output (CoglOnscreen *onscreen)
+{
+  CoglOnscreenXlib *xlib_onscreen = onscreen->winsys;
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglOutput *output;
+  int width, height;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+  output = _cogl_xlib_renderer_output_for_rectangle (display->renderer,
+                                                     xlib_onscreen->x,
+                                                     xlib_onscreen->y,
+                                                     width, height);
+  if (xlib_onscreen->output != output)
+    {
+      if (xlib_onscreen->output)
+        cogl_object_unref (xlib_onscreen->output);
+
+      xlib_onscreen->output = output;
+
+      if (output)
+        cogl_object_ref (xlib_onscreen->output);
+    }
+}
+
+static void
 notify_resize (CoglContext *context,
-               GLXDrawable drawable,
-               int width,
-               int height)
+               XConfigureEvent *configure_event)
 {
-  CoglOnscreen *onscreen = find_onscreen_for_xid (context, drawable);
+  CoglOnscreen *onscreen = find_onscreen_for_xid (context,
+                                                  configure_event->window);
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
   CoglDisplay *display = context->display;
   CoglGLXDisplay *glx_display = display->winsys;
   CoglOnscreenGLX *glx_onscreen;
+  CoglOnscreenXlib *xlib_onscreen;
 
   if (!onscreen)
     return;
 
   glx_onscreen = onscreen->winsys;
+  xlib_onscreen = onscreen->winsys;
 
-  _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
+  _cogl_framebuffer_winsys_update_size (framebuffer,
+                                        configure_event->width,
+                                        configure_event->height);
 
   /* We only want to notify that a resize happened when the
      application calls cogl_context_dispatch so instead of immediately
      notifying we'll set a flag to remember to notify later */
   glx_display->pending_resize_notify = TRUE;
   glx_onscreen->pending_resize_notify = TRUE;
+
+  if (!xlib_onscreen->is_foreign_xwin)
+    {
+      int x, y;
+
+      if (configure_event->send_event)
+        {
+          x = configure_event->x;
+          y = configure_event->y;
+        }
+      else
+        {
+          Window child;
+          XTranslateCoordinates (configure_event->display,
+                                 configure_event->window,
+                                 DefaultRootWindow (configure_event->display),
+                                 0, 0, &x, &y, &child);
+        }
+
+      xlib_onscreen->x = x;
+      xlib_onscreen->y = y;
+
+      update_output (onscreen);
+    }
 }
 
 static CoglFilterReturn
@@ -222,9 +279,7 @@ glx_event_filter_cb (XEvent *xevent, void *data)
   if (xevent->type == ConfigureNotify)
     {
       notify_resize (context,
-                     xevent->xconfigure.window,
-                     xevent->xconfigure.width,
-                     xevent->xconfigure.height);
+                     &xevent->xconfigure);
 
       /* we let ConfigureNotify pass through */
       return COGL_FILTER_CONTINUE;
@@ -261,6 +316,38 @@ _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
 }
 
 static CoglBool
+update_all_outputs (CoglRenderer *renderer)
+{
+  GList *l;
+
+  _COGL_GET_CONTEXT (context, FALSE);
+
+  if (context->display == NULL) /* during connection */
+    return FALSE;
+
+  if (context->display->renderer != renderer)
+    return FALSE;
+
+  for (l = context->framebuffers; l; l = l->next)
+    {
+      CoglFramebuffer *framebuffer = l->data;
+
+      if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+        continue;
+
+      update_output (COGL_ONSCREEN (framebuffer));
+    }
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_renderer_outputs_changed (CoglRenderer *renderer)
+{
+  update_all_outputs (renderer);
+}
+
+static CoglBool
 resolve_core_glx_functions (CoglRenderer *renderer,
                             CoglError **error)
 {
@@ -1052,6 +1139,12 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   if (glx_onscreen == NULL)
     return;
 
+  if (xlib_onscreen->output != NULL)
+    {
+      cogl_object_unref (xlib_onscreen->output);
+      xlib_onscreen->output = NULL;
+    }
+
   _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
 
   drawable =
@@ -2197,6 +2290,7 @@ static CoglWinsysVtable _cogl_winsys_vtable =
     .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address,
     .renderer_connect = _cogl_winsys_renderer_connect,
     .renderer_disconnect = _cogl_winsys_renderer_disconnect,
+    .renderer_outputs_changed = _cogl_winsys_renderer_outputs_changed,
     .display_setup = _cogl_winsys_display_setup,
     .display_destroy = _cogl_winsys_display_destroy,
     .context_init = _cogl_winsys_context_init,
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index 27cf886..fe87e06 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -83,6 +83,9 @@ typedef struct _CoglWinsysVtable
   void
   (*renderer_disconnect) (CoglRenderer *renderer);
 
+  void
+  (*renderer_outputs_changed) (CoglRenderer *renderer);
+
   CoglBool
   (*display_setup) (CoglDisplay *display, CoglError **error);
 
diff --git a/configure.ac b/configure.ac
index 9564d85..418eed3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -84,6 +84,7 @@ m4_define([uprof_req_version],          [0.3])
 m4_define([gtk_doc_req_version],        [1.13])
 m4_define([xfixes_req_version],         [3])
 m4_define([xcomposite_req_version],     [0.4])
+m4_define([xrandr_req_version],         [1.2])
 m4_define([cairo_req_version],          [1.10])
 
 dnl These variables get copied into the generated README
@@ -1063,7 +1064,7 @@ dnl         Check X11 dependencies if required
 dnl         ========================================================
 AS_IF([test "x$NEED_XLIB" = "xyes"],
       [
-        X11_MODULES="x11 xext xfixes >= xfixes_req_version xdamage xcomposite >= xcomposite_req_version"
+        X11_MODULES="x11 xext xfixes >= xfixes_req_version xdamage xcomposite >= xcomposite_req_version xrandr >= xrandr_req_version"
         PKG_CHECK_MODULES(DUMMY, [$X11_MODULES],
                           [COGL_PKG_REQUIRES="$COGL_PKG_REQUIRES $X11_MODULES"])
         SUPPORT_X11=yes



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