[gnome-flashback] backends: move output code to its own file



commit 73a4e6d7c7ad33306bc740b6e367a0edc7676c39
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sun Jul 7 13:40:51 2019 +0300

    backends: move output code to its own file
    
    Based on mutter commit:
    https://gitlab.gnome.org/GNOME/mutter/commit/e32d52b9b8eca6c82747

 backends/Makefile.am                         |   2 +
 backends/gf-monitor-manager-xrandr-private.h |   4 +-
 backends/gf-monitor-manager-xrandr.c         | 781 +------------------------
 backends/gf-output-xrandr-private.h          |  43 ++
 backends/gf-output-xrandr.c                  | 828 +++++++++++++++++++++++++++
 5 files changed, 898 insertions(+), 760 deletions(-)
---
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 8fb7432..e6b9fc4 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -74,6 +74,8 @@ libbackends_la_SOURCES = \
        gf-orientation-manager-private.h \
        gf-orientation-manager.c \
        gf-output-private.h \
+       gf-output-xrandr-private.h \
+       gf-output-xrandr.c \
        gf-output.c \
        gf-rectangle-private.h \
        gf-rectangle.c \
diff --git a/backends/gf-monitor-manager-xrandr-private.h b/backends/gf-monitor-manager-xrandr-private.h
index e76699f..d439083 100644
--- a/backends/gf-monitor-manager-xrandr-private.h
+++ b/backends/gf-monitor-manager-xrandr-private.h
@@ -3,7 +3,7 @@
  * Copyright (C) 2003 Rob Adams
  * Copyright (C) 2004-2006 Elijah Newren
  * Copyright (C) 2013 Red Hat Inc.
- * Copyright (C) 2017 Alberts Muktupāvels
+ * Copyright (C) 2017-2019 Alberts Muktupāvels
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,8 @@ G_DECLARE_FINAL_TYPE (GfMonitorManagerXrandr, gf_monitor_manager_xrandr,
 
 Display            *gf_monitor_manager_xrandr_get_xdisplay  (GfMonitorManagerXrandr *xrandr);
 
+gboolean            gf_monitor_manager_xrandr_has_randr15   (GfMonitorManagerXrandr *xrandr);
+
 XRRScreenResources *gf_monitor_manager_xrandr_get_resources (GfMonitorManagerXrandr *xrandr);
 
 gboolean            gf_monitor_manager_xrandr_handle_xevent (GfMonitorManagerXrandr *xrandr,
diff --git a/backends/gf-monitor-manager-xrandr.c b/backends/gf-monitor-manager-xrandr.c
index 780dbd7..d221354 100644
--- a/backends/gf-monitor-manager-xrandr.c
+++ b/backends/gf-monitor-manager-xrandr.c
@@ -6,7 +6,7 @@
  * Copyright (C) 2003 Rob Adams
  * Copyright (C) 2004-2006 Elijah Newren
  * Copyright (C) 2013 Red Hat Inc.
- * Copyright (C) 2017 Alberts Muktupāvels
+ * Copyright (C) 2017-2019 Alberts Muktupāvels
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,8 +30,7 @@
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/Xrandr.h>
+#include <X11/Xlibint.h>
 #include <X11/extensions/dpms.h>
 #include <X11/Xlib-xcb.h>
 #include <xcb/randr.h>
@@ -42,7 +41,7 @@
 #include "gf-monitor-manager-xrandr-private.h"
 #include "gf-monitor-private.h"
 #include "gf-monitor-tiled-private.h"
-#include "gf-output-private.h"
+#include "gf-output-xrandr-private.h"
 
 /* Look for DPI_FALLBACK in:
  * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
@@ -199,71 +198,6 @@ xrandr_set_crtc_config (GfMonitorManagerXrandr *xrandr,
   return TRUE;
 }
 
-static void
-output_set_presentation_xrandr (GfMonitorManagerXrandr *xrandr,
-                                GfOutput               *output,
-                                gboolean                presentation)
-{
-  Atom atom;
-  gint value;
-
-  atom = XInternAtom (xrandr->xdisplay, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT", False);
-  value= presentation;
-
-  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
-                                    (XID) output->winsys_id,
-                                    atom, XCB_ATOM_CARDINAL, 32,
-                                    XCB_PROP_MODE_REPLACE,
-                                    1, &value);
-}
-
-static void
-output_set_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
-                                 GfOutput               *output,
-                                 gboolean                underscanning)
-{
-  Atom prop, valueatom;
-  const gchar *value;
-
-  prop = XInternAtom (xrandr->xdisplay, "underscan", False);
-
-  value = underscanning ? "on" : "off";
-  valueatom = XInternAtom (xrandr->xdisplay, value, False);
-
-  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
-                                    (XID) output->winsys_id,
-                                    prop, XCB_ATOM_ATOM, 32,
-                                    XCB_PROP_MODE_REPLACE,
-                                    1, &valueatom);
-
-  /* Configure the border at the same time. Currently, we use a
-   * 5% of the width/height of the mode. In the future, we should
-   * make the border configurable.
-   */
-  if (underscanning)
-    {
-      uint32_t border_value;
-
-      prop = XInternAtom (xrandr->xdisplay, "underscan hborder", False);
-      border_value = output->crtc->current_mode->width * 0.05;
-
-      xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
-                                        (XID) output->winsys_id,
-                                        prop, XCB_ATOM_INTEGER, 32,
-                                        XCB_PROP_MODE_REPLACE,
-                                        1, &border_value);
-
-      prop = XInternAtom (xrandr->xdisplay, "underscan vborder", False);
-      border_value = output->crtc->current_mode->height * 0.05;
-
-      xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
-                                        (XID) output->winsys_id,
-                                        prop, XCB_ATOM_INTEGER, 32,
-                                        XCB_PROP_MODE_REPLACE,
-                                        1, &border_value);
-    }
-}
-
 static gboolean
 is_crtc_assignment_changed (GfCrtc      *crtc,
                             GfCrtcInfo **crtc_infos,
@@ -389,69 +323,6 @@ is_assignments_changed (GfMonitorManager  *manager,
   return FALSE;
 }
 
-static guint8 *
-get_edid_property (Display  *xdisplay,
-                   RROutput  output,
-                   Atom      atom,
-                   gsize    *len)
-{
-  guchar *prop;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  Atom actual_type;
-  guint8 *result;
-
-  XRRGetOutputProperty (xdisplay, output, atom,
-                        0, 100, False, False,
-                        AnyPropertyType,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &prop);
-
-  if (actual_type == XA_INTEGER && actual_format == 8)
-    {
-      result = g_memdup (prop, nitems);
-      if (len)
-        *len = nitems;
-    }
-  else
-    {
-      result = NULL;
-    }
-
-  if (prop)
-    XFree (prop);
-
-  return result;
-}
-
-static GBytes *
-read_output_edid (GfMonitorManagerXrandr *xrandr,
-                  XID                     winsys_id)
-{
-  Atom edid_atom;
-  guint8 *result;
-  gsize len;
-
-  edid_atom = XInternAtom (xrandr->xdisplay, "EDID", FALSE);
-  result = get_edid_property (xrandr->xdisplay, winsys_id, edid_atom, &len);
-
-  if (!result)
-    {
-      edid_atom = XInternAtom (xrandr->xdisplay, "EDID_DATA", FALSE);
-      result = get_edid_property (xrandr->xdisplay, winsys_id, edid_atom, &len);
-    }
-
-  if (result)
-    {
-      if (len > 0 && len % 128 == 0)
-        return g_bytes_new_take (result, len);
-      else
-        g_free (result);
-    }
-
-  return NULL;
-}
-
 static xcb_randr_rotation_t
 gf_monitor_transform_to_xrandr (GfMonitorTransform transform)
 {
@@ -501,548 +372,6 @@ gf_monitor_transform_to_xrandr (GfMonitorTransform transform)
   return rotation;
 }
 
-static gboolean
-output_get_property_exists (GfMonitorManagerXrandr *xrandr,
-                            GfOutput               *output,
-                            const gchar            *propname)
-{
-  gboolean exists;
-  Atom atom, actual_type;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-
-  atom = XInternAtom (xrandr->xdisplay, propname, False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, AnyPropertyType,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  exists = (actual_type != None);
-
-  if (buffer)
-    XFree (buffer);
-
-  return exists;
-}
-
-static gboolean
-output_get_hotplug_mode_update (GfMonitorManagerXrandr *xrandr,
-                                GfOutput               *output)
-{
-  return output_get_property_exists (xrandr, output, "hotplug_mode_update");
-}
-
-static gboolean
-output_get_integer_property (GfMonitorManagerXrandr *xrandr,
-                             GfOutput               *output,
-                             const gchar            *propname,
-                             gint                   *value)
-{
-  gboolean exists;
-  Atom atom, actual_type;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-
-  atom = XInternAtom (xrandr->xdisplay, propname, False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_INTEGER,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  exists = (actual_type == XA_INTEGER && actual_format == 32 && nitems == 1);
-
-  if (exists && value != NULL)
-    *value = ((gint*) buffer)[0];
-
-  if (buffer)
-    XFree (buffer);
-
-  return exists;
-}
-
-static gint
-output_get_suggested_x (GfMonitorManagerXrandr *xrandr,
-                        GfOutput               *output)
-{
-  gint val;
-
-  if (output_get_integer_property (xrandr, output, "suggested X", &val))
-    return val;
-
-  return -1;
-}
-
-static gint
-output_get_suggested_y (GfMonitorManagerXrandr *xrandr,
-                        GfOutput               *output)
-{
-  gint val;
-
-  if (output_get_integer_property (xrandr, output, "suggested Y", &val))
-    return val;
-
-  return -1;
-}
-
-static GfConnectorType
-connector_type_from_atom (GfMonitorManagerXrandr *xrandr,
-                          Atom                    atom)
-{
-  Display *xdisplay;
-
-  xdisplay = xrandr->xdisplay;
-
-  if (atom == XInternAtom (xdisplay, "HDMI", True))
-    return GF_CONNECTOR_TYPE_HDMIA;
-  if (atom == XInternAtom (xdisplay, "VGA", True))
-    return GF_CONNECTOR_TYPE_VGA;
-  /* Doesn't have a DRM equivalent, but means an internal panel.
-   * We could pick either LVDS or eDP here. */
-  if (atom == XInternAtom (xdisplay, "Panel", True))
-    return GF_CONNECTOR_TYPE_LVDS;
-  if (atom == XInternAtom (xdisplay, "DVI", True) ||
-      atom == XInternAtom (xdisplay, "DVI-I", True))
-    return GF_CONNECTOR_TYPE_DVII;
-  if (atom == XInternAtom (xdisplay, "DVI-A", True))
-    return GF_CONNECTOR_TYPE_DVIA;
-  if (atom == XInternAtom (xdisplay, "DVI-D", True))
-    return GF_CONNECTOR_TYPE_DVID;
-  if (atom == XInternAtom (xdisplay, "DisplayPort", True))
-    return GF_CONNECTOR_TYPE_DisplayPort;
-  if (atom == XInternAtom (xdisplay, "TV", True))
-    return GF_CONNECTOR_TYPE_TV;
-  if (atom == XInternAtom (xdisplay, "TV-Composite", True))
-    return GF_CONNECTOR_TYPE_Composite;
-  if (atom == XInternAtom (xdisplay, "TV-SVideo", True))
-    return GF_CONNECTOR_TYPE_SVIDEO;
-  /* Another set of mismatches. */
-  if (atom == XInternAtom (xdisplay, "TV-SCART", True))
-    return GF_CONNECTOR_TYPE_TV;
-  if (atom == XInternAtom (xdisplay, "TV-C4", True))
-    return GF_CONNECTOR_TYPE_TV;
-
-  return GF_CONNECTOR_TYPE_Unknown;
-}
-
-static GfConnectorType
-output_get_connector_type_from_prop (GfMonitorManagerXrandr *xrandr,
-                                     GfOutput               *output)
-{
-  Atom atom, actual_type, connector_type_atom;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-  GfConnectorType ret;
-
-  atom = XInternAtom (xrandr->xdisplay, "ConnectorType", False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_ATOM,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
-    {
-      if (buffer)
-        XFree (buffer);
-
-      return GF_CONNECTOR_TYPE_Unknown;
-    }
-
-  connector_type_atom = ((Atom *) buffer)[0];
-  ret = connector_type_from_atom (xrandr, connector_type_atom);
-  XFree (buffer);
-
-  return ret;
-}
-
-static GfConnectorType
-output_get_connector_type_from_name (GfMonitorManagerXrandr *xrandr,
-                                     GfOutput               *output)
-{
-  const gchar *name;
-
-  name = output->name;
-
-  /* drmmode_display.c, which was copy/pasted across all the FOSS
-   * xf86-video-* drivers, seems to name its outputs based on the
-   * connector type, so look for that....
-   *
-   * SNA has its own naming scheme, because what else did you expect
-   * from SNA, but it's not too different, so we can thankfully use
-   * that with minor changes.
-   *
-   * http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/drivers/modesetting/drmmode_display.c#n953
-   * http://cgit.freedesktop.org/xorg/driver/xf86-video-intel/tree/src/sna/sna_display.c#n3486
-   */
-
-  if (g_str_has_prefix (name, "DVI"))
-    return GF_CONNECTOR_TYPE_DVII;
-  if (g_str_has_prefix (name, "LVDS"))
-    return GF_CONNECTOR_TYPE_LVDS;
-  if (g_str_has_prefix (name, "HDMI"))
-    return GF_CONNECTOR_TYPE_HDMIA;
-  if (g_str_has_prefix (name, "VGA"))
-    return GF_CONNECTOR_TYPE_VGA;
-  /* SNA uses DP, not DisplayPort. Test for both. */
-  if (g_str_has_prefix (name, "DP") || g_str_has_prefix (name, "DisplayPort"))
-    return GF_CONNECTOR_TYPE_DisplayPort;
-  if (g_str_has_prefix (name, "eDP"))
-    return GF_CONNECTOR_TYPE_eDP;
-  if (g_str_has_prefix (name, "Virtual"))
-    return GF_CONNECTOR_TYPE_VIRTUAL;
-  if (g_str_has_prefix (name, "Composite"))
-    return GF_CONNECTOR_TYPE_Composite;
-  if (g_str_has_prefix (name, "S-video"))
-    return GF_CONNECTOR_TYPE_SVIDEO;
-  if (g_str_has_prefix (name, "TV"))
-    return GF_CONNECTOR_TYPE_TV;
-  if (g_str_has_prefix (name, "CTV"))
-    return GF_CONNECTOR_TYPE_Composite;
-  if (g_str_has_prefix (name, "DSI"))
-    return GF_CONNECTOR_TYPE_DSI;
-  if (g_str_has_prefix (name, "DIN"))
-    return GF_CONNECTOR_TYPE_9PinDIN;
-
-  return GF_CONNECTOR_TYPE_Unknown;
-}
-
-static GfConnectorType
-output_get_connector_type (GfMonitorManagerXrandr *xrandr,
-                           GfOutput               *output)
-{
-  GfConnectorType ret;
-
-  /* The "ConnectorType" property is considered mandatory since RandR 1.3,
-   * but none of the FOSS drivers support it, because we're a bunch of
-   * professional software developers.
-   *
-   * Try poking it first, without any expectations that it will work.
-   * If it's not there, we thankfully have other bonghits to try next.
-   */
-  ret = output_get_connector_type_from_prop (xrandr, output);
-  if (ret != GF_CONNECTOR_TYPE_Unknown)
-    return ret;
-
-  /* Fall back to heuristics based on the output name. */
-  ret = output_get_connector_type_from_name (xrandr, output);
-  if (ret != GF_CONNECTOR_TYPE_Unknown)
-    return ret;
-
-  return GF_CONNECTOR_TYPE_Unknown;
-}
-
-static void
-output_get_tile_info (GfMonitorManagerXrandr *xrandr,
-                      GfOutput               *output)
-{
-  Atom tile_atom;
-  guchar *prop;
-  gulong nitems, bytes_after;
-  gint actual_format;
-  Atom actual_type;
-
-  if (xrandr->has_randr15 == FALSE)
-    return;
-
-  tile_atom = XInternAtom (xrandr->xdisplay, "TILE", FALSE);
-  XRRGetOutputProperty (xrandr->xdisplay, output->winsys_id,
-                        tile_atom, 0, 100, False,
-                        False, AnyPropertyType,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &prop);
-
-  if (actual_type == XA_INTEGER && actual_format == 32 && nitems == 8)
-    {
-      glong *values = (glong *) prop;
-      output->tile_info.group_id = values[0];
-      output->tile_info.flags = values[1];
-      output->tile_info.max_h_tiles = values[2];
-      output->tile_info.max_v_tiles = values[3];
-      output->tile_info.loc_h_tile = values[4];
-      output->tile_info.loc_v_tile = values[5];
-      output->tile_info.tile_w = values[6];
-      output->tile_info.tile_h = values[7];
-    }
-
-  if (prop)
-    XFree (prop);
-}
-
-static void
-output_get_modes (GfMonitorManager *manager,
-                  GfOutput         *output,
-                  XRROutputInfo    *xrandr_output)
-{
-  guint j;
-  guint n_actual_modes;
-
-  output->modes = g_new0 (GfCrtcMode *, xrandr_output->nmode);
-
-  n_actual_modes = 0;
-  for (j = 0; j < (guint) xrandr_output->nmode; j++)
-    {
-      GList *l;
-
-      for (l = manager->modes; l; l = l->next)
-        {
-          GfCrtcMode *mode = l->data;
-
-          if (xrandr_output->modes[j] == (XID) mode->mode_id)
-            {
-              output->modes[n_actual_modes] = mode;
-              n_actual_modes += 1;
-              break;
-            }
-        }
-    }
-
-  output->n_modes = n_actual_modes;
-  if (n_actual_modes > 0)
-    output->preferred_mode = output->modes[0];
-}
-
-static void
-output_get_crtcs (GfMonitorManager *manager,
-                  GfOutput         *output,
-                  XRROutputInfo    *xrandr_output)
-{
-  guint j;
-  guint n_actual_crtcs;
-  GList *l;
-
-  output->possible_crtcs = g_new0 (GfCrtc *, xrandr_output->ncrtc);
-
-  n_actual_crtcs = 0;
-  for (j = 0; j < (guint) xrandr_output->ncrtc; j++)
-    {
-      for (l = manager->crtcs; l; l = l->next)
-        {
-          GfCrtc *crtc = l->data;
-
-          if ((XID) crtc->crtc_id == xrandr_output->crtcs[j])
-            {
-              output->possible_crtcs[n_actual_crtcs] = crtc;
-              n_actual_crtcs += 1;
-              break;
-            }
-        }
-    }
-  output->n_possible_crtcs = n_actual_crtcs;
-
-  output->crtc = NULL;
-  for (l = manager->crtcs; l; l = l->next)
-    {
-      GfCrtc *crtc = l->data;
-
-      if ((XID) crtc->crtc_id == xrandr_output->crtc)
-        {
-          output->crtc = crtc;
-          break;
-        }
-    }
-}
-
-static gboolean
-output_get_boolean_property (GfMonitorManagerXrandr *xrandr,
-                             GfOutput               *output,
-                             const gchar            *propname)
-{
-  Atom atom, actual_type;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-  gboolean value;
-
-  atom = XInternAtom (xrandr->xdisplay, propname, False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_CARDINAL,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  if (actual_type != XA_CARDINAL || actual_format != 32 || nitems < 1)
-    {
-      if (buffer)
-        XFree (buffer);
-
-      return FALSE;
-    }
-
-  value = ((gint*) buffer)[0];
-  XFree (buffer);
-
-  return value;
-}
-
-static gboolean
-output_get_presentation_xrandr (GfMonitorManagerXrandr *xrandr,
-                                GfOutput               *output)
-{
-  return output_get_boolean_property (xrandr, output, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT");
-}
-
-static gboolean
-output_get_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
-                                 GfOutput               *output)
-{
-  Atom atom, actual_type;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-  gchar *str;
-  gboolean value;
-
-  atom = XInternAtom (xrandr->xdisplay, "underscan", False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_ATOM,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
-    {
-      if (buffer)
-        XFree (buffer);
-
-      return FALSE;
-    }
-
-  str = XGetAtomName (xrandr->xdisplay, *(Atom *)buffer);
-  XFree (buffer);
-
-  value = !strcmp (str, "on");
-  XFree (str);
-
-  return value;
-}
-
-static gboolean
-output_get_supports_underscanning_xrandr (GfMonitorManagerXrandr *xrandr,
-                                          GfOutput               *output)
-{
-  Atom atom, actual_type;
-  gint actual_format, i;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-  XRRPropertyInfo *property_info;
-  Atom *values;
-  gboolean supports_underscanning = FALSE;
-
-  atom = XInternAtom (xrandr->xdisplay, "underscan", False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_ATOM,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
-    {
-      if (buffer)
-        XFree (buffer);
-
-      return FALSE;
-    }
-
-  property_info = XRRQueryOutputProperty (xrandr->xdisplay,
-                                          (XID) output->winsys_id,
-                                          atom);
-  values = (Atom *) property_info->values;
-
-  for (i = 0; i < property_info->num_values; i++)
-    {
-      /* The output supports underscanning if "on" is a valid value
-       * for the underscan property.
-       */
-      gchar *name = XGetAtomName (xrandr->xdisplay, values[i]);
-      if (strcmp (name, "on") == 0)
-        supports_underscanning = TRUE;
-
-      XFree (name);
-    }
-
-  XFree (property_info);
-
-  return supports_underscanning;
-}
-
-static int
-normalize_backlight (GfOutput *output,
-                     gint      hw_value)
-{
-  return round ((gdouble) (hw_value - output->backlight_min) /
-                (output->backlight_max - output->backlight_min) * 100.0);
-}
-
-static gint
-output_get_backlight_xrandr (GfMonitorManagerXrandr *xrandr,
-                             GfOutput               *output)
-{
-  gint value = -1;
-  Atom atom, actual_type;
-  gint actual_format;
-  gulong nitems, bytes_after;
-  guchar *buffer;
-
-  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
-  XRRGetOutputProperty (xrandr->xdisplay, (XID) output->winsys_id, atom,
-                        0, G_MAXLONG, False, False, XA_INTEGER,
-                        &actual_type, &actual_format,
-                        &nitems, &bytes_after, &buffer);
-
-  if (actual_type != XA_INTEGER || actual_format != 32 || nitems < 1)
-    {
-      if (buffer)
-        XFree (buffer);
-
-      return FALSE;
-    }
-
-  value = ((gint*) buffer)[0];
-  XFree (buffer);
-
-  if (value > 0)
-    return normalize_backlight (output, value);
-  else
-    return -1;
-}
-
-static void
-output_get_backlight_limits_xrandr (GfMonitorManagerXrandr *xrandr,
-                                    GfOutput               *output)
-{
-  Atom atom;
-  xcb_connection_t *xcb_conn;
-  xcb_randr_query_output_property_cookie_t cookie;
-  xcb_randr_query_output_property_reply_t *reply;
-  int32_t *values;
-
-  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
-
-  xcb_conn = XGetXCBConnection (xrandr->xdisplay);
-  cookie = xcb_randr_query_output_property (xcb_conn,
-                                            (xcb_randr_output_t) output->winsys_id,
-                                            (xcb_atom_t) atom);
-
-  reply = xcb_randr_query_output_property_reply (xcb_conn, cookie, NULL);
-
-  /* This can happen on systems without backlights. */
-  if (reply == NULL)
-    return;
-
-  if (!reply->range || reply->length != 2)
-    {
-      g_warning ("backlight %s was not range\n", output->name);
-      g_free (reply);
-      return;
-    }
-
-  values = xcb_randr_query_output_property_valid_values (reply);
-
-  output->backlight_min = values[0];
-  output->backlight_max = values[1];
-
-  g_free (reply);
-}
-
 static gint
 compare_outputs (const void *one,
                  const void *two)
@@ -1242,22 +571,11 @@ apply_crtc_assignments (GfMonitorManager  *manager,
       GfOutputInfo *output_info = outputs[i];
       GfOutput *output = output_info->output;
 
-      if (output_info->is_primary)
-        {
-          XRRSetOutputPrimary (xrandr->xdisplay, xrandr->xroot,
-                               (XID)output_info->output->winsys_id);
-        }
-
-      output_set_presentation_xrandr (xrandr, output_info->output,
-                                      output_info->is_presentation);
-
-      if (output_get_supports_underscanning_xrandr (xrandr, output_info->output))
-        output_set_underscanning_xrandr (xrandr, output_info->output,
-                                         output_info->is_underscanning);
-
       output->is_primary = output_info->is_primary;
       output->is_presentation = output_info->is_presentation;
       output->is_underscanning = output_info->is_underscanning;
+
+      gf_output_xrandr_apply_mode (output);
     }
 
   /* Disable outputs not mentioned in the list */
@@ -1547,67 +865,26 @@ gf_monitor_manager_xrandr_read_current (GfMonitorManager *manager)
 
   for (i = 0; i < (guint) resources->noutput; i++)
     {
+      RROutput output_id;
       XRROutputInfo *xrandr_output;
-      GfOutput *output;
 
+      output_id = resources->outputs[i];
       xrandr_output = XRRGetOutputInfo (xrandr->xdisplay, resources,
-                                        resources->outputs[i]);
+                                        output_id);
 
       if (!xrandr_output)
         continue;
 
       if (xrandr_output->connection != RR_Disconnected)
         {
-          GBytes *edid;
-
-          output = g_object_new (GF_TYPE_OUTPUT, NULL);
-          output->monitor_manager = manager;
-
-          output->winsys_id = resources->outputs[i];
-          output->name = g_strdup (xrandr_output->name);
-
-          edid = read_output_edid (xrandr, output->winsys_id);
-          gf_output_parse_edid (output, edid);
-          g_bytes_unref (edid);
-
-          output->width_mm = xrandr_output->mm_width;
-          output->height_mm = xrandr_output->mm_height;
-          output->hotplug_mode_update = output_get_hotplug_mode_update (xrandr, output);
-          output->suggested_x = output_get_suggested_x (xrandr, output);
-          output->suggested_y = output_get_suggested_y (xrandr, output);
-          output->connector_type = output_get_connector_type (xrandr, output);
-
-          output_get_tile_info (xrandr, output);
-          output_get_modes (manager, output, xrandr_output);
-          output_get_crtcs (manager, output, xrandr_output);
-
-          output->n_possible_clones = xrandr_output->nclone;
-          output->possible_clones = g_new0 (GfOutput *, output->n_possible_clones);
-
-          /* We can build the list of clones now, because we don't have
-           * the list of outputs yet, so temporarily set the pointers to
-           * the bare XIDs, and then we'll fix them in a second pass
-           */
-          for (j = 0; j < (guint) xrandr_output->nclone; j++)
-            {
-              output->possible_clones[j] = GINT_TO_POINTER (xrandr_output->clones[j]);
-            }
-
-          output->is_primary = ((XID) output->winsys_id == primary_output);
-          output->is_presentation = output_get_presentation_xrandr (xrandr, output);
-          output->is_underscanning = output_get_underscanning_xrandr (xrandr, output);
-          output->supports_underscanning = output_get_supports_underscanning_xrandr (xrandr, output);
-
-          output_get_backlight_limits_xrandr (xrandr, output);
+          GfOutput *output;
 
-          if (!(output->backlight_min == 0 && output->backlight_max == 0))
-            output->backlight = output_get_backlight_xrandr (xrandr, output);
-          else
-            output->backlight = -1;
+          output = gf_create_xrandr_output (manager,
+                                            xrandr_output,
+                                            output_id,
+                                            primary_output);
 
-          if (output->n_modes == 0 || output->n_possible_crtcs == 0)
-            g_object_unref (output);
-          else
+          if (output)
             manager->outputs = g_list_prepend (manager->outputs, output);
         }
 
@@ -1647,11 +924,7 @@ static GBytes *
 gf_monitor_manager_xrandr_read_edid (GfMonitorManager *manager,
                                      GfOutput         *output)
 {
-  GfMonitorManagerXrandr *xrandr;
-
-  xrandr = GF_MONITOR_MANAGER_XRANDR (manager);
-
-  return read_output_edid (xrandr, output->winsys_id);
+  return gf_output_xrandr_read_edid (output);
 }
 
 static void
@@ -1770,23 +1043,7 @@ gf_monitor_manager_xrandr_change_backlight (GfMonitorManager *manager,
                                             GfOutput         *output,
                                             gint              value)
 {
-  GfMonitorManagerXrandr *xrandr;
-  gint hw_value;
-  Atom atom;
-
-  xrandr = GF_MONITOR_MANAGER_XRANDR (manager);
-
-  hw_value = round ((gdouble) value / 100.0 * output->backlight_max + output->backlight_min);
-  atom = XInternAtom (xrandr->xdisplay, "Backlight", False);
-
-  xcb_randr_change_output_property (XGetXCBConnection (xrandr->xdisplay),
-                                    (XID)output->winsys_id,
-                                    atom, XCB_ATOM_INTEGER, 32,
-                                    XCB_PROP_MODE_REPLACE,
-                                    1, &hw_value);
-
-  /* We're not selecting for property notifies, so update the value immediately */
-  output->backlight = normalize_backlight (output, hw_value);
+  gf_output_xrandr_change_backlight (output, value);
 }
 
 static void
@@ -2023,6 +1280,12 @@ gf_monitor_manager_xrandr_get_xdisplay (GfMonitorManagerXrandr *xrandr)
   return xrandr->xdisplay;
 }
 
+gboolean
+gf_monitor_manager_xrandr_has_randr15 (GfMonitorManagerXrandr *xrandr)
+{
+  return xrandr->has_randr15;
+}
+
 XRRScreenResources *
 gf_monitor_manager_xrandr_get_resources (GfMonitorManagerXrandr *xrandr)
 {
diff --git a/backends/gf-output-xrandr-private.h b/backends/gf-output-xrandr-private.h
new file mode 100644
index 0000000..01e55b8
--- /dev/null
+++ b/backends/gf-output-xrandr-private.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 Red Hat
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_OUTPUT_XRANDR_PRIVATE_H
+#define GF_OUTPUT_XRANDR_PRIVATE_H
+
+#include <X11/extensions/Xrandr.h>
+
+#include "gf-monitor-manager-xrandr-private.h"
+#include "gf-output-private.h"
+
+G_BEGIN_DECLS
+
+GfOutput *gf_create_xrandr_output           (GfMonitorManager *monitor_manager,
+                                             XRROutputInfo    *xrandr_output,
+                                             RROutput          output_id,
+                                             RROutput          primary_output);
+
+GBytes   *gf_output_xrandr_read_edid        (GfOutput         *output);
+
+void      gf_output_xrandr_apply_mode       (GfOutput         *output);
+
+void      gf_output_xrandr_change_backlight (GfOutput         *output,
+                                             int               value);
+
+G_END_DECLS
+
+#endif
diff --git a/backends/gf-output-xrandr.c b/backends/gf-output-xrandr.c
new file mode 100644
index 0000000..c266644
--- /dev/null
+++ b/backends/gf-output-xrandr.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) 2001, 2002 Havoc Pennington
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ * Some ICCCM manager selection code derived from fvwm2,
+ * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2004-2006 Elijah Newren
+ * Copyright (C) 2013-2017 Red Hat Inc.
+ * Copyright (C) 2017-2019 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "gf-output-xrandr-private.h"
+
+#include <math.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xlib-xcb.h>
+#include <xcb/randr.h>
+
+#include "gf-crtc-private.h"
+
+static Display *
+xdisplay_from_output (GfOutput *output)
+{
+  GfMonitorManager *monitor_manager;
+  GfMonitorManagerXrandr *monitor_manager_xrandr;
+
+  monitor_manager = gf_output_get_monitor_manager (output);
+  monitor_manager_xrandr = GF_MONITOR_MANAGER_XRANDR (monitor_manager);
+
+  return gf_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
+}
+
+static void
+output_set_presentation_xrandr (GfOutput *output,
+                                gboolean  presentation)
+{
+  Display *xdisplay;
+  Atom atom;
+  gint value;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT", False);
+  value= presentation;
+
+  xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
+                                    (XID) output->winsys_id,
+                                    atom, XCB_ATOM_CARDINAL, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &value);
+}
+
+static void
+output_set_underscanning_xrandr (GfOutput *output,
+                                 gboolean  underscanning)
+{
+  Display *xdisplay;
+  Atom prop, valueatom;
+  const gchar *value;
+
+  xdisplay = xdisplay_from_output (output);
+  prop = XInternAtom (xdisplay, "underscan", False);
+
+  value = underscanning ? "on" : "off";
+  valueatom = XInternAtom (xdisplay, value, False);
+
+  xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
+                                    (XID) output->winsys_id,
+                                    prop, XCB_ATOM_ATOM, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &valueatom);
+
+  /* Configure the border at the same time. Currently, we use a
+   * 5% of the width/height of the mode. In the future, we should
+   * make the border configurable.
+   */
+  if (underscanning)
+    {
+      uint32_t border_value;
+
+      prop = XInternAtom (xdisplay, "underscan hborder", False);
+      border_value = output->crtc->current_mode->width * 0.05;
+
+      xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
+                                        (XID) output->winsys_id,
+                                        prop, XCB_ATOM_INTEGER, 32,
+                                        XCB_PROP_MODE_REPLACE,
+                                        1, &border_value);
+
+      prop = XInternAtom (xdisplay, "underscan vborder", False);
+      border_value = output->crtc->current_mode->height * 0.05;
+
+      xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
+                                        (XID) output->winsys_id,
+                                        prop, XCB_ATOM_INTEGER, 32,
+                                        XCB_PROP_MODE_REPLACE,
+                                        1, &border_value);
+    }
+}
+
+static guint8 *
+get_edid_property (Display  *xdisplay,
+                   RROutput  output,
+                   Atom      atom,
+                   gsize    *len)
+{
+  guchar *prop;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  Atom actual_type;
+  guint8 *result;
+
+  XRRGetOutputProperty (xdisplay, output, atom,
+                        0, 100, False, False,
+                        AnyPropertyType,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &prop);
+
+  if (actual_type == XA_INTEGER && actual_format == 8)
+    {
+      result = g_memdup (prop, nitems);
+      if (len)
+        *len = nitems;
+    }
+  else
+    {
+      result = NULL;
+    }
+
+  if (prop)
+    XFree (prop);
+
+  return result;
+}
+
+static gboolean
+output_get_property_exists (GfOutput    *output,
+                            const gchar *propname)
+{
+  Display *xdisplay;
+  gboolean exists;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, propname, False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, AnyPropertyType,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  exists = (actual_type != None);
+
+  if (buffer)
+    XFree (buffer);
+
+  return exists;
+}
+
+static gboolean
+output_get_hotplug_mode_update (GfOutput *output)
+{
+  return output_get_property_exists (output, "hotplug_mode_update");
+}
+
+static gboolean
+output_get_integer_property (GfOutput    *output,
+                             const gchar *propname,
+                             gint        *value)
+{
+  Display *xdisplay;
+  gboolean exists;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, propname, False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_INTEGER,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  exists = (actual_type == XA_INTEGER && actual_format == 32 && nitems == 1);
+
+  if (exists && value != NULL)
+    *value = ((gint*) buffer)[0];
+
+  if (buffer)
+    XFree (buffer);
+
+  return exists;
+}
+
+static gint
+output_get_suggested_x (GfOutput *output)
+{
+  gint val;
+
+  if (output_get_integer_property (output, "suggested X", &val))
+    return val;
+
+  return -1;
+}
+
+static gint
+output_get_suggested_y (GfOutput *output)
+{
+  gint val;
+
+  if (output_get_integer_property (output, "suggested Y", &val))
+    return val;
+
+  return -1;
+}
+
+static GfConnectorType
+connector_type_from_atom (Display *xdisplay,
+                          Atom     atom)
+{
+  if (atom == XInternAtom (xdisplay, "HDMI", True))
+    return GF_CONNECTOR_TYPE_HDMIA;
+  if (atom == XInternAtom (xdisplay, "VGA", True))
+    return GF_CONNECTOR_TYPE_VGA;
+  /* Doesn't have a DRM equivalent, but means an internal panel.
+   * We could pick either LVDS or eDP here. */
+  if (atom == XInternAtom (xdisplay, "Panel", True))
+    return GF_CONNECTOR_TYPE_LVDS;
+  if (atom == XInternAtom (xdisplay, "DVI", True) ||
+      atom == XInternAtom (xdisplay, "DVI-I", True))
+    return GF_CONNECTOR_TYPE_DVII;
+  if (atom == XInternAtom (xdisplay, "DVI-A", True))
+    return GF_CONNECTOR_TYPE_DVIA;
+  if (atom == XInternAtom (xdisplay, "DVI-D", True))
+    return GF_CONNECTOR_TYPE_DVID;
+  if (atom == XInternAtom (xdisplay, "DisplayPort", True))
+    return GF_CONNECTOR_TYPE_DisplayPort;
+  if (atom == XInternAtom (xdisplay, "TV", True))
+    return GF_CONNECTOR_TYPE_TV;
+  if (atom == XInternAtom (xdisplay, "TV-Composite", True))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (atom == XInternAtom (xdisplay, "TV-SVideo", True))
+    return GF_CONNECTOR_TYPE_SVIDEO;
+  /* Another set of mismatches. */
+  if (atom == XInternAtom (xdisplay, "TV-SCART", True))
+    return GF_CONNECTOR_TYPE_TV;
+  if (atom == XInternAtom (xdisplay, "TV-C4", True))
+    return GF_CONNECTOR_TYPE_TV;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static GfConnectorType
+output_get_connector_type_from_prop (GfOutput *output)
+{
+  Display *xdisplay;
+  Atom atom, actual_type, connector_type_atom;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  GfConnectorType ret;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "ConnectorType", False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return GF_CONNECTOR_TYPE_Unknown;
+    }
+
+  connector_type_atom = ((Atom *) buffer)[0];
+  ret = connector_type_from_atom (xdisplay, connector_type_atom);
+  XFree (buffer);
+
+  return ret;
+}
+
+static GfConnectorType
+output_get_connector_type_from_name (GfOutput *output)
+{
+  const gchar *name;
+
+  name = output->name;
+
+  /* drmmode_display.c, which was copy/pasted across all the FOSS
+   * xf86-video-* drivers, seems to name its outputs based on the
+   * connector type, so look for that....
+   *
+   * SNA has its own naming scheme, because what else did you expect
+   * from SNA, but it's not too different, so we can thankfully use
+   * that with minor changes.
+   *
+   * http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/drivers/modesetting/drmmode_display.c#n953
+   * http://cgit.freedesktop.org/xorg/driver/xf86-video-intel/tree/src/sna/sna_display.c#n3486
+   */
+
+  if (g_str_has_prefix (name, "DVI"))
+    return GF_CONNECTOR_TYPE_DVII;
+  if (g_str_has_prefix (name, "LVDS"))
+    return GF_CONNECTOR_TYPE_LVDS;
+  if (g_str_has_prefix (name, "HDMI"))
+    return GF_CONNECTOR_TYPE_HDMIA;
+  if (g_str_has_prefix (name, "VGA"))
+    return GF_CONNECTOR_TYPE_VGA;
+  /* SNA uses DP, not DisplayPort. Test for both. */
+  if (g_str_has_prefix (name, "DP") || g_str_has_prefix (name, "DisplayPort"))
+    return GF_CONNECTOR_TYPE_DisplayPort;
+  if (g_str_has_prefix (name, "eDP"))
+    return GF_CONNECTOR_TYPE_eDP;
+  if (g_str_has_prefix (name, "Virtual"))
+    return GF_CONNECTOR_TYPE_VIRTUAL;
+  if (g_str_has_prefix (name, "Composite"))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (g_str_has_prefix (name, "S-video"))
+    return GF_CONNECTOR_TYPE_SVIDEO;
+  if (g_str_has_prefix (name, "TV"))
+    return GF_CONNECTOR_TYPE_TV;
+  if (g_str_has_prefix (name, "CTV"))
+    return GF_CONNECTOR_TYPE_Composite;
+  if (g_str_has_prefix (name, "DSI"))
+    return GF_CONNECTOR_TYPE_DSI;
+  if (g_str_has_prefix (name, "DIN"))
+    return GF_CONNECTOR_TYPE_9PinDIN;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static GfConnectorType
+output_get_connector_type (GfOutput *output)
+{
+  GfConnectorType ret;
+
+  /* The "ConnectorType" property is considered mandatory since RandR 1.3,
+   * but none of the FOSS drivers support it, because we're a bunch of
+   * professional software developers.
+   *
+   * Try poking it first, without any expectations that it will work.
+   * If it's not there, we thankfully have other bonghits to try next.
+   */
+  ret = output_get_connector_type_from_prop (output);
+  if (ret != GF_CONNECTOR_TYPE_Unknown)
+    return ret;
+
+  /* Fall back to heuristics based on the output name. */
+  ret = output_get_connector_type_from_name (output);
+  if (ret != GF_CONNECTOR_TYPE_Unknown)
+    return ret;
+
+  return GF_CONNECTOR_TYPE_Unknown;
+}
+
+static void
+output_get_tile_info (GfOutput *output)
+{
+  GfMonitorManager *monitor_manager;
+  GfMonitorManagerXrandr *monitor_manager_xrandr;
+  Display *xdisplay;
+  Atom tile_atom;
+  guchar *prop;
+  gulong nitems, bytes_after;
+  gint actual_format;
+  Atom actual_type;
+
+  monitor_manager = gf_output_get_monitor_manager (output);
+  monitor_manager_xrandr = GF_MONITOR_MANAGER_XRANDR (monitor_manager);
+
+  if (!gf_monitor_manager_xrandr_has_randr15 (monitor_manager_xrandr))
+    return;
+
+  xdisplay = gf_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr);
+  tile_atom = XInternAtom (xdisplay, "TILE", FALSE);
+  XRRGetOutputProperty (xdisplay, output->winsys_id,
+                        tile_atom, 0, 100, False,
+                        False, AnyPropertyType,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &prop);
+
+  if (actual_type == XA_INTEGER && actual_format == 32 && nitems == 8)
+    {
+      glong *values = (glong *) prop;
+      output->tile_info.group_id = values[0];
+      output->tile_info.flags = values[1];
+      output->tile_info.max_h_tiles = values[2];
+      output->tile_info.max_v_tiles = values[3];
+      output->tile_info.loc_h_tile = values[4];
+      output->tile_info.loc_v_tile = values[5];
+      output->tile_info.tile_w = values[6];
+      output->tile_info.tile_h = values[7];
+    }
+
+  if (prop)
+    XFree (prop);
+}
+
+static void
+output_get_modes (GfMonitorManager *manager,
+                  GfOutput         *output,
+                  XRROutputInfo    *xrandr_output)
+{
+  guint j;
+  guint n_actual_modes;
+
+  output->modes = g_new0 (GfCrtcMode *, xrandr_output->nmode);
+
+  n_actual_modes = 0;
+  for (j = 0; j < (guint) xrandr_output->nmode; j++)
+    {
+      GList *l;
+
+      for (l = manager->modes; l; l = l->next)
+        {
+          GfCrtcMode *mode = l->data;
+
+          if (xrandr_output->modes[j] == (XID) mode->mode_id)
+            {
+              output->modes[n_actual_modes] = mode;
+              n_actual_modes += 1;
+              break;
+            }
+        }
+    }
+
+  output->n_modes = n_actual_modes;
+  if (n_actual_modes > 0)
+    output->preferred_mode = output->modes[0];
+}
+
+static void
+output_get_crtcs (GfMonitorManager *manager,
+                  GfOutput         *output,
+                  XRROutputInfo    *xrandr_output)
+{
+  guint j;
+  guint n_actual_crtcs;
+  GList *l;
+
+  output->possible_crtcs = g_new0 (GfCrtc *, xrandr_output->ncrtc);
+
+  n_actual_crtcs = 0;
+  for (j = 0; j < (guint) xrandr_output->ncrtc; j++)
+    {
+      for (l = manager->crtcs; l; l = l->next)
+        {
+          GfCrtc *crtc = l->data;
+
+          if ((XID) crtc->crtc_id == xrandr_output->crtcs[j])
+            {
+              output->possible_crtcs[n_actual_crtcs] = crtc;
+              n_actual_crtcs += 1;
+              break;
+            }
+        }
+    }
+  output->n_possible_crtcs = n_actual_crtcs;
+
+  output->crtc = NULL;
+  for (l = manager->crtcs; l; l = l->next)
+    {
+      GfCrtc *crtc = l->data;
+
+      if ((XID) crtc->crtc_id == xrandr_output->crtc)
+        {
+          output->crtc = crtc;
+          break;
+        }
+    }
+}
+
+static gboolean
+output_get_boolean_property (GfOutput    *output,
+                             const gchar *propname)
+{
+  Display *xdisplay;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  gboolean value;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, propname, False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_CARDINAL,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_CARDINAL || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  value = ((gint*) buffer)[0];
+  XFree (buffer);
+
+  return value;
+}
+
+static gboolean
+output_get_presentation_xrandr (GfOutput *output)
+{
+  return output_get_boolean_property (output, "_GNOME_FLASHBACK_PRESENTATION_OUTPUT");
+}
+
+static gboolean
+output_get_underscanning_xrandr (GfOutput *output)
+{
+  Display *xdisplay;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  gchar *str;
+  gboolean value;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "underscan", False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  str = XGetAtomName (xdisplay, *(Atom *)buffer);
+  XFree (buffer);
+
+  value = !strcmp (str, "on");
+  XFree (str);
+
+  return value;
+}
+
+static gboolean
+output_get_supports_underscanning_xrandr (GfOutput *output)
+{
+  Display *xdisplay;
+  Atom atom, actual_type;
+  gint actual_format, i;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+  XRRPropertyInfo *property_info;
+  Atom *values;
+  gboolean supports_underscanning = FALSE;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "underscan", False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_ATOM,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  property_info = XRRQueryOutputProperty (xdisplay,
+                                          (XID) output->winsys_id,
+                                          atom);
+  values = (Atom *) property_info->values;
+
+  for (i = 0; i < property_info->num_values; i++)
+    {
+      /* The output supports underscanning if "on" is a valid value
+       * for the underscan property.
+       */
+      gchar *name = XGetAtomName (xdisplay, values[i]);
+      if (strcmp (name, "on") == 0)
+        supports_underscanning = TRUE;
+
+      XFree (name);
+    }
+
+  XFree (property_info);
+
+  return supports_underscanning;
+}
+
+static int
+normalize_backlight (GfOutput *output,
+                     gint      hw_value)
+{
+  return round ((gdouble) (hw_value - output->backlight_min) /
+                (output->backlight_max - output->backlight_min) * 100.0);
+}
+
+static gint
+output_get_backlight_xrandr (GfOutput *output)
+{
+  Display *xdisplay;
+  gint value = -1;
+  Atom atom, actual_type;
+  gint actual_format;
+  gulong nitems, bytes_after;
+  guchar *buffer;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "Backlight", False);
+  XRRGetOutputProperty (xdisplay, (XID) output->winsys_id, atom,
+                        0, G_MAXLONG, False, False, XA_INTEGER,
+                        &actual_type, &actual_format,
+                        &nitems, &bytes_after, &buffer);
+
+  if (actual_type != XA_INTEGER || actual_format != 32 || nitems < 1)
+    {
+      if (buffer)
+        XFree (buffer);
+
+      return FALSE;
+    }
+
+  value = ((gint*) buffer)[0];
+  XFree (buffer);
+
+  if (value > 0)
+    return normalize_backlight (output, value);
+  else
+    return -1;
+}
+
+static void
+output_get_backlight_limits_xrandr (GfOutput *output)
+{
+  Display *xdisplay;
+  Atom atom;
+  xcb_connection_t *xcb_conn;
+  xcb_randr_query_output_property_cookie_t cookie;
+  xcb_randr_query_output_property_reply_t *reply;
+  int32_t *values;
+
+  xdisplay = xdisplay_from_output (output);
+  atom = XInternAtom (xdisplay, "Backlight", False);
+
+  xcb_conn = XGetXCBConnection (xdisplay);
+  cookie = xcb_randr_query_output_property (xcb_conn,
+                                            (xcb_randr_output_t) output->winsys_id,
+                                            (xcb_atom_t) atom);
+
+  reply = xcb_randr_query_output_property_reply (xcb_conn, cookie, NULL);
+
+  /* This can happen on systems without backlights. */
+  if (reply == NULL)
+    return;
+
+  if (!reply->range || reply->length != 2)
+    {
+      g_warning ("backlight %s was not range\n", output->name);
+      g_free (reply);
+      return;
+    }
+
+  values = xcb_randr_query_output_property_valid_values (reply);
+
+  output->backlight_min = values[0];
+  output->backlight_max = values[1];
+
+  g_free (reply);
+}
+
+GfOutput *
+gf_create_xrandr_output (GfMonitorManager *monitor_manager,
+                         XRROutputInfo    *xrandr_output,
+                         RROutput          output_id,
+                         RROutput          primary_output)
+{
+  GfOutput *output;
+  GBytes *edid;
+  unsigned int i;
+
+  output = g_object_new (GF_TYPE_OUTPUT, NULL);
+  output->monitor_manager = monitor_manager;
+
+  output->winsys_id = output_id;
+  output->name = g_strdup (xrandr_output->name);
+
+  edid = gf_output_xrandr_read_edid (output);
+  gf_output_parse_edid (output, edid);
+  g_bytes_unref (edid);
+
+  output->width_mm = xrandr_output->mm_width;
+  output->height_mm = xrandr_output->mm_height;
+  output->hotplug_mode_update = output_get_hotplug_mode_update (output);
+  output->suggested_x = output_get_suggested_x (output);
+  output->suggested_y = output_get_suggested_y (output);
+  output->connector_type = output_get_connector_type (output);
+
+  output_get_tile_info (output);
+  output_get_modes (monitor_manager, output, xrandr_output);
+  output_get_crtcs (monitor_manager, output, xrandr_output);
+
+  output->n_possible_clones = xrandr_output->nclone;
+  output->possible_clones = g_new0 (GfOutput *, output->n_possible_clones);
+
+  /* We can build the list of clones now, because we don't have
+   * the list of outputs yet, so temporarily set the pointers to
+   * the bare XIDs, and then we'll fix them in a second pass
+   */
+  for (i = 0; i < (unsigned int) xrandr_output->nclone; i++)
+    {
+      output->possible_clones[i] = GINT_TO_POINTER (xrandr_output->clones[i]);
+    }
+
+  output->is_primary = ((XID) output->winsys_id == primary_output);
+  output->is_presentation = output_get_presentation_xrandr (output);
+  output->is_underscanning = output_get_underscanning_xrandr (output);
+  output->supports_underscanning = output_get_supports_underscanning_xrandr (output);
+
+  output_get_backlight_limits_xrandr (output);
+
+  if (!(output->backlight_min == 0 && output->backlight_max == 0))
+    output->backlight = output_get_backlight_xrandr (output);
+  else
+    output->backlight = -1;
+
+  if (output->n_modes == 0 || output->n_possible_crtcs == 0)
+    {
+      g_object_unref (output);
+      return NULL;
+    }
+
+  return output;
+}
+
+GBytes *
+gf_output_xrandr_read_edid (GfOutput *output)
+{
+  Display *xdisplay;
+  Atom edid_atom;
+  guint8 *result;
+  gsize len;
+
+  xdisplay = xdisplay_from_output (output);
+  edid_atom = XInternAtom (xdisplay, "EDID", FALSE);
+  result = get_edid_property (xdisplay, output->winsys_id, edid_atom, &len);
+
+  if (!result)
+    {
+      edid_atom = XInternAtom (xdisplay, "EDID_DATA", FALSE);
+      result = get_edid_property (xdisplay, output->winsys_id, edid_atom, &len);
+    }
+
+  if (result)
+    {
+      if (len > 0 && len % 128 == 0)
+        return g_bytes_new_take (result, len);
+      else
+        g_free (result);
+    }
+
+  return NULL;
+}
+
+void
+gf_output_xrandr_apply_mode (GfOutput *output)
+{
+  Display *xdisplay;
+
+  xdisplay = xdisplay_from_output (output);
+
+  if (output->is_primary)
+    {
+      XRRSetOutputPrimary (xdisplay, DefaultRootWindow (xdisplay),
+                           (XID) output->winsys_id);
+    }
+
+  output_set_presentation_xrandr (output, output->is_presentation);
+
+  if (output->supports_underscanning)
+    output_set_underscanning_xrandr (output, output->is_underscanning);
+}
+
+void
+gf_output_xrandr_change_backlight (GfOutput *output,
+                                   int       value)
+{
+  Display *xdisplay;
+  gint hw_value;
+  Atom atom;
+
+  xdisplay = xdisplay_from_output (output);
+  hw_value = round ((gdouble) value / 100.0 * output->backlight_max + output->backlight_min);
+  atom = XInternAtom (xdisplay, "Backlight", False);
+
+  xcb_randr_change_output_property (XGetXCBConnection (xdisplay),
+                                    (XID)output->winsys_id,
+                                    atom, XCB_ATOM_INTEGER, 32,
+                                    XCB_PROP_MODE_REPLACE,
+                                    1, &hw_value);
+
+  /* We're not selecting for property notifies, so update the value immediately */
+  output->backlight = normalize_backlight (output, hw_value);
+}


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